Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .claude/skills/t27b-loop/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ Take the first unchecked item of epic #8326 that is not already claimed, either
- a fresh `git worktree add --detach /data/w-<topic> FETCH_HEAD` from the lab clone;
- `cargo build --release -p t27c` with a shared `CARGO_TARGET_DIR`.
- **Bring the files back to a sparse worktree on the Mac.** `git worktree add --no-checkout`, `git sparse-checkout set --no-cone <paths>`, then copy each file with `ssh ... cat`. `gen/` is gitignored: use `git add -f`.
- **Every new spec in a PR also gets its row** in `docs/reports/t27b_expectations.json`. A missing row turns master's `t27b-native-ratchet` red for everyone (UNLISTED). That happened 6 times in 30 minutes on 2026-10-09.
- **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.
- **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 <nonce>` on t27b-lab;
- a master receipt carries no nonce and is accepted as BASE;
Expand Down
27 changes: 27 additions & 0 deletions .trinity/seals/jobs_JobT27bLedgerSteward.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"built_by": "t27c-bootstrap@0.5.2+55eb214ac",
"closure": [],
"config": "gen=c,rust,verilog,zig;tests=zig",
"gen_hash_c": "sha256:ac9254171852a74a08f26cee469556437586afc64ff5d04662493e1b07fd122b",
"gen_hash_rust": "sha256:ed77d5abb6c3a351e727532d015617c31ea4ee337554970c03adcabc187398f4",
"gen_hash_verilog": "sha256:34d6ca3be4983dbdcea17448983cd327c17e8032a9108e06e263af180a84d899",
"gen_hash_zig": "sha256:18a382e630aa893eb6da3f1885b5eb72f705fe28491e6461e64b62643e8ae0a8",
"module": "JobT27bLedgerSteward",
"ring": 12,
"seal_schema": 2,
"sealed_at": "2026-10-10T14:43:21Z",
"sealed_by": "t27c-bootstrap@0.5.2",
"spec_hash": "sha256:869202696ec9a11cd45189276828631c767967d44ee6ba7f91404ba214e43a27",
"spec_path": "specs/jobs/t27b_ledger_steward.t27",
"tests": {
"failed": 0,
"failing": [],
"forced": false,
"passed": 2,
"total": 2
},
"toolchain": {
"test_runner": "sha256:1982ae948e24d00c5d221ccec12dfce1eb01204b53e37b0f857f93b16d64629d",
"zig": "0.16.0"
}
}
32 changes: 32 additions & 0 deletions .trinity/seals/t27b_T27bLedgerSteward.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"built_by": "t27c-bootstrap@0.5.2+55eb214ac",
"closure": [
{
"spec": "specs/tri/t27b/steward.t27",
"spec_hash": "sha256:70867c8cd8a8155d82f352b6833168f04c846a736413d6b2420d54774994123e"
}
],
"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",
"module": "T27bLedgerSteward",
"ring": 12,
"seal_schema": 2,
"sealed_at": "2026-10-10T14:40:39Z",
"sealed_by": "t27c-bootstrap@0.5.2",
"spec_hash": "sha256:e2a58a7ef6fb10c371af1dcb8cf02cd1f206f3327dc4851546f5eb4696ce4132",
"spec_path": "specs/tri/t27b/ledger_steward.t27",
"tests": {
"failed": 0,
"failing": [],
"forced": false,
"passed": 10,
"total": 10
},
"toolchain": {
"test_runner": "sha256:1982ae948e24d00c5d221ccec12dfce1eb01204b53e37b0f857f93b16d64629d",
"zig": "0.16.0"
}
}
17 changes: 17 additions & 0 deletions docs/now/2026-10-10-t27b-ledger-steward.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# NOW -- the t27b ledger steward: master's run writes the ledger rows, spec PRs stop touching them (2026-10-10)

## specs/tri/t27b/ledger_steward.t27, specs/jobs/t27b_ledger_steward.t27 (Closes #8654; see #8637 item 1, #8321 option 3)

- **The rules.** `ledger_steward.t27` decides what the steward does with each ratchet finding on a master run. It applies UNLISTED (a non-pass row gets reason `unimplemented`), UNEXPECTED PASS (a gain) and STALE (the row is dropped). The cap may rise by at most the new non-pass rows (steward.t27 `cap_rise_is_new`).
- It 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 in one run. A refused run gets one issue and no PR.
- The tests use the three hand batches as vectors. #8289 and #8646 are applied, with caps 18 -> 20 and 25 -> 26. #8548 is refused for its two failures; without them it is applied, with cap 23 -> 25.
- **The job card.** `t27b_ledger_steward.t27` is a jobs.t27 card with eight steps:
- CHECK the run is a counted master run on master's ledger;
- EFFECT the refusal issue;
- CHECK every finding is appliable;
- EFFECT the ledger issue, the composed ledger, the PR, auto-merge;
- WAIT merged.
- **Measured on the t27c lab:**
- `t27c test-report`: 10 tests with 0 vacuous, and 2 with 0 vacuous;
- `t27b corpus --blockers --reference` under qemu: both pass, with 108 and 26 runtime asserts and 0 reference disagreements.
- **Runtime.** The Queen's job runner (gHashTag/trios `queen-jobs.ts`) runs only the release card today. The missing loader, trigger, executors, journal kinds and credential are listed in gHashTag/trios#1751. Until it runs, spec PRs keep adding their own rows.
2 changes: 2 additions & 0 deletions docs/reports/t27b_expectations.json
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@
{"path": "specs/isa/tri27_bytecode.t27", "t27b": "pass"},
{"path": "specs/isa/tri27_machine.t27", "t27b": "pass"},
{"path": "specs/jobs/release_t27c.t27", "t27b": "pass"},
{"path": "specs/jobs/t27b_ledger_steward.t27", "t27b": "pass"},
{"path": "specs/lsp/language.t27", "t27b": "pass_vacuous"},
{"path": "specs/math/constants.t27", "t27b": "blocked", "blocker": "StmtAssign", "reason": "unimplemented"},
{"path": "specs/math/gf_competitive.t27", "t27b": "pass"},
Expand Down Expand Up @@ -1194,6 +1195,7 @@
{"path": "specs/tri/t27b/fuzz_oracle.t27", "t27b": "pass"},
{"path": "specs/tri/t27b/int_cast_plan.t27", "t27b": "pass"},
{"path": "specs/tri/t27b/lazy_sig_plan.t27", "t27b": "pass"},
{"path": "specs/tri/t27b/ledger_steward.t27", "t27b": "pass"},
{"path": "specs/tri/t27b/lencall.t27", "t27b": "pass"},
{"path": "specs/tri/t27b/libm.t27", "t27b": "pass"},
{"path": "specs/tri/t27b/libm_plan.t27", "t27b": "pass"},
Expand Down
143 changes: 143 additions & 0 deletions specs/jobs/t27b_ledger_steward.t27
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// SPDX-License-Identifier: Apache-2.0
; specs/jobs/t27b_ledger_steward.t27 -- the t27b ledger steward, as a job the Queen runs (specs/queen/jobs.t27)
; The steps are the ones a Claude session ran by hand three times in two days (#8289, #8548, #8646):
; read a red ratchet on a master run, write the rows the run states into
; docs/reports/t27b_expectations.json with t27b.dump_ledger's layout, open an issue and a PR that
; closes it, and enable auto-merge (#8654; see #8637 item 1). What the steward may apply and what it
; refuses is decided in specs/tri/t27b/ledger_steward.t27 (RULES), compiled to wasm like every card
; decision; this card names the steps.
; A job starts when a master run's ratchet is red and no job was started for its commit
; (ledger_steward.t27 run_starts_job), from either trigger:
; lab-latest the t27b lab publishes a master run at LAB_RUN_URL: results[] and steps.ratchet
; (verdict, counts, findings)
; native-ratchet master's NATIVE_CHECK job of NATIVE_WORKFLOW completes; its run.json and
; ratchet.json are read from the run's NATIVE_ARTIFACT (the workflow does not upload
; that artifact yet: until it does, lab-latest is the only trigger)
; The job's parameters are the run (its URL or Actions run id) and the run's commit.
; 0 CHECK steward-run the run is pinned as the subject: its ref is master, it counted
; runtime asserts, and master's ledger is the ledger the run was
; compared against (run_stewardable); otherwise the job fails as SKIP
; 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
; 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
; 4 EFFECT steward-compose compose the ledger from master's, finding by finding
; (finding_action: ADD, SET, DROP; KEEP leaves the row), rows in
; path order, a non-pass row with its first blocker and row_reason,
; max_not_pass = cap_after, source = the run; push it as one commit
; to BRANCH_PREFIX + the run's commit (12 hex)
; 5 EFFECT steward-pull-request the PR from that branch to master, body line 1 `Closes #<issue>`,
; with a table of the rows by finding, as #8548's
; 6 EFFECT steward-auto-merge enable auto-merge with MERGE_METHOD while the checks are pending
; (GitHub refuses it once they finished with a non-required red)
; 7 WAIT steward-merged the PR merged; a day without it fails the job (jobs.t27)
; Every EFFECT goes through the effects journal (control.t27 section 7) under the key
; JOURNAL_PREFIX + the run's commit + ":" + its STEP_WHAT, so a Queen that restarts mid-step looks
; before she acts and never opens a second issue or PR for one run. STEP_EFFECT names the effect kind
; each step needs from the supervisor.
; Read at run time by the supervisor's vendored compiler wasm: the supervisor reads the constants;
; the two functions are only what the card's tests ask of them.
; ASCII only (L3).
; phi^2 + 1/phi^2 = 3 | TRINITY

