5d41e106
docs: review-round records; four passages caught up with the code
a73x 2026-08-14 12:46
Commit message
docs/decisions.md
| Old | New | ||
|---|---|---|---|
| @@ -3118,8 +3118,8 @@ failed: <err>` — and a second tear after a spent redial says so in its own | |||
| 3118 | words. | 3118 | words. |
| 3119 | 3119 | ||
| 3120 | **`settled` means output went quiet, not that the process exited.** The | 3120 | **`settled` means output went quiet, not that the process exited.** The |
| 3121 | settle and pgid resolutions carry `exit_code: null` and a `phase` derived | 3121 | settle and pgid resolutions — and a timed-out wait, whatever its |
| 3122 | from marks, which on a markless shell reads `at_prompt` — the tracker | 3122 | mechanism — carry `exit_code: null` and a `phase` derived from marks, which on a markless shell reads `at_prompt` — the tracker |
| 3123 | never saw a `C`, so it is telling the truth about what it knows. Only | 3123 | never saw a `C`, so it is telling the truth about what it knows. Only |
| 3124 | `mechanism == "marks"` carries a trustworthy exit code, which is why every | 3124 | `mechanism == "marks"` carries a trustworthy exit code, which is why every |
| 3125 | reply names its mechanism. An agent that reads `exit_code` without reading | 3125 | reply names its mechanism. An agent that reads `exit_code` without reading |
| @@ -3139,9 +3139,9 @@ reply names its mechanism. An agent that reads `exit_code` without reading | |||
| 3139 | `PROMPT_COMMAND` member loses its marks (degrading to pgid/settle, not to | 3139 | `PROMPT_COMMAND` member loses its marks (degrading to pgid/settle, not to |
| 3140 | a wrong answer), and a compound string member (`a; b`) can re-arm the | 3140 | a wrong answer), and a compound string member (`a; b`) can re-arm the |
| 3141 | guard. Arrays — bash 5.1's default and what the shim prefers — are exact. | 3141 | guard. Arrays — bash 5.1's default and what the shim prefers — are exact. |
| 3142 | - **A SIGKILLed daemon orphans its `mux-shellint-{pid}` directory.** | 3142 | - **A SIGKILLed daemon orphans its `mux-shellint-{pid}-{random}` |
| 3143 | Bounded and per-pid, cleaned on every ordinary exit; a startup sweep of | 3143 | directory.** Bounded and per-pid, cleaned on every ordinary exit; a |
| 3144 | dead-pid directories is the roadmap item. `prepare()` failing is degraded | 3144 | startup sweep of dead-pid directories is the roadmap item. `prepare()` failing is degraded |
| 3145 | and never fatal: the session runs on pgid and settle, and says so on | 3145 | and never fatal: the session runs on pgid and settle, and says so on |
| 3146 | stderr, because refusing to start a daemon over an optional enhancement | 3146 | stderr, because refusing to start a daemon over an optional enhancement |
| 3147 | would invert the module's premise. | 3147 | would invert the module's premise. |
| @@ -3240,10 +3240,24 @@ two dialects. | |||
| 3240 | `client -> proxy` is production (ignoreSigpipe in the live attach path) | 3240 | `client -> proxy` is production (ignoreSigpipe in the live attach path) |
| 3241 | and is grandfathered with the debt comment at client.zig — relocating | 3241 | and is grandfathered with the debt comment at client.zig — relocating |
| 3242 | ignoreSigpipe to a leaf is the recorded fix, deliberately not taken here. | 3242 | ignoreSigpipe to a leaf is the recorded fix, deliberately not taken here. |
| 3243 | - **Test-only imports are a declared column, not an accident.** The testtmp | 3243 | - **Test-only imports are a declared column, and a mechanism.** The testtmp |
| 3244 | pattern (production modules importing test scaffolding used only inside | 3244 | pattern (production modules importing test scaffolding used only inside |
| 3245 | `test` blocks, kept out of release binaries by lazy compilation) is now | 3245 | `test` blocks) is stated per-row, and since the review round the column |
| 3246 | stated per-row. The strata computation excludes the column. | 3246 | is enforced: the eight rows with test grants get a test twin — a second |
| 3247 | module instance carrying the test imports, used only by the test builds — | ||
| 3248 | while the production instance never receives them. "Test scaffolding | ||
| 3249 | never ships" is a compile error a production reference triggers, not a | ||
| 3250 | convention lazy compilation happens to honor. The twins import | ||
| 3251 | production dependency instances, so no compilation holds two instances | ||
| 3252 | of one row. The strata computation excludes the column. | ||
| 3253 | - **Every grant must be spelled.** A build-graph-time check reads each | ||
| 3254 | row's root file and refuses every build — not just the gate — when a | ||
| 3255 | declared import never appears as `@import("name")` there. It caught its | ||
| 3256 | motivating case at once: exe→cmd, dead since its call site left | ||
| 3257 | main.zig, carried faithfully by the extract-and-diff (a pure refactor | ||
| 3258 | preserves mistakes too); the table stands at 75 edges. Stated limit: | ||
| 3259 | text cannot tell which column a use belongs to — the twin split is what | ||
| 3260 | separates the columns. | ||
| 3247 | - **refAllDecls in every test-loop module root, scope stated honestly.** | 3261 | - **refAllDecls in every test-loop module root, scope stated honestly.** |
| 3248 | Pub decls only — std.meta.declarations sees nothing private. Narrower | 3262 | Pub decls only — std.meta.declarations sees nothing private. Narrower |
| 3249 | still than the original silent-module-loss framing: in 0.15.2 a pub decl | 3263 | still than the original silent-module-loss framing: in 0.15.2 a pub decl |
| @@ -3256,21 +3270,30 @@ two dialects. | |||
| 3256 | namespace); engine was downgraded to plain because recursion reaches | 3270 | namespace); engine was downgraded to plain because recursion reaches |
| 3257 | pre-existing comptime errors inside vendored ghostty-vt (re-promote | 3271 | pre-existing comptime errors inside vendored ghostty-vt (re-promote |
| 3258 | when the dep is bumped); wasm_core is excluded because it is not in | 3272 | when the dep is bumped); wasm_core is excluded because it is not in |
| 3259 | the native test loop and a block there would never run. | 3273 | the native test loop and a block there would never run — it is the one |
| 3274 | remaining dark root: compile coverage comes from the wasm build `check` | ||
| 3275 | forces plus the ABI verify step, behavior only from the web e2e. | ||
| 3260 | - **Leak verdicts print, never panic.** The three gpa-backed binaries | 3276 | - **Leak verdicts print, never panic.** The three gpa-backed binaries |
| 3261 | (muxd, mux, muxweb) check `gpa.deinit()` and print | 3277 | (muxd, mux, muxweb) check `gpa.deinit()` and print |
| 3262 | `<binary>: LEAK: allocations outlived deinit` to stderr on `.leak` — | 3278 | `<binary>: LEAK: allocations outlived deinit` to stderr on `.leak` — |
| 3263 | never an exit-code change, because muxd's exit code carries the | 3279 | never an exit-code change, because muxd's exit code carries the |
| 3264 | session shell's. e2e captures every daemon's stderr and sweeps all | 3280 | session shell's. e2e captures every daemon's stderr and sweeps all |
| 3265 | captures at the end, behind a canary that fails if the sweep has | 3281 | captures in its EXIT trap — after the kills, before the rms — so a |
| 3266 | nothing to read; agent.sh checks the detached-daemon log after each | 3282 | failing run reports leaks too, and the trap moves the status in one |
| 3267 | clean stop, post process-exit, and fails loudly if the daemon outlives | 3283 | direction only (a leak promotes green to red, never masks the suite's |
| 3268 | the stop (a wedged daemon must not read as leak-free). Proven by | 3284 | own code). Captures deleted mid-suite bank their verdict at the moment |
| 3269 | deliberate leak: marker appeared, e2e failed at the sweep, agent failed | 3285 | of deletion (`rm_swept`), which paid off the old "uncovered $OUT.q" |
| 3270 | all three leakcheck sites. kill -9 paths print nothing — no false | 3286 | debt. The nothing-to-read canary stands wherever it would guard a green |
| 3271 | positive, no coverage, stated. Known uncovered captures: $OUT.q and the | 3287 | run and degrades to a note on an already-failing early exit — a |
| 3272 | other mid-suite rm'd files (recorded debt). muxa is arena-over- | 3288 | fabricated second defect would bury the real one. agent.sh gives every |
| 3273 | page_allocator by construction and has no verdict to check. | 3289 | daemon its own log via a per-scenario XDG_STATE_HOME and sweeps all of |
| 3290 | them at the end behind the same vacuous-green guard, so the daemons the | ||
| 3291 | per-stop leakcheck never saw (session-ended, destroyed, abandoned by a | ||
| 3292 | failing scenario) get verdicts too, and a missing log is a failure. | ||
| 3293 | Proven by deliberate leak at every layer: banked-at-rm, trap-on-green, | ||
| 3294 | trap-on-failing, and a marker in one daemon's log among five. kill -9 | ||
| 3295 | paths print nothing — no false positive, no coverage, stated. muxa is | ||
| 3296 | arena-over-page_allocator by construction and has no verdict to check. | ||
| 3274 | - **The persistence soak phase owns the classes the Zig-side LEAK | 3297 | - **The persistence soak phase owns the classes the Zig-side LEAK |
| 3275 | marker (6a) never sees.** One daemon, N client lifecycles: every cycle | 3298 | marker (6a) never sees.** One daemon, N client lifecycles: every cycle |
| 3276 | must prove its client attached (a vacuous cycle fails the phase); fd | 3299 | must prove its client attached (a vacuous cycle fails the phase); fd |
| @@ -3282,5 +3305,49 @@ two dialects. | |||
| 3282 | rm'd. First real numbers (SOAK_CYCLES=8): fds 5->5 exact; RSS | 3305 | rm'd. First real numbers (SOAK_CYCLES=8): fds 5->5 exact; RSS |
| 3283 | 9020->9128 kB (+108 kB across the 5 post-warmup cycles). | 3306 | 9020->9128 kB (+108 kB across the 5 post-warmup cycles). |
| 3284 | - **`zig build check` is the pre-commit gate**: fmt (including | 3307 | - **`zig build check` is the pre-commit gate**: fmt (including |
| 3285 | build.zig.zon) + unit tests, seconds. e2e/agent/soak stay separate | 3308 | build.zig.zon) + unit tests + `sh -n` over every script in test/, |
| 3286 | steps — minutes-long and process-spawning. | 3309 | tools/ and deps/quic (shellcheck at error severity when installed, |
| 3310 | silently skipped when not), seconds. The scripts are file args, so | ||
| 3311 | their contents hash into the graph and only edited scripts re-check. | ||
| 3312 | e2e/agent/soak stay separate steps — minutes-long and process-spawning. | ||
| 3313 | - **deadcode.sh is non-gating, run by hand.** A textual pub-decl | ||
| 3314 | cross-reference whose output is review material, not a verdict — | ||
| 3315 | refAllDecls keeps deliberately-unreferenced decls analyzable, so | ||
| 3316 | "referenced nowhere outside its file" is a prompt to look, not a | ||
| 3317 | failure. Matching is literal (`-F`/`-qxF`): a filename is never a | ||
| 3318 | regex. | ||
| 3319 | |||
| 3320 | ### Review round (2026-08-14) | ||
| 3321 | |||
| 3322 | One pragmatic review over the whole branch; every finding above Minor | ||
| 3323 | was fixed the same day, each fix proven by a test that fails without it. | ||
| 3324 | |||
| 3325 | - **The shim directory is created exclusively and named unaimably.** | ||
| 3326 | `mux-shellint-{pid}-{random}` via `xdg.makeNewPrivateDir`: one | ||
| 3327 | `mkdir(0o700)` that neither follows a symlink nor adopts an existing | ||
| 3328 | entry, then a no-follow open. Closes a CWE-59 window (shared /tmp | ||
| 3329 | sockdir + guessable pid let a local user aim the old makePath+chmod at | ||
| 3330 | a directory they chose) and, via the random half, retires the pinned | ||
| 3331 | oddity where a leftover at the pid name silently cost a session its | ||
| 3332 | marks. EEXIST degrades to no-marks as before. `makePrivateDir` keeps | ||
| 3333 | its adopting behavior for the two callers under `~` where adopting is | ||
| 3334 | correct; the two functions' doc comments now name their threat models. | ||
| 3335 | - **A timed-out await clears `exit_code`.** The timeout arm now matches | ||
| 3336 | pgid and settle, so "an exit code is real only under `marks`" is | ||
| 3337 | uniformly true rather than true except in the one arm agents hit most. | ||
| 3338 | The persisted `last_return` snapshot is untouched — only the reply. | ||
| 3339 | - **muxa exit codes are enumerated and honest.** 0 answer, 1 error | ||
| 3340 | object, 2 usage, 3 await timeout, and new 4: the reply object could | ||
| 3341 | not be written (EPIPE/ENOSPC) — previously a silent exit 0 with no | ||
| 3342 | JSON, the one shape the contract forbids. A command's exit code is | ||
| 3343 | never muxa's. The post-return scrollback fetch now gets exactly its | ||
| 3344 | own 2s window (`spanFetchDeadline()`, no run-deadline parameter to | ||
| 3345 | misuse): under `@max` a `--timeout 0` run made the bonus fetch an | ||
| 3346 | unbounded hang. | ||
| 3347 | - **A port is an observation, not a derivation.** The `$$`-derived | ||
| 3348 | numbers in agent.sh and valgrind-quic.sh are first candidates only: | ||
| 3349 | bind-with-retry steps on the daemon's own "already listening on udp" | ||
| 3350 | refusal (any other failure reports immediately), and the port written | ||
| 3351 | back is the one the up-line named, so every dial targets what was | ||
| 3352 | bound. Two suites congruent mod 900, or a tuned ephemeral range, no | ||
| 3353 | longer collide. | ||
docs/roadmap.md
| Old | New | ||
|---|---|---|---|
| @@ -253,7 +253,7 @@ blocker): | |||
| 253 | textually identical to a `PROMPT_COMMAND` member loses its marks | 253 | textually identical to a `PROMPT_COMMAND` member loses its marks |
| 254 | (degrading to pgid/settle), and a compound string member (`a; b`) can | 254 | (degrading to pgid/settle), and a compound string member (`a; b`) can |
| 255 | re-arm the guard — arrays, the modern default, are exact. | 255 | re-arm the guard — arrays, the modern default, are exact. |
| 256 | - **A SIGKILLed daemon orphans its `mux-shellint-{pid}` directory**; a | 256 | - **A SIGKILLed daemon orphans its `mux-shellint-{pid}-{random}` directory**; a |
| 257 | startup sweep of dead-pid directories is owed. Injection failing is | 257 | startup sweep of dead-pid directories is owed. Injection failing is |
| 258 | always degraded and never fatal. | 258 | always degraded and never fatal. |
| 259 | - **`--timeout N` can overshoot N** by the client's grace window (2s over a | 259 | - **`--timeout N` can overshoot N** by the client's grace window (2s over a |