diff --git a/.claude/skills/t27b-loop/SKILL.md b/.claude/skills/t27b-loop/SKILL.md index 1c32bfd3ff..efc6e6e548 100644 --- a/.claude/skills/t27b-loop/SKILL.md +++ b/.claude/skills/t27b-loop/SKILL.md @@ -69,7 +69,7 @@ Take the first unchecked item of epic #8326 that is not already claimed, either - **Bring the files back to a sparse worktree on the Mac.** `git worktree add --no-checkout`, `git sparse-checkout set --no-cone `, then copy each file with `ssh ... cat`. `gen/` is gitignored: use `git add -f`. - **Ledger rows: the steward writes them.** The t27b ledger steward (`specs/jobs/t27b_ledger_steward.t27`, rules in `specs/tri/t27b/ledger_steward.t27`, #8654) adds the row from master's next run after a spec PR merges, and opens one ledger PR with auto-merge. Once the Queen runs that card (see gHashTag/trios#1751), spec PRs leave `docs/reports/t27b_expectations.json` alone. - **Until then, every new spec in a PR still gets its row.** A missing row turns master's `t27b-native-ratchet` red for everyone (UNLISTED). That happened 6 times in 30 minutes on 2026-10-09. - - **The steward refuses** UNEXPECTED FAILURE, MOVED, BAD REASON, OVER CAP, a defect where the reference passes, a cap rise larger than the new rows, and more than 16 STALE rows. For a refused run it opens one issue labelled `t27b-ledger-steward` and no PR. Those issues are this loop's to read. + - **The steward refuses** UNEXPECTED FAILURE, MOVED, BAD REASON, OVER CAP, a defect where the reference passes, a cap rise larger than the new rows, and more than 16 STALE rows. A refused finding no longer holds the run back: the steward applies its other rows, leaves the refused row as master has it, and lists it in the ledger PR's table. Only a run with nothing else to apply, a cap rise or a STALE flood gets one issue labelled `t27b-ledger-steward` and no PR. Those issues and the REFUSE rows in steward PRs are this loop's to read. - **Receipts:** while master moves every 10-20 minutes, compare a lane's squash commit on master against its parent after the merge: - `cd /work/t27 && /work/t27c-master corpus-receipt compare BASE.receipt.json HEAD.receipt.json --challenge-head ` on t27b-lab; - a master receipt carries no nonce and is accepted as BASE; diff --git a/.trinity/seals/jobs_JobT27bLedgerSteward.json b/.trinity/seals/jobs_JobT27bLedgerSteward.json index 28400547fc..ff7b700282 100644 --- a/.trinity/seals/jobs_JobT27bLedgerSteward.json +++ b/.trinity/seals/jobs_JobT27bLedgerSteward.json @@ -1,5 +1,5 @@ { - "built_by": "t27c-bootstrap@0.5.2+55eb214ac", + "built_by": "t27c-bootstrap@0.5.2+063da049b", "closure": [], "config": "gen=c,rust,verilog,zig;tests=zig", "gen_hash_c": "sha256:ac9254171852a74a08f26cee469556437586afc64ff5d04662493e1b07fd122b", @@ -9,16 +9,17 @@ "module": "JobT27bLedgerSteward", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-10T14:43:21Z", + "sealed_at": "2026-10-18T03:55:24Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:869202696ec9a11cd45189276828631c767967d44ee6ba7f91404ba214e43a27", + "spec_hash": "sha256:87951c2be3a5159c61bb3c98591972ba3615fdaf102b56f2b63ed086e50329ca", "spec_path": "specs/jobs/t27b_ledger_steward.t27", "tests": { "failed": 0, "failing": [], "forced": false, "passed": 2, - "total": 2 + "total": 2, + "vacuous": 0 }, "toolchain": { "test_runner": "sha256:1982ae948e24d00c5d221ccec12dfce1eb01204b53e37b0f857f93b16d64629d", diff --git a/.trinity/seals/t27b_T27bLedgerSteward.json b/.trinity/seals/t27b_T27bLedgerSteward.json index 2c88da5f25..caeba23233 100644 --- a/.trinity/seals/t27b_T27bLedgerSteward.json +++ b/.trinity/seals/t27b_T27bLedgerSteward.json @@ -1,5 +1,5 @@ { - "built_by": "t27c-bootstrap@0.5.2+55eb214ac", + "built_by": "t27c-bootstrap@0.5.2+063da049b", "closure": [ { "spec": "specs/tri/t27b/steward.t27", @@ -7,23 +7,24 @@ } ], "config": "gen=c,rust,verilog,zig;tests=zig", - "gen_hash_c": "sha256:e68d532298ecd4aac96e32088f179d581147705775933c8f93ebda82221aabab", - "gen_hash_rust": "sha256:d8faa3518e7703b7f8c7e3cac99fce073a4ae01c1a7c3f1b0195e502226d3b74", - "gen_hash_verilog": "sha256:806a630d9e2970ef50e7ea4b777b101b6c24616736caa484b3dc012f55c914f9", - "gen_hash_zig": "sha256:cf898366b4bc6cd30fc0232d7dde252a94afaf03e0868d783e15ef7bf4338cd9", + "gen_hash_c": "sha256:7f6f95d85401dbb6dbaa7786ebc835a79450218b97ee0e9fa35a066805841e04", + "gen_hash_rust": "sha256:b002afd8a8e09a735fc529b7fde408eec6234131409a67812facd05a45670a31", + "gen_hash_verilog": "sha256:6c380f5e56877102b97dd5e81447238ef664b32598cafba68c3ee45fcafcb798", + "gen_hash_zig": "sha256:07cdd78d6c7a7380a47e7ebbb1047ad8645cf6670349f77af069ad317ef73621", "module": "T27bLedgerSteward", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-10T14:40:39Z", + "sealed_at": "2026-10-18T03:55:24Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:e2a58a7ef6fb10c371af1dcb8cf02cd1f206f3327dc4851546f5eb4696ce4132", + "spec_hash": "sha256:731eca094910d21ec239964b4df2f4e9890babd1d53395d000440df283b3c8b3", "spec_path": "specs/tri/t27b/ledger_steward.t27", "tests": { "failed": 0, "failing": [], "forced": false, - "passed": 10, - "total": 10 + "passed": 11, + "total": 11, + "vacuous": 0 }, "toolchain": { "test_runner": "sha256:1982ae948e24d00c5d221ccec12dfce1eb01204b53e37b0f857f93b16d64629d", diff --git a/specs/jobs/t27b_ledger_steward.t27 b/specs/jobs/t27b_ledger_steward.t27 index ba45b18c39..1bb8ffdd72 100644 --- a/specs/jobs/t27b_ledger_steward.t27 +++ b/specs/jobs/t27b_ledger_steward.t27 @@ -20,8 +20,10 @@ ; 1 EFFECT steward-refusals-issue when run_verdict is REFUSE: one issue for this run, listing each ; refused finding with its refusal_reason (or run_refusal) and the ; rows the steward would have applied; otherwise PASS, nothing done -; 2 CHECK steward-appliable run_verdict is APPLY: every finding is the steward's and the cap -; fits (cap_fits); REFUSE fails the job, pointing at the issue +; 2 CHECK steward-appliable run_verdict is APPLY: some finding is the steward's, the cap fits +; (cap_fits) and STALE is no flood; a refused finding stays as +; master has it and is listed in the PR; REFUSE fails the job, +; pointing at the issue ; 3 EFFECT steward-ledger-issue the issue the ledger commit and the PR close (the Issue Gate: ; `Closes #N` in the commit and on line 1 of the PR body), ; labelled ISSUE_LABEL diff --git a/specs/tri/t27b/ledger_steward.t27 b/specs/tri/t27b/ledger_steward.t27 index c44ce7abb0..26733c277c 100644 --- a/specs/tri/t27b/ledger_steward.t27 +++ b/specs/tri/t27b/ledger_steward.t27 @@ -23,8 +23,12 @@ ; UNLISTED spec t27b fails, mismatches, crashes or times out on (a defect is never "unimplemented"), ; a cap rise larger than the new rows, more STALE rows in one run than STALE_LIMIT (the reference ; moved, not the specs), and any finding code this file does not know. -; One refused finding refuses the run: a ledger PR that leaves the ratchet red is noise, and the person -; who reads the issue writes the whole batch, as #8548 was written. +; A refused finding is left as master has it, and the rest of the run is still applied: the PR's table +; names every refused row, so the ratchet stays red only for those. Until #10189 one refused finding +; refused the whole run, and from 2026-10-13 to 10-18 the lab refused 63 of 75 master runs for the same +; two or three rows (rust_convert, trios-mesh-node crypto, emit_kit) while about 250 safe rows waited +; and sessions wrote them into the ledger by hand 17 times in a day. A run with nothing to apply but a +; refused finding is still refused, and a stale flood or a cap rise still refuses the whole run. ; Codes are steward.t27's; the invariant at the end checks the numbering against its entry_code. ; ASCII only (L3). ; phi^2 + 1/phi^2 = 3 | TRINITY @@ -92,8 +96,10 @@ pub const RR_UNKNOWN : u8 = 8; /// The run's verdict. /// NOTHING no finding to apply: the ratchet is green, no job -/// APPLY every finding is the steward's: compose the ledger, one PR, auto-merge -/// REFUSE some finding is a person's: one issue for the run, no PR +/// APPLY some finding is the steward's: compose the ledger from those, one PR, auto-merge; a +/// refused finding stays as master has it and is listed in the PR +/// REFUSE nothing to apply but a person's finding, or the run as a whole is refused (a stale +/// flood, a cap rise): one issue for the run, no PR /// SKIP not a run the steward reads: not a master run, asserts not counted, or master's ledger /// moved since the run (the next run is read instead) pub const RV_NOTHING : u8 = 0; @@ -205,13 +211,16 @@ pub fn run_stewardable(is_master: bool, asserts_counted: bool, same_ledger: bool return true; } +/// A refused finding no longer holds back the steward's own rows: they are applied, and the refused +/// row stays as master has it. The cap is counted on the composed ledger, so a refused row never +/// raises it. pub fn run_verdict(stewardable: bool, refused: u32, applied: u32, stale: u32, cap_ok: bool) -> u8 { if (stewardable == false) { return RV_SKIP; } - if (refused > 0) { return RV_REFUSE; } if (stale_flood(stale)) { return RV_REFUSE; } if (cap_ok == false) { return RV_REFUSE; } - if (applied == 0) { return RV_NOTHING; } - return RV_APPLY; + if (applied > 0) { return RV_APPLY; } + if (refused > 0) { return RV_REFUSE; } + return RV_NOTHING; } /// The run-level reason the issue leads with when no single finding was refused. @@ -316,6 +325,33 @@ test batch_8548_without_its_failures_is_applied { assert run_verdict(true, refused, applied, 4, cap_fits(25, 23, fresh)) == RV_APPLY; } +test run_18c98cc77_applies_its_rows_around_two_refused { + // lab run 18c98cc77 (2026-10-18): UNEXPECTED FAILURE tools/t27o/rust_convert (blocked) and UNLISTED + // trios-mesh-node crypto (fail) refused; 248 UNLISTED passes waited behind them + var refused : u32 = tally_refused(0, RC_UNEXPECTED_FAILURE, T_BLOCKED, 1); + refused = tally_refused(refused, RC_UNLISTED, T_FAIL, 1); + refused = tally_refused(refused, RC_UNLISTED, T_PASS, 248); + var applied : u32 = tally_applied(0, RC_UNEXPECTED_FAILURE, T_BLOCKED, 1); + applied = tally_applied(applied, RC_UNLISTED, T_FAIL, 1); + applied = tally_applied(applied, RC_UNLISTED, T_PASS, 248); + var fresh : u32 = tally_new_not_pass(0, RC_UNLISTED, T_FAIL, 1); + fresh = tally_new_not_pass(fresh, RC_UNEXPECTED_FAILURE, T_BLOCKED, 1); + fresh = tally_new_not_pass(fresh, RC_UNLISTED, T_PASS, 248); + assert refused == 2; + assert applied == 248; + assert fresh == 0; + // neither refused row enters the composed ledger as a non-pass, so the cap holds + assert cap_fits(25, 25, fresh) == true; + assert run_verdict(true, refused, applied, 0, cap_fits(25, 25, fresh)) == RV_APPLY; + assert effect_wanted(RV_APPLY, EF_PULL_REQUEST) == true; + assert effect_wanted(RV_APPLY, EF_REFUSAL_ISSUE) == false; + // the same two refusals with nothing else to apply are still a person's + assert run_verdict(true, refused, 0, 0, true) == RV_REFUSE; + // and a refusal never lets a cap rise or a stale flood through + assert run_verdict(true, refused, applied, 0, false) == RV_REFUSE; + assert run_verdict(true, refused, applied, STALE_LIMIT + 1, true) == RV_REFUSE; +} + test the_steward_applies_these { assert finding_action(RC_UNLISTED, T_PASS) == ACT_ADD; assert finding_action(RC_UNLISTED, T_PASS_VACUOUS) == ACT_ADD;