Phase 1 / Forge / Tier 0 pre-freeze and C1.1 cost - #73
Open
guysenpai wants to merge 41 commits into
Open
Conversation
C1.1 names `bench/physics_forge_3d_integration.zig` as where the frame column is measured and it did not exist; two files cited it in the present tense. The raycast and shapecast benches reach 10 000 bodies but interrogate a static scene — they never tick — so C1.1's figures were neither met nor refuted. Two targets gated. 1 000 dynamic + 10 000 static at 60 Hz, on 1 000 awake dynamic bodies of 11 000, P = 8 809, 240 frames from frame 31: ReleaseFast median 2.001 ms, ReleaseSafe median 2.436 ms against 16.6 ms, and zero allocation attempts in steady state in both. Steady state is defined on the scene, not on the allocator: the first frame after which pairs and constraints hold for thirty frames. Defining it as "after allocation stops" would make the zero-alloc result unable to fail. Two defects in the instrument, found by reading its numbers after it answered PASS. The awake count included statics, so its guard could not fall below 10 000 and could never fire. The retention experiment grew N and P together, so it discriminated nothing; a far static field now holds P fixed at a measured 2 209 while N moves x3.72, and Theta(P*N) is refuted by ~470x. Ungated finding: an asleep scene allocates 5 280 times in steady state where an awake one allocates zero. Mechanism named at contact_constraint.zig:578 and already owned; this is its first measurement.
The brief says "frame time <= 16.6 ms" and names no statistic. Gate E settled that on the median — the permissive reading — and then attributed the choice to Guy, who had framed nothing. A 60 Hz budget is a real-time constraint: one frame in a hundred at 20 ms is a visible hitch, and a median never sees it. Nearest rank, stated in the code rather than left to a library convention: ceil(0.99 * 240) - 1 = 237, the third-worst frame of the window. Median and max stay in the report so the tail stays readable. Still passes: worst p99 observed over five runs per mode is 3.576 ms (ReleaseFast) and 5.467 ms (ReleaseSafe) against 16.6 ms. The counter-factual is the one that discriminates. Lowering the budget only proves a comparison works, and twice it did not even fail because the p99 landed under the threshold by machine noise. Three 20 ms hitches injected into 240 frames at the UNCHANGED budget give p99 20000000 ns against a median of 2050000 that clears it by 8x — a median gate would have passed a run containing three visible hitches. Reported because it argues both ways: over five runs on identical code the median moves 6%, the p99 up to 2.4x, the max up to 3.5x, with a bit-identical checksum throughout. The tail is machine noise, so this gate buys sensitivity and costs stability; the margin is claimed on the worst observation.
The corpus renamed M1.1.26 -> M1.1.15.2 (the freeze) and M1.1.25 -> M1.1.21.1 (per-island parallel resolution); the repository had not followed. Re-swept on the current tree with grep -o — occurrences, never lines: 92, of which 14 are in this milestone's own brief, which describes the rename and did not exist at the frozen sweep. 92 - 14 = 78, the frozen figure. 24 code, test, tooling, build and CI sites patched here, per-file counts reproducing the frozen table row for row. Zero survive in code. 37 stay untouched by design: 29 + 7 in two closed briefs and the v0.11.15 orchestration tag row. A journal is not retro-patched. Matched on CONTENT, not on line number, and that mattered: the brief located a test name at transform_sync_test.zig:775 and the lines had shifted with the tests gates C, D and F-D1 added — it is at 844. The .why string of no_precision_crossing.zig is diagnostic output, so the brief asked whether a test asserts its literal. Measured: none does. The phrase occurs twice, both inside the rule file. No companion patch was due. PhysicsModule.zig also gains the 27 -> 30 count correction: the assert block guards thirty entries, of which twenty-seven exclude init, deinit and step, and a guard built on twenty-seven would pass an implementation missing any of the three. And the paragraph claiming ModuleContext "does not exist in this repository" is deleted rather than softened — exact when written, obsoleted by this milestone's own gate A.
Gate F execution log with the interaction pass reported by what it crossed: the moved log walked against a concrete slot-recycling sequence rather than against its doc comment; the proxy's three holders measured at 3 authoritative writes against 3 derivations and 2 removals against 2 unbindings; the allocator found in exactly one field, PhysicsWorld holding zero of its nineteen and the adapter's 28 entries taking none. RD-5 records the p99 ruling and its STOP condition, not met. RD-6 records that C1.1's zero-allocation property is gated in a configuration a shipped game does not run, with the number that makes the criterion patch writable. CLAUDE.md §3.4: current state, one Tags row, and the open-decision delta — M1.D.13 CLOSED by measurement, the two freeze preconditions DECIDED and reduced from narrative to pointers, M1.D.12 untouched, and the three M1.1.8 leftovers re-pointed because their named owner closed without doing the work. The propagation's CLAUDE.md half does not reconcile with the frozen figure: the five rows carry 13 token occurrences, not 17, the remaining four being statements false without either token. Reported rather than reconciled away. Method lesson recorded: nine findings were defects in an instrument or a contract rather than in the engine, and the three sharpest arrived under a green PASS in a bench just written. A guard must be shown able to fail ON THE PROPERTY IT CLAIMS, not merely to fail.
guysenpai
marked this pull request as ready for review
August 27, 2026 09:35
guysenpai
marked this pull request as draft
August 27, 2026 11:34
THIS COMMIT IS RED ON PURPOSE and the next one turns it green. A test written after a fix does not prove it would have caught the defect, so the three pinning tests land first with their redness on the record: expected 1 found 2 (one entity, two bodies, two answers), expected 2 found 1, expected 400 found 256. The middle one earns its own line. Under the deduplication defect the COUNT is right — four slots, four entries — and the SET is wrong, so a test asserting only the count passes. It asserts the set. Cause, measured by reading the file rather than grepping it: the adapter's tests attested that its 28 entries EXIST and never what they ANSWER. Nine were behaviour-asserted, eight were called with their answer never asserted, and eleven were never called at all. So the two review findings are the first two of nineteen unguarded entries, and the class is swept rather than the instances patched. Four further tests take the rest of the surface to behaviour: the read-back mutators, the four single-result query entries (raycast at 9.5 m against shapeCast at 9.0 m on the same geometry, which is what distinguishes a real cast from a raycast wearing its name), and the character entries. All four pass on their first run, so the family is one of missing guards and not of missing correctness. Floor re-derived FROM THE SUITE: 1949 -> 1956 (1937 passed + 19 skipped).
F1. engine-physics-queries.md §1.11.14 puts deduplication at the tier that PROJECTS BODIES ONTO ENTITIES and makes it mandatory there. The frozen signatures of overlapShape, overlapAabb and pointQuery return []EntityId, so this IS that tier; the old comment deferred the obligation to the Tier 1 service, which sits ABOVE the interface and receives entities already projected, so nothing would ever have deduplicated. raycastAll is deliberately NOT in that class, and the distinction is the body identity: a RaycastHit carries body alongside entity plus its own position, normal and distance, so nothing is projected away and collapsing two hits would destroy information the caller was handed. Adjacent deduplication is exact rather than approximate, and that is a property of the solver's key: OverlapCollector.finish sorts on keyLess, entity-major with BodyId only as the final tie-break, so every body of one entity forms one contiguous run. Stated as a debug assertion at the site that relies on it. The second-order half is the real defect. Deduplicating AFTER the truncation under-fills the slice and evicts unique entities entitled to it: one entity holding three bodies eats three of four slots and the answer names two entities where four exist. Retention therefore runs on the deduplicated ENTITY set, through a loop that doubles the staging and re-queries, exiting the moment the slice is full or the solver returns fewer than it was offered. F2. The staging constant survives as stack_hits, a FLOOR below which no query allocates, never a ceiling: above it the adapter grows a reusable buffer from the allocator it already holds. No frozen signature moved -- the declared-cap-with-a-channel alternative does, and belongs to M1.1.15.2. One residual, written on the function: two entries are frozen as bare u32, so an allocation failure degrades to a correct prefix instead of reporting. That cannot fire on a healthy allocator, where the 256 fired on every call.
Status back to ACTIVE, PR back to draft, both reverted from a closure that should not have been taken. RD-7 records the reopening with its cause: two wrong behaviours reached a CLOSED brief because the adapter's tests attested that its entries exist and never what they answer. The class sweep is the reportable result and it is in the execution log: 9 behaviour-asserted entries against 19 that were not, so the two findings are the first two of nineteen and the class was swept rather than the instances patched. All four sweep tests passed on their first run, which makes the family one of missing guards and not of missing correctness. RD-8 records that two of the review's own figures were unit errors -- "17 CLAUDE.md sites" is 13 token occurrences plus four statements false without a token, and "37 sites" mixes occurrences with rows. Reported, not reconciled away. A fifth instrument defect is logged with the rest: a cap probe with a 500:1 box answered 265 of 400, diagnosed by changing only the probe's aspect ratio -- 500:1 gives 265, 1:1 gives 400 -- the documented GJK limit for radius-0 box cores and not the staging under test. Floor 1949 -> 1956. Closing notes and the tag row updated to match.
Status CLOSED, and the reopening stays visible in the header rather than being smoothed out: it happened, and a brief that hid it would be the same class of defect the milestone spent itself on. Closing notes gain the finding the reopening actually produced, which is neither F1 nor F2 but the nineteen. The review named two instances; the sweep produced the number -- 9 behaviour-asserted, 8 called with their answer never asserted, 11 never called at all. And the classification is what makes that number usable in one direction rather than the other: the four single-result query entries and the mutators passed on their first run, so seventeen entries were correct and unguarded while two were wrong. A family of missing guards, not of missing correctness. Without the sweep neither reading could be told from the other, and two instances would have been fixed in the belief that something had been closed.
guysenpai
marked this pull request as ready for review
August 27, 2026 14:58
guysenpai
marked this pull request as draft
August 27, 2026 16:08
G2. "28 entries, 28 called, zero remaining" counted CALL SITES. The predicate that matters is whether an entry's oracle tells it apart from a plausible NEIGHBOURING entry, and three entries passed the weak one while failing the real one. moveKinematic asserted only the pose reached -- and setBodyTransform reaches the same pose, so a teleport passed a test listed as covering the entry whose whole contract is that it DERIVES velocities. The overshoot oracle drafted to replace it discriminated nothing either: measured, both forms sit at 4.0000 after one step and are still at 4.0000 after two. The frozen surface has no velocity getter but has exactly one place where a kinematic body's velocity surfaces -- moveCharacter's ground_velocity. The oracle reads 3 m/s against 0 for a teleported twin driven in the same test. addForce is discriminated from addImpulse by the travel ratio. The first version predicted 1/dt = 60 and measured 95.99, which is exactly 1.6/dt under four TGS Soft substeps; asserted as a BAND, because pinning 96 would pin the solver's cadence in an adapter test. destroyShape is discriminated from a no-op by error.InvalidShape. Re-counting the class with the right predicate found three more: init (a zero-gravity world passed everything, all static or gravity_factor = 0), setLinearVelocity (indistinguishable from addImpulse at unit mass until made to follow one), resizeCharacter (its true asserted, its effect never). And pointQuery answered like a degenerate overlapAabb until a sphere separated them. 28 of 28 now discriminate, with one reservation named not closed: setAngularVelocity asserts the orientation changed, not which axis. Six counter-factuals fire. A seventh reported exit=0 and was refused: its mutation string named a parameter `v` where the source says `velocity`, so nothing was mutated. The guard is now in the probe -- assert the mutation is present before trusting its verdict. Floor re-derived FROM THE SUITE: 1956 -> 1961 (1942 passed + 19 skipped).
G1. The staging's doc comment said "two of the three callers are frozen as bare u32". Measured: FOUR callers, THREE bare. raycastAll, overlapAabb and pointQuery have nowhere to put an allocation failure; overlapShape is frozen anyerror!u32 and CAN report. So the arbitration -- panic or degrade, no third way -- was true of three entries and FALSE of the fourth, and `resize(...) catch return stack` silently swallowed an OutOfMemory on the one entry whose frozen signature declares it transmissible. An arbitration is worth what the enumeration of its scope is worth, and this one's was wrong on both numbers. stageBodiesFallible propagates, stageBodies absorbs, and collectEntities routes between them on a reports_allocation_failure declared ON EACH FILLER, at the call site, beside the signature it mirrors. Per site on purpose: a single enumeration written in one place is exactly what was wrong, and a const next to its own entry cannot drift from it. No frozen signature moved. Whether three signatures on the frozen surface SHOULD be unable to report an allocation failure is a contract decision for M1.1.15.2, recorded rather than settled here.
RD-9 records it, and states the cause the two reopenings share rather than treating them as two events: a conclusion is worth what the enumeration of its scope is worth. Three times, on three different objects, a green count was produced by a predicate weaker than the claim it supported -- 28 entries counted as EXISTING when the question was what they answer, four staging callers counted as three, and 28 entries counted as CALLED when the question was whether their oracles discriminate. RD-10 records the decision this surfaces and does not take: three frozen entries cannot report an allocation failure. That is a contract decision for M1.1.15.2, before its assert block rather than during it. Closing notes and the tag row updated. Floor 1956 -> 1961. Status stays ACTIVE; the brief closes on a verdict, not on my own say-so.
H1, and the defect was inside F1's own fix. dedupEntities deduplicates by ADJACENCY, correct only under an entity-major order, and the whole premise was held by std.debug.assert -- which ReleaseFast compiles to nothing. Live in the two matrix cells where a breach costs nothing, ABSENT in the mode the C1.1 bench runs and a game ships, where it silently reproduces the defect F1 had just closed: an entity returned twice, damage applied twice. And the premise is not this file's to keep. Entity-major order belongs to query/overlap.zig's collector and can stop being true through a change in its owner with nothing here moving. Fixed on the repository's own precedent: query/root.zig:380 already replaces a release-stripped class assert with an active probeAdmissible check, for this reason and in these words. The comparison is active in every mode, and on detection the function stops trusting adjacency and scans what it has written -- exact, because everything written before the break was deduplicated over an ordered run. The answer is never wrong even under a violated premise: no truncation, no panic, no error channel. The violation is COUNTED, because two of three callers are frozen bare u32 and a counter is what makes "detected" true rather than a word. Cost stated structurally: one integer comparison per projected body inside a loop that already performs one. The C1.1 bench cannot establish it -- it drives PhysicsWorld and never touches the adapter -- and saying so beats replaying it and presenting an unmoved number as evidence. Counter-factual decisive both ways: the old form gives expected 2, found 3 in ReleaseFast and PANICS in Debug. Loud where it costs nothing, silent where it costs everything. The pinning test is green in Debug, ReleaseSafe and ReleaseFast, and an ordered run must leave the counter at zero. Floor re-derived FROM THE SUITE: 1961 -> 1962.
RD-11 records H1 and why it was handled in the same push as G1/G2: the correction touches dedupEntities, which they touch already, and three passes over one file cost three matrices where one costs one. The sweep states its criterion, because the boundary depends on it. Read literally, "an assert guarding another module's contract" sweeps in almost every precondition, since callers usually live elsewhere. The discriminating question is whether a breach yields a SILENTLY WRONG ANSWER rather than a caller-auditable violation or a loud failure in the modes that run. 132 production asserts in src/modules/forge/. 15 sit in a comptime block and fire in every mode. Of the 117 runtime ones: 43 are domain preconditions on a caller-supplied parameter, 65 are local bounds or invariants of a structure the file owns, and 9 assert the class of a shape fetched from the store. The 9 are not in the class -- the category is chosen upstream by an exhaustive switch whose net M1.1.11.1 audited, and the one caller-supplied handle path already carries an active probeAdmissible check. One neighbour ruled out with its reason rather than by silence: determinism.zig:56 is stripped identically, but its arbitration is written on the function and its instrument runs on exactly the two modes where the assert is live. The H1 class had exactly one member. An eighth instrument defect is logged with the rest: the first H1 counter-factual did not compile, so it measured nothing while reporting exit=1 -- which reads exactly like one that fired. The guard grew: a counter-factual must be shown to have RUN, not merely to have been applied. Floor 1961 -> 1962. Status stays ACTIVE.
H1's gap, closed in the milestone that found it. std.debug.assert is
compiled to nothing in ReleaseFast, and the matrix carried {Debug,
ReleaseSafe} only -- so every assert in the tree was verified in exactly the
two modes where its breach costs nothing and in neither mode a game ships.
That gap is where H1 was born and it cost two review rounds.
ONE cell, ubuntu-24.04 / ReleaseFast / f32, 12 -> 13. Deliberately not an
axis: a release-stripped assert is MODE-dependent and neither platform- nor
precision-dependent, so -Doptimize alone decides whether the assert exists.
One cell detects the whole class; completing the axis would cost 50% of the
matrix for the same detection. The reason is written in ci.yml AT the cell,
with an explicit instruction not to complete it by symmetry -- a future
platform- or precision-dependent finding is a different class and owes its
own reason.
ReleaseFast joins ReleaseSafe on the 55-minute budget, both being optimising
builds. Sizing the new cell to Debug's 20 would make it fail on compile time
rather than on the class it exists to detect, which is the worst kind of red.
The cell was measured GREEN locally before being added, so its addition
carries no discovery risk -- only the standing detection it did not have.
Brief CLOSED. The two reopenings stay visible in the header: they happened,
and a brief that hid them would be the same class of defect the milestone
spent itself on.
guysenpai
marked this pull request as ready for review
August 27, 2026 17:51
I2. RD-10 recorded, as a decision for the freeze, that raycastAll, overlapAabb and pointQuery are frozen bare u32 and therefore cannot report an allocation failure. Recording it was the wrong act. engine-tier-interfaces.md §0 forbids the entry that ALLOCATES AND HAS NO CHANNEL. void is that defect's obvious shape; a u32 that truncates in silence is its disguised one -- a truncated success indistinguishable from a complete answer to a caller who sized the slice precisely to tell them apart. And "the signature does not permit reporting" is circular when the interface is not frozen and this milestone's exit criterion is that the surface be FINAL. All four are anyerror!u32. The absorbing stageBodies is DELETED rather than left unused -- it had no caller once all four report -- and the per-filler routing const goes with the asymmetry it described. A finding on the new code, produced by a counter-factual that HUNG instead of failing: restoring the absorbing staging makes collectEntities loop for ever, want doubling while a short buffer never grows and neither exit is reached. So the loop's termination rests on the staging PROPAGATING, and the older form needed its third exit for exactly that reason. Written at the loop. The counter-factual, re-aimed at the entry, gives expected error.OutOfMemory, found 0. I1. "The answer is never wrong" was too wide. The H1 guard produced an answer with no DUPLICATE, and §1.11.14 makes entity identity the key of ORDER as well as of retention -- the pinning test asserted [5, 3], the order met, which is the claim's gap visible in its own oracle. Closed by sorting on the violation path rather than by narrowing the sentence: entities are distinct by then so the BodyId tie-break cannot apply, and the sort uses query.entityKey, exported for it rather than re-derived. The cold path never runs while the premise holds. The test asserts [3, 5]; without the sort it gives expected 3, found 5. The n == out.len exit loses its unconditional "exact" label: exact only while the premise held. Pinned twice, and the pins are not the same claim: a starved allocator shows an entry DID report on one call, a signature walk shows it CANNOT fail to -- with raycast and raycastAny asserted NON-fallible as the control. Floor re-derived FROM THE SUITE: 1962 -> 1963.
RD-12 records it, and the fact worth stating once: both findings were against arbitrations the review had itself validated. RD-9's lesson -- a conclusion is worth what the enumeration of its scope is worth -- applied to the review before it applied to the code. RD-10 is SUPERSEDED. The decision it deferred to the freeze is taken here, because a surface with three entries that allocate and cannot report is not a FINAL surface, and finality is this milestone's exit criterion. A ninth instrument observation joins the list, and it is the only one that turned out to be a finding on the production code rather than on the probe: a counter-factual that HUNG instead of failing, because restoring the absorbing staging makes collectEntities loop for ever. Its termination rests on the staging propagating. Floor 1962 -> 1963. Status stays ACTIVE; the brief closes on a verdict.
guysenpai
marked this pull request as draft
August 28, 2026 02:31
Status CLOSED. The three reopenings stay visible in the header: they happened, and a brief that hid them would be the same class of defect the milestone spent itself on. Closing notes rewritten on what the milestone actually produced. The real subject was the predicate a count is taken with. Four times, on four different objects, a green count came from a predicate weaker than the claim it supported: 28 entries counted as EXISTING when the question was what they answer; four staging callers counted as three, which made an arbitration follow from a false enumeration; 28 counted as CALLED when the question was whether their oracles discriminate; and a ls-remote read after a push, which cannot tell "the ref had already moved" from "it has just moved" -- that last one on the review side, retracted on the evidence. The nineteen, and the recount that followed it: 9 / 8 / 11, then the count itself taken on the wrong predicate. 28 of 28 now carry a discriminating oracle, with setAngularVelocity's axis named as the one reservation. Ten instrument defects, none found by a red check pointing at it. Three rules survive them: a guard must be shown able to fail ON THE PROPERTY IT CLAIMS; a counter-factual must be shown to have RUN; and a probe must distinguish "the condition is false" from "I could not test it". Four preconditions left to M1.1.15.2, none of them a deferred decision -- and the contract this milestone refused to leave it is the error channel on the three bare-u32 entries, taken here because the exit criterion is that the surface be FINAL. Floor 1963 (1944 passed + 19 skipped), windows 1961.
guysenpai
marked this pull request as ready for review
August 28, 2026 02:54
guysenpai
marked this pull request as draft
August 28, 2026 03:46
J1, and it lands on the same claim twice narrowed and still too wide. H1 made the answer duplicate-free; I1 made it duplicate-free AND ordered; both leave RETENTION running on the broken order. The sort reorders what was already kept. With out.len == 2 on a run yielding 5, then 3, then 1 further on, the loop fills on [5, 3], the sort answers [3, 5], and the canonical subset under the §1.11.14 key is [1, 3]. Under a sound premise the first out.len distinct entities ARE that subset, because entity-major order delivers them increasing -- which is exactly the property the premise buys and exactly what its violation removes. No pass over what was KEPT recovers what was never COLLECTED. So the answer is error.UnorderedProjection, distinct from OutOfMemory because a broken upstream order is not an allocation failure. It is expressible only because I2 gave the three projecting entries their channel one round earlier. The alternative -- collect exhaustively, then select on the key -- needs an unbounded collection to be correct and would reopen what I2 closed. And a violated premise means another module is broken: this adapter cannot repair a retention decision it did not take. raycastAll is out of the class: it does not pass through dedupEntities and its hits carry body alongside entity. The `found < buf.len` exit was CHECKED, not assumed. On its own it speaks only of the solver's saturation and says nothing about order; it is the refusal that makes it exhaustive, so neither exit is reachable unless the premise held for the whole run. Written at the site, so removing one shows the other falling. Two removals whose reason left with the sort: containsEntity had one caller and it was the violation path; query.entityKey has zero call sites and its pub is reverted. The counter stays beside the error -- "never happened" and "happened and erred" are not the same state, and a second breach increments it again. Counter-factual: the sort-without-error form restored gives expected error.UnorderedProjection, found 2 -- the wrong subset returned rather than refused.
Status CLOSED. The four reopenings stay visible in the header: they happened, and a brief that hid them would be the same class of defect the milestone spent itself on. RD-13 is added as a shape distinct from RD-9, because the counter-measures differ. RD-9 is a conclusion drawn from a count taken on too weak a predicate. RD-13 is a claim restated twice, each time narrowed just enough to cover the counter-example in front of it and never re-derived from the contract it cites: "the answer is never wrong" was checked against duplicates, then against order, and never against retention -- which §1.11.14 names in the same sentence as the other two. The counter-measure is in the statement: a claim is re-derived from the SPEC, never from the last counter-example. Closing notes carry the fourth instance of the predicate family, what the fourth reopening settled, and the thirteen recorded deviations. Floor unchanged at 1963 (1944 passed + 19 skipped), windows 1961.
guysenpai
marked this pull request as ready for review
August 28, 2026 04:12
guysenpai
marked this pull request as draft
August 28, 2026 05:52
K1. Five formulations of one claim, each narrowed just enough to cover the previous counter-example: never wrong -> no duplicate -> duplicate-free AND ordered -> true unless the premise broke during the run -> true for the window. The fourth was written one round after that pattern was named, which is the pattern happening while it was being written. The cause is structural, not editorial. dedupEntities does not see the run, it sees the window it is handed. Measured: `n == out.len` returns BEFORE `want` doubles, so with out.len == 2 and an owner sequence [3, 5, 1] the first pass receives [3, 5], finds it ordered -- because it IS -- fills the slice and returns. The 1 never enters an observed buffer, and no wording turns a windowed observation into a statement about what it never saw. So the adapter guarantees NOTHING under a violated premise, and says so. It refuses what it observes broken, it counts, and it states that the detection is bounded to the window and is therefore not a proof. Every sentence promising a property of the run is gone. What replaces it is what stays exactly true, and the two-exit comment is rewritten on that: under a SOUND premise the first out.len distinct entities ARE the smallest under the §1.11.14 key, and a solver that returned fewer bodies than offered returned all of them. The proof goes to its owner as debt, not as work: entity-major order over the whole selection is OverlapCollector.finish's property, and only that function observes the whole selection. K2: the paragraph above the refusal still described the deleted fallback -- scanning what was written, an answer never incorrect, an O(n^2) path. containsEntity went with the sort a round earlier. A justification that outlives its mechanism, sitting directly above a paragraph that says the opposite. Deleted. The test now attests a LIMIT rather than a guarantee: the public path under truncation returns the canonical smallest two with the counter at zero, and an internally-ordered window is accepted even when a smaller element followed it. Counter-factuals: the refusal removed fails the breach test; the OWNER's sort reversed fails four, so the new test depends on that order rather than passing by construction. Floor re-derived FROM THE SUITE: 1963 -> 1964.
Status CLOSED. The five reopenings stay visible in the header: they happened, and a brief that hid them would be the same class of defect the milestone spent itself on. The fifth reopening is recorded as a DECISION, not a failure. The adapter guarantees nothing under a violated entity-major premise: it refuses what it observes broken, it counts, and it states that the detection is bounded to the window it is handed and is therefore not a proof. A property no component has the data to establish is not narrowed until it fits -- it is given up, and its proof goes to the component that can see it. RD-15 hands that to OverlapCollector.finish as a precondition. And the fact worth keeping: the fourth formulation of that claim was written ONE ROUND AFTER RD-13 named the pattern. A rule that has been named does not prevent its own next instance; it only makes it recognisable afterwards. That is why the counter-measure had to become a test asserting the limit rather than a sentence -- a future author who wants to re-promise the run has to make that test go red first. Fifteen recorded deviations, RD-1 to RD-15. Floor 1964 (1945 passed + 19 skipped), windows 1962.
guysenpai
marked this pull request as ready for review
August 28, 2026 09:09
guysenpai
marked this pull request as draft
August 28, 2026 09:31
L1. RD-15 named OverlapCollector.finish as the owner of entity-major order. Verified by reading, not by trusting the report: the replace-worst loop is in `add` -- it decides what is RETAINED -- and `finish` only sorts the prefix already kept. The owner is OverlapCollector as a whole. It matters because RD-15 is a debt LEFT to M1.1.15.2, and a debt exact on the substance and wrong on the address costs whoever picks it up the time to discover they are looking in the wrong function. Class swept rather than the two sites named: a grep found five mentions in the brief and two in module.zig. Classified by what each CLAIMS rather than by the symbol it contains -- one is about the SORT, where `finish` really does sort on root.keyLess and is where adjacency's contiguity comes from, and it is correct and untouched. The other four attributed ownership of the premise and were wrong. L2. A comment announced "the debug assertion below", replaced at H1 by the active check returning error.UnorderedProjection -- the very change the paragraph three lines above explains. It now names the active check. And the form is the cause of both. RD-15 was a fifty-six-line exposition inside module.zig carrying motive, discarded formulations and arbitration. A debt lives in the plan, in one place, and code points at it. Reduced to thirteen lines that pass the deletion test; the sibling comment on the two loop exits lost its historical motive the same way. Had RD-15 been a pointer from the start, L1 would not exist as a code finding.
guysenpai
marked this pull request as ready for review
August 28, 2026 09:56
guysenpai
marked this pull request as draft
August 28, 2026 12:41
M1. One comment line in tests/physics/forge_module_test.zig, wrong in both directions: it named OverlapCollector.finish as the owner of entity-major order -- the address L1 had already corrected everywhere else -- and called it "the only function that observes it", which is false both ways. finish does NOT see the rejected candidates; add does. The finding is my L1 sweep's PERIMETER, not its predicate. It classified mentions by what they claimed rather than by the symbol they contained, which was right, and it searched only the files the correction had just touched -- briefs/ and module.zig -- never tests/. Same shape as RD-9, applied to the scope of a grep instead of the scope of a count. Re-swept over the whole tracked tree with no directory assumption: seven files, twenty-three mentions. Two are about the SORT and exact. Three are the narrative of the correction and must keep the old address. Six already carry the corrected attribution, verified by reading. Seven are the declaration and its instantiations. Three are MeshOverlapCollector, an unrelated type caught by substring -- the same instrument under-reaches across directories and over-reaches across names. One was the defect. RD-17 records it. Floor unchanged at 1964.
guysenpai
marked this pull request as ready for review
August 28, 2026 12:45
Two corrections below the reopening threshold, carried with the CI enumeration rather than as an eighth round. The M1 sweep sub-total read 6 over seven enumerated sites -- CLAUDE.md (1), brief (5), module.zig (1). And the sweep is now dated: twenty-three mentions ON fc8d3dd, the state before this journal entry was written, since the entry itself adds mentions. A sweep count that does not say what it was taken on expires by the writing that reports it. The closing synthesis was frozen at 1963/1961 after the floor had moved to 1964/1962. The form is the defect, not the digits: a closing section that COPIES a number is expired by every later correction, because each correction adds tests. The floor is stated once, at the entry that last moved it, and the synthesis points at it. RD-18 records that this brief's post-closure threshold -- only a wrong behaviour or a test that does not guard its object reopens the branch -- existed through rounds five, six and seven and was not applied by either side. From now on a P2 or P3 documentary finding is noted, not reopened.
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.
Brief:
briefs/m1.1.15.1-tier0-pregate-and-c11-cost.md— Status CLOSED, 2026-08-27, reopened six times on external review before closing (RD-7, RD-9, RD-12, RD-13, RD-14, RD-16).M1.1.15.1 is the milestone that makes the freeze possible rather than the one that takes it. Six gates, A to F, each stopped for an explicit GO — then six rounds of external review, and what they found is worth more than what the gates delivered.
What it delivers
(a)
core.ModuleContext, minted with FOUR fields. Each ofengine-tier-interfaces.md§0's four removals is absent for a named reason and none by minimalism:registrarandevent_buswould be second declarants of actsWorldalready owns (pattern D11);asset_loaderwould be a tier inversion whose absurdity needs no invariant to see, that module's owninittaking a*ModuleContextand so receiving a pointer to itself;frame_allocatorhas no Tier 0 producer and no consumer. The*Worldis outsideARCH-030rather than an exception to it. Pinned by a negative twin with a two-mechanism predicate whose counter-factual changes the object — a refused six-field shape — never its own expected constant.(b) The Tier 1 allocator and fallibility contract.
ARCH-013forbids an allocator on a Tier 1 interface entry while sixPhysicsWorldentries need one, sosrc/modules/forge/module.zigfronts it. Measured:Forge3DModuleholds the one allocator on the physics path,PhysicsWorldholds zero of its nineteen fields as one, and 0 of the adapter's 28 entries take one.Broadphase.updatebecame infallible on an invariant — at most one unconsumed log entry per slot — not on a reservation promise, which is what let the three pose setters collapse tovoid.stepbecameanyerror!voidover eight measured allocation sites with a written failure contract.(c)
M1.D.13closed, and the C1.1 instrument written.proxyOfwent from a linear search to a denseBodyId-keyed index, with an authority statement rather than a second source:bodies[i].proxyis the fact, the index is derived,rebindProxyis the single derivation point.bench/physics_forge_3d_integration.zigis the instrument C1.1 named and that did not exist.(d)–(e) The
M1.1.26 → M1.1.15.2/M1.1.25 → M1.1.21.1renames propagated (24 code sites, 17CLAUDE.mdsites), the 27 → 30 count correction, theCLAUDE.md§3.4 patch.C1.1, measured for the first time
1 000 dynamic + 10 000 static at 60 Hz, on 1 000 awake dynamic bodies of 11 000, P = 8 809 retained pairs, 240 frames from frame 31.
Zero allocation attempts in steady state in both, where steady state is defined on the scene (pairs and constraints unchanged for thirty frames) and never on the allocator — otherwise the result would be true by choice of window and unable to fail.
Gated on the p99, not the median. A 60 Hz budget is a real-time constraint and a hitch is invisible to a centre. Reported in the other direction too: across five runs per mode on identical code with a bit-identical checksum, the median moves 6 %, the p99 up to 2.4× and the max up to 3.5× — the tail is machine noise, so this gate buys sensitivity and costs stability, and the margin is claimed on the worst p99 observed.
Retention shape (reported). N ×3.72 at a measured-constant P = 2 209 gives frame ×1.14. A Θ(P·N) step 2 would demand 33.1 million extra endpoint resolutions per frame for a measured delta of 70 µs — 2.1 picoseconds each. Refuted by ~470×.
The two reopenings, and the finding that outranks the gates
First — F1/F2/F3.
entitiesOfreturned one entry per body whereengine-physics-queries.md§1.11.14 makes deduplication MANDATORY at the tier projecting bodies onto entities, and its comment deferred that obligation to a tier above the interface which receives entities already projected — nothing would ever have deduplicated. A private staging depth of 256 capped four public entries below the caller's own slice. The cause was that no multi-result entry was exercised through the adapter at all, and the sweep produced the number:deinit,step(failure path),addBody,setBodyTransform,getBodyTransform,createShape,createCharacter,resizeCharacter,getCharacterInnerBodyinit,removeBody,setLinearVelocity,addForce,addImpulse,destroyShape,destroyCharacter,setCharacterPositionmoveKinematic,setAngularVelocity,raycast,raycastAny,raycastAll,shapeCast,overlapShape,overlapAabb,pointQuery,closestPoint,moveCharacterF1 and F2 were the first two of nineteen. And the classification is what makes that number usable in one direction rather than the other: the four single-result query entries and the mutators passed on their first run, so seventeen entries were correct and unguarded while two were wrong — a family of missing guards, not of missing correctness.
Second — G1/G2.
stageBodiessaid "two of the three callers are frozen as bareu32"; measured, four callers and three bare, the fourth beingoverlapShape, frozenanyerror!u32, which the code silently made swallow anOutOfMemoryits own signature declares transmissible. And the sweep declared closed had been closed on call sites, where the predicate that matters is whether an oracle tells an entry apart from a plausible neighbour:moveKinematicasserted only the pose reached, whichsetBodyTransformalso reaches. Re-counted with the right predicate, three more fell —init,setLinearVelocity,resizeCharacter— pluspointQuery. 28 of 28 now carry a discriminating oracle, with one reservation named and not closed:setAngularVelocityasserts that the orientation changed, not which axis.Third — H1, and the defect was inside F1's own fix.
dedupEntitiesdeduplicates by adjacency, correct only under the entity-major order ofquery/overlap.zig's collector, and the whole premise was held by astd.debug.assert— which ReleaseFast compiles to nothing. The guard was live in the two matrix cells where a breach costs nothing and absent in the mode the bench runs and a game ships. Fixed on the repository's own precedent (query/root.zig:380already replaces a release-stripped class assert with an active check, for this reason and in these words): the comparison is active in every mode, and on detection the function stops trusting adjacency and scans what it has written — exact, because everything written before the break was deduplicated over an ordered run. The answer is never wrong even under a violated premise, and the violation is counted because two of three callers have no channel. Counter-factual decisive both ways: the old form givesexpected 2, found 3in ReleaseFast and panics in Debug.Third round — I1/I2, and both findings were against arbitrations the review had itself validated.
raycastAll,overlapAabbandpointQuerywere left frozen bareu32under the argument that their signature did not permit reporting an allocation failure — circular, since the interface is not frozen and this milestone's exit criterion is that the surface be FINAL.engine-tier-interfaces.md§0 forbids the entry that allocates and has no channel, and au32truncating in silence is that entry under another return type: all four are nowanyerror!u32, the absorbingstageBodiesis deleted rather than left unused, and the per-filler routing const disappears with the asymmetry it described. And "the answer is never wrong" was too wide — the H1 guard produced an answer with no duplicate while §1.11.14 makes entity identity the key of ORDER as well as of retention, so the violation path now sorts onquery.entityKey, the solver's own key exported for it rather than re-derived, and then == out.lenexit loses its unconditional "exact" label.Fourth round — J1, on the same claim twice narrowed and still too wide. H1 established the guard, I1 made its answer duplicate-free and ordered, and both leave RETENTION running on the broken order: the sort reorders what was already kept, and with
out.len == 2on a run yielding 5, then 3, then 1 further on, it answers[3, 5]where the canonical subset under the §1.11.14 key is[1, 3]. No pass over what was kept recovers what was never collected. Under violation the answer is nowerror.UnorderedProjection— distinct fromOutOfMemory, because a broken upstream order is not an allocation failure — and it is expressible only because the third round gave those entries their channel. The alternative, collecting exhaustively and then selecting on the key, needs an unbounded collection and would reopen what the third round closed. The sort is deleted, andcontainsEntityand thepubonquery.entityKeygo with it: their reason was the violation path.The question the review left open was measured, not assumed:
found < buf.lenis not exhaustive on its own — it speaks only of the solver's saturation. It is the refusal that makes it so, since neither loop exit is reachable unless the premise held for the whole run. The label is true as a consequence of J1, and is written that way at the site.Fifth round — K1, and it closes by abandoning the claim rather than narrowing it again. Five formulations of one guarantee, each just wide enough to cover the previous counter-example: never wrong → no duplicate → duplicate-free and ordered → true unless the premise broke during the run → true for the window. The cause is structural:
n == out.lenreturns before the staging doubles, so without.len == 2and an owner sequence[3, 5, 1]the first pass receives[3, 5], finds it ordered — because it is — fills the slice and returns. The1never enters an observed buffer, and no wording turns a windowed observation into a statement about what it never saw.So the adapter guarantees nothing under a violated premise, and says so: it refuses what it observes broken, it counts, and it states that the detection is bounded to the window and is therefore not a proof. The proof goes to
OverlapCollector—addretains,finishorders — the only type that observes the whole selection (RD-15). The test attests the limit rather than a guarantee — an internally-ordered window is accepted even when a smaller element followed it — which is what stops a sixth reformulation: re-promising the run means making that test go red first. A counter-factual reversing the owner's sort breaks four tests, so the chain really does rest on that premise.Sixth round — L1/L2, two text corrections and no functional defect. RD-15's address was wrong: it named
OverlapCollector.finishwhere the replace-worst loop — the one that decides what is RETAINED — is inadd. Verified by reading rather than by trusting the report. It matters because RD-15 is a debt left to M1.1.15.2, and a debt exact on the substance and wrong on the address costs whoever picks it up the time to look in the wrong function. Swept across all five mentions, with the one that speaks of the sort left untouched because it is exact. A comment announcing astd.debug.assertthat H1 had replaced with the active check was deleted.And the form was the cause of both: RD-15 lived as a fifty-six-line exposition inside
module.ziginstead of as a pointer. A debt lives in the plan, in one place, and code points at it — the block is now thirteen lines carrying only what passes the deletion test. Had it been a pointer from the start, L1 would not have been a code finding at all.Class swept, with the criterion stated — read literally, "an assert guarding another module's contract" sweeps in almost every precondition. 132 production asserts: 15 comptime (every mode), 117 runtime — 43 caller-parameter domain preconditions, 65 local or owned-structure invariants, 9 asserting a shape's class (not in the class: the category is chosen upstream by an exhaustive
switchwhose net M1.1.11.1 audited, and the caller-supplied-handle path already carries an activeprobeAdmissiblecheck). One neighbour ruled out with its reason rather than by silence. The H1 class had exactly one member.And the gap H1 came out of is closed here. One CI cell added,
ubuntu-24.04 / ReleaseFast / f32, 12 → 13 — deliberately not an axis: a release-stripped assert is mode-dependent and neither platform- nor precision-dependent, so one cell detects the whole class where completing the axis would cost 50 % of the matrix. The reason is written inci.ymlat the cell, with an explicit instruction not to complete it by symmetry.Findings reported, not worked around
api.JointDescriptorandapi.JointIdare declared nowhere (measured), so the adapter presents 28 of 30 frozen entries and the plan line assigning joints to M1.1.15.2 is unrealisable as written.rigid/contact_constraint.zig:578) against zero awake. C1.1's zero-allocation property is gated in a configuration a shipped game does not run. Mechanism already owned; this is its first measurement.raycastAll,overlapAabb,pointQuery, bareu32). Under exhaustion they answer a correct prefix — a degradation, not a designed cap. Whether that should be so is a contract decision for M1.1.15.2 (RD-10).Method — the milestone's real subject
Four times, on four different objects, a green count was produced by a predicate weaker than the claim it supported: 28 entries counted as existing when the question was what they answer; four staging callers counted as three, which made an arbitration follow from a false enumeration; 28 entries counted as called when the question was whether their oracles discriminate; and a
git ls-remoteread after a push, which cannot tell "the ref had already moved" from "it has just moved" — that last one on the review side, retracted on the evidence. Each count was correct for what it measured and useless for what it was used to conclude.And a fourth shape sits beside it: a claim restated twice, each time narrowed just enough to cover the counter-example in front of it and never re-derived from the contract it cites. "The answer is never wrong" was checked against duplicates, then against order, and never against retention — which §1.11.14 names in the same sentence as the other two. The counter-measure is in the statement: a claim is re-derived from the spec, never from the last counter-example — and, when no component has the data to establish it, it is given up rather than narrowed until it fits, its proof handed to the component that can see it. The fourth formulation was written one round after that pattern was named, which is the fact worth keeping: a rule that has been named does not prevent its own next instance, it only makes it recognisable afterwards. That is why the counter-measure had to become a test asserting the limit rather than a sentence.
Ten instrument defects, and not one was found by a red check pointing at it. Three arrived at Gate E in a bench just written, under a green PASS. A fourth was a cap probe whose 500:1 box hit a documented GJK limit. A fifth was
echo "push exit=$?"returning the exit code of the echo. A sixth was anuntil status == completedloop reading a stale finished run as a re-run's result. A seventh was a mutation string naming a parameter the source does not have. An eighth did not compile, so it measured nothing while reportingexit=1— which reads exactly like a counter-factual that fired. A ninth was atimeoutthat does not exist on macOS, whose command failure read as a network failure. A tenth sat on the review side and was retracted on the evidence.Three rules the milestone leaves behind: a guard must be shown able to fail on the property it claims, not merely to fail — hence the p99 counter-factual injects hitches at the unchanged budget rather than lowering it; a counter-factual must be shown to have RUN, not merely to have been applied; and a probe testing a condition must distinguish the condition is false from I could not test it.
Validation
zig fmt --check— 0 files unformattedzig build lint— exit 0,dead-testsconservation OK at 1964windows-20251962);test-forge-3d580{Debug, ReleaseSafe} × {f32, f64}— exit 0, 0, 0, 0 — plusReleaseFast/f32, the mode H1 concerns, now a standing CI cellzig build forge-determinismat f32 and f64 — exit 0, 0 witnesses regeneratedgrepbracket class) — zero French prosesrc/,tests/,tools/,bench/,build.zig,.github/Four preconditions left to M1.1.15.2 — and none is a deferred decision
WELD_PHYSICS_PROTOCOL_VERSIONand the thirty surface guards (§12 disambiguates 30 from 27).api.JointDescriptorandapi.JointId, declared nowhere and presumed by its own plan line.syncInand the authority model — re-scoped out at M1.1.15 because the tick says when a write happened and never who produced it.getTriggerOverlaps, and the Forge → Tier 0 bus →EventStorebridge.What this milestone refused to leave it is a contract decision: the three bare-
u32entries received their error channel here, because the exit criterion is that the surface be FINAL and discovering a contract during the freeze is what M1.1.15's splitting exists to prevent.Also out of scope, not debt:
ModuleContext.frame_allocator;M1.D.12; wiring the adapter into a running engine.Planned tag:
v0.11.16-tier0-pregate. Merge and tag are Guy's.