Conversation
The digest input path holds zero ResolvedType references across its four functions and 86 lines - schemaDigestOf, tagSetDesc, computeLayout, schemaDigestFor - with the extractor shown to fire on isRuleArenaType in the same execution, so the zeros are results and not silences. types.zig names schemaDigest zero times; registry.zig names ResolvedType zero times; ResolvedType exists in two files, one of them a single test helper. computeLayout derives FieldKind from the AST type node, so the chain is AST -> FieldKind -> Layout -> digest and the type checker sits beside it, not upstream. The premise came from a homonym: M1.D.47's zone is a value's memory lifetime, M1.D.45's storage is @storage(.sparse), the ECS storage mode, which registry.zig:198-200 declares OUT of the hash by ARCH-005. So it is one entry for one gesture, and 44 and 45 are a second, disjoint gesture on schemaDigestOf. The 105-site figure costed option 1 of the S4/G8 arbitration, a recursive optional payload, and not the plan's deliverable; it reproduces under no selector tried - 68/6, 90/15, 298/12, 214/2. The gesture is not single: the ambiguous variants are built at eight sites, four of them resolving a type NODE, which carries no zone and cannot. Those four sit under namedTypeToResolved, 37 call sites, most of which cannot supply one. M1.D.47 is not started and the arbitration is raised. Floor re-derived from the suite and unchanged at 2379, measured twice under a live ZLS watch build; lint conservation OK at 2379; fmt clean. Zero .zig touched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Of 37 call sites, exactly ONE both admits an ambiguous collection type and knows the zone - lookupFieldType's .resource arm at :8178, which dispatches on the receiver and therefore holds the provenance already. THIRTEEN admit one without knowing it: twelve parameter/return sites and one let annotation. The remaining 22 are moot or derivative, and that is a measurement of the gate at :3896, where collection field types are resource-only and component/struct/event are rejected at :3917 - probed, 0 errors on resource and 1 undefined_symbol on each of the other three, which is the positive half that makes the zeros results. At the twelve parameter and return sites the zone is NOT DETERMINATE, which is stronger than unknown. The corpus holds zero collection parameters and zero collection returns against 18 let annotations, and zero in a corpus is not illegality: fn f(xs: int[]), fn g() -> int[], fn h(m: [int: int]) and fn i(s: Set<int>) all check with 0 errors. The deciding case is one program in which a single fn total(xs: int[]) takes an arena literal AND get(Bag).items at two call sites, 0 errors. There is no single answer for that site to know. So a zone variant meaning unknown re-arms the S4/G8 defect one level down, and the measurement points elsewhere: the zone is a property of a VALUE, not of a declared type. The thirteen sites describe signatures, which range over both zones by design. The open question is the SHAPE, not the blast radius, and it is architecture rather than a debt-milestone correction. One site is unmeasured and its own control says so: the cast target at :6255. types.zig:6256 must reject a non-builtin target, the probe reported 0 errors for a as int[], and its control 1 as bool - which that same line must reject - also reported 0. The probe never reached the arm, so all four readings are silences. Reported unmeasured rather than resolved on a blind instrument. Both probes appended to types.zig, run, reverted; file byte-identical at sha256 4e2e9e88 before and after each, zero ZZPROBE residue in the tree. Floor re-derived from the suite after the reverts and unchanged at 2379, which is what says the tree came back; lint conservation OK at 2379; fmt clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
M1.D.44 - tag identity enters the schema digest. ComponentDesc gains content_digest, hashed; TagTable.contentDigest hashes the leaf count then each path by bit index, PLACING leaves at their own index rather than iterating the map, since a bit_index is a pure function of declaration order and hash order would vary build to build. Three TagSet descriptor producers were enumerated, not one: the interpreter's tagSetDesc, the codegen emission whose comment claims it mirrors the interpreter exactly, and a Tier 0 test literal. desc.fields was measured and REFUSED as the carrier - componentFields has ten readers, several on the scene-serialization path, which walk fields by kind. The false refusal is named and pinned: appending a tag inside one word is safe and is now refused, because a whole-table digest cannot tell a surviving prefix from a changed table. Accepted deliberately; an append crossing a word boundary was already refused. The test pinning the defect as an accepted adjacent case inverts to REFUSED, declared here. Counter-factual with the surviving subset named first: dropping content_digest from the hash reddens rename, reorder and append, exactly three, and the green twin stays green. M1.D.45 - both out. storage by arbitration (ARCH-005). requires by measurement taken before deciding: it never appears beside a size, alignment or offset in registry.zig, and every closure consumer is forward-looking, so a reload editing only the requires set leaves live bytes valid and meaning what they meant. Residual raised rather than filed: a newly added requisite is an invariant live entities may violate unchecked, which is not a layout problem. M1.D.38 - the E0217 emission is removed rather than relocated. It was dead by an early return fourteen lines above; relocating it above that return would fire for two undeclared symbols, where undefined_symbol is the true diagnostic. The code stays declared with no producer and its reason, and what single-module mode answers is pinned so the wrong route reddens. M1.D.39 - the set was FOUR, not the three the entry names. The three shapes are three callers of one parseCallArgList; the grammar also gives annotations an arg_list, and parseAnnotations carries its own loop with the same defect. Enumerating the function's callers gives three, the grammar production's users four. Both fixed, both independently witnessed: the test is red before the fix and red again when only the annotation break is removed. Floor re-derived from the suite 2379 -> 2385, windows 2383. The dead-tests bilateral control refused the first attempt and the declared value was moved to the measured one. Green at Debug/f32, ReleaseSafe and -Dphysics_f64=true; lint and fmt exit 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
M1.D.48 - the four silences are closed under a control shape that fires: both negative controls fire and both INT controls stay silent in the same execution. SET, OPTIONAL, CLOSURE and a string concat of two literals all park in AsyncTask.result with zero diagnostics, so all SEVEN rule-arena forms of Value are reachable there and none was diagnosed. The string row is the one not asked for and it is the point: a concat built from a LOCAL was already refused while the identical "x" + "y" was not, though both park the same .string_run. The first was covered only because let a made it a local the sibling rule walks - coverage by accident of spelling. And the second retention point needs NO refusal, measured rather than assumed. An int? event field and an int[] event field are refused as FIELD TYPES, a struct-valued filter is refused by the filter checker, and an enum carries a discriminant. A string is the only arena form that can reach a captured filter, and captureEventFilter deep-copies exactly it. So stabilising strings alone is complete, not partial, and the fix lands at one point instead of two. The assumption is written into a test so it falls the day an optional or collection event field becomes legal. The refusal sits at .return_stmt under conc_branch == .race - where a value enters the parked result - and consults isRuleArenaType, the SAME predicate as the sibling local rule, so the two cannot drift on which forms are arena-backed. False-refusal cost measured before proposing: ZERO on the repository, the corpus holding no race construct at all. The cost is two constructed cases, both found by the green twin rejecting them: a bare string literal, not separable because a literal, a concat and a resource string are all builtin .string_; and a bare enum shorthand, which arrives .unknown and lands on the arbitrated arm instead. Both pinned in a test named for the cost. Counter-factual: disabling the refusal reddens the per-form and cost tests and leaves the green twin and the filter test green. M1.D.46 - measured first under a declaration-level negative control: all seven collisions were accepted with zero diagnostics. isReservedEngineTypeName is DERIVED from builtin_resources rather than listed, with a test walking the table, because a second list is how the two come to disagree silently. TagSet gains a named constant. E0101 is reused with a contextual message on the duplicate-test- name precedent. The green twin includes names that merely resemble reserved ones. Floor re-derived from the suite 2392, windows 2390; the control refused the first attempt. Green at Debug/f32, ReleaseSafe and -Dphysics_f64=true. Probe hygiene under the new rule: appended at end of file, retired by git checkout. The last probe also shared the file with real work, so it was cut at its marker and the removal VERIFIED on the diff - 284 insertions, zero deletions - which is the evidence a global replace could not give. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
M1.D.36 - fast_paths.zig defined its own contactMargin with its own conv_k = 16 while gjk.zig exports both. The entry says five call sites; there are four plus the definition - five OCCURRENCES. The local doc said the margin "must stay gjk.zig's", an intent the code did not enforce. Dependency direction measured before importing: the narrowphase graph is acyclic and epa.zig and shapecast.zig already depend on gjk the same way. The neighbour above it is NOT in the class: boxExtent mirrors coreExtent's .box arm at a site holding raw half-extents and contains NO constant, so it has nothing to drift. Swept out deliberately rather than folded in. Verified by the determinism instrument rather than by argument: forge-determinism exits 0 with the eight witnesses byte-identical and all four discrete invariants OK; forge suite green at f32, f64 and ReleaseSafe. M1.D.37 - the sharper fault is the PATH SET, not the comparison base. The gate named bindings/generated/ and src/core/platform/ wholesale while the adapters write exactly four files. The first holds two hand-maintained .api.zig sidecars no adapter writes; the second holds nine entries of which one is generated. So a comment edit to threading.zig turned a bindgen gate red - S4/G5 of this milestone did exactly that. Narrowed to the four, and based on HEAD because "matches the COMMITTED output" is the criterion the gate's own comment states. Then the second declarant was removed: the four paths were spelled once in the two zig fmt passes and again in the diff. generated_binding_files is now the one list all three read, so a new generated file cannot be formatted and then left ungated. Two-sided witness, re-run after that refactor: with threading.zig dirty the old predicate exits 1 and the full gate now exits 0; with a generated file dirty the predicate exits 1. Both directions, or the narrowing would be indistinguishable from switching the gate off. M1.D.41 - predicate derived, no rename performed. My first arm B gave 90 subjects against the entry's 16 because it dropped the single-entry constraint: a directory entered from outside by two or more of its files is not a module. Corrected, arm B reproduces exactly under six independent graph variants. My arm A was short by exactly three, which is the whole 8-against-11 gap: build.zig:1138 is root_source_file = b.path(spec.path) inside for (test_specs), and three of the table's entries are under src/. A literal extraction cannot see a path arriving through a loop variable - and tools/weld_lint/dead_tests.zig:920 names that exact trap in the repository's own words. The entry's count is right and mine was wrong. A ninth subject, plugin_loader/desc.zig, came from applying the arm-B re-export discriminant to an arm-A declared root. The deliverable is NOT executable: src/ holds two executables, simd/bench two, and simd/tests three files with no root.zig - each group would collapse onto one reserved name. Seven subjects cannot be renamed in place; closing them is a restructure, the same verdict forge/ carries. No lint rule written either, on the M1.D.11 precedent that a completeness rule over a non-conforming set produces filling. Classification put through an adversarial audit first: five lenses, 35 verdicts, 28 survived and 7 refuted. It found two errors and both were mine. Every decisive claim re-checked by me at the source before being written. Floor unchanged at 2392 - this gate adds no test, stated because it was measured. Green: test, lint, fmt, bindgen-verify, forge-determinism. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Measurement only; no line written, and the reason is not the one the entry worries about. Who holds the locations: World.entity_locations, the single EntityId -> Location map (world.zig:167, declared "the single map" at :20). The archetype holds none, which is exactly why releaseChunkIfEmpty ANSWERS the renumbered index instead of repairing and World.reclaimChunk does the repair - the split M1.D.18 delivered. A reuse policy needs no new seam. What a move invalidates: NOTHING, because the named remedy moves no entity. removeSwap swaps the trailing entity into the freed slot, so every chunk holds a dense prefix and free space is always at the tail - never an interior hole. Partial-chunk REUSE appends at chunk.entity_count of a non-trailing chunk, which is the operation allocateSlot already performs on the trailing one. No entity moves, no Location changes, no pointer is invalidated. That is separate from COMPACTION, which the entry's title suggests and its deliverable does not name. Were it taken it would invalidate the moved entity's Location - world-owned, and reclaimChunk already repairs that class - and any live chunk pointer, comptime_query holding cur_chunk with a chunk_idx/slot cursor, already covered by the contract that structural change never runs mid-iterateArchetype. ComponentRef supports it WITHOUT CHANGE, and M1.D.18's recorded blocker is stale: it says moving an entity invalidates the chunk_ptr of every live ComponentRef and therefore touches a just-frozen contract. M1.D.21 removed chunk_ptr in S2/G1 of this same milestone, one session before that was written. Same shape as M1.D.12's deferral condition, met by work that landed elsewhere. Cost sized: only finding a partial chunk is new. A per-spawn scan is O(chunks), worst precisely in the regime the entry names where M1.D.18 measured 8200. A maintained first_partial u32 on the archetype is O(1) amortised - one u32 and maintenance at two sites. One observable changes and it is already not an invariant: under reuse a late spawn can iterate before earlier entities. The declared contract at query.zig:16 is archetype-creation order then chunk then slot, which describes the walk and survives. Spawn order equals iteration order is not that contract and is already broken today, removeSwap moving the trailing entity into the freed slot on every non-trailing removal. What is NOT measured is the benefit. M1.D.18's 6-against-2 is the full-drain case that reclamation already closes; the half-empty regime has no measurement, and the instrument is absent - ecs_hybrid_crossover.zig:149-150 records that DynamicQuery offers neither chunkAt nor chunkCount. So the obstruction is gone, the remedy is cheaper than it reads, and what is missing is the figure nobody doubted. Recommendation: build the probe first. Delivering a Tier 0 allocation policy on an unmeasured benefit is this milestone's dominant defect with the sign reversed. Floor unchanged at 2392; test and lint green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Archetype.first_partial is a LOWER BOUND on the index of any non-full chunk, not "the first partial chunk". A bound makes each maintenance site decidable alone where an equality would make every one owe a proof: lowering is always safe, advancing must be earned, and exactly one site earns it - allocateSlot, walking past chunks it has just observed full. Four sites: the walk advances on an observation, removeSwap lowers unconditionally by @min, and the two release arms clamp. A latent defect went with it: allocateSlot returned chunks.items.len - 1 unconditionally, correct only while the trailing chunk was the sole reachable destination and a WRONG LOCATION the moment an earlier chunk can be chosen. It now returns the chosen index. No entity moves. removeSwap swaps the trailing entity into the freed slot, so a chunk's occupants are a dense prefix and its free space is always at the tail; reuse appends there, which is what the trailing path already did. Hence no location repair, no ComponentRef change, no new seam. Three witnesses in the regime reclamation does not reach - chunks stabilising above zero - each pairing the chunk count with chunks_released pinned at ZERO so the other mechanism cannot take the credit: a quarter of chunk 0 drained then refilled against a FULL trailing chunk, count stays 3; the reused entity recorded at chunk 0 with its bytes read back through that location and every displaced survivor still readable; and the bound asserted as a bound across three rounds of drain-and-refill. Counter-factual built to isolate the DECISION from the MECHANISM - the bound stays maintained and only the choice reverts to trailing-only. Predicted and measured: tests 1 and 2 redden, test 3 stays GREEN, so the first two measure the reuse and the third measures the bound. The benefit is UNMEASURED and no figure is offered. M1.D.18's 6-against-2 is the full-drain case reclamation already closes; the half-empty regime has no load measurement and the instrument is absent - DynamicQuery offers neither chunkAt nor chunkCount. What justifies the change is structural: the invalidation set is empty and the cost is one u32 and four sites. Floor 2392 -> 2395, windows 2393. Green at Debug/f32, ReleaseSafe and -Dphysics_f64=true; lint, fmt, and forge-determinism with witnesses unchanged - which matters because this is a Tier 0 allocation-policy change. S5 closes: of the fifteen entries M1.D created, NINE are closed and six leave the milestone as architecture decisions or a restructure. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
S5's subject is the fifteen entries M1.D itself created, under a rule the milestone did not have: a defect found while working closes in the same session, or it is a STOP and an arbitration, never a new number. Measured on the delivered lot (sha256 6da1d093, 648 lines): TEN of the fifteen are closed - M1.D.40 by S4, and 44, 45, 38, 39, 46, 48, 36, 37, 35 by S5 - and FIVE leave the milestone. S5 minted ZERO new entries, which is what it existed to demonstrate against a milestone that minted nine. The five that leave are not accumulated debt. M1.D.34: restricting the f64 leg changes what the matrix attests. M1.D.42: whether a directory may declare several modules. M1.D.43: a removal primitive in a registry whose ids are positional. M1.D.47: carry the zone beside the type, or split ResolvedType. Four architecture decisions. M1.D.41 is the fifth and is a restructure rather than a renaming: seven of its subjects would collapse onto one reserved name. THREE ENTRIES' OWN TEXT WAS CONTRADICTED BY THE TREE, which is this milestone's purpose applied to its own output. M1.D.47 does not overlap 44 and 45 at all - the digest input path holds zero ResolvedType references across its four functions, and the premise was a homonym, zone against @storage. M1.D.35's recorded blocker was dissolved by M1.D.21 one session earlier in the same milestone. M1.D.41's sixteen renames are unsatisfiable. The standing result: A COUNTER-MEASURE WRITTEN DOWN STOPS NOTHING UNTIL AN INSTRUMENT EXECUTES IT. dead_tests.zig:920 names the b.path(spec.path) trap in the repository's own words and the linter resolves that shape; my extraction walked into it anyway and under-counted M1.D.41 by exactly three. Tenth instance of the selector family and the first where the parade existed before the error. Beside it: one retention point measured instead of implemented, the prompt having said two - a string is the only arena form that can reach an event field, and captureEventFilter already copies it. Coverage by accident of spelling measured directly - return a + "y" refused where return "x" + "y" was not, both parking the same handle. Four errors of mine, every one caught by an instrument and none by re-reading, plus a fifth in the closing tally itself. Three blind probes in one gate whose first cause was ONE CHARACTER, wait(1s) against wait(1.0s), with both negative controls reading zero. And the latent defect in allocateSlot caught before it shipped: it returned chunks.items.len - 1 unconditionally, a wrong location the moment an earlier chunk can be chosen. Floor re-derived from the suite at every gate and never carried: 2379 -> 2395, windows 2393. Green at Debug/f32, ReleaseSafe and -Dphysics_f64=true; lint conservation OK, fmt, bindgen-verify, and forge-determinism with its eight witnesses byte-identical. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Measurement only; nothing written, because the entry's own condition was met: one part does not reserve. The world mutations of Interpreter.compile are exactly four sites - Pass A's fresh arm (registerComponentRaw, and for a resource addResource then initResourceCollections), finalizeRequires, the TagSet fresh arm and the builtin time resources' fresh arm. Passes B to E are world-read-only. Enumerated at the code, then put through a read-only adversarial audit of 37 agents, which added two side effects: getMutResource sets dirty on a fresh resource with a collection field and on no other, and finalizeRequires frees every live closure before recomputing any. OOM sweep with FailingAllocator at every index and resize_fail_index = 0, both controls firing in all three scenarios. First compile: 92 fail points, 79 change the world, and after a retry 10 resources are never seeded, 2 collection slots are null and 45 string pointers dangle. Reload adding types: 3, 2 and 23 of 60. Plain reload: 2 of 44 destroy the live @requires closures. The retry does not repair: freshness is idOf != null, so seeding is skipped for good. Everything inside the registration window reserves - each fallible call is an allocation sized by the pre-mutation world, registry and store, or a refusal decidable from it; ids are positional, so new ids and the staged closure are predictable. What does not reserve is OWNERSHIP. String defaults are immortal blocks owned by the compile's errdefer, and the committed registration writes their address into the world. A failure after the commit - passes B to E, which read the committed registry and so cannot precede it - frees what the world points at: 12 of the 12 post-registration failures dangle, and not only on exhaustion. The same split on the path that succeeds, measured: tearing the previous interpreter down after a reload zeroes the live session's collection and string pointer, and the string it was reading belonged to the old interpreter. Found by the same instrument in the same function: nine leaks from one site (rule_descs.append after compileRule), six more leak sites from the audit, one double free verified at the source, one OutOfMemory swallowed by catch continue, and one u16 offset panic decidable from the AST. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
engine-zig-conventions.md §12 now has four flat prohibitions; the new one bars decision reasoning from a code file even when it passes the three criteria. CLAUDE.md carried the criteria and none of the prohibitions, so they never reached a session start; it now carries all four. The set was reproduced first: c3e6d31..f4a92da added 372 comment lines for 392 of code, 24 blocks of six or more totalling 285. Each of the 53 blocks was judged as one unit - kept whole, reduced to the shortest wording that keeps its contract, or removed whole - from read-only per-file proposals, each reviewed, applied by a script that refuses any unit holding a non-comment line. Result: 106 comment lines for the same 392 of code, and 2 blocks of six or more where there were 24; both are pre-S5 paragraphs touched only to correct a sentence S5 made false. Verified on the diff: comment -444/+106, blank -2, code 0. The adversarial pass was cut at 9 of 18 agents by a usage limit; the five unaudited files were audited by hand and the four unrefereed findings verified at the source. All four held and are fixed - two dropped wrong-fix guards (bindgen-verify against HEAD, why @requires stays out of the digest), one sentence that described the real digest as the wrong one, one grammar pointer - and the hand audit restored a fifth guard in dead_tests.zig. Comments S5's code had made false were corrected in the same pass and counted apart: the orphan rule still listed among validateTraitImpl's checks, "0 false refusals", allocateSlot filling only the trailing chunk, a drained chunk never refilled, content_digest missing from the digest's tuple, fast_paths importing support.zig only, among others. Found and not fixed, each being a code change: the tagset_component_name constant is read by nothing in interp.zig, so S5/G4's "cannot disagree" is false; vk-gen-check checks nothing (it looks up bindgen-verify before creating it; CI runs bindgen-verify directly); and etch-grammar.md contradicts itself on annotation trailing commas, §1.5 against §21.4. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Closes M1.D.43. A registry entry owns the immortal persistent blocks its default bytes point into and destroys them with itself; the interpreter and the scene cook own none. compile() registers through one transaction: every declaration, TagSet and the builtin time resources are prepared without touching the world, every refusal and every @requires closure is computed over the predicted graph, registry and store capacity is reserved, then an infallible commit adopts entries, buffers and closures. World.deinit is the one place resource payloads are released. Same instrument, same function: seven leak sites and a double free in compile(), an OutOfMemory swallowed by initArrayBlock and initMapBlock, a u16 panic past 64 KiB (now error.LayoutTooLarge), a leak in planTableDriven, and descriptor building, which now allocates in an arena the Descriptors value owns. Tests: an allocation sweep of compile() in three scenarios with both controls, the two teardown orders, the 64 KiB refusal, and a sweep of the nineteen Level-B programs. Floor 2395 -> 2403, windows 2401. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The ownership decision and its motive, the transaction's four steps and what was set aside, the defects closed with it, the counter-factual per form, the outcome counts of the sweep, the behaviour that changed, the bounds left open, and the gates. Floor 2395 -> 2403, windows 2401. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
prepareEntry copies every non-empty string default into an immortal
block the entry owns, so registerComponentRaw and the Etch compile
follow one ownership rule and a caller keeps all it passed. An empty
default is written {0,0} on every path. A collection default that names
a container is refused (CollectionDefaultNotEmpty).
Five scene tests released a resource string by hand before World.deinit
released it again: five decrefs on freed memory, now gone.
vk-gen-check depends on the bindgen-verify step itself instead of a
lookup made before that step existed. Every TagSet name in the
interpreter and the codegen derives from tagset_component_name; the
emitted source of the five tagged programs is byte-identical. The parser
cites the annotation_args production of etch-grammar.md 1.5.
Locking tests: the refusal order of a doubly-faulty reload, no default
evaluated on reload, closures that see TagSet and the builtins, every
injected name reserved, and the two raw-path ownership properties.
Floor 2403 -> 2411, windows 2409.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The loader copied a resource's raw data into the store and rewrote only the string slots the string table names, so a scene file with non-zero bytes at a string offset put a pointer nobody owns in the store, which rollback, commit and World.deinit then decref. Every string slot is now zeroed before any allocation. prepareEntry refuses a field reaching past its default bytes (FieldOutOfBounds), and both passes of the string-default copy read the caller's immutable bytes, so overlapping slots can no longer make the copies outnumber their count. A false build claim is corrected in each place it stood: vk-gen-check's old block, the "RENDER IS HELD" note on wired tests, the stub and refusal counts, the counter-proof manifest, and shaders-check, which no CI runs. forge-asm-inventory takes each cell's precision, so the f64 build is inventoried. Comments this gate touched hold each fact once. Floor 2411 -> 2415, windows 2413. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The three found items settled, the two bounds closed, the three locks and their counter-factuals, what the sweep brought back and closed, and seven findings raised at the stop for arbitration, each measured. Floor 2403 -> 2415, windows 2413. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A type node's `data` indexes the slab of its own kind. Forty-five sites read `named_types` through it, and eleven had no guard on the kind: a resource `xs: int[]` lowered as `xs: i64` with `FieldKind.int_`, a rule parameter `xs: int[]` beside an `Entity` one was bound to the entity, and an optional node's data read far out of bounds. `AstArena.namedTypeName` returns null for every kind but `.named`, and all forty-five reads go through it, each writing its non-named branch: the codegen refuses (`UnsupportedConstruct`; `fnTypeZig` becomes fallible), the interpreter binds `.unit`, skips, or refuses (`InvalidProgram`, `RuntimeFailure`), and the guarded sites keep the branch they had. Two adjacents on the same lines. `checkRule` now refuses every non-named rule parameter with the message it already carried, instead of only an unresolvable one. The interpreter resolves `type` aliases on rule parameters as the codegen and the checker do, so `type Ent = Entity` makes the entity parameter and `type Seconds = float` binds a float. `no_raw_named_type_read` flags any `.named_types` access outside `src/etch/ast.zig`, with a bad fixture run through the real binary. Floor 2415 -> 2436 / 2434, measured: 2417/2436 passed, 19 skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The resource witnesses cannot tell the struct emission from the registration: both read the same fields and the struct pass runs first. An event reaches only the struct emission, unchecked. Floor 2436 -> 2437 / 2435, measured: 2418/2437 passed, 19 skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A parameter type that resolves to nothing was refused; a return type was resolved and never checked, so `fn f() -> Nope` type-checked. Both fn and method returns now carry the diagnostic their parameters do. No corpus program declares one: the suite stays green. Floor 2437 -> 2440 / 2438, measured: 2421/2440 passed, 19 skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Item 1 of S5/G8 ter. A literal that overflows its type, and a constant whose folding overflows or divides by zero, are refused when the program is checked; at run time both backends follow part1 §12.4: overflow panics in Debug and ReleaseSafe and wraps in ReleaseFast, an integer division by zero panics in every mode. The fold. `const_eval.fold` is the one folder: the checker admits a constant through it and the interpreter, the scene cook and the codegen store the value it gives, so a default the checker admits cannot fall back to zero at compile. Its arithmetic is checked in every mode. Field defaults, top-level consts and field filters fold through it (E1101 on overflow, division by zero or a non-finite float); `true or false` now folds instead of storing false, and a string concatenation default, which no backend could materialise, is refused. The literals. Every int literal fits `int` and every float and duration literal is finite, in any context, with `-9223372036854775808` judged negated. `literalTypeFits` refuses a literal outside `i32`, `u32` or `f32` (E0200), reached by all 27 of its callers, by resource instance values, params-block defaults and collection literal elements against a `let` annotation. A non-literal or negative array size or fill count is E1101 instead of an unknown type or a zero length. The run time. `value.zig` holds the mode policy: add, sub, mul, neg and `i64.min / -1` return null where overflow panics and wrap otherwise. A compound assignment reports the typed cause the binary operator does. `as i32` / `as u32` narrow, `as f32` rounds, a float whose truncation does not fit its integer target fails in every mode. A store into an `i32` / `u32` field wraps or refuses instead of an `@intCast` abort. An inclusive range to `i64` max ends, and `tick_until` converts its budget checked. The codegen routes integer `+ - * / %`, negation, compound assignment and narrowing casts through prelude helpers that choose by mode, where it emitted native operators (UB in ReleaseFast, and runtime signed `/` did not compile); float `%` lowers to `@rem`. Adjacents on the same paths: an omitted struct field takes the zero of its type, or its string or enum default, where the interpreter gave int 0; an `i32` filter accepts an int literal it falsely refused; the scene cook's `registerOne` mapped `SchemaChanged`, `FieldOutOfBounds` and `CollectionDefaultNotEmpty` to OutOfMemory through an `else`. Floor 2440 -> 2490 / 2488, measured: 2471/2490 passed, 19 skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The interpreter refuses at compile a default whose folding fails, where it left the slot zero; the codegen emits a constant default as its folded value and a runtime literal without its separators, and a compound integer assignment through its helper. None of these had a test that tells the form from the one it replaced. Floor 2490 -> 2495 / 2493, measured: 2476/2495 passed, 19 skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Item 3 of S5/G8 ter. The checker's resource collection branch validated the element type and `continue`d past every default check, so `xs: int[] = 5` passed, and the interpreter kept only what it could read: an array or map literal, element by element, dropping any element that failed to fold and ignoring every other form. The checker: a `T[]` default is an array literal, a `[K: V]` default a map literal, a `Set<T>` default `Set.new()` (stdlib §4.5), each element a constant of the declared element type (E1101 otherwise), a variant of it for an enum element (E0105), and a literal within its range; a wrong shape is E0200 for a constant and E1101 for anything else. A default on an element type already refused (E0222) is not checked a second time. The runtime: a fill default `[v; n]` materialises `n` copies where it kept one, an enum element is stored as its variant where it was dropped, a repeated map key keeps its last value as the runtime literal does where the default kept both, and any form the checker refuses is `InvalidProgram` where it gave an empty container or dropped the element. The scene cook refuses a resource with a collection field, which it wrote into a `.scene.bin` the loader always refuses. Adjacents: a scalar enum default is checked (E0105, E0200), where `m: Mode = .nope` stored the first variant; a float map key or set element is refused by the Hash bound (E0601) as it is on a `let`; a Vec3, Color or Duration element is E0222, which the element set's own text already limits to scalar POD, string and enum. Floor 2495 -> 2519 / 2517, measured: 2500/2519 passed, 19 skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The five unchecked refusals shared one test, so a mutation of one form was hidden behind the first source that still refused. The no-cascade witness now uses a default the element check would refuse if it ran. Floor 2519 -> 2523 / 2521, measured: 2504/2523 passed, 19 skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Item 7 of S5/G8 ter. A resource is no entity column, so `@requires` naming one is refused (engine-ecs-internals §3). The registry had no notion of the difference: a Zig component requiring `GameTime` resolved at the first compile, and the lock that pinned it now pins the refusal. The registry records a kind (`ComponentDesc.kind`, `.component` by default, an addition that moves no protocol version). A closure whose requisite is a resource is `RequisiteIsResource`, and a resource carrying requisites `RequiresOnResource`, both from `closeOne`, which every closure computation reaches. `World.addResource` records the kind and refuses an id a closure already requires, so a host making a type a resource after registering it is refused in either order. The interpreter declares the kind of every type it registers, builtin time resources included. Adjacents on the same paths: - The scene cook maps the two new refusals, its registry being its only `@requires` guard. - The scene loader refuses an archetype naming a resource and a resource section naming a component, which a crafted scene could do: the component was installed as a resource. - The codegen refuses `@requires` (`RequiresUnsupported`), which it dropped: its `register()` carries no requisite and resolves no closure. - A reload that changes a type's kind or its `@requires` is refused as a schema change, where it was accepted and never applied; a changed storage mode stays accepted and unmigrated, per §13. - A builtin resource name held by another type is refused, where the interpreter mapped any holder as the resource. - E0506 names a resource as one. Floor 2523 -> 2542 / 2540, measured: 2523/2542 passed, 19 skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Item 4 of S5/G8 ter. `annotation_arg = expression | IDENT ":" expression | IDENT`. The parser looked for a name by consuming a leading ident and, when no `:` followed, rebuilt an expression from it by hand, which skipped every form `parsePrimary` owns: `@tag(v as f32)` was refused, `@tag(none)` and `@tag(some(1))` became an ident and a call, and `@tag(Name: 1)` was refused because a capitalised name was not looked for. The argument is now a name by two-token lookahead, and otherwise an expression. Adjacents on the same production. The annotation name is an IDENT, as §1.5 writes it: a capitalised `@Unit(...)` was parsed and accepted as a custom annotation, and no source in the tree uses one. `@storage(none)` stays E0503, a value outside the domain, and not E0504, `none` being a constant. The event and structural observer readers refuse a named argument, as the tag and requisite readers already did: `@on_event(e: Hit)` was read as `@on_event(Hit)`. Floor 2542 -> 2553 / 2551, measured: 2534/2553 passed, 19 skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Item 6 of S5/G8 ter. `asset_field = [ annotation ] , IDENT , ":" ,
asset_value , [ "," ]`, in a file the user edits by hand: the reader
refused the comma between fields, the one form the ruling names, and
much of the rest of the grammar. It now takes:
- the comma between fields;
- a field annotation, parsed and not kept (the model carries none);
- comments;
- the six string escapes, decoded, with any other refused;
- triple-quoted strings with their common indentation removed;
- `_` digit separators where the lexer admits them.
It refuses what the grammar does not produce: a `+` sign, an exponent,
`1.`, a float that is not finite, an unescaped `{`, a raw newline in a
simple string, a boolean literal as a field name, and content after the
construct. The §3 schema is enforced where it was defaulted or ignored:
each fixed field and block present once and of its kind, no other
top-level field, `type` an identifier, and `uuid`, `source_hash` and
`extracted.blob` in canonical form (`SchemaViolation`, a new variant).
The writer escapes what the reader decodes, so a string holding a
quote, a backslash, a brace or a line break round-trips. The glTF
importer refuses a non-finite position or bound, which the writer had
no literal for and emitted as `nan` or `inf`.
`asset_cook` minted a fresh UUID for any intermediate the reader
refused, against §3's "Jamais régénérée"; `existingUuid` makes an
unreadable intermediate an error. Measured end to end: a second cook
keeps all three UUIDs, a corrupted intermediate fails the cook.
Two fixtures change value to satisfy the schema: the round-trip hashes
and the float test's empty `uuid` and missing blob.
Floor 2553 -> 2573 / 2571, measured: 2554/2573 passed, 19 skipped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The importer refuses a bound the intermediate format has no literal for; the test gives an accessor a maximum of 1e40, infinite as an f32. Floor 2573 -> 2574 / 2572, measured: 2555/2574 passed, 19 skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The three nested `zig build` invocations (the triangle example, the synth_100 proof, the access counter-proof cases) ran with no option at all, so each compiled for the host's native CPU, in Debug, at f32, whatever cell ran it. They now receive `-Dtarget`, `-Dcpu`, `-Doptimize` and `-Dphysics_f64` from the outer build, and each sub-project declares `physics_f64` and passes it to its `weld` dependency. CI passes the matrix precision to the two nested steps it runs. Measured: `verify-synth-100` succeeds; `ecs-access-counterproof -Dphysics_f64=true -Dcpu=baseline` 8/8. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`dead-tests` walked the lint paths only, so a test block under `examples/` was invisible to the conservation check whether or not a step collected it. Extended, the guard names one: `examples/vertical_slice/math.zig`, one block, reached by no test root because the slice module is imported across a module boundary, which collects none of its tests. It is std-only and now has its own test root. Floor 2574 -> 2575 / 2573, measured: 2556/2575 passed, 19 skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The decoder refuses a position the intermediate format has no literal for, and nothing witnessed it: under an accessor without bounds, the computed bounds are infinite too and the bound check refuses first. The test gives one vertex an x of +inf under finite accessor bounds, the only case the position check alone decides. Floor 2575 -> 2576 / 2574, measured: 2557/2576 passed, 19 skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The gate never ran. In CI, `test-ppm-psnr` reported `2/3 tests passed (1 skipped)` at 9d2609c: the example runs from its own directory and writes `examples/triangle/out/smoke_test.ppm`, while the gate read `out/smoke_test.ppm` at the repository root, found nothing and skipped. The artifact upload, pointed at the same wrong path, uploaded `test-output.txt` alone (294 bytes). The gate now reads where the example writes, fails and names the file when the capture or the golden is absent, and always re-runs, since it reads a file no input of the step declares. It leaves `zig build test`, where no cell can produce the capture and it could only skip, and `dead-tests` declares it uncollected. The workflow's `exit 0` for an absent golden goes: the golden is committed. `tests/render/capture.zig` is deleted: its spawning test duplicated the smoke job's triangle run plus this gate and skipped on every cell (no triangle binary is built in `zig build test`), and its two helper tests duplicated the gate's own. Witnessed locally: no capture fails with `PpmAbsent` naming the path; a copy of the golden passes 3/3; the same copy shifted by 8 in every channel fails. Floor 2576 -> 2570 / 2568, measured: 2553/2570 passed, 17 skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The parser implements neither the layer nor the world construct, so a .layer.etch file holding its layer got a syntax error and E0858, and one holding anything else got E0858 alone. Such a file is no longer parsed: E0840 ConstructNotImplemented is the one diagnostic it gets, E0858 no longer judges it, and the cook refuses it as ConstructNotImplemented. A scene beside it now cooks, where its parse failure refused every cook of the project. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A pull request runs cold, the cache being saved on push only, so the Release budget must cover the worst cold run. Over every run since 2026-09-01, the 75-minute budget cut one cold ReleaseFast job on a slow runner, 0.2 s short of its step's end; its cold re-run at the same sha took 44.5 minutes, a x1.69 runner ratio. The Windows cold median since 09-26 is 59.9 minutes and rising by about 0.4 to 0.6 minutes a day, so a runner that slow on it gives 101 minutes. 120 covers that with 19 %. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every gate that compares a value with its destination compared two builtins and let anything else through, so `let v: int = [1, 2, 3]`, a struct passed to an int parameter or assigned to an int local, and a struct branch beside an int one checked clean and failed at every tick. One predicate, valueFits, now judges them all: two builtins by the literal rule, a Vec3 from an array of three numbers, a builtin and a composite never, two kinds only when both are arrays, a nominal type only as the same declaration, so an alias names the component it imports. The places that need a builtin refuse a composite, a map literal refuses a non-builtin key or value, and a return inside a closure is no longer judged against the enclosing fn. A table of 42 mistyped cases and a control of the well-typed forms through the same gates pin it; an optional of a composite stays unknown. Floor 2779 -> 2784 / 2782, read from the suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A loop took the type of its first top-level break, so a break nested
in an if was never compared: `let v: int = loop { if c { break P {
x: 1 } } break 1 }` checked clean.
The checker now keeps the loops a break can target: an unlabeled
break leaves the innermost loop of any kind, a labeled one the loop
carrying its label, and a task or timer body opens a new window.
Every value breaking out of one loop must agree with the first, and
the loop takes that type. A closure body is checked at its call,
inside the caller's loops, which a break in it leaves at runtime.
Floor 2784 -> 2787 / 2785, read from the suite.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A closure body was checked only at its calls, against the caller's loops, and the interpreter passed its break or continue on to the caller's loop, so a closure could break a loop it was never written in, and one never called was never checked at all. The parser now records every break or continue whose target lies outside its closure body, and the checker refuses each once as E0911 ControlFlowEscapesClosure, called or not. At a call the body is no longer judged against the caller's loops or task branch, which also lifts a false E0906 on a closure's own return in a sync branch. At run time a jump that reaches the end of a closure fails with its own error kind. Floor 2787 -> 2791 / 2789, read from the suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The checker had no unit type and typed every value-less expression
unknown, which fits any destination: `let v: int = loop { break }`,
an if with no else, an empty block, `arr.push(1)` or `await wait(..)`
checked clean and bound unit at run time.
Unit is now a type that fits only itself. A loop left by a break with
no value, a block with no tail value, an if with no else, the builtin
effect methods and assertions, and the four wake targets and a
TaskHandle await are unit. A block whose last statement returns,
throws, breaks or continues keeps no type, so a branch that leaves
still fits. A call of a fn with no `-> type` stays unknown, as ruled.
One test changed: the labeled-loop routing test mixed `break outer 2.5`
with a valueless break on the same loop, float against unit; its last
break is now `break 0.5`. Floor 2791 -> 2793 / 2791, read from the
suite.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
valueFits left two collections, optionals or ranges of one kind to the check of their own construct, and that check exists for literals at a let only: an int[] variable passed as a string[], a map of another key type, an int? into bool?, [1, 2] as a string[] argument, a field or a fn value, and an int[2] into int[3] all checked clean. Two such values now fit when their element types are equal, two fixed arrays also on their length. The elements of a literal, a Set.from of a literal and a some(..) are each judged by the literal rule, so f32[] from [1.0, 2.0] still fits. A mistyped literal at a let keeps its per-element diagnostics and gets no second one. Floor 2793 -> 2796 / 2794, read from the suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A scene's resources block checked a field by name and a literal by range, and nothing else: a bool, an array or a string in an int field, and an int literal in a float field, all passed. And a scene file, which may only import, could name no resource at all: the checker looked a resource up in the file's own declarations and refused an imported one as E1789, and the cook registered imported components only. A mistyped resource field is now E0308 ResourceFieldTypeInvalid. An imported resource resolves through its import, its fields read from the arena that declares it, and the cook registers it under its own name and reads an alias as that name. Two components or resources of one name reaching a file are E0101, the runtime keying both by name in one registry. Floor 2796 -> 2804 / 2802, read from the suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A service call was checked by method name and arity and nothing else: each argument was typed and the type thrown away, so a bool, a string or a struct passed to an int parameter checked clean, a label the method does not declare passed, and the interpreter bound labeled arguments by position, so `toy.pair(b: 2, a: 7)` computed 27. Each argument is now bound to its parameter by label, as for a declared fn, and must fit the parameter's type, read from the arena that declares the service; the interpreter binds by label too. An int no longer converts into a float parameter at run time either, on the interpreter's path and on `Registry.call`, since Etch converts neither numeric kind into the other anywhere. The toy service gains `pair` and `half` to exercise both. Two tests changed: the registry test that asserted the int-to-float conversion now asserts its refusal, and the emitter test's two non-vacuity counts of the toy's methods go from 3 to 5. Floor 2804 -> 2808 / 2806, read from the suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A const was declared, registered and exported, and read nowhere. A lowercase name was looked up among the locals only and refused as E0102; an uppercase one, which the grammar reads as an IDENT like any other and the parser keeps as a path, typed unknown whether defined or not, so `let x: bool = MAX` and an undefined `NOPE` both checked clean. At run time every const read failed. A name that is no local now resolves to a const, the file's own or an imported one, typed by its declaration; an uppercase name that names nothing, no declaration, import or builtin type, is E0102. The interpreter indexes the consts at compile time and reads a string, an enum variant or a folded value. Floor 2808 -> 2811 / 2809, read from the suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An item bound by an import was judged by its local name only, so across 69 positions measured one by one, 61 differed from the same program with the item declared in the file: a struct literal of an imported struct, a match on an imported enum, a call of an imported fn, a method of an imported type, a default of an imported trait and a bound an imported impl satisfies were refused or unchecked. The harness compared codes only, and one position read as agreeing because both sides gave E0200, for different reasons; it now compares code and message. A name now resolves to its declaration across files: a struct, an enum, a fn with its parameters, types and labels, an alias target, the fields of a declaration written in another file, and a foreign file's own imports. Impls follow the imports (etch-resolver-types.md 7.5): an imported file's inherent and trait impls are visible, an inherent impl may target an imported type, and a trait impl may name an imported trait. A method two imported files define is ambiguous, on the E0101 collectImplMethods already reuses for 7.5, and one this file redefines is E0101 as a second local impl is. E0217 has its producer: an impl whose trait and type are both of other modules is an orphan. self in an impl on an enum is the enum. Floor 2811 -> 2818 / 2816, read from the suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
S5/G9 tervicies: each point's measurement on the base, its counter-factuals and the predictions they refuted, the tests changed, the counter-factual helper's defect and the items raised untouched. Floor 2787 -> 2818 / 2816, and the Windows floor 2785 read on the four cells of d82633a. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
S5/G9 quattuorvicies: points 1 and 2 costed by the compiler before any code, with the behaviour of both components probed against the spec, the choice each point leaves open, and where the two meet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
E0840 refused every .layer.etch and .manifest.etch file unparsed, for a layer or world construct the grammar does not have: a data layer and a world manifest each hold a scene (etch-grammar.md 21.1). E0840 is withdrawn with its witnesses, both files are parsed as any other, and E0858 requires of them exactly one scene and its imports, as of a scene file, its messages naming the scene rather than the extension. A layer or manifest holding a scene cooks to the bytes a scene file does, and a scene beside a layer file still cooks. Floor 2818 -> 2825 / 2823, read from the suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
S5/G9 quinvicies: layer and manifest files hold a scene, checked and cooked as a scene file, with the probes, witnesses, counter-factuals, the review's findings and the spec conflicts raised untouched. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A manifest holds a scene without entities, its world settings being resources (etch-grammar.md 21.1), and E1780 refused every scene without an entity or instance, so a manifest written as the grammar describes cooked and failed etch check. E1780 no longer judges the scene of a .manifest.etch; a layer's scene, which holds the layer's entities, still needs one. Floor 2825 -> 2827 / 2825, read from the suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
etch-reference-part1.md 4.2 and 4.3: a binding lives to the end of its block, a nested scope may shadow it, and a second binding of one name in one scope is refused. A construct and its body are one scope with the bindings it introduces: fn and closure params, a for variable, an if let or while let payload, a catch binding and a match arm's bindings. The checker keeps a scope log and reports a same-scope rebinding as E0101. A closure body is typed in the scope where the closure is written, its arguments where it is called, and a duplicate param is refused once, at the literal. Captures are judged on the log, so E0223 and E0221 count only what a snapshot body did not bind itself. The interpreter keeps a scope log too: a shadowed binding keeps its reference until the shadow's scope closes, and each async frame closes the scopes it opened. Spawn and timer handles bind in the enclosing scope; a behavior composite is one scope and each quest stage its own. P3's masking fixture rebinds its parameter in a nested block, its same-scope rebinding being E0101 now. Floor 2827 -> 2834 / 2832, read from the suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A world manifest's scene holds resources and no entity (etch-style-guide.md 8, etch-grammar.md 21.1). E0858 now reports each entity or instance such a scene holds, at its span, and the cook refuses the file through the same check. The witness asserting no E0858 on a manifest whose scene held an entity is removed; a table of five cases replaces it. Floor 2834 -> 2835 / 2833, read from the suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
etch-diagnostics.md declares E0218 AmbiguousInherentMethod, and etch-resolver-types.md 7.5 gives it the ambiguous call as well. Two inherent methods of one name in one file, one beside an imported one, and a call two imported modules can answer were E0101; all three are E0218 now, with their messages unchanged. The call-site witness counts E0218 where it counted E0101. Floor 2835 -> 2836 / 2834, read from the suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
S5/G9 sexvicies: the E1780 exemption, point 1 with its red runs, counter-factuals and review, the two decisions with their witnesses, and the answer on E0303. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
S5/G9 septemvicies: point 2 measured at 4aedfd5 by compiler and probe, the three representations with their costs, the demand, what point 5 left, and two defect classes the probes exposed: equality beyond the primitives, and an enum shorthand the interpreter does not resolve. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
etch-resolver-types.md 16.4: every builtin but the two handles is Eq, an optional is Eq when its payload is, an enum variant against variant, and the numbers, Duration, Time, string and Entity are Ord. The checker refused string and enum equality and accepted bool, Vec3 and Color orderings and handle equality; it passed `o == none` only by letting an unknown through. Now `==` takes two values of one Eq type, or `none` against an optional, and an ordering two values of one Ord type; assert_eq and assert_neq take the same Eq types. The interpreter compared only int, float, bool and Duration. `==` now goes through valueEql, which compares an optional by what it holds; strings order by their bytes and entities by index, then generation; a comparison of two kinds of value fails loud. Floor 2836 -> 2839 / 2837, read from the suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
etch-resolver-types.md 3.5: a shorthand names a variant of the enum its context expects, else of the one enum declaring it; E0105 when the expected type is no enum or lacks the variant, or no enum declares it, and E0106 when several do and nothing expects one. The checker typed every shorthand outside a field unknown and checked no variant; the interpreter resolved it only there, so a let, an argument, a return, a reassignment, some(.x), a struct field write and == checked clean and failed at run time. The checker now resolves each shorthand, through the branches a slot's value takes, and records the enum on the AST; the interpreter reads it. A tag path literal is marked apart, its data indexing tag_paths. A race branch returning `.b` is no longer refused: it resolves to its enum, a discriminant, and moves to that test's green twin. E0106 is declared. Floor 2839 -> 2842 / 2840, read from the suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
etch-reference-part1.md 3.6 and 6.6, etch-resolver-types.md 16.4. An optional of a non-builtin payload typed unknown, so the checker accepted what did not fit it and refused what did. The payload is now a pointer: a type node's lives in a slot reserved before checking, two per node for a generic scope and none, and one built at check time is allocated. A value fitting a T? by the implicit wrap is recorded on the AST with its layer count, and the interpreter wraps it where it is evaluated, at a constant and a field default, and through an await's call frame. `?.` reads fields and flattens, a plain access after one continues the chain, branches joining `none` make an optional, a generic wraps into its T?, and a struct field may be optional. The parser takes [K: V]?. Closed with it, found by read-only audits: an allocation failure through synthExpr turned into unknown and an accepted program; cookScope reached a type node before any slot existed; a pattern or a compound assignment was wrapped like a stored value; a closure typed at each call could wrap a value, or read a shorthand, differently per call; `?.field` held a borrowed string unretained; the named-argument gate read the optional. Optional component, resource and event fields stay refused. Floor 2842 -> 2846 / 2844, read from the suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
S5/G9 duodetricies: bad9fe1, 09d6be2 and 5db3bec, each with its red run and counter-factuals; the defects three read-only audits found in the option and closed with it; the deviation on witnesses written after their fix; residuals and readings for Guy. B6 carries the two STOPs: an optional field that is stored, and a collection of a declared type. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
S5/G9 undetricies, read only at a7a795c: every site of types.zig that accepts, propagates, produces or refuses unknown, 417 lines, the full table in briefs/artifacts. 67 produce it with no diagnostic, in twelve families, four unreachable today. One is a defect of 5db3bec, confirmed by a probe: an optional slot shared by two different generic scopes. Guy's rulings on B6 recorded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The census's F11, a defect of 5db3bec: an optional type node's slot was keyed by whether some generic scope was open, not by which, so a field `P?` read inside `fn f<P>` and again inside `fn g<T>` met two payloads under one key, a Debug panic and undefined behaviour in ReleaseFast. A node now holds one slot per payload, found by equality: a scope changes a node only through its names that are type parameters of the file, so it gets one slot per subset of them, bounded by the scopes the file can open. Floor 2846 -> 2848 / 2846, read from the suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Squash message
S5's subject is the fifteen entries M1.D itself created, under a rule the
milestone did not have: a defect found while working closes in the same session,
or it is a STOP and an arbitration, never a new number. Measured on the delivered
lot (sha256 6da1d093, 648 lines): TEN of the fifteen are closed - M1.D.40 by S4,
and 44, 45, 38, 39, 46, 48, 36, 37, 35 by S5 - and FIVE leave the milestone.
S5 minted ZERO new entries, which is what it existed to demonstrate against a
milestone that minted nine.
The five that leave are not accumulated debt. M1.D.34: restricting the f64 leg
changes what the matrix attests. M1.D.42: whether a directory may declare several
modules. M1.D.43: a removal primitive in a registry whose ids are positional.
M1.D.47: carry the zone beside the type, or split ResolvedType. Four architecture
decisions. M1.D.41 is the fifth and is a restructure rather than a renaming: seven
of its subjects would collapse onto one reserved name.
THREE ENTRIES' OWN TEXT WAS CONTRADICTED BY THE TREE, which is this milestone's
purpose applied to its own output. M1.D.47 does not overlap 44 and 45 at all - the
digest input path holds zero ResolvedType references across its four functions,
and the premise was a homonym, zone against @storage. M1.D.35's recorded blocker
was dissolved by M1.D.21 one session earlier in the same milestone. M1.D.41's
sixteen renames are unsatisfiable.
The standing result: A COUNTER-MEASURE WRITTEN DOWN STOPS NOTHING UNTIL AN
INSTRUMENT EXECUTES IT. dead_tests.zig:920 names the b.path(spec.path) trap in the
repository's own words and the linter resolves that shape; my extraction walked
into it anyway and under-counted M1.D.41 by exactly three. Tenth instance of the
selector family and the first where the parade existed before the error.
Beside it: one retention point measured instead of implemented, the prompt having
said two - a string is the only arena form that can reach an event field, and
captureEventFilter already copies it. Coverage by accident of spelling measured
directly - return a + "y" refused where return "x" + "y" was not, both parking the
same handle. Four errors of mine, every one caught by an instrument and none by
re-reading, plus a fifth in the closing tally itself. Three blind probes in one
gate whose first cause was ONE CHARACTER, wait(1s) against wait(1.0s), with both
negative controls reading zero. And the latent defect in allocateSlot caught
before it shipped: it returned chunks.items.len - 1 unconditionally, a wrong
location the moment an earlier chunk can be chosen.
Floor re-derived from the suite at every gate and never carried: 2379 -> 2395,
windows 2393. Green at Debug/f32, ReleaseSafe and -Dphysics_f64=true; lint
conservation OK, fmt, bindgen-verify, and forge-determinism with its eight
witnesses byte-identical.
Co-Authored-By: Claude Opus 5 noreply@anthropic.com
What S5 closes
Fifteen entries M1.D itself created. TEN closed, five leave the milestone.
M1.D.40M1.D.44TagSetdescriptor producers, not the one obvious siteM1.D.45storageandrequiresboth ruled OUT — one by arbitration, one by measurementM1.D.38E0217emission removed rather than relocatedM1.D.39arg_listusers and not the three the entry namedM1.D.46M1.D.48M1.D.36M1.D.37bindgen-verifynarrowed to the four generated files, one declarantM1.D.35first_partial— a partially-filled chunk is refilledThe five that leave, with their motive
None is accumulated debt.
M1.D.34— restricting the f64 leg changes what the matrix attests.M1.D.42— whether a directory may declare several modules.M1.D.43— a removal primitive in a registry whose ids are positional.M1.D.47— carry the zone beside the type, or splitResolvedType.Four architecture decisions. And one restructure:
M1.D.41— not a renaming:src/holds two executables,simd/bench/two,simd/tests/three, so seven subjects would collapse onto one reserved name.And the shape of it
M1.D produced fifteen entries while emptying its own table. Ten are closed by the session that existed for that, and S5 minted zero new ones — the rule it ran under being that a defect found while working closes in the same session, or it is a STOP and an arbitration, never a new number. It stopped five times.
Three of those entries had their own text contradicted by the tree, which is this milestone's purpose applied to its own output.
🤖 Generated with Claude Code