capability: the script gate learns the sixth stage the merge just added #226

Manually merged
tvidal merged 7 commits from feat/226-capability-the-script-gate-learns-the-sixth-st into master 2026-09-09 13:42:32 +02:00
Owner

capability: the script gate learns the sixth stage the merge just added

Along the way:

  • capability: the test entry installs its arena, like every program entry does
  • command: /next-broken — one cell out of the BROKEN queue, gated, per run
  • capability: the entry boundary check that ran before the call it watched
  • docs: the gate is scripts/test.sh, and it is invoked directly
  • plan: §0 was false in both halves — there is no unmerged work
  • docs: the Book of CkLang — gated, or it is prose

Touches 91 files: .claude/commands/next-broken.md, CLAUDE.md, docs/01-laws.md, docs/02-programs.md, docs/03-types.md, docs/04-bindings.md and 85 more.

capability: the script gate learns the sixth stage the merge just added Along the way: - capability: the test entry installs its arena, like every program entry does - command: /next-broken — one cell out of the BROKEN queue, gated, per run - capability: the entry boundary check that ran before the call it watched - docs: the gate is `scripts/test.sh`, and it is invoked directly - plan: §0 was false in both halves — there is no unmerged work - docs: the Book of CkLang — gated, or it is prose Touches 91 files: `.claude/commands/next-broken.md`, `CLAUDE.md`, `docs/01-laws.md`, `docs/02-programs.md`, `docs/03-types.md`, `docs/04-bindings.md` and 85 more.
Author
Owner

Split. The decision in resolve_install_id is a pure function now, with its own tests in src/rust/crates/ck_codegen/src/freestanding.rs.

Split. The decision in `resolve_install_id` is a pure function now, with its own tests in `src/rust/crates/ck_codegen/src/freestanding.rs`.
Author
Owner

Measured. It is a named constant in src/rust/crates/ck_driver/tests/capability.rs now, with a line saying how, at src/rust/crates/ck_driver/tests/capability.rs:531.

Measured. It is a named constant in `src/rust/crates/ck_driver/tests/capability.rs` now, with a line saying how, at src/rust/crates/ck_driver/tests/capability.rs:531.
Author
Owner

Comment added on this line in src/rust/crates/ck_driver/src/lib.rs. It was obvious when I wrote it and it will not be in a month.

Comment added on this line in `src/rust/crates/ck_driver/src/lib.rs`. It was obvious when I wrote it and it will not be in a month.
tvidal approved these changes 2026-09-09 09:30:00 +02:00
tvidal left a comment

Reads well. One question on src/rust/crates/ck_codegen/src/freestanding.rs, nothing blocking.

Reads well. One question on `src/rust/crates/ck_codegen/src/freestanding.rs`, nothing blocking.
@ -167,3 +167,3 @@
/// emitted user fns' Cranelift ids: the driver lowered `Arena::install` as an
/// ordinary user fn, so its absence here is a backend invariant break.
fn resolve_install_id(
pub(super) fn resolve_install_id(
Owner

resolve_install_id reads, decides and writes in one function. The deciding part is the piece I would want to test on its own. Worth splitting?

`resolve_install_id` reads, decides and writes in one function. The deciding part is the piece I would want to test on its own. Worth splitting?
@ -718,0 +721,4 @@
// `MonoError::kind`, never on the rendered message, like its siblings.
compile::CompileError::Monomorphize(e) => {
Ok(ck_cktest::Capability::Monomorphize(e.kind().to_owned()))
}
Owner

I had to read this line twice. Nothing wrong with it, but one line of comment would save the next person the second read, at src/rust/crates/ck_driver/src/lib.rs:724.

I had to read this line twice. Nothing wrong with it, but one line of comment would save the next person the second read, at src/rust/crates/ck_driver/src/lib.rs:724.
@ -502,0 +528,4 @@
// boundary for every raise kind rather than only the ones the pass already
// checked in-block. The may-raise seed is deliberately NOT widened: doing so
// would re-mask the mis-ordered emit exactly as `Panic::raise` did.
const BROKEN_CELL_COUNT: usize = 44;
Owner

Where does the number beside BROKEN_CELL_COUNT come from? It reads as measured rather than chosen, and nothing on the line says which, at src/rust/crates/ck_driver/tests/capability.rs:531.

Where does the number beside `BROKEN_CELL_COUNT` come from? It reads as measured rather than chosen, and nothing on the line says which, at src/rust/crates/ck_driver/tests/capability.rs:531.
`ec179d8a` gave the matrix a `monomorphize:` form between `lower:` and
`codegen:`, and the Rust twin (`tests/capability.rs`) was updated in that
commit. The script gate and `ckc outcome` — landed in `fc744ca6`, written
against the five forms — were not, and two things were wrong until now:

- `classify_compile_failure` had no `CompileError::Monomorphize` arm, so
  `impl_method_signature_size_param` still fell through to "the app stopped
  at a stage the capability matrix has no outcome form for" even though the
  form now exists. It reads `MonoError::kind()`, never the rendered message,
  the same spelling `tests/capability.rs:237` uses.
- `stage_rank` ranked `monomorphize:` at 0, below `typecheck:`, so any
  mismatch involving it would have reported the wrong DIRECTION — a forward
  move reading as a regression. Ranks are 1..6 with monomorphize at 3,
  matching `Stage`'s own order, which is where the pass actually runs.

`./scripts/test.sh`: ALL GREEN — unit 1354/1354, corpus 106 apps, capability
440 cells (ledgers 440 / 46 / 146). `cargo fmt --all --check` 0 diffs;
`cargo clippy --workspace --all-targets -- -D warnings` 0 warnings 0 errors.
`a_list_literal_in_a_test_fn_body_fails_the_suite` is fixed and renamed to
`a_list_literal_in_a_test_fn_body_runs`. BROKEN_CELL_COUNT 46 -> 45;
CELL_COUNT holds at 440 (a rename).

The bug was one word of reasoning, written down and then trusted for a year of
commits: the test path passed `None` for the entry's arena-install fn because
"the test path retargets allocations in the codegen tier (`alloc_target`)
rather than through an entry prologue". That is true of WHERE an allocation
goes — the retarget routes it to the CkLang `Arena::alloc` — and says nothing
about whether the arena EXISTS. `Arena::install` (`Arena.ck:35`) is what writes
cursor 16 and capacity 65536 into the region header. Without it `alloc` read a
zero header, `cursor + bytes > capacity` was `0 + n > 0`, and the bump raised
`OutOfMemory::Exhausted`. Every allocating `test fn` failed, whatever it
allocated and whether or not it ever read it back.

Two halves of one pair, and only one was lowered into the test object:
`compile::testfloor::floor_targets` extended its target set with `a.alloc`
alone, so `Arena::install` had no emitted body for the entry to call even if
the entry had asked. Both now reach the object, `FreestandingTestEntry` carries
`install` beside `alloc`, and `define_freestanding_entry` resolves it through
`freestanding::resolve_install_id` — the same function the `fn main` path uses,
widened to `pub(super)` rather than copied.

Nothing in the list tier moved. The cell had said so from the day it was
written ("the fix is in whatever the test entry hands a body as its allocation
target, not in the list tier"), and it was right.

Measured beyond the matrix: `ckc test tests/corpus_test_run/lists` now reports
`2 passed; 0 failed`, where the cell recorded both as failing. That app was
green throughout on `corpus_test_run`, whose `run.md` grammar has no `tests:`
verb and reconciles `exit:` only — the hole is named in plan.md §4c and stays
open.

Gates: three legs green (unit 1354, corpus 106 apps, capability 440 cells),
fmt clean, clippy 0 warnings. `architecture::r3_no_file_exceeds_loc_cap` is red
and was red before this commit (`ck_driver/src/lib.rs` 1322 lines, cap 862,
verified against the unmodified tree); this commit is net-zero on that file.
The procedure that closed `a_list_literal_in_a_test_fn_body_runs` (7934acde),
written down so the next one does not re-derive it.

Deliberately short. The three commands already here run 103-329 lines and
front-load contract prose; this one is 66 and spends its budget on the four
things that actually cost a run when they are missed:

- the cell's own prose is the brief, and the mechanism must be provable as
  arithmetic before a single edit (the last fix was diagnosed entirely by
  reading, and the first observation confirmed it outright);
- the Law 3 sweep — a fixed cell falsifies every doc comment that cited the
  broken behaviour, and those sentences are what mislead the next agent;
- the ratchet and the rename rule move in the SAME commit;
- a pre-existing red is verified against the unmodified tree and attributed,
  never absorbed, and never worsened by a line.

It also states the rotating-mask invocation inline, because `rotate-exec.sh`
is deleted and an ad-hoc `cargo` is now the easy way to break CLAUDE.md §5.
`a_loop_bound_raise_in_the_entry_tail_is_swallowed` → `..._is_reported`.
BROKEN_CELL_COUNT 45 → 44; CELL_COUNT holds at 440 (a rename).

The wrong reasoning, in one sentence: `ck_codegen::cfg::emit_return` read
"the boundary checks the raise cell before its normal return" as "before
the return is EMITTED", and put the check at the top of the function —
ahead of the `match mode` that emits the tail expression.

The mechanism, as instruction order. For the entry's
`Return(Some(call overrun))` the emit was

    load  tag        ; 0 — nothing has raised yet
    brif  tag, crash, cont
  cont:
    call  overrun    ; stores tag = 1, returns the propagation dummy
    return dummy     ; the dummy IS the exit code

so `fn main() -> Int { overrun() }` exited 0 where the law says 101. Two
things hid it. `let n = overrun(); n` escapes because the call is a Floor
STATEMENT, emitted ahead of the terminator, so the load already saw
tag = 1 — that is the twin cell,
`a_loop_bound_raise_in_a_let_slot_is_reported`, and the pair is what
localised the defect in the SLOT rather than in `while<N>`. A
`Panic::raise` tail escapes for the other reason:
`raise_lower::lower_tail` binds a sanctioned may-raise call to a temp and
emits its own in-block check — but the may-raise fn set is seeded only
from `Panic::raise` op references
(`ck_driver::compile::raise::closure_may_raise`), so a `while<N>` bound
raise seeds nothing, gets no in-block check, and rode the tail slot out.

What moved: the check now sits at each `ReturnMode`'s own return site,
after the tail has been evaluated — all four modes, including both return
sites of `emit_value_words_return`. The crash block already ends in a
`return_`, and `cont`'s only predecessor is the block the tail value was
defined in, so dominance holds and `seal_all_blocks` is unaffected. The
doc comment that stated the wrong reasoning now states why it was wrong.

What deliberately did NOT move: the may-raise seed. Widening it to cover
loop-bound raises would give `overrun` an in-block check and turn this
cell green while leaving the boundary emit mis-ordered — re-masking the
defect exactly as `Panic::raise` did. `plan_call_stack` §2.2 needs the
branch to exist AT the entry, which is now true for every raise kind.

Gates, all under the shared rotating mask:
  ./scripts/test-capability.sh -v <cell> <twin> — cell self-retired
      printing `runs: 101`, which is the proof the fix landed
  ./scripts/test.sh — ALL GREEN, 3 legs: unit 1354 passed / 0 ignored,
      corpus 106 apps, capability 440 cells
  cargo test --workspace --tests --no-fail-fast — 1610 passed,
      1 target failed: `-p ck_driver --test architecture`,
      `r3_no_file_exceeds_loc_cap` on
      `crates/ck_driver/src/lib.rs: 1322 lines (cap 862)`.
      PRE-EXISTING and not this commit's: verified by `git stash` on the
      unmodified tree — identical failure, identical 1322 lines. No file
      this commit touches is at or near its cap (`ck_codegen` cap 594,
      `cfg.rs` 331; fn cap 72, `emit_return` 68).
  cargo fmt --all -- --check — 0
  cargo clippy --workspace --all-targets -D warnings — 0

Law 3 sweep on the old cell name: three prose claims cited the swallow —
the twin cell's "the program then exits 0" pair table,
`untrapped_raise_reports_no_call_stack`'s "not hypothetical" bullet, and
the twin's STATUS line. All three rewritten to say what is true now and
to keep the mechanism as history. `plan.md` §1 ledger and §4c updated.
`test-rotate.sh` was deleted in `fc744ca6` when the rotating-mask duty moved
into the legs. Three places still named it as the live way to run the gate, so
a reader following any of them ran nothing:

- `plan.md` §1 — "Gate: `./scripts/test-rotate.sh`";
- `src/rust/README.md` — "`scripts/test.sh`, run via `scripts/test-rotate.sh`";
- `ck_driver/tests/common/mod.rs` — sized its worker pool "under
  `test-rotate.sh`'s `taskset`", which is now each leg's own.

A blunt rename would have replaced one false sentence with another, because the
two scripts do not cover the same thing. `test-rotate.sh` swept every target;
`test.sh` sequences three legs and PRINTS what it does not cover, and neither
`fmt` nor `clippy` is a leg. So each site now says what `test.sh` actually does,
and `plan.md` keeps its recorded numbers while attributing them to the run that
produced them — under the deleted wrapper, over every target. The numbers stay
true and stop implying a coverage the current gate does not have.

The remaining mentions are history and correct as written: CLAUDE.md §4/§5 and
`rotate-cpus.sh`'s header explain the deletion, and `plan.md`'s thermal note
says the `--serial` mitigation's wrapper is gone.

fmt clean, clippy `-D warnings` 0.
§0 opened with "read before starting anything" and then told the reader that
branch `worktree-agent-a7936c96325d2015b` (`ec179d8a`) fixes the gate's three
red cells and is NOT merged, and that `--test capability` has never observed it.
It merged in `d9492c2e`, and the gate has since run all 440 cells green over it
repeatedly. A section whose whole job is to be read first, carrying two false
claims, is the worst possible place for them (Law 3).

It now says where work lands: `master`. `wip_v2` is fast-forwarded into it and
finished; the `slice-*` / `worktree-agent-*` refs still in `git branch` are
spent. What the branch carried — the sixth `Capability` form
`monomorphize: NoInstantiation`, and E0757 split out of E0742 — is in the tree
and documented where it belongs, not in a queue file's merge notice.

§1's gate record replaced too. It described a run made through `test-rotate.sh`,
deleted in `fc744ca6`, and the numbers were older than the ledger table directly
below them. Now: three legs ALL GREEN (unit 1354, corpus 106 apps, capability
440 cells), fmt clean, clippy 0, and the uncovered set swept — 1610 passed
across 74 binaries with one red, `r3_no_file_exceeds_loc_cap` on
`ck_driver/src/lib.rs` (1322 lines, cap 862), named as STANDING and verified
against an unmodified tree. It is the one open item this file did not track.

The header's provenance line said every number was measured at `0659629d` on
2026-08-17, which stopped being true four commits ago. Numbers now carry their
own stamps, and an older stamp is stated to be a re-measure waiting to happen.
`docs/` was deleted in 07a49fef because it could never go green, and §6 has
said since that it "exists in NEITHER repo". That sentence is now false, so it
is corrected rather than left standing — a false claim about where the truth
lives is the defect that section is about.

What lands is a 16-chapter book plus its gate. The condition is the whole
point: every DELIVERED claim is a runnable project under `docs/examples/`
paired with the outcome it must produce, and `scripts/test-docs.sh` is a leg of
`scripts/test.sh`, so a chapter stating something the compiler does not do
turns the gate RED. 19 fixtures, 35 expectations. The old `docs/` made claims
no machine read; this one cannot.

Strict in BOTH directions, like the capability matrix: `for_does_not_lower`
pins a DESIGNED gap and fails when that gap CLOSES, because the chapter
documenting it is then wrong. A behavioural sentence carries DELIVERED or
DESIGNED, or it is a defect in the book.

WRITTEN AGAINST THE SIBLING'S STDLIB FIRST, WHICH IS WHY THE GATE EARNED ITS
KEEP ON THE FIRST HONEST RUN. Re-pointing at this repo's oracle broke four
expectations, and every one was a real defect:

  · effect rows were missing from the book entirely — the cells carry
    `effect Assert` and the transcription dropped it. E0413 now has a chapter
    section (ch.10) and a fixture; ch.5/8/12 corrected;
  · ch.3 claimed `Collection::len` does not lower. Against this oracle it
    runs — it is a TRAIT method, and `List::len` is E0301;
  · ch.13 described the sibling's 20-unit stdlib, not this repo's 38-unit
    oracle. It now sets out both, measured: `Floor.ckh` byte-identical,
    `Types.ck` 618 vs 158, `Prelude.ck` 769 vs 488, `Alloc`/`Arena` here and
    excised there;
  · paths cited `tests/gaps/`, which no checkout here contains. Bare paths are
    this repo now; `CkLang:` prefixes the sibling — the same rule §6 already
    stated for doc comments.

The book is gated against THIS repo's compiler and THIS repo's oracle. A
cross-repo `--stdlib` would make a green here a statement about a checkout
nobody in this repo controls.
tvidal manually merged commit 736f988199 into master 2026-09-09 13:42:32 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
northgale/cklang-bootstrap!226
No description provided.