Skip to content

fix: the audit's remaining findings, and doctor learns whether jobs detach - #224

Merged
fstubner merged 1 commit into
mainfrom
chore/findings-and-hygiene
Sep 5, 2026
Merged

fix: the audit's remaining findings, and doctor learns whether jobs detach#224
fstubner merged 1 commit into
mainfrom
chore/findings-and-hygiene

Conversation

@fstubner

@fstubner fstubner commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Five findings from the six-persona sweep and the acceptance passes — plus a commitment I made in review and dropped.

doctor now reports whether jobs will actually detach

resolveRunnerPath() returning undefined is not an error — it is the signal to run jobs in-process, which is right for an unbuilt checkout and wrong everywhere else. The concurrency cap is enforced by the supervisor pool, so in-process mode silently removes the bound that exists because of a measured OOM. One stderr line at dispatch time, and nothing checking it.

ok job-runner: found; jobs run detached and the concurrency cap applies

An auditor raised this, I said I would fold it in, and I did not. It took a second audit noticing.

Dead workspace locks are swept

A lock whose holder is gone is reclaimed — but only when something contends for that exact directory, and nothing contends for a path you dispatched against once and moved on from. A resource audit measured a seven-day-old lock still sitting there.

The sweep reuses isDead(), so a file is removed under exactly the rule that would let a waiter steal it. A looser second rule here would be a way to delete a lock the acquire path still considers live.

usage with no routes printed a bare header

Which reads as a broken command — the one thing it does not mean. It now says there are no routes and names both ways to get one. Carried across three releases as cosmetic; it is the first thing a user with no routes sees.

--json now holds on the error path

--json is a promise about the shape of the output, and it was kept only on the success path: a bad --config made doctor --json print a sentence, so anything parsing it got a parse error instead of the reason.

{ "ok": false, "error": "config path ... is a directory, not a file. Point --config at the config.yaml inside it." }

Without --json the plain line is unchanged. Tests pin both directions; sabotaging the envelope fails only the JSON one.

README

The configure paragraph was one 180-word block covering four separate concerns, which the new-user audit could not follow. Split under labelled headings, with the job-runner check named and --live marked as spending quota.

Carried and NOT fixed, with reasons

  • stream-subprocess has no backpressure and kills a healthy child on queue overflow. A real design change to the path every dispatch takes, and the module already documents the behaviour honestly rather than claiming otherwise.
  • MCP JSON-RPC error frames bypass the redaction sinks on the HTTP transport. Structural; never demonstrated carrying a credential.
  • Two weak test assertions (boundary-strictness, codex/cursor toBeDefined) — redundant rather than wrong.

Repo hygiene

Tracked repo is clean: no build artifacts, explicit files field, 1.89 MB published tarball, no stray or backup files, and the site is not shipped. Ignored working-tree clutter noted separately — two items predate the project rename.

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

…etach

Five findings from the six-persona sweep and the acceptance passes, plus a
commitment I made and dropped.

doctor now reports whether dist/job-runner.js is present. resolveRunnerPath()
returning undefined is not an error — it is the signal to run jobs
IN-PROCESS, which is right for an unbuilt checkout and wrong everywhere
else, because the concurrency cap is enforced by the supervisor pool. So the
setting that exists because of a measured OOM silently stops applying, with
one stderr line at dispatch time and nothing checking it. I said I would add
this after the first auditor raised it and did not; an audit noticing twice
is what it took.

Dead workspace locks are swept. A lock whose holder is gone IS reclaimed —
but only when something contends for that exact directory, and nothing
contends for a path you dispatched against once. A resource audit measured a
seven-day-old lock still sitting there. The sweep reuses isDead(), so a file
is removed under exactly the rule that would let a waiter steal it; a looser
second rule here could delete a lock the acquire path still considers live.

`usage` with no routes printed a bare header. That reads as a broken
command, which is the one thing it does not mean. It now says there are no
routes and names both ways to get one.

`--json` is a promise about the shape of the output and was kept only on the
success path: a bad --config made `doctor --json` print a sentence, so
anything parsing it got a parse error instead of the reason. Same message,
correct envelope; without --json the plain line is unchanged, and a test
pins both directions.

README: the configure paragraph was one 180-word block covering four
separate concerns, which a new-user audit could not follow. Split under
labelled headings, with the job-runner check named and `--live` marked as
spending quota.

Carried and NOT fixed, with reasons: stream-subprocess has no backpressure
and kills a healthy child on queue overflow — a real design change to the
path every dispatch takes, and the module already documents the behaviour
honestly; MCP JSON-RPC error frames bypass the redaction sinks on the HTTP
transport, structural and never demonstrated carrying a credential; two weak
test assertions that are redundant rather than wrong.

npm run check: 1258 passed, 8 skipped, exit 0.
@fstubner
fstubner merged commit 4d48fd9 into main Sep 5, 2026
4 checks passed
@fstubner
fstubner deleted the chore/findings-and-hygiene branch September 5, 2026 23:27
@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