Repository navigation
ci(bench)!: take the bench off the pull-request path - #316
Merged
Merged
Conversation
The bench leaves the pull-request path. bench.yml runs on push to main
(docs-only pushes excluded) and on workflow_dispatch, with concurrency
bench-${{ github.sha }} and no cancel, so no PR pays for or waits on it.
- Side A is the first parent, side B the pushed commit; both are worktrees
under RUNNER_TEMP with their own released tarballs and frozen install,
and the checkout holds only the harness.
- Every in-process deadline is gone: BENCH_DEADLINE_MS, the setup budget,
the run deadline, BENCH_RUN_TIMEOUT_MS, and the setup-timed-out,
budget-exceeded, timed-out and timeout shapes. Every timeout-minutes is
60; that job timeout is the only kill.
- The report carries the leg's wall time (from JOB_STARTED_AT) against a
10-minute target, never enforced, and schemaVersion moves to 2.0.
- Only a leg's total decides a regression (non-overlapping samples and
|delta| >= max(3% of A, 10 ms)). The regression block names the commit,
its parent, total then every regressed phase, and the next action: a
revert PR or root-fix P0 on main, speed evidence only elsewhere.
- Every run executes from RUNNER_TEMP/bench-run, so both sides record the
same project root, inputs digest and mutant ids. Bench configs reference
plugins relative to the config file so they survive the move.
Plan: docs/plans/2026-10-12-1800-ci-bench-off-pr-plan.md (layer 1; the
enterprise install is layer 2).
BREAKING CHANGE: bench-report.json schemaVersion 2.0 drops the
setup-timed-out and budget-exceeded abort codes and the timed-out run exit,
and adds ref, leg and the regression block.
Review finding (correctness, P3): the README said every aborted leg uploads its report, but main.ts publishes environment-incomplete without writing bench-report.json or the step summary.
Root amendment 2026-10-12: no change classifier where Turbo can do the job. paths-ignore (**/*.md, docs/**) skipped none of 11 sampled main commits; turbo query affected skips three of them. Every push now benches every leg until layer 1b scopes legs with turbo query affected. Plan: docs/plans/2026-10-12-1854-ci-bench-off-pr-plan.md supersedes the 1800 plan and records the turbo-query evidence and rule-4 reasons.
…tion Sub-conductor ruling 2026-10-12: the acceptance is a comparison per main SHA, so Turbo's affected-packages answer stays evidence, not a leg filter. The 1926 plan supersedes 1854: layer 1b is dropped, KD10 keeps Turbo as the owner of builds and cache and adds no classifier, KD11 defers affected-only selection to root.
…anged A summarized report with no regression said "no project/phase changed past the signal threshold" even when its table showed a phase that regressed under an unchanged total. The notice is now titled "Bench phase-only slowdown", says no leg total crossed the regression threshold, and lists each such cell as a phase-only slowdown. A new law drives a check-only slowdown through summarizeBench and renderBenchReport. The plan moves to docs/plans/2026-10-12-2032-ci-bench-off-pr-plan.md (supersedes 1926) and adds the CONST-W3 declaration for the bench.yml edits, the corrected cause of the stream-undecodable PR runs (#298 changes the stream contract), and that case as a known limit.
…e the PR-run evidence Review of eec5cfa found that the waiver called side B the checkout (it is the bench-b worktree) and left out the checkout ref, Turbo cache and harness install edits. Run 38284821646 also had a budget-exceeded abort on one leg, so the Problem Frame now scopes stream-undecodable to the other three legs.
… as laws bench-decisions.integration.test.ts fed hand-written NDJSON lines and report text to the pure readBenchRun and coveringTestFiles workflows and asserted their decision fields. Under root's 2026-10-12 testing law an integration test drives the real product and asserts only observable outcomes, and a pure decision gets property laws beside it (CONST-T12, CONST-T14). This layer had to change the file (ReadBenchRunCommand's exit field), so it is deleted rather than carried. The behaviours no other suite pinned are now laws over the production schemas: testsExecuted sums every drawn MutantCost, the first undecodable line is named by its 1-based number among blank and decodable lines, a verdict count other than one is stream-invalid, verdict phase durations are carried as reported or refused when absent, covering test files are the sorted distinct holders, and a non-JSON report is refused. The legacy-line decode stays pinned by the cli-contract wire-line suite. The plan moves to docs/plans/2026-10-12-2154-ci-bench-off-pr-plan.md (supersedes 2032) with the widened edit scope and test admission rows.
Root's 2026-10-13 scope law puts no new code in test/e2e-core. The layer's four new modules and their laws move to test/bench/src, which is their only consumer: run-path.schema, plan-run-path and settle-run-path (the fixed run path) and measure-leg (the leg wall time). e2e-core's mod.ts is back to main. measureLeg now decides a bench-local LegClock (LegElapsed or LegClockBehind) that main.ts maps onto the report's LegTime: make-body-purity forbids a decision from constructing a class imported from another package, and e2e-core cannot import test/bench. test/bench gains a test lane shaped like test/checker-parity: a test script, a vitest config with inSourceSchemaLaws, tsconfig.test.json and lint:tsgo over it. The plan moves to docs/plans/2026-10-13-0223-ci-bench-off-pr-plan.md (supersedes 2154).
Review of f1da499 (MiniMax lenses, report-only) found two defects in the bench's run path: - planRunPath's identity law drew cwd and CLI tails of length one or more, so the enterprise shape (cwd equal to the tree) was never generated. Dropping the `path === tree` operand left the law green; with empty tails allowed it now fails. - A cwd or CLI outside its side's tree aborted as run-path-move-failed, whose next action says to fix the runner. It is a bench bug, so it now aborts as defect. The README lists that case. The plan moves to docs/plans/2026-10-13-0238-ci-bench-off-pr-plan.md (supersedes 0223) with an accurate split of the e2e-core law files.
63ba7bb sent an outside-tree run path to `defect`. Review of that head found two problems: it contradicted KTD3, and `defect`'s next action asks for a stack trace, which a typed failure does not have. So the outside-tree case aborts as run-path-move-failed again. That code's next action, which this layer added, now names both causes. An OS error needs a runner fix; a path outside the tree is a bug in side setup. The README matches. The plan moves to docs/plans/2026-10-13-0258-ci-bench-off-pr-plan.md (supersedes 0238), with U4 brought back in line with KTD3.
Supersedes the 0258 plan. Root (2026-10-13) requires every confirmed pack violation in the a359a82 full-path receipt to be fixed, not waived. Adds KD12-KD15 and units U7-U12, the e2e-core boundary (one in-source refusal block per refined e2e-core file, LegTime as a data union), and test-admission rows for the refusal properties and the settle law.
Fixes full-path receipt findings #8 (four-channel-contracts) and #11 (tagged-unions-over-state-by-presence) on a359a82. The settle command took restoreError and runFailure as nullable Cause.pretty strings and decided on their presence. It now takes two tagged unions, RunPathMoveBack (MovedBack | MoveBackFailed { cause }) and RunPathRunEnd (RunSucceeded | RunFailed { cause }), and matches on the move-back. RunPathUnrestored carries cause: S.optional(S.Unknown) and the run's end instead of strings; the service passes each Exit's Cause as is. Text is rendered once, in the error's message, when the report's reason is built. The settle law draws the production SettleRunPathCommand and asserts unrestored iff the move-back failed, with both causes carried by identity. Plan: docs/plans/2026-10-13-0416-ci-bench-off-pr-plan.md U7.
Fixes full-path receipt finding #9 (data-only-schema-classes) on a359a82. LegMeasured and LegNotMeasured were TaggedClass variants carrying a LegTime type id that no Workflow.make checks; LegTime is only a field of BenchReport. LegTime is now an S.TaggedUnion of the same two cases, so bench-report.schema.ts declares two fewer classes and no type id, and the encoded shape is unchanged. main.ts and the render law construct through LegTime.cases. Evidence: tsc, oxlint and lint:tsgo clean in test/e2e-core and test/bench; vitest 364/364 in test/e2e-core (the 3 fewer tests are the generated codec laws of the two deleted classes; LegTime's own laws remain) and 39/39 in test/bench. Plan: docs/plans/2026-10-13-0416-ci-bench-off-pr-plan.md U8.
Fixes full-path receipt finding #10 (invariants-as-refinements) on a359a82, both fields named by root. - LegElapsed.wallMs was S.Finite, so a negative elapsed time decoded as the "clock advanced" variant. It is now finite and >= 0, with the invariant stated in the check's message. - BenchReport.ref was S.String while SummarizeBenchCommand.ref is S.NonEmptyString. It is now S.NonEmptyString, and main.ts reads BENCH_REF with Config.NonEmptyString, so an empty value takes the existing environment-incomplete path (main.ts program, UNKNOWN_REF) instead of failing at BenchReport.make. Evidence: tsc, oxlint and lint:tsgo clean in test/e2e-core and test/bench; vitest 364/364 and 39/39. The refusal properties for these refinements land with #12. Plan: docs/plans/2026-10-13-0416-ci-bench-off-pr-plan.md U9.
Fixes full-path receipt finding #12 (refusals-beside-generated-laws) on a359a82. The generated schema laws draw only accepted values. Each refined schema the PR adds or changes now states its refusals in its own file, in one import.meta.vitest block whose subject is a private predicate. Each block draws the unrefined base, appends boundary values to every draw, and compares acceptance with a predicate written from the domain: - test/bench: run-path.schema.ts (all ten path fields), leg-clock.schema.ts (elapsed ms finite and >= 0), and the commands of plan-run-path, settle-run-path and measure-leg. - test/e2e-core, the narrowest arrangement under root's scope law (plan KD13: no new file, export, dependency, config or helper): bench-report.schema.ts (LegTargetMs, ref, abort codes = the codes with a next action, rendered exit 0|1), bench-summary.schema.ts (RegressedMetric and PhaseRegression fields), bench-run.schema.ts (failure codes without timeout, exitCode a safe integer, never null) and summarize-bench (ref). Evidence: tsc, oxlint and lint:tsgo clean in both packages; vitest 44/44 in test/bench (+5) and 375/375 in test/e2e-core (+11). The pack's widen-once check is not run: root forbids hand-applied probes. Plan: docs/plans/2026-10-13-0416-ci-bench-off-pr-plan.md U10.
Fixes full-path receipt finding #15 (heavy-suites-judge-every-main-commit) on a359a82. The group was bench-${{ github.sha }} for both triggers, so a workflow_dispatch on main's head shared, and could replace, the pending push run for that commit. A push now groups by github.sha and a dispatch by github.run_id, with cancel-in-progress false, as the rule's Concurrency bullet requires. Why side A is HEAD^1 rather than the last green commit is stated in the lane's cost header (#14). Plan: docs/plans/2026-10-13-0416-ci-bench-off-pr-plan.md U11.
Fixes full-path receipt finding #14 (heavy-lanes-state-their-daily-minutes) on a359a82. bench.yml now opens with the cost header the rule asks for: - the daily figure, about 267 runner-min/day (241-319), priced on the run every push to main starts; - that run's shape, the full corpus against the first parent, and why side A is HEAD^1 rather than the last green commit (the corpus is a fixed workload; an older base selects no smaller workload and would credit several pushes' change to one commit), which also answers #15; - the per-run cost, 15.3-20.3 job-min, with the seven run ids; - the 7-day first-parent merge count, 110 in 2026-10-06T01:54Z to 2026-10-13T01:54Z (15.71/day); - whether the lane can starve PR CI, from the same week's job timestamps: only once about 40 jobs are already running; the replay without the pull_request Bench lowers hours at 40+ jobs from 11.98 to 11.45. Every figure is from GitHub Actions job timestamps already reported in the PR's Bench cost section; nothing was rerun. Plan: docs/plans/2026-10-13-0416-ci-bench-off-pr-plan.md U12.
Supersedes the 0416 plan. R3, U1 and the CONST-W3 list now carry the push-sha/dispatch-run-id concurrency group from 7d962fc. U7's test sentence matches the landed settle law (it draws the production command and asserts the carried causes, not message text, whose oracle would copy Cause.pretty). U12 records that the HEAD^1 rationale lives in the cost header.
Supersedes the 0434 plan. U11 no longer claims an inline comment; the rationale is in the cost header (U12).
…eipt Supersedes the 0435 plan. Root's merge bar (2026-10-13) requires a fixing commit on the reported head for every confirmed pack violation and accepted finding. Adds KD16 (push runs scoped from the last green main commit, legs selected by Turbo's affected set), KD17-KD19 and units U13-U18.
Fixes b30908c full-path receipt finding #5 (maintainability: the fallback hand-rebuilt every BenchReport field). reportOf now takes report runs, and writeReport's fallback calls it with the report's own fields. Plan: docs/plans/2026-10-13-0554-ci-bench-off-pr-plan.md U13.
…sals Fixes b30908c full-path receipt finding #2 (testing: the corpus empty-repo refusal lost its only test when bench-decisions.integration.test.ts was deleted). bench-corpus.schema.ts is consumed only by the bench, so it moves from test/e2e-core/src to test/bench/src, unchanged, and e2e-core's mod.ts drops its export. Beside it, one in-source refusal block states every refinement the corpus carries: each text field refuses only the empty string, and each list (repo, mutate, testFiles) refuses only the empty list. The repo list's refusal is the empty-repo case the deleted test covered; the bench aborts such a corpus as corpus-unreadable. test/bench: 54 tests (44 + 2 refusal properties + 8 generated codec laws); test/e2e-core: 367 (375 less the moved schema's 8 codec laws). Plan: docs/plans/2026-10-13-0554-ci-bench-off-pr-plan.md U15 (amended in the next plan revision: a move, not a workflow).
Fixes b2fe30db #11 (schema-laws data-only-schema-classes, confirmed): BenchRepoEntry, BenchEnterpriseCorpus and BenchCorpus were S.Class declarations, though none is a Workflow.make command, a decision variant or a tagged error; every consumer only reads their fields. They are now S.Struct schemas with exported types. BenchCorpusJson keeps its S.*-rooted chain, so its generated laws still run, and the refusal block is unchanged. Plan: docs/plans/2026-10-13-0749-ci-bench-off-pr-plan.md U28.
Fixes b2fe30db #6 (maintainability, confirmed): command.ts kept a 4000-character tail over a CommandOutcome, and run-bench.service.ts a second 4096-character tail over a string, both added by this PR. command.ts now owns the one budget (4096, private) and exports tailOf(text); runChecked, the interrupt message and runCli's stderr collector all use it. A setup failure keeps 96 more characters of output. Smoke: a plan run whose gh exits 7 after 6000 bytes of stderr fails with an annotation that carries the last 4096 characters. Plan: docs/plans/2026-10-13-0749-ci-bench-off-pr-plan.md U29 (the plan says both names are exported; only tailOf has an importer, so the budget stays private).
This was referenced Oct 13, 2026
systemfsoftware-maker
added a commit
that referenced
this pull request
Oct 13, 2026
…lution The root accepted D1, D2, D3 (guards checked first), D5 and D6, the Done grep, no-rename, no-shim, R6, R7, the inventory and the landing table. Its five review corrections are now in the plan: - no scratch-edit proofs: refusals are committed negative fixtures or a closed deliberate-red PR (bench lint ban, E2E-1's missing-journey case); - a contract codec is never mutation-graded, because Mutation selects *.workflow.ts only; PR C's codec is graded by its schema laws; - #306 and #327 fixed the ManifestDocument and CountedReportJson flakes, so the law moves unchanged with no follow-up unit; - D2's corpus change is timed before the post-#316 speed baseline, or recorded as a reviewed corpus change; - landing order A, then C at any time, B after #316 and #298, D last. PR A's evidence and its one-time cost (the first main Mutation run after merge re-verifies every mutant, because the cache path list changes) are named in the plan.
ryanleecode
pushed a commit
that referenced
this pull request
Oct 13, 2026
* docs(plans): plan the dissolution of test/e2e-core into the e2e harness, the bench and the CLI contract Per-file destinations for all 80 files, a four-layer stack (Mutation de-enrolment, bench, stream codec, harness and deletion), Stream K sequencing and the landing order of the open PRs that touch the package. ce-doc-review skipped: the root reviews this plan (r42). * docs(plans): refresh the test/e2e-core dissolution plan against main and the root's corrections Applies the root's six corrections of 22:25: MUTATED_PROJECTS is data at lockfile-closure-digest.integration.test.ts:29-34 and changes with mutation PROJECTS; stryker-js-cli-contract is not in PROJECTS, so the moved stream codec is ungraded until Stream K enrols it; the lint coupling at test/bench/oxlint.config.ts:1, test/bench/tsconfig.node.json:11, test/e2e/oxlint.config.ts:17 and test/e2e/AGENTS.md:21; kill-matrix.yml 9/15/82 and mutation.yml 36/43/64/118; the exact inventory (29 src modules plus mod.ts, 13 laws, 7 suites, 18 fixtures, 12 package files); and verified or [unverified]-marked pack and config claims. Adds Done and Does-not-count, the 28 outside files that name the package by layer, #287's overlap with PR D, #321's merge, and D6 for the three fixture schemas under tests-own-no-schemas. * docs(plans): record the root's plan review of the test/e2e-core dissolution The root accepted D1, D2, D3 (guards checked first), D5 and D6, the Done grep, no-rename, no-shim, R6, R7, the inventory and the landing table. Its five review corrections are now in the plan: - no scratch-edit proofs: refusals are committed negative fixtures or a closed deliberate-red PR (bench lint ban, E2E-1's missing-journey case); - a contract codec is never mutation-graded, because Mutation selects *.workflow.ts only; PR C's codec is graded by its schema laws; - #306 and #327 fixed the ManifestDocument and CountedReportJson flakes, so the law moves unchanged with no follow-up unit; - D2's corpus change is timed before the post-#316 speed baseline, or recorded as a reviewed corpus change; - landing order A, then C at any time, B after #316 and #298, D last. PR A's evidence and its one-time cost (the first main Mutation run after merge re-verifies every mutant, because the cache path list changes) are named in the plan. * ci(mutation): grade only the shipped products, not test/e2e-core Mutation and the kill matrix mutated four projects: the three product packages and test/e2e-core, a private test-tooling package. Test tooling is not shipped, so grading it spends shard time on code no user runs (plan R7). - mutation.yml drops test/e2e-core from PROJECTS (:36), from the INCREMENTAL_REPORTS cache paths (:43), and from the incremental (:64) and build (:118) cache keys. - kill-matrix.yml drops it from both default project lists (:9, :15) and from its build cache key (:82), which stays byte-identical to mutation.yml's so the kill matrix still restores the mutation build. - lockfile-closure-digest.integration.test.ts drops it from MUTATED_PROJECTS, the test's copy of Mutation's project list, so its live-lockfile scenario checks the projects Mutation actually grades. The package itself, its mutation script and stryker.config.ts stay until the last layer deletes the package. One-time cost: a cache entry's version is a hash of its path list, so the first main Mutation run after this lands cannot restore the previous incremental reports and re-verifies every mutant once. * test(stryker-js): read the mutated projects from mutation.yml in the lockfile test The lockfile-closure scenarios checked "every mutated project" against a hard-coded copy of Mutation's corpus, which nothing compared with the workflow. The test now reads env.PROJECTS from mutation.yml, so the two lists cannot drift apart (review run 20261013-084434-c89dc2ab, testing P2). CONST-W3: this PR edits .github/workflows/mutation.yml and kill-matrix.yml, which AGENTS.md:7 marks read-only and CONST-E9 treats as a judgment surface. Root assigned that edit when it accepted the plan (PR A). The edit only removes test/e2e-core from the corpus; no threshold, baseline or check changes. .stryker/budget-baseline.json stays as measured over four projects, and its owner decides whether to re-measure it. Plan 0900 supersedes 0218 with these review outcomes. * chore(changeset): record a none intent for the lockfile test's corpus change release.jsonc now gates intents on turbo's typecheck graph (#335), whose inputs include tests, so the test edit in this PR needs an intent. Nothing published changes. * docs(plans): name the workflow-derived corpus wherever plan 0900 cited MUTATED_PROJECTS Delta review run 20261013-090355-9173e728 (P3): the Problem Frame, the PR D ordering risk, the landing note and P9 still described the retired constant. They now give it as main's state at 381b3f5 and name the read of mutation.yml env.PROJECTS that replaces it.
…lane Fixes b2fe30db #9 (P1, boundary-testing pin-dependency-semantics, E7): the plan job's Turbo --packages output was pinned only by a measurement. scripts/turbo-affected-packages.test.ts runs the plan job's own Turbo arguments (turboAffectedArgs, now shared with plan-legs.ts) through node_modules/.bin/turbo, the binary `pnpm exec turbo` resolves to, in scratch pnpm workspaces, and decodes the output through TurboAffectedPaths, the schema plan-legs.ts decodes with. It pins three facts: a changed package reports its repo-relative directory (packages/a), a root file reports "", and no change reports []. TurboAffectedPaths moves to turbo-affected.schema.ts: the scripts lane type-checks with Deno, which cannot resolve bench-plan.schema.ts's in-source law block (@systemfsoftware/vitest, import.meta.vitest). Its generated codec laws still run there. scripts/deno.json takes #319's permission hunk byte for byte, plus an effect/ import-map entry, and scripts/assert.ts is #319's file byte for byte, so merging #319 later is a no-op for both. Deno applies the grant only with -P, which ci.yml:137 does not pass yet; root's workflow owner adds it in a separate PR. Until then these three tests fail in the Repo scripts tests step with NotCapable, as #319's do. Plan: docs/plans/2026-10-13-0954-ci-bench-off-pr-plan.md U31, KD22.
Root's E10 ruling: per-SHA concurrency groups stay, because every main SHA must be judged, and each leg job is capped at 20 minutes instead of 60. A leg's longest measured job is 7.68 job-minutes. The plan job keeps 60. Plan: docs/plans/2026-10-13-0954-ci-bench-off-pr-plan.md U32, KD2.
…iption and plan Fixes the f13b992..aabe4c2 correctness lens finding #2 (P3, confirmed): cf39161 lowered each Bench leg job's timeout to 20 minutes, but the @systemfsoftware/stryker-bench description still said GitHub's 60-minute job timeout is the only kill, and the 0954 plan's CONST-W3 caps bullet, R13 and the read-bench-run admission row still said 60 minutes. The plan job keeps 60, so plan.ts's step-summary line is unchanged. Plan: docs/plans/2026-10-13-0954-ci-bench-off-pr-plan.md U32, KD2.
ryanleecode
approved these changes
Oct 13, 2026
ryanleecode
left a comment
Contributor
There was a problem hiding this comment.
Root verdict: full-path receipt (feb5085) plus delta lenses f13b992..aabe4c2 and aabe4c2..227773f (MiniMax M3.1, 0 findings) cover the exact head. CI green on 227773f, including the turbo pin tests under -P (run 38416467229, check job 115365565303: 13 passed, 0 failed). 0 open threads, 0 behind main. The open P3 (stale KD22 sentence at plan:101) is plan prose with no behaviour, so I've deferred it to the next plan revision rather than adding a push and a CI re-run.
systemfsoftware-maker
added a commit
that referenced
this pull request
Oct 13, 2026
…shortcut Brings main up to efd1996 (#328 type queries, #322 CheckerRuntime split, #331, #313, #316, #329, #340). Two import conflicts, both additive: type-query.handle.ts keeps main's context-link imports and this branch's shared decided from ts-compiler.handle.ts; compare-sides' law imports both Deferred (main) and DigestCall (this branch). #322 removed CheckerRuntime.layer from ./runtime, so the importer-shortcut and isolated-declarations integration tests now build the runtime with TypeScriptCheckerRuntime.layer.
This was referenced Oct 13, 2026
systemfsoftware-maker
added a commit
that referenced
this pull request
Oct 13, 2026
The owner's merge of main (73cc2e0) brought #316's scripts/turbo-affected-packages.test.ts into //#test:scripts. It imports test/bench/src/turbo-affected.ts and turbo-affected.schema.ts, which were in no input: an edit to them left the task's hash unchanged and --affected selected nothing. Both are now inputs; measured on Turbo 2.10.12, a bench edit moves the hash (96c8c1fd -> 8e13d64d) and selects the task. The plan moves to a new dated file that supersedes the 10:28 one.
This was referenced Oct 13, 2026
systemfsoftware-maker
added a commit
that referenced
this pull request
Oct 13, 2026
Root's reading of the first four green main Bench push runs after #316 (38429923569, 38430066932, 38436282117, 38436508210) replaces the plan's [unverified] baseline run ids, with the per-leg median-of-four phase table. Those runs read `instrument` as about 0 ms on every leg because the mark is misplaced, so they are the baseline later T10 layers compare against, not the T9 reading: R18 now reads `S` from the first four green main push runs after U1 merges. R17 states Bench's non-overlapping-samples rule, since a single typescript-checker run took 33.5 s against about 46 s for the other three.
ryanleecode
pushed a commit
that referenced
this pull request
Oct 13, 2026
…0 U1) (#348) * perf(stryker-js): enter the instrument phase before instrumenting The `instrument` phase mark and its `instrument` span ran in the instrument cells' write phase, after `instrumentFiles` and `makeSandbox` had already run in the read phase. Instrumentation and the sandbox copy therefore counted under `prepare`, and every Bench sample read `instrument` as 0-1 ms, so the instrument share of wall clock that gates the T9 native-core restart could not be read. Both cells (`instrument.cell.ts` for runs, `plan-instrument.cell.ts` for plans) now open the phase span and emit the mark at the start of the read phase; the write phase only offers file skips. The new engine integration test drives a dry-run-only run and a plan through the published `Engine` entry points and asserts from OTel span intervals that the gather and sandbox acquisition spans fall inside the single `instrument` span and that the phase mark ends before gathering starts. It failed on the old placement and passes on the new one. Lands the T10 infection-filtered execution plan (U1 of it, R18, KTD9). * docs(plans): record the pre-U1 Bench baseline for T10 Root's reading of the first four green main Bench push runs after #316 (38429923569, 38430066932, 38436282117, 38436508210) replaces the plan's [unverified] baseline run ids, with the per-leg median-of-four phase table. Those runs read `instrument` as about 0 ms on every leg because the mark is misplaced, so they are the baseline later T10 layers compare against, not the T9 reading: R18 now reads `S` from the first four green main push runs after U1 merges. R17 states Bench's non-overlapping-samples rule, since a single typescript-checker run took 33.5 s against about 46 s for the other three. * test(stryker-js): pin the instrument phase on a failing sandbox build Review finding (P3, testing): the changeset says a run that fails while instrumenting or building its sandbox now reports the `instrument` phase as entered, but no scenario drove that failure. The new scenario writes projects whose `buildCommand` exits 1 and runs dry-run-only mutation runs in them. Through `Engine.mutationTestCell` and the stage event queue it asserts the run fails in the `instrument` stage with reason `Sandbox initialization failed`, after `prepare` then `instrument` were reported entered. Through `Engine.strykerCell` and the span exporter, in a second fresh project, it asserts the same stage failure and that the failed gathering and sandbox work stay inside the one `instrument` phase span. Against main's cells the phases read `['prepare']` and no phase span exists; on this branch both hold. Every scenario runs inside its temp project's working directory, so config discovery cannot pick up the package's own Stryker config.
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.
Summary
No pull request runs the bench any more.
bench.ymlruns on each push tomainand onworkflow_dispatch. A plan job picks side A and the legs: on a push, side A is the lane's last green main commit, and a leg runs only when Turbo reports its package, or an engine directory every leg builds, affected since then. The full corpus runs on a dispatch, when no green ancestor exists (side A is then the first parent), and when the lane's own inputs changed. Time is reported, never enforced: GitHub's job timeout is the only kill, 20 minutes on each leg job and 60 on the plan job. When a leg'stotalregresses, the verdict names the commit, its base, the metrics and root's next action.This is layer 1 of the split in
docs/plans/2026-10-13-0954-ci-bench-off-pr-plan.md, which supersedes the 0749, 0711, 0658, 0629, 0554 and 0435 plans and, through them, the 1800, 1854, 1926, 2032, 2154, 0223, 0238, 0258, 0416 and 0434 plans. Layer 2 (the enterprise install) is judged separately and stacks on this one. #312 stays open and untouched. Most hunks here are ported from it, without its run-identity gate, provisional reports and lockfile pins.Breaking:
bench-report.jsonmoves from1.1to2.0. Thesetup-timed-outandbudget-exceededabort codes and thetimed-outrun exit are gone, and the report addsref,legand the regression block.CONST-W3 declaration:
AGENTS.md:7lists.github/workflows/as Read-only, and this PR edits.github/workflows/bench.yml. The edits are:mainplusworkflow_dispatch, withpull_requestandpaths-ignoreremoved, and both checkouts atgithub.sha;bench-${{ github.event_name == 'push' && github.sha || github.run_id }}, no cancel,timeout-minutes: 20on the leg job and 60 on the plan job;pnpm --filter @systemfsoftware/stryker-bench plan, which outputs side A and the legs;actions: read, so it can list the lane's own runs; and the bench job'sif, which skips it when no leg is selected;bench-aat the plan job's base andbench-batHEAD, each with its own released tarballs and install;github.shaand passed asBENCH_TURBO_CACHE_DIR;JOB_STARTED_AT,BENCH_REF, and uploads onalways();The grant is Ryan's (root) intent of 2026-10-12 to take the bench off the PR path. CONST-E9 does not apply: Bench grades main's commits, not this PR. No other workflow file is touched.
What changes
mainplusworkflow_dispatch, with no path filter. Concurrency isbench-${{ github.event_name == 'push' && github.sha || github.run_id }}withcancel-in-progress: false, so a push groups by its commit and a dispatch by its run id. Each leg job hastimeout-minutes: 20and the plan job 60 (root's E10 ruling,cf39161d). The workflow opens with its cost header (see Bench cost).chooseBase(test/bench/src/choose-base.workflow.ts) picks side A: on a push, the newest green Bench push run onmain(by run creation time) whose commit is an ancestor ofgithub.shaand is notgithub.sha, from thegh apilisting andgit merge-base --is-ancestor; with none, or on a dispatch, the first parent.selectLegs(test/bench/src/select-legs.workflow.ts) then picks the legs from the diff against side A andturbo query affected --packages. The lane's own inputs aretest/bench(as Turbo reports it),bench.yml,.github/actions/released-tarballs/and the flake pin; a change to any of them runs the full corpus. The job writes its choice and its wall time to the step summary. It fails when the checkout has uncommitted changes, because Turbo adds them to the affected set, and when a commit has neither a green ancestor nor a first parent. When no leg is selected, the bench job is skipped and the run is green.github.sha. Each is a worktree underRUNNER_TEMPwith its own released tarballs and its ownpnpm install --frozen-lockfile, and each is timed. The checkout holds only the harness.BENCH_DEADLINE_MS, the setup budget, the run deadline andBENCH_RUN_TIMEOUT_MSare all deleted, along with the report shapes only they produced.JOB_STARTED_AT. The report and step summary show the leg's wall time against a 10-minute target (LegTargetMs). Going over changes neither the exit code nor the verdict level.totaldecides a regression. It needs non-overlapping four-run samples and |Δ median| >= max(3% of A's median, 10 ms). The regression block liststotalfirst, then every other phase of that leg that regressed. Onrefs/heads/mainthe next action is "root opens a revert PR of the culprit or a root-fix P0"; on any other ref it is "dispatched on : speed evidence only, no revert". The report'sregressionis a tagged union,PhaseRegressedorNoLegRegressed. A regression issummarizedand exits 0 with an::errorline. Onlyfailed/abortedexit 1.RUNNER_TEMP/bench-run. The side's tree is renamed there for the run and back afterwards, including when the run fails. The leg aborts withrun-path-occupied,run-path-move-failedorrun-path-unrestored. Bench configs now reference plugins relative to the config file, so they still resolve after the move. On the old two-root layout, A/A run 38135504932 recorded different ids per side and reported a false regression of +1311.5 ms (3.5%) on the typescript-checker leg.Turbo compliance
This PR adds no hash cache, change classifier or cache gate, and it removes one: the hand-written
paths-ignore(**/*.md,docs/**) on the bench trigger. Evidence is fromturbo query(turbo 2.10.12) on this branch:paths-ignoreversus Turbo. Over 11 sampled main commits,paths-ignoreskips none.turbo query affected --packages <leg packages> --base <parent> --head <sha> --exit-codeskips every leg on61d40db4,ee2c4cfband3495e21d, and runs only the affected legs onddeb67ab(e2e-core and enterprise) and3cb13423(enterprise). This PR uses that only as evidence: every push to main benches every leg, because the acceptance is one comparison per main SHA. Skipping legs Turbo calls unaffected is deferred to root (plan KD11).turbo run buildwith one shared--cache-dir. The enterprise closure comes fromturbo run build --dry=json. Task hashes for@systemfsoftware/stryker-js's closure (15 tasks) are identical in the checkout and in a worktree at another path, so the shared cache hits by content only.Remaining bespoke mechanisms, each with its rule-4 reason:
.sfs-depsreleased tarballs, cached on thestryker-publishedflake pin (thereleased-tarballsaction and the same-pin step). These are Nix store paths: a Nix flake output that Turbo neither builds nor hashes. A pin change that matters also changespnpm-lock.yaml, which Turbo reports asLockfileChanged(6c6eaaa9).actions/cachetransport of.turbo/cache. The content is Turbo's own task cache. The repo has no Turbo remote cache: noTURBO_TOKEN,TURBO_TEAMorTURBO_APIin any workflow, and noremoteCacheinturbo.json. Enabling one needs a credential and belongs to Stream K's repo-wide audit, which also covers the same transport inci.yml,mutation.ymlandkill-matrix.yml.Not caches:
workload-digest.service.tsonly proves both sides ran one workload and skips no work, and the pristine-tree restore resets the tree between runs. The bench runs the CLI with--full(run-bench.service.ts:90), so Stryker's incremental reuse plays no part in a measured run.Tests and root's testing law
Root's law of 2026-10-12: every new or changed integration test drives the real product through its public surface on a real project and asserts only observable outcomes, and a pure decision gets property laws beside it.
Integration tests: none are new and none remain changed. This PR had to change one line of
test/e2e-core/tests/bench-decisions.integration.test.ts(ReadBenchRunCommand's exit field). That file did not drive the product. It fed hand-written NDJSON lines, corpus text and report text to the purereadBenchRun,coveringTestFilesandBenchCorpusJson, and asserted decision fields. Under the law, and CONST-T12/T14, it is a decision test filed as an integration test, so0db0ffa8deletes it. A compliant replacement would run the bench CLI on a real corpus. That is the Bench lane itself, which runs on main and on dispatch, and its evidence is the A/A run below.Pure-core property tests, each beside its workflow and drawing production schemas:
test/bench/src/__tests__/, new, with the modules moved there under the scope law (below):measure-leg;plan-run-path, including a cwd equal to the tree, the enterprise shape;settle-run-path.test/e2e-core/src/__tests__/, changed:summarize-bench:totaldecides, the 10 ms floor, and a phase-only regression gives no block;render-bench-report: next action by ref, exit and level independent of wall time, and the phase-only slowdown notice;read-bench-run: thetimed-outlaws are removed with the shape. Laws re-homed from the deleted file cover the cost sum over drawnMutantCost, the 1-based number of the undecodable line among blank and decodable lines, a verdict count other than one, and phase durations carried or refused;covering-test-files, re-homed: sorted distinct holders, and non-JSON text refused.test/benchtest lane. It now has atestscript, avitest.config.tswithinSourceSchemaLaws()in the same shape astest/checker-parity, andtsconfig.test.json. Root'scheckruns it through Turbo with every other package'stest. It is not in the mutation lane:mutation.yml:36hard-codesPROJECTS, which Stream K owns, so the moved workflows have laws but no mutation grading until K enrolstest/bench.Not re-pinned, because another suite or the type already holds them:
not-recordeddecode, pinned bypackages/stryker-js-cli-contract/tests/run-event-wire-line.integration.test.ts:324-338(the sameRunEventWireLinecodecreadBenchRunuses);bench-corpus.schema.tsmoved intotest/bench/src, its only consumer (8689134a), and its in-source block checks that every text field refuses only the empty string and every list, the repo list included, refuses only the empty list.Placement under root's scope law (2026-10-13)
The law: no new code goes into
test/e2e-core, no commit or PR scope is namede2e-core, and no newcore,commonorsharedtest-tooling package is created. Stream D owns dissolvingtest/e2e-core.Moved to
test/bench/src(e28351fc).run-path.schema.ts,plan-run-path.workflow.ts,settle-run-path.workflow.tsandmeasure-leg.workflow.ts, with their laws.test/benchis their only consumer, and they importBenchSideandLegTimefrom@systemfsoftware/stryker-e2e-core.test/e2e-core/src/mod.tsis back to main.New
leg-clock.schema.ts.measureLegnow decides a bench-localLegClock(LegElapsedorLegClockBehind), andmain.tsmaps it onto the report'sLegTime. The reason:make-body-purityforbids a decision from constructing a class imported from another package, and e2e-core cannot importtest/bench.Left in e2e-core for Stream D. Edits to six pre-existing modules stay where they are:
bench-report.schema.ts,bench-summary.schema.tsandrender-bench-report.workflow.ts;timeoutfailure code andRunExit, inbench-run.schema.tsandread-bench-run.workflow.ts;summarize-bench.workflow.ts, which rescaled the pre-existing∀abklaw.Their four law files stay beside their modules, and the re-homed laws must not be lost (root, 2026-10-12).
Scope names. The PR title is
ci(bench)!. Commit0db0ffa8is titledtest(e2e-core), and renaming it needs a force push, which this branch never does. Main squash-merges under the PR title, so that subject never reaches main.Counter-critic. One
adversarial-reviewerround (served byminimax-code/MiniMax-M3.1-Flash-Preview, with no fallback) upheld every placement decision. It corrected the e2e-core rationale above into that three-way split, which is now in the plan.Result for the 19 files (root's merge-cycle check, 2026-10-13).
At
e5844df9the PR changed 19 files undertest/e2e-core: 7 added, 11 modified and 1 deleted.e28351fcmoved all 7 added files totest/bench/src, so ata359a82dthe PR adds no file undertest/e2e-core(git diff --name-status 25120175 a359a82d -- test/e2e-core: 10M, 1D).The 10 modified files can't move either: every export this PR adds to them is used by a declaration that already exists on main, inside e2e-core. Moving any of them would mean moving those modules, which is Stream D's work.
25120175)LegTargetMs,LegTime(anS.TaggedUnionofLegMeasuredandLegNotMeasured)BenchReport(bench-report.schema.ts:50, nowleg: LegTime),renderBenchReport(render-bench-report.workflow.ts:268,legTextat:274)RegressedMetric,PhaseRegressionBenchSummary(bench-summary.schema.ts:100,regressionat:120),BenchReportOutcome(bench-report.schema.ts:69,summarized.regressionat:75),summarizeBench(summarize-bench.workflow.ts:391),renderBenchReportBenchRunFailureCode(narrowed in place)BenchRunInvalid.code(bench-run.schema.ts:52, now:48)Every other line this PR adds to those files is either a non-exported helper inside a workflow that already existed, or one in-source refusal block per refined file (next section). No code needs to move.
Pack compliance round (root, 2026-10-13)
Root required every confirmed pack violation in the
a359a82dfull-path receipt to be fixed. None was merged as is or waived. Each fix is its own pushed commit:four-channel-contracts26c352b9RunPathUnrestoredcarriescause: S.optional(S.Unknown)and the run's end. It no longer holdsCause.prettystrings. The service passes eachExit'sCause, and the text is rendered once, in the error'smessage, at the report edge.tagged-unions-over-state-by-presence26c352b9RunPathMoveBack(MovedBackorMoveBackFailed { cause }) andRunPathRunEnd(RunSucceededorRunFailed { cause }), and matches on the move-back. No nullable state fields remain.data-only-schema-classes08eff64aLegTimeis anS.TaggedUnion. Two classes and a type id are gone, and the encoded shape is unchanged.invariants-as-refinementsf501c320LegElapsed.wallMsis finite and>= 0.BenchReport.refisS.NonEmptyString, andBENCH_REFis read withConfig.NonEmptyString, so an empty value aborts asenvironment-incomplete.refusals-beside-generated-laws486ae9b4test/benchthat is 5 files; in e2e-core, 4 files (boundary below).heavy-suites-judge-every-main-commit7d962fcagithub.run_id).heavy-lanes-state-their-daily-minutes913fcbf9bench.ymlopens with the cost header.5a088c60re-priced it for the affected-only selection.#13 (
surface-changes-are-versioned) stays rejected:@systemfsoftware/stryker-e2e-coreis private.The e2e-core boundary (plan KD13). Two fixes had to touch pre-existing e2e-core files. #9 did because
LegTimeis declared there and consumed byBenchReport. #12 did because the rule puts a refusal in the refined schema's own file and bans a separate refusal file. Each took its narrowest form:if (import.meta.vitest !== void 0)block to each ofbench-report.schema.ts,bench-summary.schema.ts,bench-run.schema.tsandsummarize-bench.workflow.ts. Each block covers only the refinements this PR added or changed there.No e2e-core file, export, dependency, config or helper is added. The mechanism already exists there:
annotation.schema.ts:107, andincludeSourceatvitest.config.ts:12. Stream D moves the blocks with their files.Deviations, recorded in the plan:
Cause.pretty.Evidence
227773f4(contains maincec409b1, ci(ci): apply scripts/deno.json permission grants in the repo scripts tests #338's-Patci.yml:137; checked withgit merge-base --is-ancestor), all green, attempt 1, nothing rerun:CI 38416467229: every job
success(cache-sweepskipped), includingcheck(115365565303),checker-parity (1)-(6) ((4)is 115365665126) andchecker-parity-report. Incheckthe Gate ran:GATE: success,PLANTED: success, Turbo18 successful, 18 total.Changeset Check 38416468148, Nix 38416467685 and Commitlint 38416467054:
success.The Turbo pin ran and passed in the Repo scripts tests step (
checkjob 115365565303, stepRun ./bin/deno test -P --config=scripts/deno.json scripts/):f13b9920(base2083ba43, merged with main381b3f5b):tsc -b,oxlintandlint:tsgoexit 0 intest/bench, andvitest runpasses 18 files and 107 tests;test/e2e-coreis unchanged sincefeb50851, wheretsc -b,oxlintandlint:tsgoexit 0 andvitest runpasses 44 files and 367 tests;dprint checkexits 0.chooseBasetaking the first listed run, andchooseBasenot excluding the pushed commit, each fail the push law (shrunk counterexamples).summarizeBenchdecidingNoLegRegressedwhen a leg regressed fails five laws acrosssummarize-benchandrender-bench-report. Each file was restored and its suite passes.ci.ymlgreen-run listing (side A is the newest ancestor, not the first listed), an empty listing and a dispatch (both take the first parent), a commit whose parent is a lane input (full corpus), and a failinggh(one escaped::errorline carrying the last 4096 characters).b30908ce(base25120175):tsc -b,oxlintandlint:tsgoexit 0 intest/benchandtest/e2e-core;dprint checkandgritlint checkexit 0;vitest runpasses 10 files and 44 tests intest/bench, and 45 files and 375 tests intest/e2e-core. That is the 11 new refusal properties, less 3 generated codec laws for the two deletedLegTimeclasses.path === treedropped fromplanRunPath, theplan-run-pathidentity law fails. Restored, it passes.git stash.896a494c: run 38297580066 (dispatched by the sub-conductor), all 5 jobs green.Side A is the merge's first parent
17e21b47. It is product-identical: the merge brought in ci(e2e): run the e2e legs on every main push instead of on pull requests #314, which is workflows and docs only.On every leg the report is schema
2.0andsummarized, withregression: nulland workloadsame.All 8 runs share one project root, under
RUNNER_TEMP/bench-run.On every leg both sides recorded the same inputs digest and the same mutant-id digest:
266e2253/241c14a16129db89/60bd6521b518457e/85be64a8991132ef/4d840b34Every
totalreads no-signal: +115 ms, −419 ms, −2 ms and −418.5 ms.Leg wall times against the 10-minute target: 7.5, 4.9, 3.6 and 3.6 min.
9b518ba7: run 38375500452 (dispatched by root), all 5 jobs green. This closes E8 and E11. It is the executed evidence foratRunPath's renames, and it covers every runtime change since896a494c.Side A is the merge's first parent
f13b9920. It is product-identical: the merge brought in ci(repo): gate change intents on turbo's typecheck task graph #335, which changesrelease.jsonconly.On every leg the report is schema
2.0andsummarized, withregression: NoLegRegressedand workloadsame.On each leg all 8 runs share one project root, under
RUNNER_TEMP/bench-run.On every leg all 8 runs, both sides, recorded the same inputs digest and the same mutant-id digest (SHA-256 of the sorted mutant ids, one per line):
266e2253/241c14a16129db89/60bd6521b518457e/85be64a8864392bd/4d840b34Every
totalreads no-signal: −254 ms, −196.5 ms, +202 ms and +20.5 ms. No other phase separates; e2e-core'scheckisnot-measured.Leg wall times against the 10-minute target: 7.6, 3.0, 1.9 and 2.0 min, all under the new 20-minute leg cap.
The mutant-id digests equal run 38297580066's on all four legs, and so do three of the four inputs digests; the enterprise inputs digest differs from that run's
991132ef, while both sides of this run agree on864392bd.Reports read from the run's
bench-report-*andbench-runs-*artifacts.Review
Merge-bar receipt: correctness lens
aabe4c2a..227773f4(exact head)Root named this lens as the remaining merge blocker; it supersedes the
feb50851range as the head-covering receipt. One named, report-onlycorrectnesslens, run id20261013-160821-9393a660, reviewed exactlyaabe4c2a22a9ea0dcc929457fc4aff728ea7e804..227773f4b31032c38aefe1bdc8f6de085a810146on a clean checkout of the head. The range holds:4646f883(the 20-minute leg-job timeout intest/bench/package.jsonand the 0954 plan);227773f4ofmainb4db6d8e, which brings incec409b1(ci(ci): apply scripts/deno.json permission grants in the repo scripts tests #338,-Patci.yml:137),e3875838(chore(repo): remove the vendored Effect subtree and cite upstream permalinks #332),701be706(refactor(repo): clear 19 of 20 turbo boundaries diagnostics without touching a gate #326),2269a986(fix(stryker-js)!: never remove or break another run's sandbox when runs share tempDirName #331) andb4db6d8e(fix(stryker-js): reuse dry-run coverage only under the root that recorded it #313).The merge is conflict-free:
git show --cc 227773f4is empty, and the tree equalsgit merge-treeof both parents.main's side touches no PR-owned file. Its only workflow or script hunk iscec409b1; thetest/e2e-corefixture edits are #326's. Artifacts:/tmp/compound-engineering-0/ce-code-review/20261013-160821-9393a660/correctness.json,full.diff(range log,4646f883, the combined diff,cec409b1and the overlap facts),files.txtandserved-models.json.Served model: the lens ran on
correctness-reviewer-minimax. Its session record shows onemodel_changetominimax-code/MiniMax-M3.1-Flash-Previewand MiniMax M3.1 for all 9 assistant turns (providerminimax-code), with no error, no 429, no fallback, and no Grok,xai-oauthor Claude call.Result: 0 findings. It checked every timeout claim against
bench.yml(plan jobtimeout-minutes: 60at:42, leg job20at:82) andtest/bench/README.md. It did not execute the bench or CI.test/bench/src/plan.ts:115prints "GitHub's 60-minute job timeout is the only kill" in the plan job's step summarybench.yml:42), so it is correct. The lens notes only that a reader could mistake it for the leg claim.bench.yml's valuesLimitation: the lens did not re-raise the stale KD22 sentence at
docs/plans/2026-10-13-0954-ci-bench-off-pr-plan.md:101("ci.yml:137runs ... without-P"). That line is unchanged in this range, but the mergedcec409b1made it false, and the prompt asked about such statements. It stays the open P3 confirmed by lens20261013-160030-440b1a8ebelow, with no fixing commit, because the remaining rounds are body-only. E7 and Evidence record the current state.Pin evidence on the exact head (verified by root and in this session): CI run 38416467229,
checkjob 115365565303, ran./bin/deno test -P --config=scripts/deno.json scripts/, executed 3 tests fromscripts/turbo-affected-packages.test.ts, allok, and endedok | 13 passed | 0 failed (534ms).Earlier delta correctness lens:
feb50851..227773f4This receipt, with the full-path receipt of
feb50851below, covers the exact head227773f4b31032c38aefe1bdc8f6de085a810146. One namedcorrectnesslens, run id20261013-160030-440b1a8e, reviewed exactlyfeb50851dfb6417eee0486e3c1f90ed2538e7b5e..227773f4b31032c38aefe1bdc8f6de085a810146on a clean checkout of that head. The range holds 8 PR commits (a888bb61,66086e27,5f437647,f13b9920,6e7a210c,cf39161d,a0c3d693,4646f883) and 4 merges ofmain(e972b7da,9b518ba7,aabe4c2a,227773f4) bringing in 11 main commits, which the lens excluded. Its diff is restricted to the 16 files the PR commits touch. Artifacts:/tmp/compound-engineering-0/ce-code-review/20261013-160030-440b1a8e/correctness.json,full.diff,files.txtandserved-models.json.Served model: the lens ran on
correctness-reviewer-minimax. Its session record shows onemodel_changetominimax-code/MiniMax-M3.1-Flash-Previewand MiniMax M3.1 for all 23 assistant turns (providerminimax-code), with no error, no 429, no fallback, and no Grok,xai-oauthor Claude call.Result: 1 finding, P3; no behavioural defect. It traced all 8 PR commits and found the schema moves keep every call site, and
tailOfkeeps the same tail semantics for both callers.-P;ci.ymlalready passes itdocs/plans/2026-10-13-0954-ci-bench-off-pr-plan.md:101227773f4: KD22's second bullet still saysci.yml:137runs without-Pand that root merges once the owner PR lands, which was true when written and is stale since #338 (cec409b1) merged in. No fixing commit: root ruled this round a body-only edit with no push. The current state is recorded in E7 and Evidence (pinsokin run 38416467229). The one-sentence plan fix waits for root's next push authorization.Residual risks it recorded: the consumers of the
5f437647and66086e27moves were checked by text search only (no symbol-aware references); the 20-minute leg cap rests on measured job-minutes (longest 7.68) with no measured cold-cache push distribution. Testing gap: the pins' red/green status on the merged head was, in the plan, narrative only. Evidence now quotes the CI log line instead.Limitation: this is one lens, not a 12-lens full-path run. Root asked for exactly one named correctness lens over this range. The pack and persona lenses' last full run is the
feb50851receipt below.Full-path receipt: head
feb50851, range2083ba43..feb50851, 47 changed filesThe review is one
ce-code-reviewrun on the full path (mode:agent depth:full base:origin/main), report-only, run id20261013-071850-b2fe30db. Its artifacts are in/tmp/compound-engineering-0/ce-code-review/20261013-071850-b2fe30db/:review.json,metadata.json,stages.jsonl, the per-lens JSON, the pack slices,validator-verdicts.jsonandserved-models.json. All 12 reviewers and the validator returned, and none timed out. Onfeb50851, CI (38361719238), Nix (38361718942) and Commitlint (38361718958) passed; Changeset Check (38361719714) failed for a reason outside this PR (E9).Verdict from
ce-code-review: Ready with fixes. The review confirmed one P1, #9, the Turbo--packagespin. Root ruled on it (E7) and it is now fixed in6e7a210c. Every other confirmed finding has a fixing commit pushed after the review (table below), except #3, which root closed with A/A run 38375500452 (E8).Served model per lens. Each model is read from that subagent's session record: its
model_changeevents and themodelon each assistant message. Every lens ran on a session-local*-minimaxcopy of its persona (KD19). Each started on MiniMax M3.1, took one Token Plan 429, and continued on DeepSeek V4.1 Flash through the profile's fallback chain; the validator stayed on MiniMax M3.1. No call went toxai-oauthor Grok.correctness-reviewer-minimaxcorrectness-reviewer-minimaxcorrectness-reviewer-minimaxgeneralist-reviewer-minimaxgeneralist-reviewer-minimaxadversarial-reviewer-minimaxgeneralist-reviewer-minimaxgeneralist-reviewer-minimaxgeneralist-reviewer-minimaxgeneralist-reviewer-minimaxgeneralist-reviewer-minimaxgeneralist-reviewer-minimaxcorrectness-reviewer-minimaxThe merge leaf is not a lens and decides no verdict. It ran on
docs-engineer, which the operator profile serves on Claude Haiku 4.5 (43 turns) (E4, still open). The cross-model peer did not run, because its configured route isxai-oauth/grok; the adversarial lens ran in-process instead.Corrections made by the dispatch context. The merge leaf anchored pack finding #12 at
bench-plan.schema.ts:72(TurboAffectedPaths, which is rooted atS.fromJsonStringand has its generated laws). The schema-laws slice quotes:41(GreenRunsJson), so #12 was restored to:41before validation, and the validator was told. The merge leaf also selected only the four P1s for validation. Per Stage 5b step 2, the dispatch context re-selected every P0/P1 plus every actionable finding, eight in one batch: #8, #9, #11, #12, #3, #10, #2 and #6. P3 #7 was over the cap and is unvalidated.Packs. Graded from
systemfsoftware/systemfsoftwaremain763fd663:.compound-engineering/config.yaml:cell-architecture,boundary-testingandschema-laws;package-topology,mutation-disciplineandci-wall-clock.Every rule file was ruled: 54 rules in all. 32 are matched by the diff (26 comply, 5 violate, 1 pre-existing) and 22 are not matched.
feb50851file:line)c0cf821breceiptcallable-vs-resource-syntaxtest/bench/src/run-bench.service.ts:133,test/bench/src/run-bench.service.ts:147decode-never-casttest/bench/src/plan-legs.ts:60,test/bench/src/plan-legs.ts:106four-channel-contractstest/bench/src/run-path.schema.ts:68,test/bench/src/run-path.schema.ts:27handle-state-privacytest/bench/src/run-bench.service.ts:85pipeline-compositiontest/bench/src/run-bench.service.ts:288pure-decision-workflowstest/bench/src/choose-base.workflow.ts:38,test/bench/src/select-legs.workflow.ts:52sandwich-phase-ordertest/bench/src/run-bench.service.ts:142-147,test/bench/src/run-bench.service.ts:186-199scoped-lifecycle-boundariestest/bench/src/command.ts:34,test/bench/src/command.ts:37service-and-layer-boundariestest/bench/src/main.ts:269,test/bench/src/run-bench.service.ts:2882344be12single-namespace-barreltest/e2e-core/src/mod.ts:1-3pin-dependency-semantics6e7a210ctest/bench/src/plan-legs.ts:102,test/bench/src/bench-plan.schema.ts:72real-system-oracles.github/workflows/bench.yml:68,test/bench/src/plan-legs.ts:53staged-protocol-evidencetest/bench/src/run-path.schema.ts:50arbitrary-filter-floorstest/bench/src/leg-clock.schema.ts:11,test/checker-parity/src/Parity.schema.ts:279cross-field-invariants-as-struct-checkstest/bench/src/plan-run-path.workflow.ts:31data-only-schema-classes5f437647)test/bench/src/bench-corpus.schema.ts:558f6cc3dinvariants-as-refinementstest/bench/src/leg-clock.schema.ts:11,test/e2e-core/src/bench-report.schema.ts:105law-failure-is-a-codec-defecttest/bench/src/bench-plan.schema.ts:72refusals-beside-generated-lawstest/bench/src/leg-clock.schema.ts:47rich-type-over-foreign-encoded66086e27)test/bench/src/bench-plan.schema.ts:4158f6cc3dtagged-unions-over-state-by-presencetest/e2e-core/src/bench-report.schema.ts:25-28feb50851tests-own-no-schemastest/bench/src/__tests__/choose-base.workflow.property.test.ts:7condition-branch-agreementtest/e2e-core/src/mod.ts:3,test/e2e-core/tsdown.config.ts:6enumerated-entry-modulestest/e2e-core/src/mod.ts:3,test/e2e-core/src/mod.ts:1-27import-time-inertnesstest/e2e-core/src/bench-report.schema.ts:146,test/e2e-core/src/bench-run.schema.ts:77public-signature-typestest/e2e-core/src/bench-report.schema.ts:25,test/e2e-core/src/bench-summary.schema.ts:105surface-changes-are-versionedtest/e2e-core/src/mod.ts:3,test/e2e-core/src/bench-run.schema.ts:33flake-outputs-build-once-on-main.github/workflows/bench.yml:32,.github/workflows/bench.yml:26heavy-lanes-state-their-daily-minutes.github/workflows/bench.yml:1heavy-suites-judge-every-main-commit.github/workflows/bench.yml:35one-stuck-job-backstop.github/workflows/bench.yml:42,test/bench/src/plan.ts:113-1172ec2aeb5pr-gate-runs-only-what-the-diff-affects.github/workflows/bench.yml:26,test/bench/src/plan-legs.ts:102Not matched by the diff. These rules'
applies_whenreaches no changed line:pipeable-dual-parity,resource-vs-handle-duality,staged-lawful-builders,store-serializable-unit-of-work,store-unit-of-work-handleexpected-values-never-come-from-the-subjects-output,fake-and-real-store-laws,no-mocks-on-internal-glue,port-fakes-pass-the-port-contractrecursive-schema-suspenddeclared-entry-points,one-access-pathkill-survivors-with-contract-properties,suppress-by-shape-in-an-ignorer,unkillable-survivors-are-dead-codegates-share-one-job,light-checks-run-the-pinned-tool,no-dev-shell-on-verdict-lanes,no-lane-grades-the-repos-own-docs,red-main-reverts-its-culprit-same-day,release-promotes-the-staging-proven-build,setup-under-a-minutec0cf821brows now ruled not matched: none.Findings and dispositions. The merge produced 12 findings: 7 from the persona lenses and 5 from pack violations. The validator confirmed 6 and rejected 2; #1, #4, #5 and #7 were not validated.
docs/plans/2026-10-13-0711-ci-bench-off-pr-plan.md:32a888bb61: the plan's scope-law bullet namesfeb50851beside0db0ffa8. Rewriting either subject needs a force push, and the squash merge carries only the PR titleci(bench)!.test/e2e-core/src/bench-summary.schema.ts:98a888bb61: KD13 now names the exports the pack fixes added (LegTargetMs,LegTime,RegressedMetric,Regression,PhaseRegressed).test/bench/src/run-bench.service.ts:1899b518ba7is the executed evidence; no in-source exception; theabortRunPathlaws keep the failure-path restore.test/bench/src/plan-legs.ts:53.github/workflows/bench.yml:36cf39161d.test/bench/src/command.ts:24f13b9920:command.tsowns the one 4096-character budget and exportstailOf.test/bench/src/command.ts:5BenchSetupFailedlives inprepare-side.schema.ts, which predates this PR.test/bench/src/run-bench.service.ts:142test/bench/src/plan-legs.ts:1026e7a210c(root's E7 ruling):scripts/turbo-affected-packages.test.tspins the three facts through the plan job's own Turbo arguments andTurboAffectedPaths. CI has executed it on the exact head227773f4: run 38416467229,checkjob 115365565303, ran./bin/deno test -P --config=scripts/deno.json scripts/, executed 3 tests fromscripts/turbo-affected-packages.test.ts(allok), and ended `oktest/bench/src/plan-run-path.workflow.ts:31test/bench/src/bench-corpus.schema.ts:55f437647: the corpus schemas areS.Structs.test/bench/src/bench-plan.schema.ts:4166086e27:GreenRunsJsonis rooted atS.Array(...), and∀x_GreenRunsJson_=xruns.The lenses recorded 22 residual risks and 14 testing gaps (
review.json). Those not already in Known limits or the plan's Risks: the plan job's push path (ghlisting,gitancestry, Turbo) first runs on the first push tomain;per_page=100caps the green-run listing, which falls back to the first parent with the full corpus; and a change only toturbo.json,patches/**or.npmrcis reported by Turbo as root-only, so it selects no leg.Delta correctness lens:
f13b9920..aabe4c2a(this round)One
correctnesslens, run id20261013-095812-e6ed3e89, reviewed exactlyf13b992031bfb939aead64de88144a239a644235..aabe4c2a22a9ea0dcc929457fc4aff728ea7e804: 64 files, of which the PR's own commits are6e7a210c(#9),cf39161d(E10) anda0c3d693(plan); the merges9b518ba7andaabe4c2abring inmain. Artifacts:/tmp/compound-engineering-0/ce-code-review/20261013-095812-e6ed3e89/correctness.json,correctness.compact.jsonandserved-models.json. It ran oncorrectness-reviewer-minimax; its session record shows MiniMax M3.1 (minimax-code/MiniMax-M3.1-Flash-Preview) for all 41 assistant turns: no 429, no fallback, and no call toxai-oauthor Grok. It found theturboAffectedArgsrefactor produces an argv byte-identical to the previous inline literal, andTurboAffectedPathsmoved verbatim.-Pscripts/deno.json:6-Patci.yml:137in a separate root-owned PR, and this PR does not editci.yml(E7). Root merges #316 only after that PR is onmainand the three pin names appear in the Repo scripts tests log on the merged-main head. Resolved: #338 (cec409b1) is in head227773f4, and run 38416467229's Repo scripts tests step shows all three pinsok(Evidence).test/bench/package.json:54646f883(also the 0954 plan's caps bullet, R13 and an admission row). Pushed in the merge-up push that made head227773f4.Residual risks it recorded: the pins run the repo's Turbo 2.10.12 against a scratch workspace declaring
pnpm@9.15.0, so a Turbo or pnpm bump that changes the facts fails loudly; the pins cover direct file changes, not dependent propagation (DependencyChanged), whichselectLegsalso consumes; theTurboAffectedPathsmove was checked by search. Testing gaps:turboAffectedArgs' argument order is checked only through the pin's run, and nothing forcesplan-legs.tsto keep using the shared helper.Range correctness lens:
a359a82d..f13b9920One
correctnesslens, run id20261013-081023-0696f63c, reviewed exactlya359a82de4eb1d1627b6ec983435861cfaee40cc..f13b992031bfb939aead64de88144a239a644235(119 files, including main merged in throughe972b7da; the lens judged this PR's own commits). Artifacts:/tmp/compound-engineering-0/ce-code-review/20261013-081023-0696f63c/correctness.json,correctness.compact.jsonandserved-models.json. It ran oncorrectness-reviewer-minimax. Its session record shows MiniMax M3.1 (minimax-code/MiniMax-M3.1-Flash-Preview) for all 32 assistant turns: no 429, no fallback, and no call toxai-oauthor Grok.Result: 0 findings. It checked every consumer of the renamed shapes (
parentSha→baseSha,PhaseRegression→Regression,LegTimeasS.TaggedUnion), thereportOfsignature change inmain.ts, the cause-carrying run-path rework, and thatabortRunPathstays exhaustive with aNEXT_ACTIONper code. Residual risks it recorded: leg selection rests on Turbo'squery affectedsemantics (E7);per_page=100without pagination; a green run whose bench job was skipped becomes a future side A, by design; the::errorescaping was checked by reading. Testing gaps: no end-to-end test ofplanLegsagainst realgh/Turbo output (E7), thefailWithannotation andwriteWallTimeare unasserted, and the non-mainbaseShasentence of the regression block is not pinned.Fixes for the final receipt's confirmed findings
b2fe30db#a888bb61(plan 0749)f13b99205f437647GreenRunsJsonloses its generated laws66086e27e2e-corea888bb61(answered in the plan; no force push)--packagespin6e7a210cfix(bench): pin Turbo's affected-packages output in the repo scripts lane (E7)atRunPathrenames' evidencecf39161dci(bench): give each Bench leg job a 20-minute timeout (E10)After these commits,
test/benchpassestsc -b,oxlint,lint:tsgoand 107 tests (18 files), and∀x_GreenRunsJson_=xruns. A plan dispatch on the head decodes the real corpus and selects all four legs, and a failingghyields an annotation with the last 4096 characters of its output.c0cf821breview of5a088c60: confirmed findings and their fixesRun id
20261013-063533-c0cf821b, artifacts in/tmp/compound-engineering-0/ce-code-review/20261013-063533-c0cf821b/. That review stopped after validation (KD20), because its fixes move the head; the final receipt above reviews the head that holds them. Every lens started on MiniMax M3.1 and continued on DeepSeek V4.1 Flash after a 429; both validator batches stayed on MiniMax M3.1.377814dde42ae9f6(plan9e053e77)377814dd920a29cca31b3f41377814dd*.service.tsextension used withoutContext.Servicedeclaration (pack: cell-architecture, service-and-layer-boundaries.md)2344be12git status --porcelainplus a BenchSetupFailed, and plan-legs.service.ts:88-90 pins Turbo's JSON shape with the TurboAffectedJson decode that fails the step, so no unverified vendor assumption remains in a comment.S.Struct(pack: schema-laws, data-only-schema-classes.md)58f6cc3d58f6cc3dNullOrinstead of tagged union (pack: schema-laws, tagged-unions-over-state-by-presence.md)feb508512ec2aeb5Escalations to root (all closed by root's resolution of 2026-10-13; plan KD22)
--packagespin (refactor(repo): drop the starter package and reframe the repo #9, P1). Fixed, not waived, in6e7a210c, without stacking on or waiting for build(turbo): declare cross-package test reads and pin Turbo's selection semantics #319.scripts/turbo-affected-packages.test.tsruns the plan job's own Turbo arguments (turboAffectedArgs, shared withplan-legs.ts) throughnode_modules/.bin/turbo, the binarypnpm exec turboresolves to, in scratch pnpm workspaces. It decodes throughTurboAffectedPaths, the schemaplan-legs.tsdecodes with, now inturbo-affected.schema.tsso Deno can type-check it; its generated codec laws still run. It pins three facts: a changed package reportspackages/a(no./, no trailing/), a root file reports"", and no change reports[].scripts/deno.jsoncarries build(turbo): declare cross-package test reads and pin Turbo's selection semantics #319's permission hunk byte for byte (plus aneffect/import), andscripts/assert.tsis build(turbo): declare cross-package test reads and pin Turbo's selection semantics #319's file byte for byte. CI has executed the pin on the exact head. Root's ci(ci): apply scripts/deno.json permission grants in the repo scripts tests #338 (cec409b1,./bin/deno test -Patci.yml:137) is in227773f4, and CI run 38416467229's Repo scripts tests step (checkjob 115365565303) loggeda changed package is reported as its repo-relative directory, with no './' and no trailing '/' ... ok (100ms), the two other pinsok, andok | 13 passed | 0 failed (534ms). Before ci(ci): apply scripts/deno.json permission grants in the repo scripts tests #338, onaabe4c2a, the same step failed exactly these three withNotCapable(run 38380075673, job 115239848238).atRunPath's renames (build(deps): bump effect from 4.0.0-rc.112 to 4.0.0-rc.115 #3, P2). Closed: A/A run 38375500452 on9b518ba7is the executed evidence; no in-source admission exception; theabortRunPathlaws keep the failure-path restore.systemfsoftware/pnpm-release-management2b895557(build(deps): bump @effect/platform-node from 4.0.0-rc.112 to 4.0.0-rc.115 #56, 2026-10-13T07:07Z) refuses"gate": { "strategy": "paths" }whereturbo.jsonexists:paths gate refused: turbo.json makes turbo the source of change evidence — set "gate": { "strategy": "turbo" } in release.jsonc. Main'srelease.jsonc:14sets"paths". The check passed here on2344be12(07:02Z) and has failed sincee42ae9f6(07:11Z). Root's ruling (2026-10-13): root or the release owner fixes the gate onmain; this PR does not touchrelease.jsonc, and it mergesmainin once the fix lands. Inherited, not a defect of this PR (root, 2026-10-13). On headf13b9920,check / a publishable-package change needs an intent(run 38365575312, job 115191289316) fails with exactly that line. The deps stream's one-line fix is ci(repo): gate change intents on turbo's typecheck task graph #335 (fix/release-gate-turbo,b5570098: the shared gate strategy becomesturbo), and root merges it first. This PR does not editrelease.jsonc, adds no changeset to get past the gate, and does not rerun the job. Once ci(repo): gate change intents on turbo's typecheck task graph #335 is onmain, this PR mergesmainin once, and that push is the round's only new CI. Cleared by the merge-up. ci(repo): gate change intents on turbo's typecheck task graph #335 (1c9f9407,"gate": { "strategy": "turbo", "task": "typecheck" }) is onmain, andmainwas merged in once as9b518ba7; that merge is the round's only push. Before pushing, the gate binary CI builds (changeset-managementfrompnpm-release-management2b895557#release-tools) ran on the merged tree against base1c9f9407and printedchangeset gate: no publishable package changed (24 member(s)), exit 0. Turbo's package-level answer for the same range agrees://(bench.yml),@systemfsoftware/stryker-benchand@systemfsoftware/stryker-e2e-core(files changed) and@systemfsoftware/stryker-e2e(dependent of e2e-core), all"private": true. So no changeset is needed or added;release.jsoncandturbo.jsonare untouched. A second merge ofmain,aabe4c2a(refactor(repo): move driver-backed layers beside their services #322, fix(stryker-js): kill or delete the report decode's surviving mutants #334, chore(repo): pin the compound packs to a commit and enroll the seven that apply #336, ci(mutation): grade only the shipped products, not test/e2e-core #337; none touches a file of this PR), went into the same single push as this round's three commits, because the repository's pre-push hook refuses a tip that does not contain remotemain. The changeset gate binary ran again on the final tree against3797debd:no publishable package changed (24 member(s)). On headaabe4c2a, Changeset Check 38380076185 passed, as did Nix 38380075607 and Commitlint 38380075591.cf39161d(the plan job keeps 60). A leg's longest measured job is 7.68 job-minutes; run 38375500452's longest leg took 7.6 min.9b518ba7, and it concluded success with all 5 jobs green (Evidence). This session's four dispatch attempts had each been refused withHTTP 403,actions=write.Superseded full-path receipt: head
b30908ce, range25120175..b30908ce, 32 changed filesThe review is one
ce-code-reviewrun on the full path (mode:agent depth:full base:origin/main), report-only, run id20261013-043732-5519a046. Its artifacts are in/tmp/compound-engineering-0/ce-code-review/20261013-043732-5519a046/:review.json,metadata.json,stages.jsonl, the per-lens JSON, the pack slices,validator-verdicts.jsonandserved-models.json. All 12 reviewers and both validator batches returned, and none timed out.b30908ceis the remote branch tip; CI, Nix, Changeset Check and Commitlint passed on it. Nothing was rerun.Verdict from
ce-code-review: Ready with fixes. Every pack violation thea359a82dreceipt confirmed is fixed by a pushed commit (table below) and is now ruled comply. One pack violation remains confirmed, #7 (heavy-suites-judge-every-main-commit, side A isHEAD^1rather than the lane's last green commit). It was escalated to root as E3, and root chose the rule's scope (KD16). #7 is fixed in6a86a164(side A is the lane's last green main commit, affected legs only), refined bye42ae9f6(chooseBase: the newest green ancestor, never the pushed commit). The next full-path receipt, run20261013-071850-b2fe30dbonfeb50851, rules the rule comply, citingbench.yml:35and the ancestor check attest/bench/src/plan-legs.ts:67.bench.yml,plan.ts,plan-legs.ts,choose-base.workflow.tsandselect-legs.workflow.tsare unchanged fromfeb50851tof13b9920, so that ruling holds on the head. Four persona findings (one P2, three P3) are confirmed and listed for root.Served model per lens. Each model comes from that subagent's session record: its
model_changeevents and theprovider/modelon each assistant message. No call went toxai-oauthor Grok. No lens tried MiniMax this round. Every reviewer and validator started on DeepSeek V4.1 Flash, withresolvedModelIsFallback: false, because the operator profile (/opt/omp-profile/omp-config.yml,task.agentModelOverrides) mapscorrectness-reviewer,generalist-reviewerandadversarial-reviewerstraight to DeepSeek. That breaks the MiniMax-first rule, and it is escalated as E5.correctness-reviewercorrectness-reviewercorrectness-reviewergeneralist-reviewergeneralist-revieweradversarial-reviewergeneralist-reviewergeneralist-reviewergeneralist-reviewergeneralist-reviewergeneralist-reviewergeneralist-reviewercorrectness-reviewercorrectness-reviewerThe finish leaves are not lenses, and they decide no verdicts. They ran on
docs-engineer, which the operator profile serves on Claude Haiku 4.5 (E4, still open): merge leaf (Claude Haiku 4.5 (81)), report leaf (Claude Haiku 4.5 (56)).Corrections made by the dispatch context. The merge leaf returned unnumbered findings, so they were numbered 1-7 in merge order and attributed to their source lens.
finish-input.jsonhad carriedscope.head_sha,pr.head_ref_oid,scope.files,scope.diffand the pack directories over from thea359a82drun. All were corrected after the leaves ran, andreview.json's andmetadata.json'shead_shawere set tob30908ce(the correction is recorded inmetadata.json). The lens and validator prompts used this run's directory and head. The merge leaf only merged lens returns, and the report leaf only rendered run-directory artifacts. Thedispatchstage has a start entry but no end entry instages.jsonl.The cross-model peer did not run, because its configured route is
xai-oauth/grok. The adversarial lens ran in-process instead.Packs. Graded from
systemfsoftware/systemfsoftwaremain763fd663. The upstream main is unchanged since the last receipt:.compound-engineering/config.yaml:cell-architecture,boundary-testingandschema-laws;package-topology,mutation-disciplineandci-wall-clock.Every rule file was ruled: 54 rules in all. 21 are matched by the diff (16 comply, 2 violate, 3 pre-existing) and 33 are not matched.
file:line)a359a82dreceiptdecode-never-casttest/e2e-core/src/read-bench-run.workflow.ts:102four-channel-contractstest/bench/src/run-path.schema.ts:6826c352b9pure-decision-workflowstest/bench/src/measure-leg.workflow.ts:11scoped-lifecycle-boundariestest/bench/src/run-bench.service.ts:78service-and-layer-boundariestest/bench/src/prepare-side.service.ts:559,test/bench/src/run-bench.service.ts:292no-mocks-on-internal-gluetest/bench/src/__tests__/settle-run-path.workflow.property.test.ts:6,test/bench/src/run-bench.service.ts:21arbitrary-filter-floorstest/bench/src/leg-clock.schema.ts:11data-only-schema-classestest/bench/src/run-path.schema.ts:10,test/bench/src/measure-leg.workflow.ts:1308eff64ainvariants-as-refinementstest/e2e-core/src/bench-report.schema.ts:88f501c320refusals-beside-generated-lawstest/e2e-core/src/bench-run.schema.ts:77486ae9b4tagged-unions-over-state-by-presencetest/e2e-core/src/bench-run.schema.ts:5326c352b9tests-own-no-schemastest/bench/src/__tests__/plan-run-path.workflow.property.test.ts:9enumerated-entry-modulestest/e2e-core/src/mod.ts:6,test/e2e-core/src/bench-summary.schema.ts:107,test/e2e-core/src/bench-report.schema.ts:25public-signature-typestest/e2e-core/src/bench-summary.schema.ts:121,test/e2e-core/src/bench-report.schema.ts:25,test/e2e-core/src/mod.ts:5-7import-time-inertnesstest/e2e-core/src/bench-report.schema.ts:146,test/e2e-core/src/bench-report.schema.ts:23surface-changes-are-versionedtest/e2e-core/src/bench-run.schema.ts:33flake-outputs-build-once-on-main.github/workflows/bench.yml:91heavy-lanes-state-their-daily-minutes.github/workflows/bench.yml:1,.github/workflows/bench.yml:7-8,.github/workflows/bench.yml:11-12913fcbf9heavy-suites-judge-every-main-commit.github/workflows/bench.yml:787d962fca; #7 (scope clause) fixed in6a86a164ande42ae9f6, comply onfeb50851one-stuck-job-backstop.github/workflows/bench.yml:35,.github/workflows/bench.yml:54pr-gate-runs-only-what-the-diff-affects.github/workflows/bench.yml:19-22Not matched by the diff. These rules'
applies_whenreaches no changed line:callable-vs-resource-syntax,handle-state-privacy,pipeable-dual-parity,pipeline-composition,resource-vs-handle-duality,sandwich-phase-order,single-namespace-barrel,staged-lawful-builders,store-serializable-unit-of-work,store-unit-of-work-handleexpected-values-never-come-from-the-subjects-output,fake-and-real-store-laws,pin-dependency-semantics,port-fakes-pass-the-port-contract,real-system-oracles,staged-protocol-evidencecross-field-invariants-as-struct-checks,law-failure-is-a-codec-defect,recursive-schema-suspend,rich-type-over-foreign-encodedcondition-branch-agreement,declared-entry-points,one-access-pathkill-survivors-with-contract-properties,suppress-by-shape-in-an-ignorer,unkillable-survivors-are-dead-codegates-share-one-job,light-checks-run-the-pinned-tool,no-dev-shell-on-verdict-lanes,no-lane-grades-the-repos-own-docs,red-main-reverts-its-culprit-same-day,release-promotes-the-staging-proven-build,setup-under-a-minuteRows that changed ruling without a matching code change:
red-main-reverts-its-culprit-same-day(last round violate and rejected) andcallable-vs-resource-syntax,real-system-oraclesandstaged-protocol-evidence(last round comply) were ruled not matched this round. These are the reviewers' readings ofapplies_when, and nothing was edited to obtain them.Findings and dispositions. The merge produced 7 findings: 5 from the persona lenses and 2 from pack violations. The validators confirmed 5 and rejected 2.
test/bench/src/run-bench.service.ts:18426f90637(every run-path abort decided in a lawed workflow).test/e2e-core/src/bench-corpus.schema.ts:16S.NonEmptyArrayholds the refusal and the generated codec laws draw only accepted values. A refusal block inbench-corpus.schema.tswould be new code intest/e2e-coreoutside this PR's refinements, which KD13 excludes. Fixed in8689134a(the schema moved intotest/bench/srcwith its refusal block)..github/workflows/bench.yml:20docs/plans/2026-10-13-0435-ci-bench-off-pr-plan.md:41) names CONST-E9 and root's grant.test/e2e-core/src/render-bench-report.workflow.ts:94c26ac0ab(one verdict walk).test/bench/src/main.ts:2645683035f(the fallback uses the report builder).test/e2e-core/src/bench-run.schema.ts:33test/e2e-core/package.jsonis"private": true, the same ruling as #13 last round (KD12)..github/workflows/bench.yml:78HEAD^1against the rule's last-green scope). Fixed in6a86a164, refined bye42ae9f6(root's ruling KD16); comply onfeb50851.The residual risks the lenses recorded are already listed in Known limits, the plan's Risks or the settled decisions: a 60-minute kill leaves no report; the enterprise registry install (layer 2); a burst of merges stacks legs; and
bench.yml's environment is verified only by a live run.Fixes for the
b30908cereceipt's confirmed findingsb30908ce#f13b992026f90637fix(bench): decide every run-path abort in a lawed workflow8689134afix(bench): move the corpus schema into test/bench and state its refusalsc26ac0abfix(bench): walk the phase verdicts once in the report renderer5683035ffix(bench): build the report-unwritable fallback with the report builderheavy-suites-judge-every-main-commit, scope clause,bench.yml:786a86a164fix(bench): bench a push from the lane's last green main commit, affected legs only; refined bye42ae9f6fix(bench): choose side A in a lawed decision, the newest green ancestorEach mapping comes from the fixing commit's own message, which names the
b30908cerow it fixes. The rejected rows keep their reasons above: #3 (CONST-W3 declaration names CONST-E9) and #6 (test/e2e-coreis private, KD12).The clause #7 violated, quoted from
heavy-suites-judge-every-main-commit.md: "a push run tests the diff from the lane's last green main commit to this commit, after checking that commit is an ancestor of this one. The full corpus runs only on aworkflow_dispatchthat names no jobs, when the lane's own inputs change (its workflow file, its config, its tool pin), and as the fallback when no green ancestor is found, so a run never under-tests." Onf13b9920: side A isneeds.plan.outputs.base(bench.yml:106-110), chosen bychooseBaseas the newest green push run whose SHA passesgit merge-base --is-ancestorand is not the pushed commit;selectLegsruns the full corpus on a dispatch, when no green ancestor exists, and when the lane's inputs changed; there is no schedule.bench.ymlhas no dispatch inputs, so every dispatch names no jobs and runs the full corpus.Fixes for the
a359a82dreceipt's confirmed violationsa359a82d#b30908cef13b9920four-channel-contracts26c352b9fix(bench): carry run-path settle outcomes as tagged causesdata-only-schema-classes08eff64afix(bench): make the report's leg time plain datainvariants-as-refinementsf501c320fix(bench): refine the leg's elapsed time and the report's reftagged-unions-over-state-by-presence26c352b9(same commit as #8)refusals-beside-generated-laws486ae9b4fix(bench): state the refusal boundary of every refinement this PR addsheavy-lanes-state-their-daily-minutes913fcbf9ci(bench): state the lane's measured runner-minutes in its headerheavy-suites-judge-every-main-commit7d962fcaci(bench): give each dispatch its own concurrency group (dispatch-group clause); the last-green scope clause (E3) fixed later in6a86a164(KD16)b30908ce(scope clause, E3); comply onfeb50851Each mapping is taken from the fixing commit's own message, which names the
a359a82drow it fixes. Validator-rejected rows of that receipt keep their reasons: #13 (surface-changes-are-versioned, the package is private, KD12).Escalations raised by the
b30908cereceipt (E3, E5 and E6 resolved by root on 2026-10-13: KD16, KD19 and KD17; E4 stays open above)heavy-suites-judge-every-main-commitagainst root's settled KD1, KD10 and KD11 (finding feat(repo): split stryker-js into language and plugin-interface #7). Resolved: root chose the rule's scope (KD16); fixed in6a86a164ande42ae9f6. As raised: build(deps): bump @effect/platform-node-shared from 4.0.0-rc.112 to 4.0.0-rc.115 #15's dispatch-group clause is fixed (7d962fca). The rule's scope clause still wants a push run scoped from the lane's last green main commit.bench.yml:78takesHEAD^1, and the header (bench.yml:4-6) states why. Root decides between two options:docs-engineer, which is served on Claude Haiku 4.5.agentModelOverrides. The last round's MiniMax attempts all returned the Token Plan 429 on their first turn. No dispatch can choose a model, and the profile is root's to change. Root decides whether to accept this receipt or to have it rerun once the reviewer agents route to MiniMax.Superseded full-path receipt: head
a359a82d, range25120175..a359a82d, 32 changed filesThe review is one
ce-code-reviewrun on the full path (mode:agent depth:full base:origin/main), report-only, with run id20261013-030833-9213d365. Its artifacts are in/tmp/compound-engineering-0/ce-code-review/20261013-030833-9213d365/:review.json,metadata.json,stages.jsonl, the per-lens JSON, the pack slices,validator-verdicts.jsonandserved-models.json. All 12 reviewers and both validator batches returned, and none timed out.Verdict from
ce-code-review: Not ready. Seven pack violations are confirmed and escalated to root below (E1–E4). Root's 2026-10-13 merge-cycle intent names exactly one blocker for this cycle: placement under the no-new-test/e2e-corerule, plus this final-head receipt. The escalated violations are not part of that blocker. No fix work has started on them.Served model per lens. Each model comes from that subagent's session record: its
model_changeevents and theprovider/modelon each assistant message. No call went toxai-oauthor Grok. Every lens started on MiniMax, which returned 429 ("Token Plan rate limit reached"). The harness then switched to its configured DeepSeek fallback and recordedresolvedModelIsFallback: true.correctness-reviewercorrectness-reviewercorrectness-reviewergeneralist-reviewergeneralist-revieweradversarial-reviewergeneralist-reviewergeneralist-reviewergeneralist-reviewergeneralist-reviewergeneralist-reviewergeneralist-reviewercorrectness-reviewercorrectness-reviewerThe finish leaves are not lenses. They decide no verdicts: the validators judged every finding. They ran on
docs-engineer(see E4):ci-wall-clockrulings and the pack findings. Run 2 dropped the persona findings. Both were rejected and are kept as*.rejected-1/-2. Run 3 is the merge of record. Its finding docs(global): rewrite root README for stryker-js-effect #8 location (bench.yml:52) was corrected totest/bench/src/run-path.schema.ts:52, from the slice evidence.The cross-model peer did not run, because its configured route is
xai-oauth/grok. The adversarial lens ran in-process instead.Packs. The cached pack resolution was stale (
02bf9653, 2026-10-10) and had noci-wall-clockpack. All packs were therefore graded fromsystemfsoftware/systemfsoftwaremain763fd663:.compound-engineering/config.yaml:cell-architecture,boundary-testingandschema-laws;package-topology,mutation-disciplineandci-wall-clock;storybook-testingandui-statealso exist upstream, but no UI or story file is in the diff.Every rule file was ruled: 54 rules in all. 21 are matched by the diff (11 comply, 9 violate, 1 pre-existing) and 33 are not matched.
file:line)callable-vs-resource-syntaxtest/bench/src/measure-leg.workflow.ts:25,test/bench/src/run-path.schema.ts:7Workflow.makevalues; the new tagged classes stay non-callable recordsdecode-never-casttest/bench/src/run-bench.service.ts:130,test/bench/src/plan-run-path.workflow.ts:11RUNNER_TEMPlisting reachesplanRunPaththrough a decoded command, with no castfour-channel-contractstest/bench/src/run-path.schema.ts:52,test/bench/src/run-bench.service.ts:167,test/bench/src/prepare-side.schema.ts:6RunPathUnrestoredstoresCause.prettytext inrestoreError/runFailureinstead ofcause: S.optional(S.Unknown)(siblingBenchSetupFailedhas it)pure-decision-workflowstest/bench/src/measure-leg.workflow.ts:28,test/bench/src/main.ts:142measureLegdeclareserror: S.Never; time is read in the shell and passed in the commandscoped-lifecycle-boundariestest/bench/src/run-bench.service.ts:173Effect.uninterruptibleMask, so teardown survives interruptionservice-and-layer-boundariestest/bench/src/prepare-side.service.ts:16,test/bench/src/main.ts:317*.service.tsare on unchanged linesno-mocks-on-internal-gluetest/bench/src/__tests__/measure-leg.workflow.property.test.ts:11,test/bench/src/run-bench.service.ts:176,test/bench/src/run-bench.service.ts:82vi.fn/vi.mock/stand-inFileSystem; laws drive pure decisionsreal-system-oraclestest/bench/src/run-bench.service.ts:173staged-protocol-evidencetest/bench/src/run-path.schema.ts:7,test/bench/src/leg-clock.schema.ts:10,test/bench/src/run-bench.service.ts:170atRunPathneeds aRunPathMoveIn, settle yieldsRunPathRestored/RunPathUnrestoredarbitrary-filter-floorstest/e2e-core/src/bench-report.schema.ts:27,test/e2e-core/src/bench-summary.schema.ts:110,test/e2e-core/src/__tests__/render-bench-report.workflow.property.test.ts:434NonNegativeFinite,NonEmptyArray), so derived arbitraries need no filterdata-only-schema-classestest/e2e-core/src/bench-report.schema.ts:26,test/e2e-core/src/bench-report.schema.ts:33,test/bench/src/measure-leg.workflow.ts:27LegMeasured/LegNotMeasuredareTaggedClasses with a family TypeId, but noWorkflow.makedecidesLegTimesince the decision moved toLegClockinvariants-as-refinementstest/bench/src/leg-clock.schema.ts:6,test/e2e-core/src/bench-report.schema.ts:27,packages/stryker-js-plugin-interface/src/Metrics.schema.ts:20LegElapsed.wallMsis bareS.Finite(the>= 0rule lives in the decider); reportrefis bareS.Stringtagged-unions-over-state-by-presencetest/bench/src/settle-run-path.workflow.ts:13,test/bench/src/run-path.schema.ts:52,test/e2e-core/src/bench-report.schema.ts:75SettleRunPathCommand.restoreError/runFailureareNullOrand the decider branches on presencetests-own-no-schemastest/bench/src/__tests__/settle-run-path.workflow.property.test.ts:13,test/bench/src/measure-leg.workflow.ts:6refusals-beside-generated-lawstest/bench/src/run-path.schema.ts:9,test/e2e-core/src/bench-summary.schema.ts:110,test/e2e-core/src/bench-report.schema.ts:21run-path.schema.tsNonEmptyStrings; e2e-coremetrics,nextAction) have no in-source refusal property; precedent attest/e2e-core/src/annotation.schema.ts:107surface-changes-are-versionedtest/e2e-core/src/bench-run.schema.ts:31,test/e2e-core/src/bench-report.schema.ts:94test/e2e-corewith no changesetheavy-lanes-state-their-daily-minutes.github/workflows/bench.yml:1,.github/workflows/bench.yml:4-6bench.ymlhas no header comment pricing runner-minutes per day (the figures are in this PR body only)heavy-suites-judge-every-main-commit.github/workflows/bench.yml:12,.github/workflows/bench.yml:68bench-${{ github.sha }}instead ofgithub.run_id; side A isHEAD^1with the full corpus every push, not the last green commit with an affected-only runone-stuck-job-backstop.github/workflows/bench.yml:19,.github/workflows/bench.yml:38timeout-minutes: 60, in-process deadline deletedpr-gate-runs-only-what-the-diff-affects.github/workflows/bench.yml:4-6paths-ignoreare removedred-main-reverts-its-culprit-same-day.github/workflows/bench.yml:144,.github/workflows/bench.yml:153always()on uploadsNot matched by the diff. These rules'
applies_whenreaches no changed line:handle-state-privacy,pipeable-dual-parity,pipeline-composition,resource-vs-handle-duality,sandwich-phase-order,single-namespace-barrel,staged-lawful-builders,store-serializable-unit-of-work,store-unit-of-work-handleexpected-values-never-come-from-the-subjects-output,fake-and-real-store-laws,pin-dependency-semantics,port-fakes-pass-the-port-contractcross-field-invariants-as-struct-checks,law-failure-is-a-codec-defect,recursive-schema-suspend,rich-type-over-foreign-encodedcondition-branch-agreement,declared-entry-points,enumerated-entry-modules,import-time-inertness,one-access-path,public-signature-typeskill-survivors-with-contract-properties,suppress-by-shape-in-an-ignorer,unkillable-survivors-are-dead-codeflake-outputs-build-once-on-main,gates-share-one-job,light-checks-run-the-pinned-tool,no-dev-shell-on-verdict-lanes,no-lane-grades-the-repos-own-docs,release-promotes-the-staging-proven-build,setup-under-a-minuteFindings and dispositions. The merge produced 16 findings: 7 from the persona lenses and 9 from pack violations. The validators confirmed 7 and rejected 9.
.github/workflows/bench.yml:38test/bench/src/prepare-side.service.ts:505test/bench/src/run-bench.service.ts:197test/e2e-core/src/bench-summary.schema.ts:106BenchSummary.regressionis the plan's named carve-out. Root still decides the scope-law reading; see E2.test/e2e-core/src/summarize-bench.workflow.ts:103not-recordedonly on verdict lines from before 19.0.0, wheremutation-testalready absorbed reporting time, sototalstays comparable.test/bench/src/run-bench.service.ts:289reducepasses the index).test/e2e-core/src/render-bench-report.workflow.ts:83test/bench/src/run-path.schema.ts:52test/benchonly.test/e2e-core/src/bench-report.schema.ts:26test/e2e-core.test/bench/src/leg-clock.schema.ts:6LegElapsed, E2 for e2e-coreref.test/bench/src/settle-run-path.workflow.ts:13test/benchonly.test/bench/src/run-path.schema.ts:9test/bench, E2 for e2e-core.test/e2e-core/src/bench-report.schema.ts:94test/e2e-core/package.json:3is"private": true..github/workflows/bench.yml:1.github/workflows/bench.yml:12.github/workflows/bench.yml:144applies_whenis not reached, since no failure handler is added. Root's KD3 (report-only) stands.Residual risks the lenses recorded are all already in Known limits, the plan's Risks, or settled decisions:
test/benchis not in the mutation lane (Stream K).Escalations raised by that receipt (E1 and E2 resolved by root on 2026-10-13; see Pack compliance round)
E1. Fixes ready with no conflict, all in
test/benchorbench.yml. They wait only on root's go-ahead: root's stop-before-ce-work rule blocks them while E2 and E3 are open.d6524aato8ce4ef6#11: the settle inputs andRunPathUnrestoredbecome tagged outcomes carryingcause: S.optional(S.Unknown), in place of nullableCause.prettystrings.LegElapsed.wallMsbecomesReport.NonNegativeFinite.run-path.schema.ts, in the shape oftest/e2e-core/src/annotation.schema.ts:107.github.run_id).E2. Pack against the scope law. refactor(repo): drop the starter package and reframe the repo #9 (
LegMeasured/LegNotMeasuredbecomeTaggedStruct), feat(repo): ignorer protocol on ancestors-as-data and the oxc vocabulary #10 (refrefined) and chore(deps): bump the other-minor-patch group across 1 directory with 7 updates #12 (refusal blocks formetrics/nextAction) all need edits intest/e2e-core, which the scope law closes to new code. Root decides between two options:test/e2e-coreuntil Stream D moves the files.E3. Pack against root's settled KD1, KD10 and KD11. Two
ci-wall-clockrules collide with root's choice that every main SHA is benched against its first parent with the full corpus:heavy-suites-judge-every-main-commitwants an affected-only run from the lane's last green commit;heavy-lanes-state-their-daily-minutes(build(deps): bump @effect/vitest from 4.0.0-rc.112 to 4.0.0-rc.115 #14) must price the run every push starts, and labels a full-corpus figure as a fallback.Root decides between two options:
E4. Finish-leaf routing. AGENTS.md OP20 and the
review-leaf-routinggate send the merge and report leaves todocs-engineer, which omp config serves onanthropic/claude-haiku-4-5. Every lens and both validators ran on MiniMax or DeepSeek. Root decides whether Claude on the leaves is acceptable, or whetherdocs-engineershould be remapped before the final receipt.Earlier rounds (superseded)
These were reviews of individual lenses, not full-path. Their dispositions carry forward and were re-judged above:
f1da499b,63ba7bb9anda359a82d: six lenses each, served by MiniMax with DeepSeek fallback. They drove63ba7bb9, the enterprise cwd-equals-tree draw, andb9ebfbb5, which names both causes inrun-path-move-failed.e5844df9: MiniMax with DeepSeek fallback.The passes on
f6b3ec88,c8372c03,eec5cfa5and the plan includedxai-oauth/grok-4.7, so under Ryan's rule they count as unreviewed. The fixes they drove (e684c838,eec5cfa5,b91e981a) are on this head.Mutation law scope (root resolution, 2026-10-13)
Ryan's nightly-only mutation law covers the repository's mutation-testing quality lanes,
mutation.ymlandkill-matrix.yml, which Stream K owns (#289 aligns with K). It does not cover Bench. Bench is a speed measurement of the product over fixtures, like the e2e lane, which also runs on every push to main. Sobench.ymlstays on push tomainplusworkflow_dispatchand compares each main SHA with the lane's last green main commit. This PR edits neither mutation workflow, and no evidence here waits on a Mutation run of main. Any later mutation evidence will come from the next nightly run or from a dispatch root authorizes.Bench cost (heavy-postsubmit rule)
Every figure below comes from GitHub Actions job timestamps (
completed_at − started_at, summed over jobs) for the 7 days 2026-10-06T01:54Z to 2026-10-13T01:54Z: 2,471 runs and 11,718 jobs that ran. Nothing was rerun. Bench has not yet run on a push tomain, so per-push cost is taken from the sevenworkflow_dispatchruns of this design: two sides, eight runs per leg, no in-process deadline.Per leg (the old plan job and four legs, all
ubuntu-latest; every one of these runs benched the full corpus)896a494c)Across all seven runs, a push costs 15.3–20.3 job-minutes (median 17.0). The jobs of 38297580066 are 114972296814, 114974586413, 114974586425, 114974586457 and 114974586517. GitHub's timing API reports 0 billable ms for that run, because this public repository runs on standard runners. So the cost is runner occupancy, not billed minutes.
Per day
Releasepush runs onmain, matching the 110 first-parent commits ingit log.pull_requestruns). This PR therefore cuts Bench's runner use by about 220–310 job-minutes per day.Compared with Mutation (same window, job-minutes per run)
Can Bench starve PR CI? Partly: when the runner pool is already busy.
The org's concurrent-job limit is not visible to this token;
GET /orgs/systemfsoftwarereturns noplanfield. The observed behaviour stands in for it:ubuntu-latestpeaked at 88 concurrent jobs (2026-10-10T22:51Z).needsstart within a 2 s median while fewer than 40 jobs are running.CI / checkjobs queued for a median of 2 s, with a 90th percentile of 401 s.Bench holds at most 4 runners per push, and the plan job ends before any bench job starts. Each leg runs 2.0–7.7 minutes, and each SHA has its own concurrency group, so a burst of merges stacks legs.
Replaying the measured week with PR Bench removed and a full-corpus run added at each observed main-push time (a model; it assumes the added jobs start without queueing). The affected-only selection only removes jobs from it:
Net: Bench can add queue time to PR CI only when the pool is already at about 40 or more jobs, and this PR lowers that total load compared with today.
Not measured: a Bench run on an actual push to
main(the first comes after merge); the org's configured concurrency limit; the queue cost of jobs thatneedsanother job (theircreated_atis not their queue time). Raw data is in session scratch under/tmp/bench-cost/.Known limits
Enterprise install still resolves from the registry.
npm install --before=<lock time>runs with no deadline now, so a bad upstream publish would run that leg to the 20-minute backstop on main. It no longer fails PRs. Layer 2 owns the fix.A leg job killed at 20 minutes may leave no
bench-report.json. The job's timeout conclusion is then the only signal.One comparison can span several commits. Side A is the lane's last green main commit, so when an earlier push's run is red or still running, the next push benches every commit since the last green one and the culprit is one of them. Main squash-merges, so each commit is one PR.
A commit that changes the stream contract gets no comparison. Both sides' NDJSON is decoded with the checkout's
RunEvent(read-bench-run.workflow.ts:2). When the commit changes the wire format, as feat(stryker-js-cli-contract): stream mutant lines carry their facts and next action #298 does, the other side's stream isstream-undecodable, the leg isfailedand that SHA has no comparison. Before this PR, the same cause, not the deadlines, failed three legs of PR run 38284821646. The fourth leg'sbudget-exceededabort was a deadline failure. Follow-up boundary: decode each side with its own contract, in a separate unit.Overlap with other open PRs:
mod.tsexport line.read-bench-run.workflow.property.test.tsandbench-decisions.integration.test.ts.prepare-side.service.ts.Whichever PR lands second takes a plain merge of main.