module JobT27bLedgerSteward;

pub const JOB_NAME : str = "t27b-ledger-steward";
pub const REPO : str = "gHashTag/t27";
pub const BASE_BRANCH : str = "master";
pub const LEDGER : str = "docs/reports/t27b_expectations.json";
pub const RULES : str = "specs/tri/t27b/ledger_steward.t27";

/// Where a master run is read, by trigger.
pub const TRIGGERS : [2]str = ["lab-latest", "native-ratchet"];
pub const LAB_RUN_URL : str = "https://t27b-lab-production.up.railway.app/latest.json";
pub const NATIVE_WORKFLOW : str = "t27b-native.yml";
pub const NATIVE_CHECK : str = "t27b-native-ratchet";
pub const NATIVE_ARTIFACT : str = "t27b-native-ratchet";

/// The two parameters a job of this card starts with.
pub const PARAMS : [2]str = ["run", "commit"];

/// What the steward writes.
pub const BRANCH_PREFIX : str = "steward/t27b-ledger-";
pub const ISSUE_LABEL : str = "t27b-ledger-steward";
pub const GENERATED_BY : str = "t27b ledger steward (specs/jobs/t27b_ledger_steward.t27), #8654";
pub const MERGE_METHOD : str = "squash";
pub const JOURNAL_PREFIX : str = "t27b-ledger:";

