docs: brief for the T9 library-credential race, the second broker start passes Node's TIMEOUT_MAX interval so no timer tick overlaps the verb sync; mutant must fail a 50-run stress loop (mos-1594, Vikunja 622, #1826); doc only

This commit is contained in:
jason.woltje committed 2026-10-24 13:05:40 -05:00
1 parent 3e5041b43b
commit 2ce476d54c
1 file changed
+49
@@ -0,0 +1,49 @@
# T9 library-credential race on the second broker start
Written 2026-10-24 by Sage for Mos's mos-1594 (Vikunja 622, #1826), after Fethry's row 266 gate saw
bus 999/1 (fethry-0032) and Sage's sage-4185/4187 placed it.
## Brief: the T9 verb sync on the second start runs with no timer tick able to overlap it
**Why.** `packages/bus/tests/library-credential.test.mjs` T9 (`:265`) starts the broker twice with
`librarySync: { command, intervalMs: 50 }` (`:289-290`, one `start` closure for both). On the second start, after a revoke, it waits
for the first `library-sync-held` line (`:315`) and then expects the `library.sync` verb to refuse
`library-fetch-no-grant` (`:317`). That wait doesn't order the verb against later ticks:
- `packages/bus/src/runtime.mjs:615` keeps `setInterval(tick, intervalMs)` running after held, and
`tick` (`:606-611`) only skips while a pass is in flight. Only `stopSync` (`:618`), called from
`close()` (`:679`), stops it.
- `packages/library/src/sync.mjs:140-141` refuses `library-sync-busy` while `running` holds the
mirror, and `:203` clears it in `finally`. So the startup pass has released by the time its held
line is written, but a later 50 ms tick can hold the mirror when the verb arrives.
When that happens, `:317` sees `library-sync-busy`. Under load it fails. On a quiet host it passes.
**What ships.**
1. In T9 only, the second start (`:314`) passes `intervalMs: 2147483647`. That is Node's `TIMEOUT_MAX`, so
only the startup tick (`runtime.mjs:614`) fires while the test is open. One more and Node clamps
the delay to 1 ms with a `TimeoutOverflowWarning`, so don't round it up. Runtime validation
(`runtime.mjs:122`) accepts it. The first start keeps 50 ms, because `:304`
("the timer and the verb") needs ticks. A short comment at the change names mos-1594 and the reason.
2. No assertion in the file changes, and no other test changes. No product code changes. If the fix
seems to need either, stop and tell Sage. That's a mos-1272 question for Mos.
3. `agents/<seat>/work/queue-<row>/BUILD.md` records the stress runs and the mutant below as plain
text, with commands, counts and host load average.
**Mutant and stress (Mos's condition).** Build a stress loop that runs T9 alone
(`node --test --test-name-pattern` on the file), 50 or more times, each run in a fresh mktemp -d
TMPDIR under `/mnt/storage/scratch/tmp`.
- M1 puts `intervalMs: 50` back on the second start. It must fail at least once in the loop. If 50 ms
never fails on the host, run M1 at `intervalMs: 1` as well and record both results. If neither
fails, stop and report, because the fix can't be shown to matter.
- With the fix in place, the same loop passes every run.
- Restore the file after the mutant, and show `git diff --stat` empty against the build commit.
**Gate.** Boyd gives the first Codex review, and Dewey does the landing review. Every touched test
file runs whole, the stress loop and M1 run as above, and the bus suite and every `scripts/test-*.sh`
are green on Sage's landing gate. All test runs are slot work: wait for Sage's CLEAR.
**Out of scope.** The broker timer, `stopSync`, a test-only stop hook, other library tests, and the
row278 sign-out flake (Vikunja 620, Sage's).