Skip to content

fix: three findings from a six-persona audit sweep - #223

Merged
fstubner merged 1 commit into
mainfrom
fix/empty-spawn-logs
Sep 5, 2026
Merged

fix: three findings from a six-persona audit sweep#223
fstubner merged 1 commit into
mainfrom
fix/empty-spawn-logs

Conversation

@fstubner

@fstubner fstubner commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Six independent auditors, different lenses: security, correctness, new user, returning maintainer, resources, tests. Most of what they reported was already answered by the code. Three were not.

A test whose premise never held

dispatch-log's "when the log dir is unwritable" test set the log dir to a path containing a NUL byte. That reads as obviously invalid and is not — assigning it to process.env truncates the value at the NUL, so the log dir became a perfectly writable directory and the write succeeded. not.toThrow() then passed for the absence of the condition the test names.

The auditor found the weak assertion. The dead premise turned up while fixing it. The test now blocks the directory with a regular file, asserts the warning fires, and asserts it fires once rather than per dispatch. Deleting the warning — the auditor's own sabotage — fails it.

Empty spawn logs accumulated forever

Measured here: 129 files going back three weeks, zero live heartbeats, 780 bytes between them — six bytes each.

Keeping these is deliberate and the reason is sound: a supervisor that died is the one that left a stale heartbeat, and its bootstrap output is the only explanation of why. That covers a log with something in it. An empty one explains nothing — and the liveness check reads this directory on every drain, which is the same permanent per-dispatch cost the heartbeat cleanup was added to stop paying.

Empty logs past the staleness window are dropped; non-empty ones and fresh ones are kept. A test pins all three cases.

The quick start spent your quota without saying so

The README told a brand-new user to run doctor --live as step three of installing. The tool's own code describes that flag as "consume quota", and the acceptance procedure says it "can burn a subscription window".

Quick start now uses plain doctor, with --live described as the deliberate step it is. The same paragraph notes configure is optional — which the README already said, 150 lines below where someone deciding whether to run it would look.

What was rejected, and why

  • "Workspace lock released before use" — misread. That lock makes the snapshot atomic; after copy/git_worktree takes its copy each dispatch is isolated. The whole-dispatch lock is the branch above, for shared_locked.
  • "Jobs stuck forever when the runner is missing" — defended by the caller: start.ts runs in-process and returns before the slot queue is reached, and it warns.
  • "Breaker updates run unserialised" — answered by file-lock.ts's own comment, which names BreakerStore as the caller that must not drop an update.
  • Security audit — no HIGH or MEDIUM findings.
  • Maintainer audit — several "risks" quoted fix records in past tense as if open. Pointed at stale prose, it found none.

npm run check: 1254 passed, 8 skipped, exit 0.

Six independent auditors with different lenses. Most of what they reported
was already answered by the code; these three were not.

A test whose premise never held. dispatch-log's "when the log dir is
unwritable" test set the log dir to a path containing a NUL byte, which
reads as obviously invalid and is not: assigning it to process.env
truncates the value AT the NUL, so the log dir became a perfectly writable
directory and the write succeeded. `not.toThrow()` then passed for the
absence of the condition the test names. The auditor found the weak
assertion; the dead premise turned up while fixing it. It now blocks the
directory with a regular file, asserts the warning fires, and asserts it
fires once rather than per dispatch. Deleting the warning — the auditor's
own sabotage — fails it.

Empty supervisor spawn logs accumulated forever. Measured here: 129 files
going back three weeks, zero live heartbeats, 780 bytes between them, six
bytes each. Keeping these is deliberate and the reason is sound — a
supervisor that died is the one that left a stale heartbeat, and its
bootstrap output is the only explanation. That covers a log with something
IN it. An empty one explains nothing, and the liveness check reads this
directory on every drain, which is the same permanent per-dispatch cost the
heartbeat cleanup was added to stop paying. Empty logs past the staleness
window are now dropped; non-empty ones and fresh ones are kept, and a test
pins all three cases.

The README told a brand-new user to run `doctor --live` as step three of
installing, with no indication that it contacts providers and spends quota
— the tool's own code describes the flag as "consume quota". Quick start
now uses plain `doctor`, and `--live` is described as the deliberate step
it is. Same paragraph says `configure` is optional, which the README
asserted 150 lines later and not where someone deciding whether to run it
would see.

npm run check: 1254 passed, 8 skipped, exit 0.
@fstubner
fstubner merged commit 88b4e67 into main Sep 5, 2026
4 checks passed
@fstubner
fstubner deleted the fix/empty-spawn-logs branch September 5, 2026 23:00
@fstubner fstubner mentioned this pull request Sep 6, 2026
fstubner added a commit that referenced this pull request Sep 6, 2026
Version bump, changelog heading, and the acceptance record for the release.

Also the seven findings from the final acceptance pass, which returned
CONDITIONAL:

- The release notes declared `doctor --json` still printed text on a load
  failure. It does not; this same release fixed it.
- PRs #223 and #224 had no changelog entries at all — eight user-visible
  changes, including doctor gaining a check that makes it exit 1 in an
  unbuilt checkout, and a lock sweep that runs before every job start.
- The `usage` empty state and the doctor job-runner check were pinned by no
  test: removing both left the suite byte-identical at 1258 passed.
- pruneDeadWorkspaceLocks deleted an unreadable lock on sight, measured at
  25ms against the acquire path's deliberate 1018ms grace, while its
  docblock, its commit message and its test all asserted the two rules were
  the same. It now honours the grace. `wx` creation leaves a real empty-file
  window, and this runs before every job start, so losing that race deletes
  a live holder's lock and lets two dispatches edit one shared_locked
  workspace.
- Removing the sweep's call from start.ts failed no test.
- `--json` was honoured only for the exact token `--json`, and four error
  shapes bypassed the envelope entirely; `unknown command` printed the help
  block to stdout, so `frobnicate --json | jq` was fed usage text. All four
  emit the envelope now and usage moved to stderr, which also means main()
  throws for a missing prompt rather than returning 1.
- The redaction sink inventory omitted result.md and both
  installOutputRedaction calls; removing the first left that file 20/20
  green.

Live dispatch on this build: cursor_cli returned pong; codex_cli refused as
rate-limited, which costs nothing and counts as a refusal.

npm run check: 1269 passed, 8 skipped, exit 0, green in one full run.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant