Skip to content

chore(debt): close sixteen Phase 1 debt entries (M1.D) - #81

Merged
guysenpai merged 105 commits into
mainfrom
phase-1/debt/phase-1-debt
Sep 21, 2026
Merged

guysenpai merged 105 commits into
mainfrom
phase-1/debt/phase-1-debt

Conversation

@guysenpai

@guysenpai guysenpai commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

Closes the M1.D debt milestone. Do not merge or tag without Guy's own decision — this PR is ready for review, nothing more.

The title's count was corrected, and the correction is itself the milestone's seventh unit error. It read twenty-two; measured on the delivered lot (sha256 8fb85acb…, 648 lines) the table carries nineteen [CLOS — …] marks, of which SIXTEEN are M1.D's — the other three being M1.1.15.1, one closed before the milestone, and a [CLOS — <jalon>] template line. The title counted marks instead of closures, which is exactly what six entries of that same table were corrected for. It now reads sixteen: what the milestone CLOSED, not what the table carries.

What it delivers

Sixteen entries closed by the milestone itself (3 in S1, 6 in S2, 7 in S4); two were already closed before it; 31 of 49 remain open, and 9 of those 49 were minted during it — the last two M1.D.47 (the storage zone absent from ResolvedType) and M1.D.48 (a parked async result re-raised with no bounds check). A debt table that grows while it is being emptied is what measuring produces when the measuring is real.

The milestone's own description was false and is corrected where it lives. CLAUDE.md called it "a documentation and instrumentation milestone: no program line is delivered". Measured at close: 103 commits, 245 .zig files, floor 2290/2288 → 2379/2377 (+89 tests), and two rewrites under frozen surfaces (M1.D.21 Tier 0, M1.D.31 Forge).

Four external NO-GOs

The fourth (one defect, the same unit error one level up) — both checks were armed on the await node rather than on the property that matters, this point suspends the parent: race and sync park the parent with no await in its own statements. Enumerated again at the interpreter — every suspension originates at a return .suspended, eleven of them, of which seven propagate (driveLoop), three are await target families, and one is beginRaceSync. One predicate now carries both checks, armed on that set. Zero false refusals.

The third (one defect, a fourth kind) — the predicate was right and the SET it walked was wrong: refuseArenaLocalsAcrossAwait iterates the named locals, while a for loop's ForFrame retains a store handle to a value that is nobody's local. That is the unit error this milestone had already documented four times, the fifth instance landing inside the fix written to close the fourth. Closed by enumerating the seven frame variants field by field rather than adding an arm — exactly two retain, one already covered — and the refusal's shape was decided by measuring the type/zone cells one by one, because a first form cost a capability (no resource collection iterable in an async rule) rather than a false refusal.

The first two NO-GOs, and a class between them

The first (six defects) — a type predicate missing three of the seven rule-arena shapes; a hot reload reusing a component id without confronting its schema, and confronting it per declaration; a kinematic move deriving its angular velocity from a rotation the store normalises or drops; a reason-walk and its predicate reading different visited sets; a rename check establishing uniqueness on one side only.

The second (two P1 of a class the first had not reached) — and the class is what matters: the type checker was treating an ABSENCE of information as a guarantee. An unresolved type reached a capture unrefused, with the program clean of every diagnostic, at the cost of an abort from ordinary code; and a rule-arena local living across an await opened no escape window, EscapeSite having declared the variant and never produced its producer, while etch-memory-model.md had named that site in a correction dated 2026-09-10.

Between them, an internal adversarial review refused a closure seven more times, every finding against code written the same day — two of them this repository's costliest class, committed inside the correction that was fighting it.

Two standing lessons

A green fix with its counter-factual establishes its own case and nothing more. The reviews found eight defects in fixes delivered green, three of them in entries marked closed. Every one had a counter-factual, and every counter-factual was correct — it showed the fix had power over the case it treated, and said nothing about the neighbouring one. Naming the adjacent case is not enough either: one had been named safe, with a reason, and the reason was false. An adjacent case is shown safe; it is not declared safe.

Seven unit errors — counting a set different from the one named — of which TWO were committed inside the fix written to close the previous one, and the seventh in this very title, which counted closure marks instead of closures. That is not bad luck: a unit error is the one class a green counter-factual cannot expose, since the fix genuinely has power over the members it does reach. What ended the series was enumerating the set at the code — seven AsyncFrame variants read field by field, then eleven return .suspended sites classified into seven propagations, three await target families and one beginRaceSync — never better reasoning about the reported instance. The second enumeration establishes that no further source exists, and a reader can check that.

What is reported, not fixed

  • M1.D.43–M1.D.46, minted at the first NO-GO: the OutOfMemory path the pre-validation does not cover; tag identity inexpressible in the schema digest; storage and requires unhashed; builtin names unreserved, whose collision panics.
  • Two consequences of the arbitrated upper bound, named at their sites: an async fn on a struct receiver that suspends is refused (exact when self is read after the await, over-refusal when before); and every string is treated as rule-arena, the resolved type not distinguishing persistent from run.
  • A second numbering collision: the plan numbers M1.D.17 the Etch reload debt closed here, while CLAUDE.md carries a different debt under that number which the plan carries under none.

Measurements

Floor derived once, at the end, measured twice with no concurrent build: 2360/2379 macOS, closure agreeing independently at 2379; 2377 windows. Head b71017e. Green at Debug and ReleaseSafe, the latter from zig build's own exit code. zig build lint conservation OK.


Proposed squash message

chore(debt): close sixteen Phase 1 debt entries (M1.D)

M1.D set out to close a debt table against measurement rather than against
its own wording. Measured on the delivered lot: forty-nine entries,
nineteen closure marks of which sixteen are this milestone's, thirty-one
entries open, and nine of the forty-nine minted during it. A table that
grows while it is being emptied is what auditing for real produces.

The milestone's own premise did not survive it. It was described as
delivering no program line; at close it had changed 245 `.zig` files,
moved the floor from 2290/2288 to 2379/2377, and rewritten two paths
under frozen surfaces. Corrected where it lives rather than left standing.

Two external reviews reopened the closure. The first found six defects: a
type predicate missing three of the seven rule-arena value shapes, a hot
reload reusing a component id without confronting its schema and
confronting it per declaration, a kinematic move deriving its angular
velocity from a rotation the store normalises or drops, a reason walk and
its predicate reading different visited sets, and a rename check
establishing uniqueness on one side only.

The second found two of a class the first had not reached, and the class
is the finding: the type checker was treating an absence of information
as a guarantee. An unresolved type reached a capture unrefused with the
program clean of every diagnostic, at the cost of an abort from ordinary
code; and a local living across a suspension opened no escape window at
all, the enum having declared that site and never produced it.

Between them an internal adversarial review refused a closure seven more
times, every finding against code written the same day, two of them this
repository's costliest class committed inside the correction that was
fighting it.

What outlives the milestone is narrower than its subject. A green fix
with its counter-factual establishes its own case and nothing more: the
two reviews found eight defects in fixes delivered green, three of them
in entries marked closed. Naming the neighbouring case is not enough
either — one had been named safe, with a reason, and the reason was
false. An adjacent case is shown safe; it is not declared safe.

Seven unit errors ran through it — counting a set different from the one
named — of which two were committed inside the fix written to close the
previous one, and the last in this commit's own title, which counted
closure marks instead of closures. A unit error is the one class a green
counter-factual cannot expose, since the fix genuinely has power over the
members it does reach.

What could not be closed is named and numbered rather than implied.

guysenpai and others added 30 commits September 14, 2026 15:16
Absorbs points 1 and 2 of the cache-saturation arbitration, which were
merged as e9371a9 and are not reachable from any ref (cf. RD-1).

The -build save and its size measurement are deleted, and the budgets go
55 -> 75 and 20 -> 35. A budget must separate SLOW from HUNG: against a
cold windows-2025/ReleaseSafe cell measured at 52-57 min and a cold Debug
cell at 21, the previous pair separated nothing.

M1.D.8's written hypothesis is refuted before the change, not after. The
brief requires the -build restore hypothesis to be settled against the
existing logs; measured over 82 ubuntu-24.04-arm/ReleaseSafe job logs in
two windows -- 38 from 2026-08-13..08-21, the window the debt was observed
in, none of them expired, and 44 from 2026-09-11..09-14 -- ZERO restores
landed on a -build entry. 51 landed on a bare sha, 31 were fully cold. The
size guard never fired on that cell; the one warning in the set is a tar
failure at save time.

What the same logs do show is a fully cold restore 12 of 38 then 19 of 44,
which is the 129-of-132 recompilation the entry describes. The cause is the
absence of the lineage, not its contamination.

The all-or-nothing doctrine lived in the removed block and the surviving
block referenced it, so it is moved rather than dropped; the three dangling
references to the deleted step are rewritten. The superseded budget
justification is removed rather than left beside its correction.

Verified: YAML parses, the matrix job keeps exactly three cache steps, and
every steps.<id> reference resolves. 1200 -> 1094 lines.
M1.D.9, confirmed against the tree then closed. The cache key carried os,
mode, zig version and zon hash and NO cpu axis, and neither zig build
invocation passed -Dcpu, so both compiled for the runner's native CPU;
ZIG_CPU did not appear in the file at all.

Both halves move together. A key can only record an axis the build
controls, so pinning the key alone would register an axis nothing
exercises -- which is why this was left rather than half-done. The
archived crossover numbers therefore shift once, being now measured at
baseline; the file's own header states the real perf gates run locally on
the reference machine and that CI carries the compile-and-run smoke.

A live false justification goes with it: the key's comment claimed it
shared ci.yml's exact key scheme so the warm cache was reused repo-wide.
It does not -- ci.yml's keys carry a precision axis and a per-commit
component this one never had, and the two shapes diverged without the
claim being revisited.

Found in passing and in scope through C1.10: one actions/upload-artifact@v4
against six at @v6, where the allow-list pins @v6 and refuses even @v5 by
name. All seven uses across the four workflows now match it.

