Repository navigation
Test suite: 17 files side by side, the kit chain in forks, warm CLI processes - #128
Merged
Merged
Conversation
…eaf steps run in forks WIP: assertion-trace evidence and timings to follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…st it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r servers bounded at 2 min Starting node is most of a CLI call's cost on a loaded Mac; each warm process still runs one command, fresh, with its own cwd, environment and argv (output byte-identical to spawnSync). Calls with stdin and DEVTEAM_TEST_COLD=1 spawn as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… only The copy's name extends the original's, so a second pass rewrote paths it had already rewritten and git saw the worktrees as prunable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…test file main's tmux shim is kept (the one shim); the split's own is dropped. The 33 multi-repo tests land in five files of their own; the artifact steps fork together. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… place The test process could read the file between its creation and its write, and take an empty code for 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y itself Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…test file Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e the parallel files It times the kit's coverage function against 500 ms; on a 4-core runner beside seven other test processes it read 501. The bound is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Member
Author
|
Review (upstream loop): CHANGES. One blocker. tmux safety and assertion coverage hold. Blocker
Should-fix
Nits
What held
On the "sent seconds ago" lost-Enter case: I'd keep it as written, since a kit clock override is a separate change. Method: read-only review, plus one test file ( |
…hat stays, hello counts an open question, ports below 32768 - a fork or warm process inherits its parent's PATH, shim first: the real tmux is the first on PATH that isn't a shim - the shim is written once, whole, at a path named for its content, and never removed - hello's inbox checks compared 0 with 0 once the steps that left questions open moved to forks; it asks one first - port blocks stay in 20000-32699, below Linux's ephemeral range - the CI comment names where the shim is Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sions runs in line forge git now asserts release-production is below rung 3, which the decisions step's correction leaves: a later step reads it, so decisions no longer forks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Member
Author
|
Merging (upstream loop). Re-review: every finding addressed (bounds checks alone in bounds.test.mjs, fixed-path shim, real tmux found past the shim, hello asks a real question, ports below 32768, CI comment). I verified independently at 38cde42:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Makes the kit's suite several times faster in wall time on the same checks: the one-file suite is split into 17 files that run side by side, most of the "devteam kit" chain runs in forks of its fixture, and CLI calls go to node processes started ahead of time. No assertion is removed, loosened or skipped.
docs/devteam/scripts/devteam.mjsis not changed.Run it:
node --test --test-concurrency=8 test/*.test.mjs(the README says so).DEVTEAM_TEST_COLD=1spawns every CLI call fresh, as before.Timings in CI (GitHub's 4-core runners)
Main's numbers are its run at 05:48Z today; this branch's are the slower of its two jobs. All 8 jobs green.
Timings (this Mac, back to back, one run at a time)
The machine was heavily loaded the whole time (load average 7–27 on 10 cores: other fleets, another session's kit suite,
syspolicyd/Spotlight), and every node exec waited on macOS's security checks (0.3–3.7 s sequential). Absolute numbers are inflated; the pairs are what compare.* the 283 s run failed two dashboard tests on a missing import, and the 318 s run one test on a race in the warm-process runner; both fixed since (commits below).
CPU (user+sys) is about the same: Node 24 537 s (main) vs 601 s; Node 20 470 s vs 575 s. The gain is wall time from running in parallel, not less work.
Node 18: both Node 18 runs of this branch overlapped another session's Node 18 kit suite on the same machine, and the second ran 91 minutes. A single file on Node 18 runs within 1.4× of Node 20 (77–93 s vs 59 s), warm or cold, so I don't see a Node 18 defect in the approach, but I can't show a clean local Node 18 number. CI's numbers on this PR are the clean comparison (main's CI: 4.6–5.6 min per Node job).
What changed and why
test/kit.test.mjsheld the whole suite, and node runs one file's tests in order. The 80 standalone tests moved, verbatim, into 16 files grouped by area; each heavy test (fleet park, coverage at landing, url links with no lines) has a file to itself. Helpers moved totest/lib/kit.mjs. Each test process gets its own tmux server (main's tmux shim, kept as the one shim) and its own block of 100 ports, claimed by lock file intest/lib/ports.mjsinstead ofpid % 290, so files (and another checkout's suite) never share one.test/lib/chain.mjs) are one stateful story, so they can't simply be separate tests. Instead, at a forked step's place the run copies the fixture (snapshot), and a child process (test/lib/fork.mjs) runs that step on the copy (restorerewrites the copy's absolute paths in git's files andrun/, in one pass). The forked step sees exactly the state it would have seen in line; the steps after it go on without it. 31 of 48 steps fork (the list and the rule are at the top oftest/kit.test.mjs); consecutive steps that build on each other fork together (approve-plan → D4 → spend; the five artifact steps). Each forked step is still a subtest of "devteam kit" with its own name, pass/fail and diagnostics.test/lib/cli.mjs,test/lib/cli-runner.mjs). Most of a CLI call's cost here was starting node. Each test process keeps three node processes started ahead of time; a call hands one its cwd, environment and argv, it importsdevteam.mjsexactly asnode devteam.mjs …would and exits. One process per call, so every call is still fresh. Byte-identical stdout, stderr and exit status against spawnSync onstatus,metrics --json,validateand an unknown command; ~10× faster per call when exec is slow (1148 → 117 ms). Calls that pipe stdin spawn as before.test/bounds.test.mjs(the coverage function timed against 500 ms, moved verbatim) runs alone in its own step first: beside seven other test processes on a 4-core runner it read 501 ms. The bound is unchanged.helloasks one question before it counts the inbox (one line added at the top of that step: with the steps that left questions open now in forks, its two inbox checks compared 0 with 0).Flakes made deterministic
serverUpanduntilgave up after 30 s, shorter than a dashboard's first backgroundhelloon a loaded machine; now 2 min (a healthy run returns at once).Not fixed — needs a decision. Lost Enter, "sent seconds ago: a slow submit is not double-pressed" writes a record dated 5 s ago and expects the kit (threshold 30 s) not to press. When the supervisor's
metricstakes more than 25 s to reach the pane check, the record is 30 s old and the kit rightly presses. It failed on main's suite under load too (once in my baseline runs), and fails more often here because the machine is busier while forks run. I moved the record's write to just beforemetrics(it used to be written before the pane even started). Making it deterministic needs either a clock the kit reads from the environment (a behaviour change to devteam.mjs, which this PR doesn't make) or dating the record in the future (which changes what the step checks). Reviewer's call.Every assertion still runs
Test names: main has 129 (as of 012e082, #105 merged in) (
devteam kit, its 48 subtests, 80 top-level tests); this branch has the same 129 names, none added or removed (checked by script).Assert lines, textually: every line that calls
assert.in main'stest/kit.test.mjs(012e082), trimmed and sorted, against the same over this branch'stest/*.test.mjsandtest/lib/*.mjs:#105's new checks (release decisions in the merge chain, hand-written default and release decisions, malformed
releasevalues) are in the chain steps, verbatim. One consequence:forge gitnow asserts release-production is below rung 3, which thedecisionsstep's correction leaves, sodecisionsruns in line again (31 of 48 steps fork).Assertion calls were traced by a preload that wraps every
node:assert/strictmethod and records, per call site (the calling line's source text), each call and its normalized actual value (ids, shas, times, temp paths and ports masked). Node 24, complete runs:Runs where a test failed differ only after the failing line (the main Node 24 run that hit the link-probe flake; the Node 20 branch run that hit lost Enter: 4 sites, all after it).
Same assertions, different values. Two runs of main's own suite differ in actual values at 19 sites (times, durations, ordering). Against main, this branch differs at 39, so about 20 come from forks: a forked step no longer sees what the other forked steps did. Every one still passes and checks the same thing; the notable ones:
hello: its inbox checks counted 4 open questions on main and would have counted 0 here; the step now asks one first, so they compare a real count (see review fixes).log-check,goal list,roadmap,history,retro --print,knowledge --status accepted: the same lines asserted, among fewer others (another fork's goals, rules or log lines aren't there).review-worktree's "and git forgets them", the artifact refusals: the same refusal or listing, with a settings-drift warning line or a shorter worktree list beside it.acts(), a review draft'slogLines(): equal before and after, on smaller logs.Found on the way
TMUX_TMPDIRnames a deleted directory; this suite's own exit handlers hit that once and killed a live fleet's sessions. Main'stmux-guardshim (merged since) is the one kept here; fleet fixtures stop their own server before removing its directory.nodefrom this terminal waits onsyspolicyd/trustd(0.3–3.7 s sequentially, ~25–45 ms in parallel). Adding the terminal app under Privacy & Security → Developer Tools and restarting it should remove most of that, for every devteam fleet on the machine too.Behaviour changes to
devteam.mjs: none.🤖 Generated with Claude Code