milestone 18: collapse duplicated infrastructure into shared listener core, crud list helper, resource shells, transport race, name and line helpers, ui modules
This commit is contained in:
+14
-12
@@ -64,37 +64,39 @@ The repo's history proves this theme's stakes: commit 35f2324 fixed a process-ki
|
||||
|
||||
## Theme 2: Duplicated infrastructure, drift already underway (11 findings)
|
||||
|
||||
- **[high] src/server/dot_server.zig:3 — connection-server lifecycle machinery duplicated across four listeners.** *(merged: tcp_server.zig:218, dot_server.zig:180, dot_server.zig:452 — four findings describing the same debt)*
|
||||
> Status (m18): all 11 findings closed by milestone 18 (specs/milestone-18.md).
|
||||
|
||||
- **[high — CLOSED m18] src/server/dot_server.zig:3 — connection-server lifecycle machinery duplicated across four listeners.** *(merged: tcp_server.zig:218, dot_server.zig:180, dot_server.zig:452 — four findings describing the same debt)*
|
||||
tcp_server, dot_server, doh_server, and web/server each carry a private copy of the same slot pool, claim/finish/beginShutdown protocol, accept loop with error mapping, cancel-protection dance, and select-based idle race. `race`/`expire`/`readPrefix`/`readBody`/`writeReply` are byte-identical between tcp and dot; dot_server's header says outright "This file mirrors tcp_server.zig". Drift is live: the counter is `accepted` on TCP vs `connections` on DoT/DoH; `decideClaim` is written two ways for identical semantics; doh_server ships a module-level `serve()` that nothing calls (dead divergent glue); the milestone-10 review log records a TLS-context leak fixed by hand-porting a pattern between copies — the predicted failure mode has already fired once. This is the most invariant-heavy concurrency code in the repo (mutex-ordered shutdown, cancel-protection windows), copied four times with no compiler help. The milestone-10 "mirror tcp_server's shape" ruling was a parallel-session build instruction, not an architectural decision against extraction. Fix: one shared listener core (comptime-parameterized slot pool + claim/shutdown + accept loop + race) with the per-connection serve function and TLS handshake stage as variation points; at minimum, extract race/expire/readPrefix/bump/Stop into src/server/ helpers.
|
||||
|
||||
- **[medium] src/storage/repositories/groups_repo.zig:29 — list/free/errdefer scaffolding hand-rolled 18 times across seven repos.**
|
||||
- **[medium — CLOSED m18] src/storage/repositories/groups_repo.zig:29 — list/free/errdefer scaffolding hand-rolled 18 times across seven repos.**
|
||||
Every repo re-implements prepare → ArrayList → the load-bearing errdefer ordering → per-column columnTextAlloc → append, plus a matching freeX and allocation-failure test. Decisive detail from verification: the milestone-4 spec's own reference sample declares the two errdefers in the reverse (fatal, use-after-free) order — every implementation silently corrected it, and a future repo copied from the spec reproduces the UAF unless its author also copies the test. Fix: a shared comptime helper in crud.zig (`list(RowType, sql, readRow)` plus paired free) so the memory-safety choreography exists once.
|
||||
|
||||
- **[medium] src/web/handlers/groups.zig:168 — CRUD shell duplicated across seven handler files; the 4-line configDb switch appears 40 times.**
|
||||
- **[medium — CLOSED m18] src/web/handlers/groups.zig:168 — CRUD shell duplicated across seven handler files; the 4-line configDb switch appears 40 times.**
|
||||
list/get/remove handlers and the applyCreate/Update/Delete shells are identical modulo repo function and message string, and the copies have already diverged in lock scope and reload flavor (local.zig holds the lock through publish; blocklists adds pruneFiles; groups alone reads back under the lock). Fix: a comptime resource descriptor (repo fns + conflict message + reload flavor) generating the shells, keeping the genuinely different per-resource decision functions hand-written. Note the four distinct reload flavors make this less mechanical than the finding implies.
|
||||
|
||||
- **[medium] web/src/features/local/RecordsTab.tsx:25 — RecordsTab/ZonesTab are structural copy-paste, and Tailwind class constants are re-declared across 12 files.** *(merged: RecordsTab.tsx:14, the class-constant half of the same debt)*
|
||||
- **[medium — CLOSED m18] web/src/features/local/RecordsTab.tsx:25 — RecordsTab/ZonesTab are structural copy-paste, and Tailwind class constants are re-declared across 12 files.** *(merged: RecordsTab.tsx:14, the class-constant half of the same debt)*
|
||||
The two tabs share identical FormState/openForm/onSubmit/onDelete plumbing, mutation trio, and byte-identical class constants; the focus-visible literal appears in 21 files, and drift has already shipped: PrefixesEditor and GroupsPage inputs omit the focus-visible outline entirely, silently violating the milestone-9 accessibility floor. The project's own precedent (InlineError was hoisted and deduplicated during milestone 9) says this is house style left unapplied. Fix: shared form-shell/table-shell components and a ui/ directory (or one classes.ts module).
|
||||
|
||||
- **[low] src/local/forward_client.zig:260 — race-against-deadline and stream-error-unwrap scaffolding copied across three transports.**
|
||||
- **[low — CLOSED m18] src/local/forward_client.zig:260 — race-against-deadline and stream-error-unwrap scaffolding copied across three transports.**
|
||||
The Outcome union + expire() select race is byte-identical between pool.zig and forward_client.zig (`fn expire(` appears 14 times tree-wide); the cancel-protected close helpers and mapPhase/sendFailure/receiveFailure repeat between dot_client and forward_client. The predicted drift is already real: doh_client has none of the unwrap (see Theme 3). Fix: shared helpers in transport.zig, which already owns the framing helpers and error taxonomy.
|
||||
|
||||
- **[low] src/local/records.zig:197 — normalizeName duplicated verbatim in forward_zones.zig, with a third partial copy in dns_cache.buildKey.**
|
||||
- **[low — CLOSED m18] src/local/records.zig:197 — normalizeName duplicated verbatim in forward_zones.zig, with a third partial copy in dns_cache.buildKey.**
|
||||
Divergence risk is not hypothetical: filter/rules.zig and filter/compiler.zig already carry near-variants with differing byte-rejection policies. Fix: move it to dns/name.zig next to fromText (both callers already import it).
|
||||
|
||||
- **[low] src/filter/compiler.zig:64 — the subtle takeDelimiter/StreamTooLong/discard streaming loop exists twice (compile and manager.collectSample).**
|
||||
- **[low — CLOSED m18] src/filter/compiler.zig:64 — the subtle takeDelimiter/StreamTooLong/discard streaming loop exists twice (compile and manager.collectSample).**
|
||||
Both copies are correct and tested, and the cited behavioral differences are intentional per call site; the risk is a std.Io.Reader semantics change or a third copy-paste. Fix: a shared bounded-line-iterator helper. (The 35f2324 linkage in the original finding was inaccurate — that bug was reader-buffer aliasing, a different class.)
|
||||
|
||||
- **[low] src/cli.zig:906 — upstream client construction duplicated between probeUpstreams and app.Upstreams.build.**
|
||||
- **[low — CLOSED m18] src/cli.zig:906 — upstream client construction duplicated between probeUpstreams and app.Upstreams.build.**
|
||||
Concrete drift hazard: cli.zig hardcodes the DoH buffer sizes as literals that app.zig defines as named constants — changing the constants leaves `check` probing different buffers than `run` uses, undermining the probe's stated purpose. Fix: a shared build-one-client-per-entry helper, or at minimum shared size constants.
|
||||
|
||||
- **[low] src/cli.zig:722 — ZON parse failure rendered through two channels in `check` vs `import`.**
|
||||
- **[low — CLOSED m18] src/cli.zig:722 — ZON parse failure rendered through two channels in `check` vs `import`.**
|
||||
check prints the multi-line zon_diag inline (embedding newlines mid-FAIL-line, contradicting the one-line-per-problem promise in docs/reference/configuration.md:332), import routes through reportParseFailure/Diagnostics. Fix: expose reportParseFailure (it is currently private) and route check through it.
|
||||
|
||||
- **[low] build.zig:155 — test-suite module wiring duplicated block-for-block for host and aarch64, plus a re-spelled target triple.**
|
||||
- **[low — CLOSED m18] build.zig:155 — test-suite module wiring duplicated block-for-block for host and aarch64, plus a re-spelled target triple.**
|
||||
Divergence is caught loudly (compile/link errors, blocking CI qemu job), just late. Fix: an addTestSuite helper mirroring addExecutable, triple from cross_targets.
|
||||
|
||||
- **[low] tests/fuzz/blocklist_fuzz.zig:160 — Smith corpus encoders (u32-LE length-prefix convention) copy-pasted between the two fuzz files.**
|
||||
- **[low — CLOSED m18] tests/fuzz/blocklist_fuzz.zig:160 — Smith corpus encoders (u32-LE length-prefix convention) copy-pasted between the two fuzz files.**
|
||||
A Smith stream-format change caught in one file and missed in the other leaves that corpus silently decoding to garbage while targets stay green. Fix: a shared dependency-free tests/fuzz/smith_encode.zig — no build.zig change needed since dns_fuzz.zig already imports corpus.zig by relative path.
|
||||
|
||||
## Theme 3: Silent failures and swallowed errors (11 findings)
|
||||
@@ -111,7 +113,7 @@ The repo's history proves this theme's stakes: commit 35f2324 fixed a process-ki
|
||||
- **[medium — CLOSED m16] web/src/features/queries/QueryLogPage.tsx:88 — load-more accumulation silently develops a mid-table row gap when the base page refetches.**
|
||||
The refocus-after-30s refetch shifts the newest-100 boundary up while `extra` starts strictly below the old cursor; the missing rows are in neither, and the stale cursorOverride means load-more never heals it. On a live DNS server the trigger is routine. Fix: useInfiniteQuery (or disable background refetch while extra is non-empty), or detect the discontinuity and reset the accumulation.
|
||||
|
||||
- **[low] src/upstream/doh_client.zig:157 — DoH mapError never unwraps the stashed cause behind ReadFailed/WriteFailed.**
|
||||
- **[low — CLOSED m18] src/upstream/doh_client.zig:157 — DoH mapError never unwraps the stashed cause behind ReadFailed/WriteFailed.**
|
||||
Verification narrowed the blast radius: the pool's select harness means shutdown cancellation is handled correctly despite this, so the reachable impact is rare mid-exchange local-resource errors (e.g. ENOBUFS) recorded as peer faults against a healthy upstream — still a stated spec-invariant violation, fixed once for DoT and left in DoH. Fix: the same unwrap the other two transports carry.
|
||||
|
||||
- **[low — CLOSED m16] src/filter/manager.zig:1237 — commitStatus silently drops the outcome of a source with no status entry.**
|
||||
|
||||
Reference in New Issue
Block a user