M1.D.14 needs no work: the budget raise is already in the tree at 20
minutes with its margin written at the site.

Measured, not inferred: the repository cache stands at 9.94 GiB of its
10 GB ceiling over 31 entries, of which 13 entries and 8.81 GB -- 82 % --
are one single commit's matrix run. Two consecutive commits cannot
coexist, so eviction is reached in ONE commit rather than in a handful,
which is the mechanism behind the cold restores of M1.D.8 and which no key
policy reaches. Reported, not acted on.
M1.D.16 re-posed rather than implemented, which is what the brief asks.
The written remedy is not merely written: measured on the tree, a `changes`
job already diffs base against head in bash and gates all thirteen cells on
its verdict -- stronger than the paths-ignore the entry describes, and
failing for the same reason. On a pull_request event the run is about the
merge result, so the diff runs from the PR's base; a documentation-only
commit on a branch whose earlier commits touched code still yields
code=true.

Narrowing it to the last commit is a one-line change and a wrong one: the
matrix is required per head sha, so a skip would let a merge proceed on a
sha whose verdict was inherited rather than produced, possibly from a red
run. The honest question is whether this tree's code is identical to one
already verified green -- a content question needing a digest and a way to
read the earlier verdict. Left to Guy with its cost stated; the bound is
now written at the site so it reddens when it falls.

Separately, ci.yml carried six lines of French prose, which CLAUDE.md
records with its owner named as whoever next opens the file. Measured at
exactly six, corroborating the record, and rendered into English. The
load-bearing halves were kept rather than paraphrased away: the bash step
exists to avoid an action outside the §7.3 allow-list, ci-gate is the
single required check, and `skipped` counting as green is what makes the
doc allow-list load-bearing.

Verified: ci.yml parses, six jobs intact, build-and-test still gated on
changes, every steps.<id> reference resolves, and the French measurement
re-run on the result returns zero.
The two measurements the arbitration requires before any application, and
one of them decides against the lever that was chosen.

Cold-time aggregation, widened because the 82 logs of the previous gate
are all ubuntu-24.04-arm/ReleaseSafe and carry no per-OS time: 130
successful cells over ten runs, measured on job wall-clock rather than on
build+test. In optimising modes the three operating systems sit in the
same cold band, 34.7 to 43.5 minutes -- windows is not the outlier the
premise assumed. Cold ubuntu ReleaseSafe at 41.0 exceeds warm windows
ReleaseSafe at 7.6 by a factor of 5.4, so caching only the four windows
cells would drop the wall onto ~41 minutes of cold linux against ~43.5
today. By the switch rule stated with the arbitration, lever (b) falls and
the route is (a).

Lever (a) needs the composition of the archive, which nobody has read.
The decomposition is restored on the surviving final-measure step -- it
lived on the post-build step this milestone deleted, and removing it with
the step was a loss.

M1.D.19, fifteenth occurrence, on this branch: hang signature once,
sibling class absent, zero assertions, 312/314 steps, 2255/2283 tests,
45.8 min, conclusion failure not cancelled, sibling precision cell green.
The hung executable is fd40064d4ae2024a593b818f42cecad5, a sixth distinct
identity matching none of the five recorded. Re-run at the same sha:
13/13 green.
The composition measurement, on all thirteen cells: .zig-cache/o is 96.7
to 98.2 per cent of the archive everywhere, the rest being z, h, b, c and
tmp together. On-disk totals 2.55 to 5.49 GB, compressing to the 564-785
MB entries measured earlier.

There is no subtree to exclude. The hypothesis behind lever (a) was that
linked test binaries form a separable bulk; they do not form a separable
subtree -- they live inside o, one hash directory each, mixed with every
other compilation output, and dropping any leaves a manifest in h
referencing an object that is gone. That is the file_hash FileNotFound
corruption this workflow already documents at its save step.

The arithmetic is sharper than two lineages: 8.81 GB for one matrix
lineage plus 1.72 GB for the rest is 10.53 GB, so ONE lineage and the rest
already exceed the 10 GB cap before any question of holding two.

Stated with its arithmetic rather than proposed: the largest component of
the rest is setup-zig's own cache at 1.16 GB, which was measured being
evicted anyway. Reclaiming it gives L <= 4.72 GB, which is seven cells at
the measured size -- exactly the seven optimising cells -- and puts the
wall on the slowest cold Debug cell at 16.3 min against 43.5 today.
Whether that action's caching can be turned off is a property of another
repository.

Second occurrence of the scheduler park class, with its dump. Neither
flake class: hang signature absent, sibling class absent, zero assertions,
10.6 min. Structurally identical to the first -- pending_count 0,
chunk_count 13 distributed 4/3/3/3, parks_entered 0 on all four workers,
steals_s 0, shutdown false -- and on the f32 leg where the first was f64,
so the class is not precision-bound. Two witnesses now exist to check any
answer against; reading the scheduler is the next session's work.
The action does not cache a toolchain: it caches .zig-cache itself, the
same directory this workflow manages, its tarball being cached separately.
Every job that also manages that directory was therefore carrying two
layers of one directory, restored one over the other and saved twice.

Measured on the job logs already in hand, and the key is worse than
supposed. It is weldengine-setup-zig-zigcache-v1-<job>-zig-<arch>-<os>-
<version>--<run>-<n>: it carries the JOB name, the architecture and the
Zig version, and carries neither the optimize mode nor the precision nor
the cpu pin. Three prefixes exist for thirteen cells -- the five
ubuntu-24.04 cells share one, the four windows-2025 cells another, the
four ubuntu-24.04-arm cells the third. So the tree zig build works on has
been a mixture of Debug, ReleaseSafe, ReleaseFast, f32 and f64 outputs
from sibling cells: a first-order candidate for the 129-of-132
recompilation, for the 2.55-5.49 GB spread of the archive, and for the
f32/f64 asymmetry that was never about precision but about which lineage
the shared layer happened to carry.

use-cache: false at the five sites whose job manages this directory, four
in ci.yml and one in bench.yml. nightly-fuzz.yml is left alone on a
measured motive: it has no cache step of its own, so nothing is duplicated
there and the argument does not reach it.

The comment claiming the action's cache was global and left at its default
is replaced rather than left beside its correction -- the same family as
the stale justification found in bench.yml.

Point 3 applied with it: the per-sha save is conditioned on a push event.
On a pull_request, github.sha is the merge commit, which changes whenever
either side moves, so a lineage written there names a sha no later run can
ask for. Reads are untouched; pull requests keep matching through the
prefix ladder.
Both applications verified rather than inferred. The setup-zig overlay is
gone: every one of the thirteen cells carried two of its cache lines
before and zero after, and the action states it -- "use-cache is false;
nothing to save". Point 3 holds: zero matrix cells saved on this pull
request run, where thirteen would have.

R is measured down by 1.34 GB. The six setup-zig-zigcache entries were
runtime_smoke_test 448 MB, bench_ecs_smoke 411 on linux and 352 on
windows, vertical_slice_smoke 133 MB, and -- measured, not rounded --
build_and_test at 188 and 184 BYTES. Thirteen cells share one key per
operating system and race to save it; one wins and the rest fail, so the
matrix half of that layer stored nothing while costing a restore in every
cell.

L did not measurably shrink and the deltas are not attributable. On-disk
totals over twelve comparable cells went 43.32 to 42.14 GB, -2.7 per cent,
with movement in both directions and large. Two changes landed together
and the after-run restores from a different cache state, so a cell that
matched a fresh lineage may now match a staler one. The two big falls are
on the two cells that were largest before, which is consistent with those
having carried the most foreign content; consistent is not established and
is not claimed. The o share reproduces at 96.5-98.1 per cent.

Structural consequence: pull request runs no longer save, so the
compressed size of one lineage can no longer be measured from this branch.
It is measurable on the first push to main carrying these changes, and the
cut waits on that number rather than being sized on a state that no longer
exists.
Two modifications, no comment added to either workflow.

The save condition gains one branch alongside main. With point 3 applied,
no pull request lineage is refreshed at all, so a milestone living on one
branch across four sessions restores older at every gate and drifts away
from its own cache -- the symptom arrived on the first run, where
ubuntu-24.04/ReleaseSafe/f64 was still going when the next push
superseded it. One branch writes; every other pull request stays silent,
which was the object. Second benefit: L becomes measurable from the
branch, so the cut no longer waits on a push to main. The full form of
point 3 returns at the merge of this milestone, with the cut sized.

The key gains a version component, zig-v2-, on the key and on both
restore levels at every site that caches .zig-cache -- eight lines in
ci.yml, two in bench.yml. The lineages currently in place were written
WITH the setup-zig overlay, so they hold a mixture of Debug, ReleaseSafe,
ReleaseFast, f32 and f64 outputs; restoring them re-imports exactly what
the previous gate removed, and natural expiry will not clear them since
they are accessed on every run. One run starts cold, after which a lineage
describes what the file actually does. Without it the next measurement of
L would still describe the state before.

The block asserting "SAVED ONLY FROM A PUSH, never from a pull request"
is removed rather than left beside its correction: this commit makes it
false. Nine lines.
Measured on run 34886074430: Save Zig cache (final) was SKIPPED on every
completed cell while the measure step above it succeeded, and no zig-v2
entry appeared. On a pull_request event github.ref_name derives from
refs/pull/<n>/merge and is "81/merge"; the branch name lives in
github.head_ref. The clause therefore could not match on a pull request,
and on a push it was unreachable since this workflow only triggers push on
main -- dead in both directions.
R: setup-zig-zigcache entries now number zero against six totalling
1.34 GB, so that cost is fully reclaimed. What remains is 189 MB of
tarballs, 247 MB of non-matrix v2 lineages, and 1.23 GB of v1 leftovers
that age out -- R in steady state is about 0.44 GB, not 1.72.

L: thirteen fresh entries total 8127 MB against 8.81 GB before, within
per-cell variation. Removing the overlay did not shrink a lineage's
compressed archive; it was duplicated storage and a correctness problem,
never a bloat of o/.

