Open issues, by the harm they do¶
290 open, sorted into six tiers by the kind of harm each one does rather than by age or label. Most carry no label at all, so this ordering is the triage rather than a view onto one that already existed.
Derived 2026-10-14 by make issues, which reads the live issue list; titles and statuses are as of that date. Whether every open issue still has a tier is checked daily against the live list (make issues-check, in .github/workflows/issues.yml). The tier assignments below are committed in issue-tiers.toml and reviewed like code.
| Tier | What it means | Open |
|---|---|---|
| 0 | Breaks for a user | 23 |
| 1 | A gate that does not gate | 52 |
| 2 | A claim nothing measures | 63 |
| 3 | Measured cost | 24 |
| 4 | Cannot be reached | 43 |
| 5 | Convergence and hygiene | 85 |
12 of them have a pull request open against them; the Status column says which.
Tier 0 — Breaks for a user¶
Reproducible through an interface someone actually uses — a crash, a race, a link failure, or a stub documenting a signature the extension does not have.
| Issue | Summary | Status |
|---|---|---|
| #982 | Carrier lock, not acquisition, fails on ~1/3 of draws — and the coarse residual that kills it is asymmetric about zero | open |
| #1060 | settle_floor()'s 5/Bn budget does not cover slewing out a clock offset — measured 12.7x too short, and it makes a working receiver read as chance | open |
| #1284 | dll: the full-epoch and partial-correlation loops apply the loop filter with different gains behind the one steer | open |
| #1289 | async_dsss_receiver: the carrier slips cycles at 40 dB-Hz in both flavours, and decodes at 1% between them | open |
| #1443 | Stub and extension disagree on a call signature in 10 sacred fragments (jm apply has been saying so) | open |
| #1654 | Array parameters without a str_hint still let numpy parse a str (279 calls); the size-guard half is done | open |
| #1680 | track.Dll: at segments=1 the code loop's realised Bn is 56-79x the configured bn, with zeta ~0.05 (the integral path is scaled by SF, the proportional path is not) | open |
| #1958 | psd: adjacent bands both count the bin they share, so a partition overstates the whole (+3.0 dB on a DC tone) | open |
| #1992 | detector/detector2d/acq push silently drop the rest of the input once results fill (Python defaults lose 488 of 1000 samples) | open |
| #2011 | stream: producers ignore send failures (wfmgen exits 0 after dropping blocks; StreamSink.send returns an int) and close() loses the tail | in review (#2300) |
| #2014 | buffer: F32Buffer.consume(0) releases the outstanding borrow instead of nothing (jm releases codegen treats 0 as omitted) | open |
| #2029 | specan PullSource: one seq_account in the library, redeliveries keyed on num_delivered, and the fail-open holes #2020 left | open |
| #2038 | burst_capture: a full detection queue drops hits uncounted, and set_state writes before its checks | open |
| #2040 | wfm: StreamSink.send should raise on a refused block, not return an int (blocked on jm) | open |
| #2057 | adopt just-makeit#2187: a buffer's destroy() must not unmap under its live views (#2016 item 4) | open |
| #2063 | stream: Pull.close() during a GIL-free recv() tears down the context under the fetch (#2016's class, receive path) | open |
| #2095 | _SynthEngine.set_wtype(PN|BPSK|QPSK) on an engine created otherwise segfaults on the next step | open |
| #2110 | clib: dp_fmod_pos can return m (outside [0,m)), so dll_replica reads code[sf], one past the copy |
in review (#2305) |
| #2111 | resamp: set_state trusts a forged delay_head, and the next output reads past delay_buf | in review (#2301) |
| #2112 | float-domain holes from #2109's review: loop embedders without the bn/zeta predicate, det_verify_count int overflow, Dll lock geometry | open |
| #2142 | set_state trusts index fields from the blob: MovingAverage writes out of bounds; delay, wfm_synth read out of bounds | open |
| #2234 | BurstCapture: a tiled capture of a repeated chirp reports the wrong Doppler tile 13-78% of the time, start up to 7 periods off (pre-existing, D = 1 too) | open |
| #2291 | wfm_json.c converts JSON numbers to integer fields with bare casts: num_samples=1.5, -1, 232 and seed=-1, 232 are accepted (jm's hardened reader never reaches Composer.from_json) | open |
Tier 1 — A gate that does not gate¶
This repo's own doctrine turned on itself: a claim nothing runs is prose. Each is a check that reports green because it cannot see the thing it names — several confirmed by sabotage.
| Issue | Summary | Status |
|---|---|---|
| #682 | test: §12's blob-determinism check compares in float at 1e-9, so it asserts float-equality not bit-identity | open |
| #692 | characterization: the category has no envelope gate and no figure-freshness gate | open |
| #696 | docs-check and docs-drift-check keep two copies of the generator --check list, and CI only runs one | open |
| #716 | Validation figures and CSVs are gated by nothing — validate-check only compares rendered markdown | open |
| #736 | check_doc_face_parity compares Examples/Raises only, so the divergence that shipped was invisible — and lock-detection.md is stale on CarrierNda | open |
| #744 | check_lifecycle: verify the phase-9 example too, by having each report cite it | open |
| #847 | Phase 4's sabotage rule is stated on three pages and executed by none | open |
| #871 | check_stimulus_sources: a hand-computed Es/N0 sigma is an invented level convention, and no marker catches it | open |
| #919 | libdoppler.a's declared link line is only checked where pthread is a separate library | open |
| #936 | check_stimulus_sources: the pulse marker's C branch cannot fire — it is matched per line against a signature clang-format always splits | open |
| #967 | check_doc_paths does not check build/ paths, so documented invocations can rot silently | open |
| #1000 | 46 header @code examples reach no stub, so no gate executes them — and one of them was wrong | open |
| #1010 | 19 Python benchmarks take the benchmark fixture and never call it |
in review (#1972) |
| #1028 | Nothing enforces the UBSan alignment ratchet — it grew 821 → 853 unnoticed | open |
| #1118 | StreamSink cannot be certified through its Python face: validate-check runs after nats-down, and a report must render identically either way | open |
| #1129 | wfm_plan: nothing can observe whether a restore took the cached fast path, so a silent fallback to full rebuild would pass every test | open |
| #1201 | Parallel make targets re-sync one shared venv against each other: a plain uv run removes the docs group under a running docs-check, and a make pyext sync broke a commit hook's mkdocs build |
open |
| #1306 | make gallery keeps two hand-maintained lists that must agree, and a script in only one orphans its PNG | open |
| #1328 | A spot check's budget is wall-clock; gate its WORK COUNT instead | open |
| #1350 | tests/install/rust-test.sh has no execution home — nothing runs it, which is why its broken make goal survived | open |
| #1366 | bench: acc_f32's madd/madd2d/get/dump benchmarks measure a no-op | in review (#1980) |
| #1377 | tests-ssot's private-RNG check is blind to libc generators (rand_r, random, drand48) | open |
| #1388 | Sanitizer jobs skip all 23 validators — a heap overflow in validate_receiver_pullin shipped because of it | open |
| #1566 | validate_capture_dwell_pd fails make validate: its full sweep asserts nothing | open |
| #1637 | wfm: the Field corpus cannot see a constant field (why it missed #1624) | open |
| #1657 | build: -Wswitch is never an error, so an unhandled enum value warns and ships | open |
| #1663 | DP_NODISCARD: an ignored refusal is invisible to every compiler flag | open |
| #1671 | gallery-scripts-check cannot see a plot whose file name the script derives rather than quotes | open |
| #1770 | A mypy gate: doppler's own code type-checked against its stubs, ratcheted | open |
| #1772 | release-freshness-check: one changed asset passes every changed gallery script | open |
| #1900 | make test-python ends with SIGTERM (Error 143) at ~50% on a different test each run | open |
| #1915 | Two manifests restate acq_result_t's fields by hand, and drifted the first time the struct grew | open |
| #1920 | tests-ssot: the macro-stamped framer is outside the serializable/streaming gates | open |
| #1977 | make bench exits 0 when its Python half fails to collect (upstream: just-makeit#2180) | open |
| #1982 | Release and CI-image container builds pull stock FROMs inside BuildKit, so #1979's pre-pull cannot reach them | open |
| #1994 | ratchets: check_warnings and check_alloc_helpers switch off when their list is absent at the base; move both onto _gitbase.added_since_base | open |
| #1997 | docs sh gate: the holes an executed fence still has (exit-status-only refusals, substitutions, pipefail 141, shadowing, the wfmgen/cat trigger) | open |
| #1999 | release: the pre-publish smoke-wheel installs only cp312, so a per-Python wheel defect is found after PyPI is written | open |
| #2004 | build: a parent compiler change drops make build's and make pyext's -D options; pyext then exits 0 having built no extensions (likely #1996) | open |
| #2012 | stream: core NATS subscribers lose frames across disconnect/reconnect with no signal; no error/disconnect callbacks, and the docs describe a slow-consumer drop that cannot happen | open |
| #2013 | stream: the 'durable' JetStream tier deletes backlog older than 1 h (MaxAge) for a lagging consumer, while stream.h and stream.pyi say it cannot / keeps frames forever | open |
| #2059 | validation reports: _self_check resolves §N.M but never F |
in review (#2147) |
| #2062 | adopt just-makeit#2188 (jm_bench.h drops entries past 32 silently); restore U1's hop nfft/2 bench rows; guard the cap meanwhile | open |
| #2077 | state: verify #2022's C-only setters with a C-side twin of the mutator gate | open |
| #2078 | state: 11 serializable classes have no test_state_serialization CASES row, so #2022's gate cannot probe their mutators | open |
| #2093 | state gate (#2022): what #2085's review leaves -- refused-restore probe, NONDETERMINISTIC masking, C-only discovery | open |
| #2182 | Validation reports restate header claims by hand; nothing ties a claim to its header sentence or its verdicts (deferred design) | open |
| #2205 | A changelog fragment without its leading "- " passes every PR gate and fails only at release assembly | open |
| #2218 | acq: a burst engine's measured per-dwell Pfa is 2x its target (1.97e-3 against 1e-3, ZC 127 x 16, D = 5) | open |
| #2224 | acq: a tiled burst engine's pd_predicted/pd_burst price W·D·cb samples, not D·cb -- it reports ~1.000 (0.62-0.94 measured) and underpowered stays clear where it should warn | open |
| #2235 | acq: since #2230, a tiled burst search at D > 1 reports raw hits a whole tile off, off the ridge, for band-limited repeated chirps (9-12% of hits; D = 1: none) | open |
| #2256 | ci: ECR Public's anonymous DATA quota ('toomanyrequests: Data limit exceeded') outlasts stock-pull.sh's 75 s retry window | open |
Tier 2 — A claim nothing measures¶
A header, report or design doc asserts a number or a behaviour that no test and no validator establishes. Not wrong — unestablished, which is a quieter problem.
| Issue | Summary | Status |
|---|---|---|
| #645 | MpskReceiver timing loop: two measured default changes not taken — DTTL for BPSK/QPSK, and bn_timing's knee | open |
| #646 | The resamp double-emit fix is pinned at RateSync level only — MpskReceiver, where it was observed, has no regression test | open |
| #684 | test(symsync): the embedded timing NCO never states its rate, and the gate that would catch it never landed | open |
| #689 | DSSS test captures inject 3.01 dB less noise than their cn0_dbhz label, and correcting it exposes non-monotonic acquisition | open |
| #711 | agc: the header promises level-independent settling, and §6 measured otherwise | open |
| #732 | carrier_nda: the acquisition contract needs the seeding number (|df| \<= bn/M), and the arm's absolute wall it does not name | open |
| #733 | carrier_nda: the half-symbol arm's 1/2 + 1/(M+1) coherent gain does not reproduce |
open |
| #734 | carrier_nda: the lock statistic's H0 spread is M-independent per look, and is not through the arm | open |
| #751 | The DTTL's "degrades faster at low SNR" claim is unmeasured — the RateSync validator adds no noise | open |
| #754 | MpskReceiver: false lock (negative lock, correct frequency and timing) at any m_out mpsk.md §8 would not derive — the cost of #644 | open |
| #814 | MpskReceiver: six header claims are covered by nothing in either language (F9) | open |
| #830 | The MpskReceiver validation report characterises the complex face only — the real face has no Python evidence | open |
| #831 | MpskReceiver: state the link and the requirement, derive the loops (mpsk-refactor 4.3) | open |
| #855 | The timing rate residual doubles with a carrier offset present, and nothing characterises the coupling | open |
| #932 | symsync_lock's published false-alarm rate moves 1.5x with -ffast-math, and its header states a conclusion neither value supports | open |
| #962 | Streaming: CI8/CI16 have no round-trip test, and the format names are unprefixed in a public header | open |
| #975 | The 100 ms interrupt bound is published as a guarantee and measured only at idle (io-termination §5) | open |
| #997 | acq: the non-coherent Pd model is NOT unreliable past a few hundred looks — measured monotone to 1024, \<1 sigma to 512 | open |
| #1064 | acq: realized Pfa is ~1.65x the requested target — the threshold's N omits the Doppler interpolation the gate's maximum runs over | open |
| #1128 | wfm.Plan: a ranged-gap scene draws a different length per seed, and render()'s documented Returns says it does not | open |
| #1133 | gold: the CCSDS constants are certified against the published vector, not against the document | open |
| #1191 | acq peak list: a strong emitter's same-code-phase sidelobes pass the two-epoch rule under long non-coherent integration | open |
| #1275 | async_dsss_pool: a neighbour within a kilohertz crossing a departed receiver's code phase at a chip or two a second is, to that receiver, a return — the pool's zone is where it can be told apart | open |
| #1307 | The burst-demod test codes have 0.78 autocorrelation sidelobes, so the tests tolerate timing error no real waveform would | open |
| #1311 | burst_capture k_lo is 3*reps+2 by assertion, not derivation, and it inflates the merge radius and min_gap by 1.7x | open |
| #1475 | 30 serializable objects are outside the shared state round trip: blob determinism unchecked | open |
| #1530 | burst capture: min_gap is conservative once emission follows stream time -- re-derive or re-measure it | open |
| #1677 | CarrierNda: the loop gains ignore the discriminator's slope of 2, so the realised noise bandwidth is ~1.67x the configured bn | open |
| #1775 | wfm_frame: decide bounded vs unbounded field list and contiguous vs bitmask cover | open |
| #1777 | Certify wfm_data and dp_hash64 in the Validation Log | open |
| #1814 | bench_wfm_synth::test_bench_step can calibrate to 1 iteration and time the clock, not step() | open |
| #1867 | DopplerChannel is not certified: no validation report (adding-algorithms phases 7-8) | open |
| #1874 | Apply the signed-count refusal (DP_ERR_INVALID) to the other count-returning calls that collide with a valid 0 | open |
| #1896 | Chunk-invariance: burn down the ratchet (49 stateful streaming objects have no test) | open |
| #1921 | docs: assert the printed-output text fences the snippet gate does not check | open |
| #1928 | Audit: SIMD kernels whose scalar tail rounds differently from the vector body (the #1893 pattern) | open |
| #1967 | bench(carrier_acq): the C benchmark credits 65536 samples to a search that stops after one block | open |
| #1969 | bench(interp_table): the C benchmark names its rows nearest/linear/cubic; the methods are floor/nearest/linear | open |
| #1970 | bench(ber_meter): the C stimulus maps symbol indices through a Gray-label constellation | open |
| #1971 | bench(pn): the C benchmark's polynomials are not maximal-length in dp_pn's convention | open |
| #1991 | bench: stamp the measured TREE in doppler_meta, so bench-commits-check verifies provenance offline, not just reachability | open |
| #2001 | Stubs: none_on_empty readouts lack | None until just-makeit#2183 ships | open |
| #2039 | stream: an end-of-stream names no stream, so any publisher's EOS on a base ends every subscriber there | open |
| #2053 | psd: windows are symmetric (N-1), not the periodic form spectral estimation uses -- Hann n=2 is zero, -82 dBc leakage, BH ENBW 2.036 | in review (#2140) |
| #2071 | burst_despreader: create() and set_bn_* pass an unvalidated bn to dp_loop_filter_init, which trusts its embedders to validate | in review (#2306) |
| #2079 | state (#2022 family 1): LO/NCO norm_freq is not in the blob, and every tuned composition inherits it | open |
| #2080 | state (#2022 family 2): a rate is state and a reject key -- RateConverter, Resampler, CIC restore into another rate | open |
| #2081 | state (#2022 family 3): correlator and detector set_ref / set_threshold values are not serialized | open |
| #2082 | state (#2022 family 4): wfm_synth's setters are not serialized | open |
| #2083 | state (#2022 family 5): receivers' and meters' setters -- RateSync bn and lock, BerMeter truth/align, acq carrier, DSSS lock_raw | open |
| #2087 | burst_capture: configure_search_raw() mid-stream resets acq position but not samples_fed, so every later epoch is early | open |
| #2104 | state: composite set_state restores children in sequence, so a refused later child leaves the parent half-restored | open |
| #2114 | specan PullSource: a read returned 10 of 1200 frames and timed out (intermittent, CI Python 3.10) | open |
| #2143 | bench(psd): power_onesided is timed one call per round, so its 1,024 cell moves in 7% clock steps | in review (#2146) |
| #2159 | specan: the source/engine handshake is written three times and the copies disagree | open |
| #2187 | PSD kernel extraction is byte-identical on x86 only; run the before/after dump on aarch64 | open |
| #2188 | docs/specan/chirp_frames.json is stale and no test reads it | open |
| #2200 | Published Python benchmarks under ~300 ns carry the timer's overhead whenever pytest-benchmark calibrates one iteration per round | open |
| #2211 | PSD kernel follow-ups from #2209's review: ENBW off the kernel, meters carry an unused ring, the identity dump's gaps | open |
| #2219 | rate_hypotheses follow-ups: tiled Monte-Carlo, per-event rate, DsssBurstReceiver, a deterministic guard for refine | open |
| #2229 | acq: a dwell straddling a burst's start with under one epoch of signal reports its Doppler up to ~D/2 bins off (first hit only; tiled and narrowband alike) | open |
| #2236 | acq: resolve a tiled burst's tile alias over a delay window and re-estimate the code phase at the resolved hypothesis (ridge-aware; the fix for the chirp raw-hit errors) | open |
| #2289 | nogil: ten methods now really release the GIL (jm 0.105.0) and none has a thread test for a shared object | open |
Tier 3 — Measured cost¶
Performance findings with a number attached. Filed, not fixed; each names the measurement that produced it.
| Issue | Summary | Status |
|---|---|---|
| #502 | Performance: prepare() scales O(n) with users instead of sharing RRC filters | open |
| #845 | make coverage's -O0 tree costs 6 minutes, and -O2 takes two bit-exactness tests red under CI's clang 18 | open |
| #890 | snr: both _db_series forms are O(nwindow) — they re-scan the window at every sample | open |
| #896 | Multi-config C benchmarks charge the clock ramp to whichever config runs first | open |
| #905 | arith: the Q8 shift kernels are slower than their Q15 twins, not faster | open |
| #1006 | DsssBurstReceiver: non-overlapping frames cost a band of burst offsets NEAR the knee (77% for a good code) | open |
| #1173 | BurstCapture: the per-burst window copy is measured only at a 20 kB window, and it scales with burst_len | open |
| #1247 | acq: bound the block depth D to a smooth slow-time length -- 308 = 4·7·11 at the operating point, and the transforms are now the searcher's cost | open |
| #1269 | AsyncDsssReceiver builds its refine and track chains on every seed and hand-over — the pool's "sized once at create" holds for the pool, not its receivers | open |
| #1309 | acq interpolates Doppler but not code phase: nx_out is 0, so the burst start is quantised to one sample | open |
| #1313 | The two long sweeps over BOUND objects sit in native/validation/, not characterization/ — 60% of every PR's C suite | open |
| #1320 | wfm_writer raw cf32 write is ~7% slower than v0.47.0 with byte-identical source | open |
| #1342 | wfm_writer integer writes are ~3x slower since #1117: every component goes through the single-sample converter step | open |
| #1370 | coverage: the sweep validators are excluded from instrumented ctest, then run again through pytest — 28 of the job's 42 minutes | open |
| #1381 | perf: three benchmarks flat for three releases moved in v0.51.0 (wfm_synth step 2.3x, acc_trace 1.5x, wfm_writer ci16 1.26x) | open |
| #1697 | DSSS burst receiver: TX/RX usability and speed findings (frame_syms, naming, refine_span, error drift, events padding, 4 MSa/s) | open |
| #1816 | ci-changes: a release's waiver file counts as source, so the release commit runs the full matrix | open |
| #1856 | resamp: the ctrl step truncates to 32 bits, a constant ~8e-11 rate bias (0.2 Hz at 2.5 GHz) — may not matter | open |
| #1937 | bench: wfm Reader Python cells moved +11% / +21% in v0.65.0, one cell per build; not the binding, cause unmeasured | open |
| #1978 | acc_f32/acc_cf64: add2d and madd2d are private copies of steps and madd, and miss the SIMD path (4.4x) | open |
| #2006 | acc_f32/acc_cf64: madd (and so madd2d) is a serial scalar reduction; give it steps' SIMD path | open |
| #2074 | PSD's dB conversion is 70-83% of a frame: decide on a cheaper conversion or a power-first default (Spectrogram U3) | in review (#2117) |
| #2094 | PSD must blaze: the per-frame fold on the SIMD accumulate path, measured against the FFT | open |
| #2287 | acq: per-dwell Pfa at rate_hypotheses n = 3 and n = 4 reads above the target (1.20e-3, 1.07e-3 at 1e6 dwells), with #2218 | open |
Tier 4 — Cannot be reached¶
The capability exists in C and no caller can get to it, or it exists and nothing tells anyone it does.
| Issue | Summary | Status |
|---|---|---|
| #545 | Reading a capture is undiscoverable and undocumented — enriched view over wfm_reader + a guide | open |
| #658 | AGC: a seed that cannot fire on silence — buy back the acquisition spread | open |
| #661 | RateSync reports over-drive but is silent about under-drive | open |
| #668 | RateSync.clipped conflates transmitter over-drive with noise-crest clipping | open |
| #735 | carrier_nda_get_nco_freq is unreachable from Python and telemetry — both published frequency views are the same view | open |
| #750 | AGC has no signal-presence notion: on a noise floor it applies 59.9 dB and the next burst arrives hot | open |
| #813 | fec: virtual fill (4.4.2 shortened codeblock) is not implemented, so a frame off the 223*I grid has no path through the outer code | open |
| #844 | stream: ten public send entry points have ZERO execution — the CI8/CI16/CF32 slice of PUSH/REQ/REP, including the durable JetStream tier | open |
| #865 | doppler reads 131.0-B-3; B-6 is current — and it changed the randomiser default | open |
| #959 | Streaming API shape: role typing, ack ergonomics, control-plane parity, and an undocumented size ceiling | open |
| #966 | Pull workers have no subscription drain, so leaving a queue group drops in-flight work | open |
| #981 | Composer cannot SET seed_advance from Python — the object face and the JSON face disagree | open |
| #998 | acq: no property reports the SEARCHED Doppler reach, and doppler_span_hz reads like it does | open |
| #999 | acq: a push shorter than one dwell returns nothing, and nothing says how long a dwell is | open |
| #1035 | The stream carries complex only; a real sample type is refused at the sink | open |
| #1057 | Three public classes are reachable only from the generated API reference — and two are the odd ones out in a 13-member family | open |
| #1073 | A Bits type with methods, rather than six free conversions threading out-parameters | open |
| #1202 | EventLog: the standalone finalize has no Python face — a crashed run's sidecar can only be rendered from C | open |
| #1206 | wfm compose: sources in a segment cannot be shifted independently — add a per-source start offset | open |
| #1229 | AsyncDsssReceiver: one telemetry attach should forward to the embedded Acquisition | open |
| #1294 | wfm: the compose Synth and the Plan's DSSS source have no per-frame code-only window; only _SynthEngine.set_dsss_window has it | open |
| #1364 | Windows (clang-cl): what remains after #1360 — the C library compiles, the link and the benchmarks wait on jm | open |
| #1372 | Push.send_eos reports no transport detail, so a full store and a deleted stream are indistinguishable (send distinguishes them) | open |
| #1405 | vcpkg: a static triplet needs a single-linkage build | open |
| #1431 | stream.Push.send_eos() is bound at runtime but absent from stream.pyi | open |
| #1481 | acq burst path: no code-Doppler (time compression) compensation, and it bites wideband preambles hardest | open |
| #1571 | ddc: the plain DDC's CIC is uncompensated and cannot be asked to compensate (flat only to ~0.1 fs_out) | open |
| #1692 | wfm: FrameDesc has no from_json constructor (a description is built from JSON only as a scene's "frame" key) | open |
| #1722 | wfm: Plan's refusals carry no reason (const char **why, #1686) | open |
| #1776 | wfm: the composer has no state triplet | open |
| #1780 | wfm: no Python face serializes a synth pulling from a data source | open |
| #1803 | Python face for dp_syncword_search, plus M-ary (QPSK 4-fold) ambiguity resolution | open |
| #1828 | Install the benchmark/test harness headers so downstream projects can use them | open |
| #1829 | Stream subscriber: expose frame-gap and duplicate accounting from the wire header | open |
| #1865 | Plan: a background=True source with Doppler is refused (fold sums it before any channel can run) | open |
| #1894 | Streaming spectrogram: a Spectrogram object over the ring's framed face (PSD per frame, no averaging) | open |
| #1966 | Spectrogram: separate the frame length from the transform length (non-power-of-two nfft) | open |
| #1983 | MpskReceiver(agc=0) refuses a prefix every probe it registers fits, on its disabled AGC's names, and leaves dead slots | open |
| #1987 | AccTrace.alpha: the setter refuses silently in Python until just-makeit#2182 ships | open |
| #2024 | framer: no Python face, so the framed face shipped without the Python example and bench that phase 9 owes | open |
| #2031 | stream: reassemble more than one chunked frame at a time, so two publishers on a subject do not abandon each other's frames | open |
| #2089 | stream: the wire header gains a sender/epoch id (owner decision on #2017) | open |
| #2183 | PSD reads back hop, window, beta, pad and alpha, and PSD.mode by name | open |
Tier 5 — Convergence and hygiene¶
Duplication, stale pins, harness drift, and the long tail. Real, none of it urgent — and the tier that grows when the ones above it are held.
| Issue | Summary | Status |
|---|---|---|
| #535 | DsssReceiver: derive segments, adopt async's squaring carrier combine | open |
| #630 | GitHub Actions: every pin is stale (up to 5 majors), and the Node 20 forcing we rely on is temporary | open |
| #639 | CI is make-SSOT for every gate — the drift is all in the plumbing layer | open |
| #651 | Makefile diverges from the shared convention: no PYTEST command variable, and make test runs only ctest |
open |
| #655 | UBSan alignment ratchet: 821 misaligned member accesses | open |
| #678 | util: no C test harness, and square_clip.c duplicates its own body | open |
| #685 | feat(telemetry): attaching a pipeline takes one call per object; the one-call form was written then lost to a redesign | open |
| #694 | make gallery: GALLERY_CHARACTERIZATIONS is plural but the recipe only works at n=1 | open |
| #712 | make docs-check gives a wall of false broken links when site/ is absent | open |
| #720 | clang-tidy: 172 findings block gating at zero — triage, judgement, and config, in that order | open |
| #721 | Three helpers are byte-identical across modules; zeropad_1d is the one that will drift | open |
| #730 | jm_test.h is deliberately not vendored: a jm 0.58.0+ test scaffold needs converting to dp_test.h | open |
| #756 | Retire wfm_synth_mls_poly() in favour of pn_mls_poly() | open |
| #802 | Cleanup plan for the test/measurement layer: a convergence plan, not a deletion plan (measured) | open |
| #856 | design/bpsk-receiver is superseded by the collapse and the view — triage its design doc, then close it out | open |
| #893 | bench: six benchmarks are declared at a level jm was never told about (three below an object, three inside c_deps) | open |
| #894 | Certify viterbi as its own object, rather than inside conv |
open |
| #900 | Audit: what is left to declare, and the capabilities with no Python path | open |
| #906 | The function-only modules are benchmarked in C but not in Python | open |
| #933 | Migrate the four inline LCG streams off scripts/.private-rng-ratchet | open |
| #937 | Four validation harnesses hand-roll AWGN instead of calling the library's awgn, and nothing says they shouldn't | open |
| #974 | The dp_stream_* interrupt aliases were meant to be removed, and nothing tracks it — our own binding still uses all of them |
open |
| #1009 | Certification reports open with caveats, not orientation — a caller arriving cold has no entry point | open |
| #1114 | noise_steps matches steps()'s AWGN chunking to defend against a path that no longer exists, and nothing would catch its return | open |
| #1123 | wfm_reader_header_tag neither bounds-checks nor documents that it must not be — the only one of four enumerators that does neither | open |
| #1186 | changelog.d fragments using reference-style links break mdformat once assembled next to another entry | open |
| #1188 | native/validation: move every validator's --check onto dp_test.h | open |
| #1228 | docs(gallery): a page for acq_surface_demo — the searcher's surface and probes | open |
| #1242 | just-makeit.toml carries a stale "NOT adopting c_style" block eleven lines below c_style = "clang-format" |
open |
| #1299 | The "slot by both coordinates" phrasing repeats in six more places, including a generated results.md | open |
| #1302 | Burn down docs/.design-page-baseline: 24 design-page record sections across 15 pages | open |
| #1305 | form_partials: the last partial runs long when est_segments does not divide acq_sf | open |
| #1380 | cvt: two of twelve converters abort on OOM, the other ten raise MemoryError | open |
| #1396 | test-snippets: the PAGE= usage example names a page that no longer exists | open |
| #1398 | validation: 28 private _csv helpers in 8 variants -- one Report.csv() that owns the write flag | open |
| #1425 | ring buffer: does burst_capture's position-addressed history belong in the ring? (at / pos / restore) | open |
| #1463 | stream: _ready_queue timed out after 10s on a server-acked probe (test_push_pull_header_all_fields, py3.13 CI) | open |
| #1465 | Windows: POSIX-only test carve-outs in the Python suite | open |
| #1489 | test_compose: live three-block chain can miss its 10 s recv budget under a parallel suite | open |
| #1495 | Acquisition (continuous): take the samples model the burst engines now use | open |
| #1496 | Two library copies of the chip → ±1 rule: wfm.bpsk_map and cvt.bin_to_nrz | open |
| #1547 | Packaging: the relocatable .pc defeats pkg-config's system-dir filtering, and jm can now own the install section (dry run attached) | open |
| #1550 | carrier_acq's pfa is per-test while acq's is per-search: same name, two meanings | open |
| #1570 | docs: types.md and cvt/iq.py name RTL-SDR as a CF32 / int16 source (it streams cu8) | open |
| #1577 | Vendored symbols: extend the dp__v_ respell to macOS, Windows and the shared libraries' export tables | open |
| #1608 | Docker image re-resolves the jm pin against PyPI instead of installing the locked wheel | open |
| #1638 | check_wfmgen_flag_docs: the checker cannot see short flags, so -o is waived | open |
| #1651 | Header @code backlog: 79 C examples do not compile or run (ratchet from #1635) | open |
| #1665 | A failure message can name a make target that no longer exists | open |
| #1667 | Patch-coverage gate has no way to name an unreachable enum case, so a -Wswitch fix alone is always red | open |
| #1676 | package-linux sends the builder's dnf output to /dev/null, so a failed rpm-build install cannot be diagnosed | open |
| #1694 | wfm: Source.frame's getter returns JSON that its setter refuses; drop it when just-makeit#1753 lands | open |
| #1698 | wfm_compose: 105 source/segment field docs are declared twice (header comment + manifest doc), gated equal; derive from the header only (jm#1703) | open |
| #1723 | wfm: one copy/free pair for wfm_source_t's arrays (wfm_plan.c duplicates wfm_compose.c's) | open |
| #1728 | ci: two jobs upload an artifact named coverage-report in the same run | open |
| #1740 | curl without --fail outside the image: install-deps pipes get-jb.sh into bash (Makefile, standard.mk), release-watch.sh | open |
| #1774 | ber.FrameMeter takes Frame.check()'s result, not a bare CRC outcome | open |
| #1800 | rx: object constructors lose dp_wfm_frame_desc_rx's reason; needs an object-level create_why in jm | open |
| #1815 | Adopt Keep a Changelog 2.0.0: Breaking marker within its type, an optional version summary, and a gate for six types | open |
| #1826 | ci: a docs-only diff should run the docs-reading tests, not the full Python job | open |
| #1850 | check_warnings.py docstring still says '99 hand-owned fragments' after #1446 adoptions | open |
| #1853 | PN(length=1) builds a no-feedback register that wfm_synth and wfm_frame refuse | open |
| #1886 | Adopt fft, wfm_synth, dp_tlm and dp_tlm_capture to jm: held on extra_methods and just-makeit#2059 | open |
| #1887 | scripts/.warnings-exempt floors at 2: stream_ext.c and resample_ext_extra.c are not jm-adoptable | open |
| #1897 | Composite set_telemetry leaves dead probe slots when a child refuses after the parent registered | open |
| #1906 | Refresh prose that still says the interrupt flag is volatile sig_atomic_t (follow-up to #1027) | open |
| #1912 | Validation: migrate conv, rs and ccsds_tm onto _validation_common.harness_blocks (three private copies of the harness CSV parser) | open |
| #1957 | psd: scalar readouts return 0.0 for 'no measurement', a value a real measurement also returns | open |
| #1984 | scripts: seven gates strip C comments with a /*.*?*/ regex that does not know about string literals | open |
| #1990 | examples/downstream-jm's two sacred capture fragments miss jm's binding fixes (the last jm-apply binding warnings) | open |
| #1993 | docs gate: the sh fence's src/ -> |
open |
| #2005 | build: the vendored nats.c custom command has no DEPENDS (a vendor bump never rebuilds) and does not forward the ccache launcher | open |
| #2023 | dp_chunk_inv.h: a stop-and-resume mode, so the detectors' ci_*_resume twins (and acq's) are one harness | open |
| #2027 | stock-pull.sh --dockerfile pulls every stock FROM, whatever stage the build targets | open |
| #2030 | lint: a make target a uv-less CI job runs may not call uv (nats-vendor-fresh-check failed train #2026) | open |
| #2032 | stock-images gate: a shell build-arg value is never resolved, and a workflow pre-pull's scope spans jobs | open |
| #2037 | dp_tlm: read the telemetry ring through its API, not head/tail/mask (#1426 baseline) | open |
| #2045 | Makefile:1674 says gen-c-api-check runs via docs-invariants; it runs only as its own pre-commit hook | open |
| #2054 | ring gate blind spots, burst_capture's plain resets, and one acq drain helper (#2042 leftovers) | open |
| #2066 | package-linux: the builder image's dnf failure is discarded (docker build -q >/dev/null), and dnf has no retry | open |
| #2108 | One dB conversion: every remaining log10 site switches to dp_power_to_db_f32 or says why it keeps double | in review (#2126) |
| #2189 | specan nits from the #2180 review: dBm import direction, step() naming, fastapi skip | open |
| #2254 | examples/: 33 print calls in 16 scripts hold characters cp1252 cannot encode, so they crash on a redirected stdout on Windows | open |
| #2279 | hand-owned fragments: six uint64_t / size_t parses use the masking K (dp_tlm x4, wfm_synth pn_poly and span), so a negative or over-wide int wraps | open |
| #2286 | wfm_synth .prod(): ~60 lines of hand-written glue and a hand-edited sacred fragment, and no public Synth.prod, until jm 0.105.0 | in review (#2297) |
How an issue gets its tier¶
By asking what goes wrong if it is never fixed, and nothing else. Age does not raise a tier and neither does effort — a one-line fix that stops a crash outranks a week of hygiene. The tiers are deliberately about harm rather than cost, so that the order to work in falls out of the table instead of being argued each time.
The daily make issues-check run goes red if an open issue has no tier or a tier names an issue that is closed — so this page cannot rot in either direction without saying so. make issues refuses while an issue is untiered, and drops a closed issue's row itself.