/// Step kinds by number (specs/queen/jobs.t27: CHECK 0, WAIT 1, EFFECT 2), and the executor each
/// step names. A name the supervisor does not know fails the step: it never guesses.
pub const STEP_COUNT : u32 = 8;
pub const STEP_KIND : [8]u8 = [0, 2, 0, 2, 2, 2, 2, 1];
pub const STEP_WHAT : [8]str = [
"steward-run", "steward-refusals-issue", "steward-appliable", "steward-ledger-issue",
"steward-compose", "steward-pull-request", "steward-auto-merge", "steward-merged"
];

/// The effect kind each step needs ("" for a CHECK or a WAIT).
pub const STEP_EFFECT : [8]str = ["", "issue", "", "issue", "push", "pull-request", "auto-merge", ""];

/// Which ledger_steward.t27 effect code (EF_*) each EFFECT step is; 255 for a CHECK or a WAIT.
/// effect_wanted(run_verdict, code) says whether the step acts or answers PASS with nothing done.
pub const STEP_EF : [8]u8 = [255, 0, 255, 2, 1, 3, 4, 255];

/// The refusal issue is the only effect before the appliable CHECK; the ledger issue, the ledger and
/// its PR come only after it, so every check passed at the subject before the first of them
/// (jobs.t27 effect_may_run).
pub const REFUSAL_STEP : u32 = 1;
pub const APPLIABLE_STEP : u32 = 2;
pub const LEDGER_ISSUE_STEP : u32 = 3;
pub const COMPOSE_STEP : u32 = 4;

/// Whether a step writes the steward's batch: an EFFECT after the appliable CHECK (the ledger issue,
/// the branch, the PR, auto-merge). The refusal issue before it only reports.
pub fn writes_the_repo(step: u32) -> bool {
if (step >= STEP_COUNT) { return false; }
if (STEP_KIND[step] != 2) { return false; }
return step > APPLIABLE_STEP;
}

/// The ledger_steward.t27 effect code of a step, 255 for a CHECK, a WAIT or a step past the end.
pub fn step_ef(step: u32) -> u8 {
if (step >= STEP_COUNT) { return 255; }
return STEP_EF[step];
}

test the_card_writes_the_ledger_only_after_its_checks {
assert writes_the_repo(0) == false;
assert writes_the_repo(REFUSAL_STEP) == false;
assert writes_the_repo(APPLIABLE_STEP) == false;
assert writes_the_repo(LEDGER_ISSUE_STEP) == true;
assert writes_the_repo(COMPOSE_STEP) == true;
assert writes_the_repo(5) == true;
assert writes_the_repo(6) == true;
assert writes_the_repo(7) == false;
assert writes_the_repo(STEP_COUNT) == false;
assert STEP_KIND[0] == 0;
assert STEP_KIND[APPLIABLE_STEP] == 0;
assert STEP_KIND[7] == 1;
}

test every_effect_step_names_its_effect {
assert step_ef(REFUSAL_STEP) == 0;
assert step_ef(LEDGER_ISSUE_STEP) == 2;
assert step_ef(COMPOSE_STEP) == 1;
assert step_ef(5) == 3;
assert step_ef(6) == 4;
assert step_ef(0) == 255;
assert step_ef(APPLIABLE_STEP) == 255;
assert step_ef(7) == 255;
assert step_ef(STEP_COUNT) == 255;
}

invariant the_steps_are_ordered {
assert REFUSAL_STEP == 1;
assert APPLIABLE_STEP == 2;
assert LEDGER_ISSUE_STEP == 3;
assert COMPOSE_STEP == 4;
assert COMPOSE_STEP < STEP_COUNT;
}
Loading
Loading