And the fact that decides the cut: the optimising cells are SMALLER than
the Debug ones, 577 MB mean against 681. Caching the seven optimising
cells and leaving the six Debug cold gives 2L + R = 8.33 GB, 1.67 under
the ceiling, and puts the wall on the slowest cold Debug cell at a
16.3-minute median against ~43.5 today. By mode, not by OS and not by
per-cell size.

Two red cells on the cold run, two classes, neither from this gate: f64
carries the hang signature twice with zero assertions and a seventh
distinct hung-executable identity; f32 carries the sibling class once,
with win32_thread_safety_test.test.concurrent createWindow +
destroyWindow failing without output. Per job each carries one class and
not the other, so the recorded exclusivity survives; the run-level
co-occurrence is new.
The cut, by mode. matrix.mode != 'Debug' gates the restore as well as the
save: with the restore left running, the six Debug lineages would be
accessed every run and would never reach the seven-day expiry, so
restricting writes alone would not reclaim their 4088 MB.

Sized on measurement rather than on symmetry. The optimising cells are
SMALLER than the Debug ones -- 577 MB mean against 681 -- so caching the
seven gives 2L + R = 2 * 3.94 + 0.44 = 8.33 GB against a 10 GB ceiling,
1.67 of margin, and puts the wall on the slowest cold Debug cell at a
16.3-minute median against ~43.5 today. By OS was refuted on wall-clock;
per-cell was refuted by o/ at 97 per cent everywhere.

Nothing depends on the skipped restore beyond the timing report, whose two
uses of its outputs carry a fallback; a Debug cell reports
cache_matched_key=none, which is honest rather than a defect.

Journalled with it: M1.D.29 with the provenance to replay it -- run
34887367465, windows-2025/ReleaseSafe/f32, hang signature absent, sibling
class once, and win32_thread_safety_test.test.concurrent createWindow +
destroyWindow failing without output. First time one of these classes
yields a test name rather than a binary identity.
13/13 green and the matrix wall falls from ~43.5 to 16.8 minutes. The
slowest cold Debug cell is windows-2025 at 16.8 against a 35-minute
budget, so the cut holds with margin and Debug needs no second look; the
cached cells run 1.0 to 8.3. The prediction was a 16.3-minute median and
the measured maximum is 16.8, which is the figure a wall is made of.

The ceiling is not respected yet and the reason is transitional: the
repository stands at 13.51 GB over 23 entries because the six Debug
lineages, 4449 MB, are stale but present -- never written or read again,
and not yet at the seven-day expiry. Self-correcting rather than a defect,
since eviction is LRU and nothing accesses them any more; the v1 leftovers
of the previous gate are already at zero by that same mechanism. Steady
state stays 2 * 4039 MB + 0.44 GB = 8.33 GB.
Run 34898572095 on 505a63d, a briefs-only commit. Two red cells, both
windows-2025/ReleaseSafe, at cached-cell durations -- so the build ran.

They are two different classes, not two occurrences of one test. f32
carries the hang signature once with no test failed and two tests lost,
hung executable 1dddaf0a77dab4b512e2547e0aedc439. f64 carries the sibling
class once, with win32_thread_safety_test.test.concurrent createWindow +
destroyWindow failing without output. The two classes also swapped
precision relative to the previous run, so neither is bound to one.

The win32 binary identity 513b5cd2dc9c83702db9e4e9ada6b9dd is the same as
the previous run's occurrence, where it fell on the other precision. An
identical content hash across precisions means the artifact does not
depend on -Dphysics_f64, so the precision label carries no information for
it: one binary failed in two consecutive runs.

Re-run at the same sha: 13/13 green. Known flake class by the entry's own
criterion.

What the re-run does not settle: attempt 1 matched lineage 76dfc07 and
attempt 2 matched 16cdc80, which attempt 1 had just saved. Both variables
moved together, so the re-run establishes non-determinism at this sha and
nothing about the lineage hypothesis. A forced-cold run at the same sha is
what separates them.
Totals re-measured at the opening: ci.yml 579 comment lines in 60 blocks
against 525 of program, bench.yml 73 in 9. The 60 against 61 is a grouping
edge and no exit criterion depends on it; the named block at l.230 is 72
lines.

First pass, mechanical: 25 blocks and 352 lines open on a milestone
identifier, 61 per cent of the comment mass. One more than previously
counted, the extra being "E5 / C0.8 end-to-end" at l.832.

Second pass over the remaining 35 blocks and 227 lines: 12 instances at 9
distinct sites are external guards that stay, 20 blocks and 147 lines are
design bound for the corpus, 3 blocks are narrative.

Reported rather than quietly compressed: the block explaining use-cache
false exists four times, identical, written by the script that applied the
previous gate at every call site. It is heap 1 by substance but heap 1 is
kept word for word and the extraction exception is three lines, so a
five-line block repeated four times is neither. It goes up for
arbitration.

Nothing deleted in this gate: heap 2 has no other home yet.

Also journalled: the same binary identity on both precision legs means
that artifact does not depend on -Dphysics_f64 yet is compiled, cached and
executed twice. The measurement that decides whether the case is isolated
-- counting how many of the 132 test binaries share a hash across one
windows ReleaseSafe pair -- needs no new run and belongs to the next S1
gate.
ci.yml goes from 579 comment lines in 60 blocks to 65 in 16; bench.yml
from 73 to 27. Program lines are untouched in both -- 525 and 58, identical
before and after -- so no step, condition or key moved.

Heap 3 deleted without destination: 25 blocks opening on a milestone or
gate identifier, three narratives, and the block claiming a subtree of
linked test binaries could be excluded from the saved path, which this
milestone measured false at o between 96.7 and 98.2 per cent.

Heap 2 deleted now that it has a home in engine-platform.md, section CI du
depot moteur (normatif): 19 blocks and 147 lines of design and
dimensioning. A one-line pointer at the head of the file names that
section, with the rule that a change of design goes to the document first.

Heap 1 kept word for word, plus four external guards extracted at three
lines each under the named reserve: the Windows runners ship no software
Vulkan driver; a workflow_dispatch workflow absent from the default branch
cannot be dispatched, measured as HTTP 404; the allow-list note on the two
cache sub-actions, already three lines and kept verbatim; and the bench's
permanent absence of a pass threshold.

The four duplicated use-cache paragraphs become one line at each site
rather than a paragraph at the first: the guard belongs where the reader
acts.

Reported rather than resolved: two milestone identifiers survive inside
the single block classified heap 1 and kept verbatim. Neither opens a
block, so the first criterion holds, but section 12 admits none at all and
removing them would be the in-place reformulation this gate forbids. And
three bench.yml blocks are heap 2 with no home, so they stay.
Zero identifiers across the three workflows, and the diff carries only
comment lines -- checked by filtering the hunks for any non-comment change
and finding none. Program lines stand at 525, 58 and 44.

The two identifiers inside the block kept word for word were attributions
rather than guards: removing them leaves both sentences identical for the
rest, and the guard beside them -- version 0.16.x taken literally by the
action and resolving to a 404 on the mirrors -- is untouched.

nightly-fuzz.yml carried a fourteen-line header opening on a milestone,
four identifiers, and an account of a sentence that used to stand there
and was false. Deleted, with what no reading of the file provides
extracted at three lines: scheduled runs fire only from the default
branch, and a workflow_dispatch workflow must also exist there before it
can be dispatched, measured as HTTP 404.

The three bench.yml blocks stay, deliberately. The owner document was
delivered at 907 lines and sha256 33672b04; the copy available here is the
earlier one at 887 and bed07c1d. Verifying them is therefore impossible,
and deleting on the strength of a statement rather than a reading is what
the instruction forbids.

The precision measurement cannot be taken from the existing logs: a cell
carries 15 zig-cache/o hashes, not 132, the form appearing only on a
failed command. The build graph answers more strongly because it derives
rather than samples -- physics_f64 is plumbed at exactly three sites, so
every test binary whose closure does not reach forge_3d is identical
between the two legs by construction. The per-binary count is NOT
established by that read and is not inferred from it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The primary key carries the commit sha, so no save is ever overwritten and a
run restores the previous lineage then saves the whole of `.zig-cache`, which
has no collector. A lineage is therefore a superset of its predecessor and
grows monotonically: 577 MB per cell measured cold, then 700, 713 and 734 over
three consecutive runs. Convergence does not save it — at 750 MB per cell,
`2 x 7 x 0.75 + 0.57 = 11.1 GB` against a 10 GB ceiling. No choice of cell
count survives a monotone growth; it only moves the date.

The key gains the ISO week, computed once in `changes` and propagated so the
thirteen cells of a run share one value. Every rung of the restore ladder
carries it, which is what cuts the lineage: measured over the ladder, 26 of 26
entries written in the preceding week are reachable without it and 0 with it,
while inside one week 7 of 7 optimising cells still restore their own — the
`build.zig.zon` rung included. A lineage lives seven days and starts from
zero, so its size is bounded by what a week accumulates. The cost is one cold
run per week. The `v2` prefix stays: it invalidates on a change of shape, the
week bounds in time, and they are different axes.

`bench.yml`'s ubuntu leg and `ci.yml`'s `runtime-smoke-test` rendered the same
key byte for byte, and the post step said so — `Cache hit occurred on the
primary key, not saving cache`. The key carries every axis that changes the
content produced and the job identity is one of them. Rendering every key of
the three workflows over the full matrix: 1 collision before, 0 after, 16
distinct primary keys before and 17 after.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The timing report emitted `cache_key=`, a hand copy of the build-and-test
primary key. It lied twice in two commits on this branch: 95e66ea added the
`v2` epoch to the four key lines and left it behind, 685575b added the ISO week
and left it behind again, so it named a string GitHub will never hold, three
lines above a `cache_matched_key` that contradicted it. Deleted rather than
resynchronised, because resynchronising rebuilds the same trap at the next
change of key. `cache_matched_key` carries what was actually served, which is
the useful fact and the one the cache debt entry reads; `grep -rn cache_key`
over the tree finds no other reader, script or gate.

A cell went red with 314 of 314 steps succeeded, no test lost and no assertion
fired, because `Upload CI timing artifact` got a 403 from the artifact service.
A diagnostic artefact must not decide the verdict of the cell it measures, so
that step — and only that step, checked by enumerating every step carrying the
field — is `continue-on-error`. The collected-test total stays a hard failure
in `zig build test`: that number is an observable of the product, an artifact
upload is an observable of the platform.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`ComponentRef` carried `{ chunk_ptr, slot }` on the table arm and the
entity alone on the sparse one, and the table arm's `refBytes` cast the
pointer and read the slot WITHOUT comparing the entity. A handle that
survived a despawn therefore read whichever row `Archetype.removeSwap`
had moved into that slot — silently, with no diagnostic and no error.

The handle becomes `{ entity, component_id, mutable }` for both storages
and every access re-resolves through `World.componentBytes`, which
already answers for both backends from an `EntityId` and already asks the
two questions the table arm never asked. A stale handle now fails with
`BridgeError.StaleComponentRef`, surfaced as a spanned runtime failure
with its own `RuntimeErrorKind` rather than folded into
`UnsupportedExpr`: the expression is supported and the handle is not.

That is `etch-reference-part1.md` §5.3 a — a ref designates a pair and is
re-resolved at every access — and it is what makes §5.3's corollary true:
a ref held by a timer, a `branch`, a `spawn`, a `race`/`sync` branch or
across an `await` is safe and at worst fails loudly. `cloneLocalsInto`
copies a `Value` verbatim into those snapshots, so the handle outlives
the tick by construction and the safety could not rest on a temporal
argument about deferred structural ops.

MEASURED by counter-factual, the chunk anchor restored with the two new
tests in place: the ref to the despawned entity returned
`.{ .float_ = 22 }` — the successor's value — and the write through a
survivor whose row had moved was LOST, the entity still reading 22 where
99 was written. Two distinct failure modes, so the pair is not redundant.

E0223 lands with it, refusing a rule-arena handle captured by a construct
that outlives the rule body (`etch-resolver-types.md` §8.2), checked at
the REFERENCE so the criterion is what the body uses and not what its
scope holds. The predicate is conservative, and the reason is measured:
`let items = [1, 2, 3]` resolves to `array_fixed` and is rule-arena,
while `get(Inv).items` resolves to `array_dyn` and is persistent, so the
resolved type does not carry the zone. A first version keyed on
`array_dyn` alone refused the SAFE case and admitted the UNSAFE one, both
at once. The widening refuses two safe captures — a literal string, a
persistent resource collection — pinned in `escape_false_refusal` so the
frontier is visible; measured cost on the suite: zero. A false refusal is
a compile error the author reads, a missed escape is a use-after-free
nobody sees.

`etch-memory-model.md` §11 owns the provenance analysis that makes the
answer exact; this pass does not compute it.

Test floor re-derived FROM THE SUITE, never from the closure:
`2275/2294 tests passed (19 skipped)` on macOS, 314/314 steps, windows
two lower by the guard's own `only_on = .windows` table. Green at f32 and
f64, Debug and ReleaseSafe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
guysenpai and others added 16 commits September 18, 2026 06:48
Verdict. `M1.D.5` delivered as documentation, its declared exclusion's cause
completed from one Zig 0.16 removal to two, the repair itself still due.
`M1.D.6` delivered — 33 tests, coverage 131 to 138 of 203, each verified by
mutation — and open on what the instrument does not see. `M1.D.4` re-posed, its
written mechanism refuted on source, content and gate. `M1.D.26` re-derived by
producer with nothing engraved. `M1.D.15` (a) measured and given an observer;
(c) found already arbitrated out in the tree.

Two of my own claims were wrong. A NUMBER: the bindgen gate is `M1.D.37`, not
`M1.D.35`, and the false one stood in two files because I wrote it twice in one
act. A CAUSE, and it is the one worth carrying: I reported a widget block as
breaking the grammar's positional-before-named rule when it carries a trailing
comma the grammar permits. I took a diagnostic's message for its cause, then
generalised the side to the whole class — so six divergences went back to
unattributed.

The standing result is one finding on four unrelated subjects: a static instrument
cannot be complete and an empirical one settles it in one run. Five revisions of a
coverage oracle still wrong against an `anytype` parameter; a builder sweep
returning zero for an enum with twelve produced variants; `@embedFile` at the
grammar failing 11 of 15 blocks; and the smallest form, believing a report instead
of producing the observation. Every static reading erred by over-reporting the
gap, so none shipped a false green and none could be believed.

Also new: a counter-factual on what one asserts cannot move. Swallowing the 32
codes declared unreachable predicted nothing would change, and nothing did.

Residuals named where a reader lands on them; the two defects found in passing
live at the plan as `M1.D.38` and `M1.D.39` rather than here. S3 delivered 34
tests and ZERO production lines — `src/` gains 29 lines, all comment.

Floor re-derived from the suite 2313 to 2348, windows 2346 by the guard's own
arithmetic, conservation agreeing independently at 2348 at every gate.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Seven fingerprints verified at 16 hex before any read, all matching
weld-spec/, with a control on an eighth file returning a different digest.
Two of the seven had moved since the M1.D drop and they are the two S4
needs current; 33 copies of the plan exist across 21 distinct states, so
only the digest identifies.

M1.D.30's count under the normative unit. Fichier racine binds the
root_source_file, so the two arms are a declared module root plus the
single file entering a directory from outside its subtree, which is what
render/gal/root.zig illustrates. 44 candidates under src/, 25 conforming,
19 not, in two classes the entry merges: 14 module roots not named
root.zig, and 5 executable roots not named main.zig carrying pub fn main.
The other readings measure something else: 28 counts directories whose
files are imported one by one, 69 counts executables the rule sends to
main.zig.

The lint rule the entry specifies catches none of its own three sites.
b.addModule has four real call sites, rooted at core/root.zig,
render/root.zig, foundation/root.zig and assets/shaders/embed.zig.
forge/module.zig is a b.createModule (build.zig:179); math/math.zig and
simd/simd.zig occur in build.zig zero times. The written predicate flags
one file tree-wide, outside src/, and none of the three. Third instance
of a remedy measured not to reach its subject, after M1.D.16 and M1.D.4.

Arm A and the cache.zig discriminant disagree on exactly one file:
plugin_loader/desc.zig is a declared module root and is re-exported by
plugin_loader/root.zig:16. src/modules/forge/ has no root.zig and three
declared module roots side by side (sync.zig:166, module.zig:180,
sensor_events.zig:824), so renaming module.zig leaves two beside it.

simd's internal imports, which the entry declares non mesurés, measured
at zero: foundation/root.zig:11 is the only reference in all of src/.
The engine-directory-structure.md claim holds in substance, not letter —
the document renders a tree, so the literal path returns zero corpus-wide
and :444 carries it.

M1.D.25's premise moved and this branch moved it: 15 diagnostics on 10
files against 732/519/146, measured with zig-out/bin/weld_lint, which
visits 221 of 249 files there. The exclusion is spelling-dependent and
its only direct caller is dead code. M1.D.28's figure is not reproducible
and M1.A, its referral target, has closed. M1.D.11's ten is the set that
installs, not the site set: 42 mains, 10 installing. M1.D.1 is out of
scope by the brief's own line, its deliverable being a new capability.
M1.D.12's deferral condition is met by interfaces/PhysicsModule.zig.
M1.D.7's ratio is arithmetic over a capacity the tree derives by search.
M1.D.2's premise is contradicted at 52 646 ns against a 62 000 gate
nothing enforces. M1.D.3's 7-58 % on Fedora has no witness in the tree.

No rename. It waits on a discriminant that owes a number: a rename leaves
the fingerprint unchanged under a different path, and until fingerprint
--check can tell a move from an edit, the only mechanical fix is the one
tools/weld_lint/main.zig:317-320 forbids in writing.

Floor re-derived FROM THE SUITE: 316/316 steps, 2329/2348 passed, exit 0.
zig build lint exit 0, conservation OK at 2348. zig fmt --check exit 0.
154 lines added, all markdown; zero production lines changed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
M1.D.40. `fingerprint --check` reported every absent baseline path as MISSING
and printed one remedy for it — undo the code edit that produced it — which
names an act a deliberate `git mv` has not performed, while the same text
forbids regenerating the baseline. Both exits were closed for a rename.

The discriminant costs nothing: `census.fingerprint` takes `source` and never
the path, so a displacement leaves the digest bit-identical. A baseline path
that vanished while an unlisted file of the same run carries that exact digest
is a rename, and no token moved.

`census.classify` is the split, pure and testable: `.moved` (same path, other
digest), `.renamed` (gone, exactly one unlisted file at that digest),
`.ambiguous` (gone, several), `.missing` (gone, none). Only a resolved rename
does not fail. The pairing must be UNIQUE to be a verdict — digests are unique
across this tree today, measured at 336 of 336, but that is a property of the
tree and not of the function, and two empty files collide at e3b0c442, the
sha256 of nothing this milestone's own register names as a disguise.

The trailer is now one remedy per outcome. A single text over three of them is
the same defect one level up: it would name an act two of the three readers
have not performed.

Seven witnesses, and two counter-factuals rather than one, because the first
prediction was wrong and is corrected rather than adjusted. Removing the digest
LOOKUP reddens the rename case AND the ambiguous case — ambiguity is that same
lookup's other outcome, not a neighbour — and the panic it raises truncates the
run, so two tests go unobserved. Keeping the lookup and refusing to RESOLVE a
unique pairing isolates the decision: red on the rename alone, green on the
other six including ambiguity, all reached. census.zig restored bit-identical
after each.

End to end on the binary: rename only exits 0 RENAMED; rename plus one changed
token exits 1 MISSING; an edit under the original name exits 1 MOVED; an added
comment stays green; two unlisted candidates exit 1 AMBIGUOUS.

Floor re-derived FROM THE SUITE, not from the closure, which is what the
conservation message prescribes: `zig build test --summary all` reports
2336/2355 on macOS, and the closure agrees independently at 2355. Windows 2353
by the guard's own only_on table. 316/316 steps, exit 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
M1.D.30, two of its three named sites. `engine-zig-conventions.md`
§ *Fichier racine* makes `root.zig` the root of every module and sub-module and
cites `render/gal/root.zig` as the sub-module case; `math/math.zig` and
`simd/simd.zig` were that shape under the directory's own name.

Measured before touching either. Neither appears in build.zig at all: they are
entered only from src/foundation/root.zig:8 and :11, one line each, and the
resolved-import referrer set of each is exactly that file. No sibling imports
either by name. Both already carried the canonical root shape — doc header,
private import layer, doc-commented re-exports, comptime pin block — so nothing
but the file name moves. Their headers name the DIRECTORY and stay true.

build.zig:443 named `simd.zig` in prose and is corrected; a path-anchored
selector cannot see that class, which is how it was missed at G1.

The five build.zig occurrences of src/foundation/simd are tests/ and bench/
roots, none of them this root. Residual references to the old spellings outside
briefs/: zero. Inside briefs/: eight files, NOT edited — a brief is a dated
record, and that includes the two m1.E-*.tsv baselines under briefs/artifacts/.
`fingerprint --check` now reports both as RENAMED with the digest unchanged,
which is what M1.D.40 was for and what the previous code called MISSING.

src/modules/forge/module.zig is NOT renamed. Measured: that directory has no
root.zig and build.zig declares THREE modules over it — sync.zig:166,
module.zig:180, sensor_events.zig:824 — published as three names to different
consumers. Renaming one leaves two declared roots beside the new root.zig, which
moves the defect rather than closing it. Reported, not arbitrated.

316/316 steps, 2336/2355 passed, exit 0. lint exit 0, conservation OK at 2355.
fmt exit 0. Added: 191 code, 45 comment across the gate; both renames carry a
zero-line diff, git recording them as pure renames.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
M1.D.40 delivered and two of M1.D.30's three sites renamed. The record carries
what the gate measured rather than what it intended.

The lot was not opened, third time this milestone: announced 7b6e8734 on no
copy, weld-spec carrying the announced line count at 640 with e80c0e2e. G2 did
not depend on it.

The correction that matters: at G1 I called the fingerprint step a blocker a
rename would turn red. Measured here, it reports 54 diverged, of which 20 were
already diverged at the branch base by content identity, and nothing runs it in
CI or a hook. The mechanism was exact and the consequence was never measured.

Two counter-factuals rather than one, because the first prediction was wrong:
removing the digest lookup reddens the rename case AND the ambiguous one, and
its panic truncates the run so two tests went unobserved rather than green.
The second mutation isolates the decision from the mechanism.

A tooling fact: zig build truncates a failing step's captured output — 50 MOVED
lines printed against a summary of 54, where the binary run directly prints 54.

forge/module.zig measured and not moved: no root.zig in that directory and
three declared module roots published under three names, so renaming one leaves
two beside it. Three readings, none arbitrated here.

One over-match of my own inside this gate's measurement: a bare-basename sweep
credited forge's sync.zig with a referrer that imports gal/vulkan/sync.zig.

Floor 2348 -> 2355 from the suite, windows 2353. 316/316, lint 0, fmt 0.
Decount by diff-density: 191 code, 45 comment (31 doc), density 19.07%.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
M1.D.25, both defects it does not carry.

The exclusion was spelling-dependent and the defect was wider than the
perimeter. `inPerimeter` verdicts on the first real segment, so for
/Users/.../tests that segment is `Users` and the excluded subtree came back
inside: `lint tests` exited 0 where `lint $(pwd)/tests` exited 1 with 15
diagnostics on the same files. Measured while fixing it, the same absolute
argument also made `fingerprint` write a machine-local path into the baseline —
the hazard census.normalizePath already answers for separators — and `census`
emit rows nobody can key on. One contract broken in three subcommands.

Refused at the walk root rather than repaired downstream: the repo root is not
knowable from a path, so any normalisation here would be a guess.
`scan.isRepoRelative` plus one check in `scan.collectZigFiles`, the single choke
point every call site passes through. `walkRoots` reports it rather than
propagating, because a returned error reaches the user as a stack trace and the
thing worth saying is which spelling to use.

Witness end to end: `lint tests` 0; `lint $(pwd)/tests` 2 with the spelling
named; `lint ./src/foundation` 0; census and fingerprint on an absolute root
both 2. Nine assertions on the predicate, including the non-vacuity pair that a
bare colon is not a drive letter.

The only direct inPerimeter caller was unreachable and is removed: main.zig
reported each `pending` entry under `if (pending.len != 0)` while `pending` is
empty and noPathOutsideCoverage asserts it stays empty. Thirty lines cut.
matchesPending went with them, a pub one-line wrapper over hasPathPrefix whose
own doc named that block as its caller.

Test removal declared: no assertion is lost. matchesPending's six assertions
across three tests are re-pointed at hasPathPrefix, the primitive they were
always exercising, under the same names. What a re-opener owes is written at
`pending` — the reporting loop must come back with the entry.

collectPaths is deleted with it; walkRoots serves lint, census and fingerprint
from one path and main.zig is 12 lines shorter net.

doc_comments caught a defect in this change: the new function was inserted
between collectZigFiles' doc comment and its declaration. Found by the suite,
not by re-reading. Both docs now sit against their own declarations.

Floor re-derived FROM THE SUITE 2355 -> 2358, windows 2356. 316/316 steps,
2339/2358 passed, exit 0. lint 0, conservation OK at 2358. fmt 0.
Decount: added 70 code, 39 comment (32 doc), density 35.78%.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fourth fingerprint mismatch, not opened: 973a680a on no copy while weld-spec
carries the announced line count at 642 under ba6ae827. The three G3 rows were
read at G1 under the verified 0bc203ca and are byte-identical at the same line
numbers, and the lot's changes name 30, 40, 41, 42.

M1.D.25 — 15 diagnostics on 10 files against 732/519/146, the instrument
enumerating 221 of 249 there. Both defects the entry does not carry are closed:
the spelling-dependent exclusion, which also made fingerprint write
machine-local baseline paths and census emit unkeyable rows, and the
unreachable pending report. Test removal declared, no assertion lost.

M1.D.28 — 703 nested undocumented pub across 111 files, measured with the
rule's own predicate through its own plumbing, probe reverted bit-identical.
512 matches no perimeter: src 669, +tests 687, all 703, modules+core 504. The
restriction is a named suppression rather than an omission, and the referral
target M1.A has closed, so the deliverable has no holder.

M1.D.11 — the ten is the set that INSTALLS, re-measured: 42 column-0 pub fn
main of which 7 reference float_env, and 3 of 4 src/ thread spawns in
production, all installing. So 35 mains install nothing and the entry's "c'est
la garde qui est différée, pas la conformité" is false at the predicate's own
scope. The boundary is literally src/: all 7 are under it, and the 18
bench/*.zig reference float_env zero times including the float-heavy physics
benches, while their two siblings under src/foundation/simd/bench do install.
The guard is not written here because it would redden 35 sites on its first
run, which is the mechanism M1.D.28 records one entry above.

doc_comments caught a defect in this gate's own change, found by the suite.

Floor 2355 -> 2358 from the suite, windows 2356. 316/316, lint 0, fmt 0.
Decount: 70 code, 39 comment (32 doc), density 35.78%.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
M1.D.1, what remains after its deliverable is removed as out of perimeter —
a first exercising implementation is a new capability.

The header of api_stub_test.zig reads "a silent addition, removal or rename of
a callback breaks the test". The file calls all 79 callbacks BY NAME and holds
zero occurrences of @typeinfo or std.meta.fields, so a removal or a rename
stops it compiling and an ADDITION is never called. Two thirds true on the file
the repository treats as the freeze's documentation.

A count assertion over the seven surfaces closes it: 24 / 8 / 6 / 2 / 8 / 17 /
14. The counter-factual's GREEN branch carries the proof: a ninth
stub-defaulted field on WeldMemoryAPI, reusing an existing stub so the mutation
compiles, reddens ONLY the count test (expected 8, found 9) while the by-name
"WeldMemoryAPI: all stubbed" test stays green — which is exactly the hole. So
nothing in the repository detected a silent addition before this; no size pin
on the C surface fires.

The by-name half is genuinely exhaustive, audited rather than assumed: every
field of all seven sub-APIs is called, zero uncalled. Only the addition clause
was false and the header is now true as written.

CLAUDE.md:117 said the tree has exactly ONE site handing a plugin a table.
Measured, four: loader.zig:158, :202, :268, :287. The ordering argument is
untouched and now precise — one, the entry at :202, hands the table before the
version check at :208/:215, the other three are post-check lifecycle callbacks.

Two faults of my own, both in this gate's instruments. The field count came
from a grep whose class [a-z_]+ has no digits, so draw_vec3_edit never matched
and WeldEditorAPI read 16 where it holds 17 — the same character-class defect
recorded at S1/G4 for forge_3d_tests, caught only because the assertion was
written against the compiler. And the first counter-factual named a stub that
does not exist, failing to compile for a reason unrelated to what it tested.

Floor re-derived FROM THE SUITE 2358 -> 2359, windows 2357. 316/316 steps,
2340/2359 passed, exit 0. lint 0, conservation OK at 2359. fmt 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
M1.D.7's tree side. bench/results/ecs_hybrid_crossover.md reads "chunk
compaction is INTRA-chunk only and `archetype.zig` releases no chunk, so the
count follows the cumulative number of adds and never the live population".

The first clause holds. The second does not: releaseChunkIfEmpty frees a chunk
at zero occupancy (archetype.zig:381) and World.reclaimChunk performs the
renumbering repair (world.zig:1173), both delivered at S2/G2 of this same
milestone, AFTER those numbers were taken.

A head note on the M1.B/G11 precedent rather than a refresh: the figures are
the record of the run that produced them. What is corrected is the mechanism
sentence, because M1.D.7's oracle would otherwise inherit a claim the tree
contradicts. What reclaim does NOT do is compact a chunk that stabilises above
zero, which is the shape this report measures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
M1.D.1 is out of perimeter for its deliverable, a new capability. Its two
remaining defects are closed: the freeze test that claimed to catch an addition
and could not, and CLAUDE.md's one-site claim where four sites hand the table.
The counter-factual's green branch carries the proof — a ninth stub-defaulted
field reddens only the count test while the by-name test passes.

M1.D.12's deferral condition is met by interfaces/PhysicsModule.zig, and its
cost figure measures work a move does not do: one @import in the whole tree, so
the move is one git mv plus one line, not eleven rerouted sites. Not moved —
where the engine's world scalar lives is the class of decision ruled outside a
rename's mandate for forge/ at G2.

M1.D.7: the capacity is derived by search and not by arithmetic, so a ratio can
be right about the number and wrong about the method; the denominator an oracle
needs has zero call sites. A committed report it would cite carries a head note.

Two faults of my own, both in this gate's instruments: a character class
without digits, the same defect recorded at S1/G4, and a counter-factual that
died of its own compile error before testing anything.

Floor 2358 -> 2359 from the suite, windows 2357. 316/316, lint 0, fmt 0.
Decount: 19 code, 10 comment on the .zig files, density 34.48%.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
M1.D.3's engine side. threading.zig read "Used by the job system scheduler
(worker pinning)". Measured: setAffinity has ZERO production callers. Every
reference in the tree is its own declaration, its own inline test,
core/root.zig:57's re-export comment, or tests/platform/threading_test.zig, and
src/core/jobs/ names affinity or pinning zero times on a whole-word match. The
Windows SetThreadAffinityMask arm and the Linux pthread_setaffinity_np arm live
entirely inside this file, and the macOS arm is a documented no-op.

The header now states that, and the consequence a bench protocol meets: the
mechanism exists, nothing calls it, and pinning is reachable only on Linux and
Windows where no orchestrator runs today.

lint caught the first draft of this comment naming a milestone identifier,
which engine-zig-conventions.md §12 admits in no file.

316/316 steps, 2340/2359 passed, exit 0. lint 0, conservation OK at 2359. fmt 0.
Floor unchanged, which is expected for a comment-only change and is stated
because it was measured. Decount: 0 code, 9 comment (9 doc).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
M1.D.2's band is misattributed in both halves, verified at the source rather
than inherited. M0.8 holds zero S1 mentions and zero microsecond figures; M0.9
mentions S1 five times and holds zero occurrences of the band. Both endpoints
are M0.1, and that brief diagnoses each where it reports it: 69.2 is a
cold-start first run with followers at 41.7-42.2, and 89.1-91.5 is
--workers=14 with "the S1 bench is an undersized workload for 14 workers"
stated in the same paragraph, while the same document measures --workers=4 at
51.5 / 52.3 / 52.5. The gate itself is a double derivation, 62 = 57.2 + 5 where
57.2 = 54.5 x 1.05, every term descending from the 2026-05-09 spike figure.

M1.D.3: the engine claimed a mechanism it does not have, corrected in place.

The position asked for on an unenforced gate: NEITHER a measurement debt nor a
CI debt. The first licenses re-basing a derived constant nothing consults. The
second licenses CI enforcement, which cannot supply the protocol that makes the
number mean anything, and which M1.1.9 already retired four wall-clock
assertions over. What it is: an authority defect. The wrapper enforces the
measurement's CONDITIONS and reports its VALUE, so enforcement is correctly
placed; the constant calls itself the live gate while its only possible
enforcer declines to act on it, and the producer the entry asks for already
exists in that same orphaned script. Recommendation recorded, decision Guy's.

M1.D.37 reproduced live by ordinary work: the comment-only edit reddened the
bindgen gate. Content identical throughout, unstaged 2339/2359 with one
failure, staged 2340/2359 green — isolating the cause to the index comparison.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The journal and the five rubrics. No program line: 188 markdown lines, zero
.zig files touched.

FIVE premises were false AT DEPOSIT — M1.D.30's lint predicate reaching none of
its own three sites, M1.D.28's 512 which the same predicate read at 1384/730 at
the commit that wrote it, M1.D.11's claim that only the guard was deferred
against 35 entries that install nothing, M1.D.2's band attributed to two briefs
of which one holds zero S1 mentions, and M1.D.3's Fedora figure with no witness
in the tree. TWO more were made false afterwards: M1.D.25's 732/519/146, exact
at M1.E and reduced to 15/10 by twenty-two commits of this branch, and
M1.D.12's "no second consumer", satisfied by interfaces/PhysicsModule.zig at
M1.1.15.2. The distinction separates an entry that was wrong from an entry the
world moved under.

FOUR OF THE NINE COUNTED A DIFFERENT SET FROM THE ONE THEY NAMED, and that is
the class rather than staleness: M1.D.11's ten counts the sites that install,
M1.D.12's eleven counts alias occurrences a move never touches, M1.D.30's three
is derivable from no reading, M1.D.2's band counts a cold first run and a
--workers=14 measurement where it names the machine at rest. In each the number
is correct about something, which is why it survived review — a wrong figure
invites arithmetic, a figure over the wrong set invites agreement.

SIX faulty selectors of mine in one session, of which this record carried FOUR:
G1's glob off by one and G5's -i pinning matching spinning lived in session
messages and not in the brief until now, which is the milestone's own subject
applied to its own journal. What caught five of the six was an instrument fired
beside the selector in the same execution on a case known to exist; the sixth
was caught by refusing a grep where a compiler could answer.

The C-ABI freeze's green branch is the whole proof: a ninth stub-defaulted
field reddens only the count test while the by-name test passes, so nothing in
the repository saw a silent addition to a frozen ABI.

Residuals named where a reader lands on them: M1.D.41's sixteen sites,
M1.D.42's forge case, and the four S4 entries that stay open with their
deliverable stated.

Floor 2313 -> 2348 -> 2359 across S3 and S4, re-derived at every gate.
316/316 steps, 2340/2359 passed, lint 0, fmt 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
R1 is widened BY DERIVATION from `Value`'s rule-arena variants, not by adding
the arm the report named: `.closure` and `.struct_t` were missing too, and
treating the named case would have left both. `Value.optional` is a per-body
store handle WHATEVER its payload, so `?int` escapes exactly as `?string` and
refusing every optional is exact rather than over-refusal.

R2 gives both arms one `ComponentDesc`. The `orelse` arm is structurally
unreachable — measured, the registry has exactly one entry-append site and it
always derives the digest — so its direction is free and it now REFUSES: an
unknown layout is not a matching layout. Same arbitration carried to the two
other `orelse` arms of this lot.

R3 takes OPTION A, arbitrated by Guy on measurement: pre-validate every declared
schema before the first mutation. `schemaDigestOf` does not hash
`default_bytes`, and `compileTypeDecl` splits exactly there — 70 lines that
mutate nothing outside their locals against 64 that materialise defaults and
allocate immortal blocks the digest never reads. So the pass needs no cached
intermediate and no double allocation. It also closes R2's site: the `TagSet`
refusal fires AFTER Pass A, so refusing it where it is met strands every type the
program declared. Two per-site fixes covered neither; one pass covers both, and
the counter-factual reddens both tests. What it does NOT close is named: an
`OutOfMemory` mid-pass still half-registers.

R4 sweeps the class rather than its two named instances: `setBodyTransform`
carries the same partial commit as `moveKinematic`. R5's fix is to make both
walks read the CURRENT visited set and continue past a silent field; on `main`
`reasonOf` follows only two forms, so the asymmetry is a defect of M1.D's own
widening. R6's uniqueness becomes two-sided, counted and not consumed.

AN INTERNAL ADVERSARIAL REVIEW THEN CONFIRMED SEVEN FINDINGS, ALL AGAINST CODE
WRITTEN THE SAME DAY. `setBodyTransform`'s new early return made `sync_in`
count a pose write and a wake that no longer happened, under a comment declaring
the wake composed "unconditionally" — closed AT THE SEAM, which normalises first
through the one predicate and counts the refusal apart, leaving the frozen
signature alone. Both forge doc contracts still named a stale handle as the only
no-op. `Interpreter.init` does not exist; the entry point is
`Interpreter.compile`. `engine-etch-*` names no file in the corpus — a
fabricated citation, removed. `schemaDigestOf` hashes neither `storage` nor
`requires` either, so two of `schemaDigestFor`'s three inputs were inert and
the pass allocated `@requires` names for nothing. `.ambiguous.count` had
changed meaning while all three consumers still declared the old one. And the
stated reason for excluding the builtin time resources was false: nothing
reserves those names.

Two more found by me. `reasonOfIn` kept an `else` where `carriesMarkedIn`
is exhaustive, which would let the next divergence compile silently — the R5
class rejoined; and R1's "adjacent case" test captured an int, clean because an
int is POD, the CONTRARY reason to the uncovered one. It stands as a
non-vacuity control and the boundary is declared in writing instead.

Three tooling incidents, self-reported. A background `zig build … | tail`
reported exit 0 while the build failed with 1. Counts taken while another build
ran gave 2371/2372/2369 with phantom failures. And a review subagent wrote
`tests/etch/zz_probe_verify_test.zig` into the tree and registered it in
`build.zig`: the symptom is a collected total that CLIMBS while nothing of mine
changed. Workflow stopped, `build.zig` reverted, all ten tests verified mine.

Floor re-derived FROM THE SUITE once, at the end, measured three times for that
reason: 2350/2369 on macOS, windows two lower. `zig build lint` conservation
OK. Forge green at f32 and f64, Debug and ReleaseSafe; full suite green at
Debug and ReleaseSafe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
THE JOURNAL AND THE FIVE RUBRICS. Four sessions, 97 commits, 245 `.zig` files
and 4 `.md`. Sixteen entries closed by the milestone itself — three in S1, six
in S2, seven in S4 — two already closed before it, twenty-nine of forty-seven
still open, and SEVEN of those forty-seven minted during it. A debt table that
grows while it is being emptied is what measuring produces when the measuring is
real.

THE MILESTONE'S OWN DESCRIPTION WAS FALSE AND IS CORRECTED WHERE IT LIVES.
`CLAUDE.md` called it "a documentation and instrumentation milestone: no program
line is delivered". Measured at close: 245 `.zig` files changed, floor 2290/2288
to 2369/2367, and two rewrites under frozen surfaces. True when written; the world
moved under it. The brief's frozen Scope never said it — it refuses a new
CAPABILITY, which six fixes and a pre-validation pass are not — so this is a
`CLAUDE.md` correction and not a deviation.

SEVEN INTERNAL REFUSALS, ALL AGAINST CODE WRITTEN THE SAME DAY, and that is the
result of this gate more than the six corrections. An adversarial review — five
reviewers by dimension, each finding handed to a verifier charged with refuting
it — refused the G6 closure seven times. The two that carry: `setBodyTransform`
gained an early return while `sync_in` counted `poses_applied` for a write that
no longer happened, under a comment declaring the wake unconditional; and both
frozen doc contracts in `world.zig` still described a stale handle as the only
no-op. The costliest class in this repository, committed inside the correction
that was fighting it.

TWO FABRICATIONS OF MINE, both found by the review and neither by me.
`Interpreter.init`, which does not exist — the entry point is
`Interpreter.compile`; and `engine-etch-*`, cited as the pre-validation pass's
sole normative justification while naming no file in a corpus `CLAUDE.md`
enumerates exhaustively and forbids guessing at.

THE SUBAGENT CONTAMINATION, AND THE SYMPTOM THAT CAUGHT IT. A review agent wrote
`tests/etch/zz_probe_verify_test.zig` into the tree and registered it in
`build.zig`. What exposed it was a NUMBER THAT MOVED: the collected total climbed
2369 → 2373 → 2375 across runs in which I had added no test. An instrument that
counts more than it was given is a contamination, never a progress — and it reads
differently from cache contention, which makes the total VARY in both directions
with phantom failures. The rule had been in memory since M1.1.1 and did not fire:
a rule in memory does not execute itself, only an observable symptom triggers it.
The symptom is now written beside it.

Two more tooling incidents, self-reported: a piped background `zig build`
reported exit 0 for a build that failed with 1, and a counter-factual on a
comptime function compiled clean under `zig build` alone, which does not analyse
it — repassed through `zig build test` it gave "switch must handle all
possibilities".

A SECOND NUMBERING COLLISION IS REPORTED AND NOT ARBITRATED. The plan numbers
`M1.D.17` the Etch reload debt closed at G6; `CLAUDE.md` carries a different
debt under that number, which the plan carries under none — measured on the
delivered lot, zero occurrences of `buildSchemaRemap` or `.sav` in 646 lines.
M1.E corrected a first such collision; this is a second. The table belongs to the
corpus.

FLOOR DERIVED ONCE, AT THE END, and measured three times for the reason above:
`2350/2369` on macOS with the closure arriving at 2369 independently, 2367 on
windows. Entry floor was 2290/2288, so the milestone adds +79 tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A one-line correction to the milestone row written moments earlier: it named
`b18f5c10`, which is S2's close, for S4's. Verified against `git log`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@guysenpai guysenpai changed the title Phase 1 / Debt / Phase 1 debt fix: close sixteen debt entries and mint seven measuring them (M1.D) Sep 19, 2026
@guysenpai
guysenpai marked this pull request as ready for review September 19, 2026 11:11
guysenpai and others added 2 commits September 20, 2026 04:03
A SECOND external NO-GO, on two P1 the first had not reached. Both were closed
by the same realisation: the type checker was treating an ABSENCE of information
as a guarantee.

P1-a. The `.optional` and `.some_lit` arms return `.unknown` whenever the
payload is not a builtin, and `isRuleArenaType` answered false for it — so an
optional carrying a struct reached a captured body with the program type-checking
ENTIRELY clean, zero diagnostics, while the optional store is reset at the rule
body boundary. The cost was an abort reachable from ordinary code.

I had named that case an accepted adjacent one, on the ground that `.unknown`
"is the statement that the type is unknown". It is not: the tag carries two
meanings — the fallback after a diagnostic, and a deferral emitted with no
diagnostic at all — and the reasoning silently assumed the first. That is the
exact limit of the green twin as we had just completed it: naming the adjacent
case is not enough, it must be shown SAFE and not merely outside the treated one.

Both options were measured before the arbitration. Preserving the lifetime
information needs a recursive payload — `optional` carries a `BuiltinType`, not a
`ResolvedType` — across 105 sites in 7 files, against a deferral documented as
deliberate. The conservative refusal has ONE production reader and costs one
false refusal, then zero once a separable third gap is closed: a fixed array had
no method arm at all, so `items.len()` fell to `.unknown` where the dynamic
array, map and set arms each answer `int`. That arm lands here too.

P1-b is a missing capability and not a fix. `openEscapeWindow` has three callers
and `EscapeSite` declares six variants, five produced — `async_frame` had its
slot reserved and no producer, so no widening of the predicate could ever have
reached it. `etch-memory-model.md` names that site explicitly, in a correction
dated 2026-09-10; the contract existed and the implementation did not.

Reproduced as an abort on the array store, with the type checker accepting the
program first. The review's witness is better built than ours: the async rule
alone passes, and it is the neighbouring SYNCHRONOUS rule whose body-end reset
invalidates the suspended local. A test exercising one rule masks the class by
construction, so every test here carries that neighbour.

The upper bound ships — every rule-arena local in scope at a suspending `await`,
read afterwards or not — because the refined rule is not liveness but a syntactic
walk of the following statements, which an indirect read through a closure or a
branch crosses unseen. Its measured cost is two false refusals, each verified
genuinely safe rather than assumed so, one of them by rebuilding the program with
a neighbouring rule and watching it pass.

AND REFUSING `self` IS WARRANTED, NOT CONSERVATIVE. The measurement showed the
rule also reaches the `self` of an `async fn` on a struct, so the deciding
program was written: a method reading `self.base` AFTER its `await`, with a
neighbour, ABORTS on the struct store. The existing test survived only because it
read `self.base` before suspending. The consequence is named rather than hidden —
an async fn on a struct receiver that suspends is now refused — and that test
becomes a free `async fn`, its replaced assertion named at the site.

Counter-factuals: removing the predicate reddens P1-a; disabling the refusal
reddens both P1-b tests while the green twin stays green, which is what shows the
refusal targets rule-arena storage and not suspension.

Floor re-derived FROM THE SUITE once, at the end, measured twice: 2354/2373 on
macOS with the closure agreeing independently, 2371 on windows. Green at Debug
and ReleaseSafe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
THE JOURNAL AND THE FIVE RUBRICS. Four sessions, 100 commits, 245 `.zig` files
and 4 `.md`. Measured on the delivered lot: 47 entries, 18 closed of which
SIXTEEN by this milestone (3 in S1, 6 in S2, 7 in S4), two closed before it, 29
open, and 7 of the 47 minted during it.

The milestone's own premise did not survive it. It was described as delivering
no program line; at close it had changed 245 `.zig` files, moved the floor from
2290/2288 to 2373/2371, and rewritten two paths under frozen surfaces. Corrected
where it lives rather than left standing.

TWO EXTERNAL NO-GOs REOPENED THE CLOSURE. The first, six defects: a type
predicate missing three of the seven rule-arena shapes, a hot reload reusing a
component id without confronting its schema and confronting it per declaration,
a kinematic move deriving its angular velocity from a rotation the store
normalises or drops, a reason walk and its predicate reading different visited
sets, and a rename check establishing uniqueness on one side only.

The second, two P1 of a class the first had not reached, and the class is what
matters: the type checker was treating an ABSENCE of information as a guarantee.
An unresolved type reached a capture unrefused with the program clean of every
diagnostic, at the cost of an abort from ordinary code; and a rule-arena local
living across an `await` opened no escape window at all, `EscapeSite` having
declared the variant and never produced its producer.

Between them, an internal adversarial review refused a closure seven more times,
every finding against code written the same day — two of them this repository's
costliest class, committed inside the correction that was fighting it.

THE STANDING LESSON, AND IT OUTLIVES THE MILESTONE. A green fix with its
counter-factual establishes its own case and nothing more. The two reviews found
EIGHT defects in fixes delivered green, THREE of them in entries marked closed.
Every one of those fixes had a counter-factual and every counter-factual was
correct: it showed the fix had power over the case it treated, and said nothing
about the neighbouring one. The green twin's missing half — naming the adjacent
case — was instituted at the first NO-GO. The second showed that naming is not
enough either: `.unknown` had been named an accepted adjacent case WITH a reason,
and the reason was false. An adjacent case is shown safe; it is not declared
safe.

The form that showing takes is the one the last gate used without being asked.
When the measurement revealed the refusal also reached the `self` of an `async
fn` on a struct — a case the arbitration had not seen — the deciding program was
written rather than the case filed as an assumed cost. It aborts. The refusal
was warranted, and the pre-existing test survived only because it read `self`
before suspending.

Also recorded: the subagent that wrote into the tree, caught by a collected total
that CLIMBED while nothing of mine changed; two fabrications of mine, a symbol
that does not exist and a spec file that names nothing in the corpus; and the
pipeline-exit-code family reaching FOUR in one milestone, the fourth arriving
after the first three were written into this brief — a rule recorded does not
execute itself.

Floor derived once, at the end, measured twice with no concurrent build:
2354/2373 on macOS with the closure agreeing independently, 2371 on windows.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@guysenpai guysenpai changed the title fix: close sixteen debt entries and mint seven measuring them (M1.D) chore(debt): close twenty-two Phase 1 debt entries (M1.D) Sep 20, 2026
guysenpai and others added 3 commits September 20, 2026 10:04
A THIRD external NO-GO, and a fourth kind of defect: the predicate was right and
the SET it walked was wrong. `refuseArenaLocalsAcrossAwait` iterates
`ctx.locals`; a loop variable is an `int`, so it answers false on it, while the
`ForFrame` retains a handle to a temporary that is nobody's local. That is the
unit error this milestone documented four times already — `M1.D.11` counting the
installing set, `M1.D.12` alias occurrences, `M1.D.2` a cold machine, `M1.D.30`
three sites derivable from no reading — with the fifth instance inside the fix
written to close the fourth.

ENUMERATED FIELD BY FIELD RATHER THAN GIVEN AN ARM. `AsyncFrame` has seven
variants; exactly two retain something that can be a per-body store handle —
`ForFrame.iter`, whose `.array`/`.map` forms carry a `handle: u32`, and
`CallFrame.scope`, already covered since G8's `self` is that field. The other
five carry offsets, cursors, NodeIds and StringIds. A read-only verification pass
confirmed the enumeration cell by cell and found, beyond the frames, that the
runtime unit is frames plus locals plus result plus captured filter values.

THE REFUSAL'S SHAPE WAS DECIDED BY MEASUREMENT, NOT BY PREFERENCE. The resolved
type does not carry the storage zone, measured cell by cell: an array literal and
a local holding one resolve `.array_fixed` and no resource path produces that
type, since a resource fixed-array field is not a collection field at all and its
read is `undefined_symbol`; but `.array_dyn` carries both a resource slice and
`let xs: int[] = [1, 2]`, and `.map_t` carries both a resource map and a local
map literal. A first form refusing everything but `.range` cost a CAPABILITY —
no resource collection iterable in an async rule — which is not a false refusal.
So the rule covers `.array_fixed`, the half a type can decide, at zero measured
false refusals; the rest is named and pinned.

TWO LEAKS THE FIRST COST MEASUREMENT MISSED. `conc_loop_depth` resets at three
boundaries and the new counter reset at none: measured, `for x in [..] { branch {
await ... } }` produced a false refusal, a branch body running on a child task
with its own frame stack, and a timer body produced a second diagnostic on a
program already refused by E0901. Both closed at the same three boundaries,
re-measured at zero. A counter that does not know the boundaries of its subject.

The diagnostic names no variable, because naming one would be false: `x` is the
element. It names the iteration retained across the suspension.

What remains is pinned rather than left silent. A NAMED local of an ambiguous
type is covered — by the sibling rule, not this one. An UNNAMED literal of an
ambiguous type is not, and is asserted at zero so the day the resolved type
carries the zone that test fails and names what to tighten. Two sites outside the
frames are recorded with their measurements: `AsyncTask.result` parks a race
branch's return in the husk and re-raises it ticks later with no bounds check
where `forAdvance` has one, measured reachable; and the wake filter stabilises
strings only.

One self-report: the first counter-factual measured nothing. Replacing the
predicate body with `return false` left a parameter unused, so 34 steps failed to
COMPILE and no test reddened — which reads exactly like a fix with no power.

Floor derived once, at the end, measured twice with no concurrent build:
2357/2376 on macOS, closure agreeing independently, 2374 on windows.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A FOURTH external NO-GO, and the same unit error one level up — the second time
a fix written to close one commits another of its kind. The gate before walked
the named locals instead of what the frame retains; this one armed on a keyword
instead of on every point that suspends. `refuseArenaLocalsAcrossAwait` had a
single call site and the iterator check sat in the same block, both gated by the
`await` node, while `race` and `sync` park the parent with no `await` anywhere in
its own statements.

ENUMERATED AT THE INTERPRETER, NOT PRESUMED. Every parent suspension originates
at a `return .suspended`; there are eleven. Seven are in `driveLoop` and are the
verdict propagating upward rather than new sources. Three are `stepBodyStmt`'s
`await` target families — a `TaskHandle`, `wait`/`wait_unscaled`, and the two
event forms. One is `beginRaceSync`, parking the parent on
`children_any`/`children_all`. `branch`/`spawn` detach, so the parent runs on.
The missing source was exactly one, and it is the one that does not carry the
keyword.

`await_suspendable` turned out not to be await-specific at all: its own doc calls
it the async driver's frame-driven spine, a property of the POSITION, so the
race/sync arm reuses it unchanged. What is await-specific is `is_head`, E0904's
requirement that the await be the statement's full right-hand side — and
`race`/`sync` are statements by construction.

So both checks now live in ONE predicate named on the property, called from the
two arms, with a single place where the question is asked. Adding two branches to
the existing condition would have left the next review to find the third.

The refusal is conservative by necessity rather than by preference, and that is
written at the site: `beginRaceSync` returns `.advanced` when no branch is
admitted, so whether a `race`/`sync` suspends at all depends on a runtime guard.

Cost measured before proposing: ZERO false refusals, suite green at 316/316.
Counter-tests red before (`0` where `1` is required) and green after, for `sync`
and for the loop variant, each carrying the neighbouring synchronous rule that
makes the refusal non-arbitrary. Green twin: the same `sync` in the same place
with a POD local instead of an array, accepted — without it a rule refusing every
`race`/`sync` outright would pass both counter-tests. Counter-factual: removing
the arming reddens both and leaves the twin green.

One defect caught by an assertion rather than by reasoning: the predicate took a
`NodeId` and the race/sync arm handed it a statement, so `exprSpan` fired its
`assert(id.category == .expr)`. It takes the span now, each caller computing it
with the function for its own category.

Floor derived once, at the end, measured twice with no concurrent build:
2360/2379 on macOS, closure agreeing independently, 2377 on windows.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
THE JOURNAL AND THE FIVE RUBRICS. Four sessions, 103 commits, 245 `.zig` files
and 4 `.md`. Measured on the delivered lot: 49 entries, 18 closed of which
SIXTEEN by this milestone, two closed before it, 31 open, and 9 of the 49 minted
during it — the last two being the storage zone absent from `ResolvedType` and a
parked async result re-raised with no bounds check.

The milestone's own premise did not survive it. Described as delivering no
program line, it changed 245 `.zig` files, moved the floor from 2290/2288 to
2379/2377, and rewrote two paths under frozen surfaces. Corrected where it lives.

FOUR EXTERNAL NO-GOs. The first, six defects. The second, two of a class the
first had not reached: the type checker was treating an absence of information as
a guarantee. The third, the predicate right and the walked SET wrong — named
locals instead of what the frame retains. The fourth, inside the fix written for
the third: both checks armed on the `await` keyword instead of on every point
that suspends the parent, `race` and `sync` suspending with no `await` in the
parent's own statements. Between them an internal adversarial review refused a
closure seven more times, every finding against code written the same day.

TWO RESULTS OUTLIVE THE MILESTONE, and the second is the one four reviews paid
for.

A green fix with its counter-factual establishes its own case and nothing more.
Eight defects were found in fixes delivered green, three in entries marked
closed. Naming the adjacent case is not enough either: one had been named safe,
with a reason, and the reason was false. An adjacent case is shown safe; it is
not declared safe.

And six unit errors — counting a set different from the one named — of which TWO
were committed inside the fix written to close the previous one. That is not bad
luck: a unit error is the one class a green counter-factual cannot expose, since
the fix genuinely has power over the members it does reach. What ended the series
was enumerating the set AT THE CODE — seven `AsyncFrame` variants read field by
field, then eleven `return .suspended` sites classified into seven propagations,
three `await` target families and one `beginRaceSync` — and never better
reasoning about the reported instance. The second enumeration establishes that no
further source exists, and a reader can check that.

Also recorded: the subagent that wrote into the tree, caught by a collected total
that CLIMBED while nothing of mine changed; two fabrications of mine, a symbol
that does not exist and a spec file that names nothing in the corpus; and the
pipeline-exit-code family reaching FIVE, the last of which was read correctly
before committing — the only one that cost nothing, and it arrived after the
other four were written into this brief.

Floor derived once, at the end, measured twice with no concurrent build:
2360/2379 on macOS with the closure agreeing independently, 2377 on windows.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@guysenpai guysenpai changed the title chore(debt): close twenty-two Phase 1 debt entries (M1.D) chore(debt): close sixteen Phase 1 debt entries (M1.D) Sep 21, 2026
The squash title read "twenty-two entries closed". Measured on the delivered lot:
nineteen `[CLOS — …]` marks, of which SIXTEEN are this milestone's — the other
three being another milestone's, one closed before this one, and a template line
in the file's own model. The title counted MARKS instead of CLOSURES, which is
precisely what six entries of that same table were corrected for.

It is the seventh instance of the class, it was raised once and re-issued
unchanged before being corrected by a count that had not been done, and it is
recorded beside the other six: what is at issue is the class, not whose hand
committed it. My own control measurement carried a trace of the same thing — a
count of "twenty" that swept in a `[CLOSED]`, which is a different token.

Title corrected to `sixteen`: what the milestone closed, not what the table
carries.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@guysenpai
guysenpai merged commit c3e6d31 into main Sep 21, 2026
20 checks passed
@guysenpai
guysenpai deleted the phase-1/debt/phase-1-debt branch September 21, 2026 12:41
guysenpai added a commit that referenced this pull request Sep 23, 2026
The two cache corrections and their measured cost on run 35926601770,
the two known windows classes red at b3fd951, the tag split and its
four counter-factuals, the sync-for shrink check, and (A) with its
seventeen witnesses and twenty counter-factuals, predicted before they
ran. B3 carries its answer.

CLAUDE.md read PR #81 as unmerged in two rows, and drew from that a
conclusion now false; both corrected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
guysenpai added a commit that referenced this pull request Sep 24, 2026
The token measured in CI, the PR #81 cache deleted, the tool and its
fourteen counter-factuals, the cost of the changes job, and the gain
predicted for this very commit before it is pushed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant