From 52f666af5538323b575f4c0695923d89564d43f5 Mon Sep 17 00:00:00 2001 From: Guy Senpai Date: Thu, 30 Jul 2026 22:27:54 +0200 Subject: [PATCH 01/12] docs: add the spec reference reconciliation chore brief --- briefs/chore-spec-reference-reconciliation.md | 211 ++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 briefs/chore-spec-reference-reconciliation.md diff --git a/briefs/chore-spec-reference-reconciliation.md b/briefs/chore-spec-reference-reconciliation.md new file mode 100644 index 0000000..991d4bb --- /dev/null +++ b/briefs/chore-spec-reference-reconciliation.md @@ -0,0 +1,211 @@ +# Chore — Spec Reference Reconciliation (`engine-spec.md §x` → `ARCH-nnn`) + +> **Status:** PLANNED +> **Branch:** `chore/spec-reference-reconciliation` +> **Tag:** none — pure maintenance chore, merged to `main` without a tag (precedent: `chore-language-normalization`, commit `8434b6e`). Sits **before M1.1.11.1**. +> **Depends on:** M1.1.11 (`main` at `09bd386`, tag `v0.11.11-plane-halfspace`). Blocks nothing functionally; should land before M1.1.11.1 so the next milestone starts from a tree whose spec references resolve. +> **Language of this brief:** English (`engine-development-workflow.md` §4.2). +> **KB files attached to the Claude Code prompt:** `engine-invariants.md`, `engine-audit-checklist.md`, `engine-corpus-map.md`. + +## Context + +The knowledge-base restructuring of 2026-07-30 renumbered `engine-spec.md` from 27 sections to 10, migrated its cross-cutting decisions into a citable registry `ARCH-001`..`ARCH-028` (`engine-invariants.md`), and extracted the roadmap, the Phase −1 archive and the corpus changelog into their own documents. `engine-invariants.md` §1.2 states the resulting referencing policy: + +```text +Cross-cutting architectural decision → ARCH-nnn +Domain detail → canonical filename + named anchor +Roadmap → engine-roadmap.md + phase id +History → the relevant archive +``` + +and forbids any inter-file reference of the form `engine-spec.md §x`. The same paragraph records that the KB files live outside the repository, so the **only** surface on which the policy is mechanically checkable is the versioned one: `briefs/`, `CLAUDE.md`, code comments. That surface was never migrated. + +Two reasons it survived. First, the `chore-language-normalization` pass explicitly listed doc-comment cross-references of the form `engine-*.md §X` under **Preserve verbatim**, which was correct at the time and had the side effect of freezing them. Second, no milestone since has owned the repository side of the restructuring. + +Measured on `main` at `09bd386`: + +- **69** citations of the form `engine-spec.md §x`, over **38 files** — 13 source/tool files plus `examples/triangle/build.zig`, 22 briefs, `CLAUDE.md`, `validation/s5-go-nogo.md`. +- Of those, **1 dangling pointer** (`§26.1` — section 26 was dissolved) and **5 wrong-section references** (`§4`, "Système de plugins", cited for the component SoA/POD invariant, which lived in `§2.1`). +- `CLAUDE.md` § *Quick links spec* lists **45 of the 75** corpus files. Missing: `engine-corpus-map.md` (the entry point of every recon), `engine-invariants.md` (where every `ARCH-nnn` is defined), `engine-roadmap.md`, `engine-phase-minus-1-archive.md`, `spec-changelog.md`, `engine-audit-checklist.md`, and 15 of the 17 `etch-*` files. The section's own closing instruction — stop and ask Guy if a spec is not in the list — turns a 40 % incomplete list into a generator of unjustified stops. + +This is **not** a numbered milestone: zero new capability, nothing to gate on behaviour. It is a maintenance pass, merged untagged. + +## Objective + +Make every spec reference in the repository resolve under the current corpus, with **zero semantic change** to code, contracts, measurements or acceptance records. References only. + +## Scope — IN + +1. **Code comments (`.zig`)** — 14 files: + - `src/core/ecs/chunk.zig`, `comptime_query.zig`, `registry.zig` + - `src/core/resources/api.zig`, `root.zig` + - `src/core/rtti/root.zig`, `type_info.zig` + - `src/core/scene/loader.zig`, `root.zig` + - `src/etch/test_runner.zig` + - `src/foundation/root.zig` + - `src/modules/forge/forge_3d/root.zig` + - `tools/etch_test/main.zig` + - `examples/triangle/build.zig` +2. **Closed briefs** — 22 files: `S0`, `S1`, `S2`, `S3`, `S4`, `S5`, `S6`, `M0.0`, `M0.2`, `M0.3`, `M0.4`, `M0.7`, `M0.8`, `m0.6`, `M1.0.3`, `M1.0.4`, `M1.0.5`, `M1.0.6`, `M1.0.13`, `M1.0.15`, `M1.1.0`, `M1.1.5`. References only — see *What must not be touched*. +3. **`validation/s5-go-nogo.md`** — one reference. Same rule as the briefs. +4. **`CLAUDE.md`** — the three in-line references plus a full rewrite of § *Quick links spec* (replacement text supplied below). + +## Scope — OUT + +- **No `weld_lint` rule.** Decided by Guy: the substitution is a one-off, and a permanent rule is not warranted for a doctrine whose enforcement surface is three directories. The exit check is the acceptance grep, run by hand before the gate signal. +- **No code change.** No identifier, no logic, no signature, no test. The diff must contain only comment lines (`//`, `///`, `//!`) and markdown. +- **No figure, measurement, verdict, acceptance line, recorded deviation or reading date edited** — in any file, and in particular not in the closed briefs. +- **No KB file edited.** The KB is Claude.ai's channel; this chore is the repository side only. +- **No new tag.** + +## Substitution table + +Deterministic. Replace the **reference**, never reword the sentence carrying it. + +| Old reference | Subject | New reference | +|---|---|---| +| `§1.6` | C keepers | `ARCH-024`; detail `engine-c-bindings.md` | +| `§2` | ECS overview | `ARCH-005`; detail `engine-ecs-internals.md` | +| `§2.3` | Archetype storage, 16 KB chunks | `ARCH-005`; detail `engine-ecs-internals.md` | +| `§2.5` | RTTI / component registry | `ARCH-007`; detail `engine-ecs-internals.md` | +| `§2.9` | Resources | `ARCH-006`; detail `engine-ecs-internals.md` | +| `§3.1` | Tier 0 / re-export convention | `engine-zig-conventions.md` § "Fichier racine : `root.zig` (module) vs `main.zig` (exécutable)" | +| `§3.5` | Everything in-tree, no separable libs | `ARCH-017` | +| `§4` | Component SoA/POD invariant | `ARCH-004` — **corrects a wrong-section reference** | +| `§5` | Render module | `engine-render.md` | +| `§16` | Asset Pipeline | `engine-asset-pipeline.md` | +| `§19`, `§19.1` | Scene serialization | `engine-scene-serialization.md` | +| `§22.2`, `§22.3`, `§22.3.0` | see disambiguation rule | see disambiguation rule | +| `§24.10` | `dt` as an injected rule parameter | `etch-reference-part1.md` | +| `§25`, `§25.3` | see disambiguation rule | see disambiguation rule | +| `§26.1` | `weld` CLI | `engine-platform.md` | + +The five `§4` sites are `src/core/ecs/registry.zig:52`, `:65`, `:77`, `briefs/M1.0.3-resource-nonpod-fields.md:154`, `briefs/M1.0.6-prefabs-crossrefs-extensions.md:39` and `:223`. Every one of them cites `§4` ("Système de plugins") for the POD invariant, which lived in `§2.1`. `briefs/M1.0.6:223` even annotates it "POD/SoA en §511-519", i.e. the master's line range, which confirms the intent. The target is `ARCH-004`. + +**Named anchors are deliberately not used** except on `§3.1`. An anchor is a second thing that can go stale, and verifying one requires holding the target document. Where the file *is* the domain owner — which `engine-corpus-map.md` §2 establishes — the filename alone is unambiguous. `engine-zig-conventions.md` covers fifteen unrelated topics, so it is the one case where the filename designates nothing on its own; the section *number* is dropped there too, since a shifting number is the exact failure mode being repaired. + +## Disambiguation rule — `§22.x` and `§25.x` + +Those belong to two successive numberings of the same "Stratégie & Roadmap" section (`engine-audit-checklist.md` §1.2 carries the trap note). Decide on what the citation **designates**, never on the number: + +- it names a spike (`S0`..`S6`) or the spike list → `engine-phase-minus-1-archive.md` +- it designates a phase or a roadmap line → `engine-roadmap.md` §3 +- it designates the per-module phase map → `engine-roadmap.md` §4 + +Verified examples: `briefs/S0-bootstrap.md:52` reads "§22.3.0 (Phase −1 spike list, S0 entry)" → archive. `briefs/S5-etch-codegen-zig.md:19` reads "§25.3 / S5" → archive. `briefs/M0.0-lint-custom.md:38` must be read on its own line before deciding. + +## List-form citations + +A citation may group several sections after one filename — `engine-spec.md` (§3.5, §5, §22.2). Only the first `§` is adjacent to the filename, so a grep keyed on the filename undercounts. **Every member of such a list is in scope.** Known instances: `M0.4:304`, `M1.0.4:144`, `M1.0.5:145`, `M1.0.6:223`, `S0:250`, `S1:55`, `S1:154`, `S2:245`, `S3:300`, `S4:228`, `S6:295`, `m0.6:198`, `M0.2:390`. Establish the full set with `grep -rn "engine-spec.md" --include=*.zig --include=*.md .` and read each hit line whole. + +## Bare references + +A bare `engine-spec.md` with no section number stays as it is — a file-level reference is legal. **Exception:** if the sentence attributes to the master a domain it no longer holds (roadmap, a module specification, Etch, the editor), retarget the file. Two known cases: + +- `CLAUDE.md:150` — "Anything else requires a dedicated derogation in `engine-spec.md`": the derogation now belongs with `ARCH-024` and `engine-c-bindings.md`. +- `CLAUDE.md:158` — "`engine-spec.md` — top-level architecture, modules, roadmap (§22)": the master carries no roadmap; this line is absorbed by the *Quick links* rewrite below. + +## What must not be touched + +A closed brief is a closed record, and `validation/s5-go-nogo.md` is one too. That doctrine governs their **content** — measurements, figures, verdicts, acceptance lines, recorded deviations, reading timestamps like "read 2026-06-28 09:40". None of it is corrected here, **not even a value now known to be wrong**. Repairing a broken pointer is not rewriting the record. The diff of a brief must contain references and nothing else. + +## `CLAUDE.md` § *Quick links spec* — replacement text + +Replace the whole section with: + +```markdown +## Quick links spec + +The full specification lives in the claude.ai knowledge base — 75 files +(54 `engine-*`, 17 `etch-*`, 4 others), referenced by name (no repo path — +`spec/` is not in the repo). + +Reading order is not free: it follows ownership. + +1. `engine-corpus-map.md` — which file owns which domain (§2) and what spec + status it carries (§1). Entry point of every recon. +2. `engine-invariants.md` — the 28 cross-cutting decisions, citable as + `ARCH-001` … `ARCH-028`. Where every `ARCH-nnn` used in this repo is + defined. +3. the owner document — all detailed normative content for its domain. +4. `engine-spec.md` — constitution only (architecture, ECS, tiers, plugins) + plus the canonical catalogue of the 18 Tier 1 modules. It holds no module + mini-spec and no roadmap. + +**Never cite `engine-spec.md §x`.** The master's numbering has shifted several +times and silently invalidated references — that is why the `ARCH-nnn` registry +exists. Cross-cutting decision → `ARCH-nnn`. Domain detail → canonical filename ++ named anchor. Roadmap → `engine-roadmap.md` + phase id. History → the relevant +archive. + +Process and conventions: `engine-development-workflow.md`, +`engine-zig-conventions.md`, `engine-audit-checklist.md`, +`engine-directory-structure.md`, `engine-terminologie.md`, `spec-changelog.md` + +Phases: `engine-roadmap.md`, `engine-phase-minus-1-archive.md`, +`engine-phase-0-criteria.md`, `engine-phase-0-plan.md`, +`engine-phase-1-criteria.md`, `engine-phase-1-plan.md`, +`engine-phase-2-criteria.md` + +Tier 0 and foundation: `engine-tier-interfaces.md`, `engine-c-api.md`, +`engine-c-bindings.md`, `engine-ecs-internals.md`, `engine-ipc.md`, +`engine-platform.md`, `engine-coordinate-system.md`, `engine-units.md`, +`engine-simd.md`, `engine-layout.md` + +Tier 1 owner documents: `engine-render.md`, `engine-physics-forge.md`, +`engine-physics-forge-2d.md`, `engine-audio-pulse.md`, `engine-ai-cortex.md`, +`engine-animation-kinesis.md`, `engine-vfx-ember.md`, +`engine-networking-relay.md`, `engine-ui.md`, `engine-tools-editor.md`, +`engine-sequencer.md`, `engine-sprite.md`, `engine-input-system.md`, +`engine-debug.md`, `engine-media.md`, `engine-asset-pipeline.md`, +`engine-synapse.md`, `engine-conduit.md`, `engine-git-integration.md` + +Etch — 17 files: `etch-grammar.md`, `etch-reference-part1.md`, +`etch-reference-part2.md`, `etch-reference-part3.md`, `etch-parser.md`, +`etch-ast-ir.md`, `etch-bytecode.md`, `etch-resolver-types.md`, +`etch-diagnostics.md`, `etch-stdlib.md`, `etch-memory-model.md`, +`etch-abi-zig.md`, `etch-validation-ecs.md`, `etch-style-guide.md`, +`etch-language-server.md`, `etch-visual-scripting.md`, `etch-crdt-ops.md` + +Cross-cutting topics: `engine-scene-serialization.md`, +`engine-gameplay-systems.md`, `engine-movement.md`, `engine-vr-ar.md`, +`engine-collaboration.md`, `engine-compression-zlc.md`, +`engine-motion-design.md`, `engine-project-settings.md`, +`engine-color-picker.md`, `engine-mach-reference.md` + +Templates and mockups: `brief-milestone_template.md`, +`prompt-claude-code_template.md`, `prompt-editor-mockups.md` + +**Never guess a spec filename.** The list above is the whole corpus; if a spec +named in a brief or in conversation is not in it, stop and ask Guy. +``` + +## Execution steps + +Each `Ei` ends at a **STOP point**: push, then halt and await review + GO. Grep cannot verify that an `ARCH-nnn` matches the subject of the sentence it lands in; that check is a human read of the diff. + +- **E1 — Source, tools, example.** The 14 files of Scope IN §1. Includes the three `§4` → `ARCH-004` corrections and the `§26.1` dangling pointer. → **STOP / review / GO** +- **E2 — `CLAUDE.md`.** The three in-line references plus the *Quick links* rewrite. → **STOP / review / GO** +- **E3 — Closed briefs and validation record.** The 22 briefs plus `validation/s5-go-nogo.md`, references only. → **STOP / review / GO** +- **E4 — Final verification.** Acceptance greps, `zig build lint`, `zig fmt --check src tests build.zig`, `zig build test`, `zig build test-forge-3d -Dphysics_f64=true`. Open the PR. + +Rationale for the split: E1 carries the semantic corrections and must be read closely; E3 is the largest volume but the lowest risk and the strictest constraint (references only); separating them keeps each review batch tractable. + +## Acceptance criteria + +1. `grep -rn "engine-spec.md" --include=*.zig --include=*.md . | grep "§"` → zero lines. +2. `grep -rnE "engine-spec\.md.{0,6}(section|Section) [0-9]" --include=*.zig --include=*.md .` → zero lines (the alternative spelling of the same fault). +3. Every `ARCH-nnn` introduced lies in `[ARCH-001, ARCH-028]` and its title, read in `engine-invariants.md` §2, matches the subject of the sentence it lands in. Numbering is append-only; no identifier outside that range exists. +4. Every KB filename introduced is one of the 75 listed in the rewritten *Quick links* section. No invented filename. +5. `git diff` contains only comment lines (`//`, `///`, `//!`) and markdown. No `.zig` statement, declaration or signature changed. +6. The diffs of the 22 briefs and of `validation/s5-go-nogo.md` contain references only — no figure, measurement, date, verdict or acceptance line. +7. `zig build lint` green, `zig fmt --check src tests build.zig` green, `zig build test` green, `zig build test-forge-3d -Dphysics_f64=true` green. +8. Any site that does not fall under the substitution table is raised at the STOP point **before** being substituted — never resolved by guessing. + +## Out of scope (explicit, not debt) + +- The `etch-diagnostics.md` registry divergence — `E0304`, `E0305`, `E0306`, `E0307` absent from the whole corpus, `E0910` and `E0001` absent from the owner registry while present elsewhere. That is paid on the KB side by Claude.ai, not in the repository. +- `src/modules/audio/` → `src/modules/pulse/` (`engine-directory-structure.md` §9.1 and `engine-zig-conventions.md` both name `pulse/`; `forge/` sets the precedent that the directory carries the module name, not the `weld.toml` slot). A rename touching `build.zig` and imports is not a comment-only chore. +- `CLAUDE.md` § *Current state* being stale by one milestone after every merge, and the two dead entries in § *Open / deferred decisions* (Codeberg, retired in July 2026 per `engine-development-workflow.md` §7.2; and the `engine-zig-conventions.md` `solvers_{2d,3d}` reconciliation, now paid). Same file, different fault class — owned by the M1.1.11.1 CLAUDE.md patch. +- The `-Dphysics_f64=true` CI leg, absent from `.github/workflows/` across twelve forge milestones. Owned by whoever next opens `ci.yml`. +- The residual wall-clock latency assertions (`tests/ipc/handshake.zig:135` and four others), same class as the four removed at M1.1.9. Owned by the next milestone that opens `tests/ipc/`. From 31bff8e80596a592b0b81e9efff1d72f3a334078 Mon Sep 17 00:00:00 2001 From: Guy Senpai Date: Thu, 30 Jul 2026 22:35:56 +0200 Subject: [PATCH 02/12] docs: retarget spec references in source, tools and the example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Repair the 18 `engine-spec.md §x` pointers carried by the 14 source, tool and example files, under the referencing policy of engine-invariants.md §1.2. Comment lines only — no identifier, no signature, no test touched. Cross-cutting decisions become ARCH ids: - §4 → ARCH-004 on the three registry.zig POD-invariant sites. §4 is "Système de plugins"; the POD/SoA invariant lived in §2.1, so these three were wrong-section references and are corrected, not merely repointed. - §2.9 → ARCH-006 (resources/api.zig, resources/root.zig, comptime_query.zig, rtti/type_info.zig). - §2.3 → ARCH-005 (chunk.zig), §2.5 → ARCH-007 (rtti/type_info.zig), §3.5 → ARCH-017 (scene/loader.zig, scene/root.zig, foundation/root.zig, forge_3d/root.zig, examples/triangle/build.zig). Domain detail becomes the owner filename: - §19.1 → engine-scene-serialization.md (scene/loader.zig), - §26.1 → engine-platform.md (etch/test_runner.zig, tools/etch_test/main.zig) — the one dangling pointer, §26 dissolved, - §3.1 → engine-zig-conventions.md, root-file section (rtti/root.zig). Two sites cite content the registry explicitly disclaims as detail — the 16 KiB chunk dimension and the resource lifecycle-tag table — and carry `detail engine-ecs-internals.md` alongside the ARCH id. --- examples/triangle/build.zig | 2 +- src/core/ecs/chunk.zig | 3 ++- src/core/ecs/comptime_query.zig | 2 +- src/core/ecs/registry.zig | 6 +++--- src/core/resources/api.zig | 2 +- src/core/resources/root.zig | 4 ++-- src/core/rtti/root.zig | 3 ++- src/core/rtti/type_info.zig | 8 ++++---- src/core/scene/loader.zig | 11 ++++++----- src/core/scene/root.zig | 2 +- src/etch/test_runner.zig | 6 +++--- src/foundation/root.zig | 2 +- src/modules/forge/forge_3d/root.zig | 2 +- tools/etch_test/main.zig | 2 +- 14 files changed, 29 insertions(+), 26 deletions(-) diff --git a/examples/triangle/build.zig b/examples/triangle/build.zig index 2d6c967..1aa5df0 100644 --- a/examples/triangle/build.zig +++ b/examples/triangle/build.zig @@ -12,7 +12,7 @@ pub fn build(b: *std.Build) void { // Dependency on the Weld engine (local path in the Phase 0 monolithic // repo). Phase 5+: potentially url + hash if separable extraction - // is validated (cf. engine-spec.md §3.5). + // is validated (cf. ARCH-017). const weld = b.dependency("weld", .{ .target = target, .optimize = optimize, diff --git a/src/core/ecs/chunk.zig b/src/core/ecs/chunk.zig index 08a6899..30ca5b0 100644 --- a/src/core/ecs/chunk.zig +++ b/src/core/ecs/chunk.zig @@ -53,7 +53,8 @@ const EntityId = entity_mod.EntityId; const Tick = tick_mod.Tick; /// Total chunk size — locked to 16 KiB to fit comfortably in L1D on modern -/// x86-64, Apple Silicon, and ARM Cortex CPUs (cf. `engine-spec.md` §2.3). +/// x86-64, Apple Silicon, and ARM Cortex CPUs (cf. `ARCH-005`; detail +/// `engine-ecs-internals.md`). pub const ChunkSize: usize = 16 * 1024; /// Required alignment of the chunk and of every SoA column within it. diff --git a/src/core/ecs/comptime_query.zig b/src/core/ecs/comptime_query.zig index 403fe7d..7a554fd 100644 --- a/src/core/ecs/comptime_query.zig +++ b/src/core/ecs/comptime_query.zig @@ -103,7 +103,7 @@ pub fn ComptimeQuery(comptime tuple: anytype) type { while (self.arch_idx < self.world.archetypes.items.len) : (self.arch_idx += 1) { const arch = self.world.archetypes.items[self.arch_idx]; // M0.2 / E3 — singleton resources are invisible to - // user queries (cf. `engine-spec.md` §2.9). + // user queries (cf. `ARCH-006`). if (arch.is_singleton) continue; var all_present = true; for (self.comp_ids) |cid| { diff --git a/src/core/ecs/registry.zig b/src/core/ecs/registry.zig index 63ced6a..5055855 100644 --- a/src/core/ecs/registry.zig +++ b/src/core/ecs/registry.zig @@ -49,7 +49,7 @@ pub const FieldKind = enum { /// `StringSlot`). **Resource-only by construction** (M1.0.3): the Etch /// validator rejects `string` on `component` and `fieldKindFromTypeName` /// only emits this kind for the `.resource` origin, so no component can ever - /// carry it — the component SoA/POD invariant (`engine-spec.md` §4) is + /// carry it — the component SoA/POD invariant (`ARCH-004`) is /// untouched. Tier-0 stays string-agnostic: it stores/copies the 16 raw /// slot bytes; the Etch runtime owns the pointed-to bytes' lifetime. string_, @@ -62,7 +62,7 @@ pub const FieldKind = enum { enum_, /// An `Entity` field slot: an `EntityId` (`packed struct(u64)`, 8 bytes, /// 8-aligned). POD — no heap, no teardown — so the component SoA/POD invariant - /// (`engine-spec.md` §4) is untouched. **Component-only by construction** + /// (`ARCH-004`) is untouched. **Component-only by construction** /// (M1.0.6 D-A): the exact mirror of `.string_`/`.enum_` (resource-only) — /// `fieldKindFromTypeName` emits `.entity_` only for the `.component` origin. /// An unassigned / dangling slot holds `EntityId.dead` (all-ones); at scene @@ -74,7 +74,7 @@ pub const FieldKind = enum { /// holding the persistent-heap pointer of the owned container block. Like /// `.string_`, **resource-only by construction** — the Etch validator gates /// collection fields to resources, so no component SoA slot ever carries one - /// (the POD invariant, `engine-spec.md` §4, is untouched). Tier 0 stores/ + /// (the POD invariant, `ARCH-004`, is untouched). Tier 0 stores/ /// copies the 8 raw slot bytes; the Etch runtime owns the container's lifetime. array_, /// A map field slot (`[K: V]`, M1.0.17). Same 8-byte `CollectionSlot` diff --git a/src/core/resources/api.zig b/src/core/resources/api.zig index ab21a67..72504d9 100644 --- a/src/core/resources/api.zig +++ b/src/core/resources/api.zig @@ -2,7 +2,7 @@ //! //! Resources are singleton instances of POD types — exactly one //! value of each resource type lives in the world at any given -//! time (cf. `engine-spec.md` §2.9). The implementation routes +//! time (cf. `ARCH-006`). The implementation routes //! through the ECS dynamic archetype path: each `setResource(T)` //! spawns a dedicated entity holding the component `T` plus a //! `ResourceMarker` marker. The marker keeps the resource's diff --git a/src/core/resources/root.zig b/src/core/resources/root.zig index f6eb612..9857fe6 100644 --- a/src/core/resources/root.zig +++ b/src/core/resources/root.zig @@ -3,8 +3,8 @@ //! Public surface of the M0.2 / E3 resource subsystem. //! //! Resources are singleton-entity components — exactly one value of -//! each resource type lives in the world (cf. `engine-spec.md` -//! §2.9). Wired into the ECS via the dynamic archetype path: +//! each resource type lives in the world (cf. `ARCH-006`). +//! Wired into the ECS via the dynamic archetype path: //! `setResource(world, gpa, value)` spawns a dedicated entity in a //! singleton-flagged archetype, `getResource` / `getResourceMut` //! route through the existing component access machinery, and diff --git a/src/core/rtti/root.zig b/src/core/rtti/root.zig index 24d25fe..5273f2c 100644 --- a/src/core/rtti/root.zig +++ b/src/core/rtti/root.zig @@ -7,7 +7,8 @@ //! standalone surface without any metier consumer wired yet (the S6 //! IPC swap is E2, resources are E3, events are E4). //! -//! Re-exports follow the `engine-spec.md` §3.1 Tier 0 convention: +//! Re-exports follow the Tier 0 convention of `engine-zig-conventions.md` +//! § "Fichier racine : `root.zig` (module) vs `main.zig` (exécutable)": //! flat surface (`rtti.Registry`, `rtti.TypeInfo`, …) with sub-module //! aliases (`rtti.type_info`, `rtti.hash`, …) for tests and internal //! consumers that need to address private symbols. diff --git a/src/core/rtti/type_info.zig b/src/core/rtti/type_info.zig index 44c68d6..b455e8e 100644 --- a/src/core/rtti/type_info.zig +++ b/src/core/rtti/type_info.zig @@ -13,7 +13,7 @@ //! E2, resources are E3, events are E4 — those land on top of this //! file without changing its public surface. //! -//! See `engine-spec.md` §2.5 and `briefs/M0.2-rtti-resources-events-bindgen.md`. +//! See `ARCH-007` and `briefs/M0.2-rtti-resources-events-bindgen.md`. const std = @import("std"); @@ -45,9 +45,9 @@ pub const Category = enum(u8) { /// FROZEN — see engine-phase-0-criteria.md C0.5 (M0.2) /// Lifecycle hint for resources. Drives the serialization / -/// replication policy (cf. `engine-spec.md` §2.9 table). Only carries -/// meaning when `TypeInfo.category == .resource`; `null` for the other -/// categories. +/// replication policy (cf. `ARCH-006`; detail `engine-ecs-internals.md`). +/// Only carries meaning when `TypeInfo.category == .resource`; `null` for +/// the other categories. pub const Lifecycle = enum(u8) { /// `@config` — serialized in scene files, not in saves, not /// replicated. diff --git a/src/core/scene/loader.zig b/src/core/scene/loader.zig index bdb1988..02a8d87 100644 --- a/src/core/scene/loader.zig +++ b/src/core/scene/loader.zig @@ -10,7 +10,7 @@ //! `world.addResource`). //! //! Tier discipline: imports `weld_core` internals only — never `weld_etch` -//! (`engine-spec.md` §3.5). The cook driver's Etch coupling lives in +//! (`ARCH-017`). The cook driver's Etch coupling lives in //! `src/etch/scene_cook.zig`; the loader consumes only the neutral byte image. //! //! ## Stages (gate-split, see `briefs/M1.0.5-scene-load.md`) @@ -711,10 +711,11 @@ pub fn runtimeDeactivate(world: *World, gpa: std.mem.Allocator, entity: EntityId /// commit. Each touched resource is recorded in `journal` so the load is /// transactional (D2). An empty string keeps the zeroed slot (`ptr == 0`). /// -/// Scene resources are *injected into the resource map at load* (`engine-spec.md` -/// §19.1): the scene value is authoritative and overrides a value the running -/// program already installed (e.g. a declared resource's defaults) rather than -/// erroring; the overridden value's old string blocks are decreffed at commit. +/// Scene resources are *injected into the resource map at load* +/// (`engine-scene-serialization.md`): the scene value is authoritative and +/// overrides a value the running program already installed (e.g. a declared +/// resource's defaults) rather than erroring; the overridden value's old +/// string blocks are decreffed at commit. fn loadResources( world: *World, gpa: std.mem.Allocator, diff --git a/src/core/scene/root.zig b/src/core/scene/root.zig index ca4f3cb..5e96f2c 100644 --- a/src/core/scene/root.zig +++ b/src/core/scene/root.zig @@ -5,7 +5,7 @@ //! verbatim by the M1.0.5 loader). //! //! **Imports `weld_core` internals only — never `weld_etch`** (tier discipline, -//! `engine-spec.md` §3.5 / the M1.0.4 brief Notes). The Etch coupling +//! `ARCH-017` / the M1.0.4 brief Notes). The Etch coupling //! (descriptors, const-eval, `writeValueAsBytes`) lives in //! `src/etch/scene_cook.zig`, which consumes this surface. diff --git a/src/etch/test_runner.zig b/src/etch/test_runner.zig index 4b7db66..1a475fd 100644 --- a/src/etch/test_runner.zig +++ b/src/etch/test_runner.zig @@ -17,9 +17,9 @@ //! builtins underneath it). //! //! `RunReport` OWNS its strings (an internal arena); the caller need only keep -//! `ast` alive for the duration of `run`. The `weld test` CLI (`engine-spec.md` -//! §26.1) will consume this same library — the `etch_test` shim (E5) is its -//! Phase-1 driver. +//! `ast` alive for the duration of `run`. The `weld test` CLI +//! (`engine-platform.md`) will consume this same library — the `etch_test` +//! shim (E5) is its Phase-1 driver. const std = @import("std"); const weld_core = @import("weld_core"); diff --git a/src/foundation/root.zig b/src/foundation/root.zig index 6dda36a..6fd515b 100644 --- a/src/foundation/root.zig +++ b/src/foundation/root.zig @@ -1,6 +1,6 @@ //! Foundation — transversal sibling submodules consumed across the engine. //! -//! Per `engine-spec.md` §3.5 and `engine-simd.md` §4, `math` and `simd` are +//! Per `ARCH-017` and `engine-simd.md` §4, `math` and `simd` are //! sibling submodules with no mutual dependency. M0.6 shipped `simd` (the //! batched-kernel module); M1.1.0 adds `math` — its first consumer is Forge 3D. diff --git a/src/modules/forge/forge_3d/root.zig b/src/modules/forge/forge_3d/root.zig index 76a3fa8..87cf164 100644 --- a/src/modules/forge/forge_3d/root.zig +++ b/src/modules/forge/forge_3d/root.zig @@ -1,5 +1,5 @@ //! `forge_3d` — the native Zig 3D physics solver (Tier 1, in-tree per -//! `engine-spec.md` §3.5). M1.1.0 laid the foundations: the `Real` scalar, the +//! `ARCH-017`). M1.1.0 laid the foundations: the `Real` scalar, the //! `ShapeStore`, per-body `MotionProperties` with analytic inertia, and the SoA //! `BodyManager`. M1.1.1 added the shared `pipeline/broadphase.zig` (a dynamic //! multi-layer AABB tree — BVH); M1.1.2 added `pipeline/narrowphase/` diff --git a/tools/etch_test/main.zig b/tools/etch_test/main.zig index f5267c3..2fb6e2a 100644 --- a/tools/etch_test/main.zig +++ b/tools/etch_test/main.zig @@ -1,7 +1,7 @@ //! `etch_test` — thin CLI shim over the M1.0.15 Etch test runner //! (`weld_etch.test_runner`). The shim owns arg parsing + file I/O + report //! printing only; parse + type-check + run all live in the library, which -//! `weld test` (`engine-spec.md §26.1`) will consume through the same entry. +//! `weld test` (`engine-platform.md`) will consume through the same entry. //! //! CLI: //! etch_test [ ...] From e4a605c652b1489069c22cf3064e57dd2de6fc64 Mon Sep 17 00:00:00 2001 From: Guy Senpai Date: Thu, 30 Jul 2026 23:48:05 +0200 Subject: [PATCH 03/12] docs: retarget the three references outside the .zig and .md set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The chore's own survey greped `--include=*.zig --include=*.md`, so three sites carrying the same fault were invisible to it and would have survived a green acceptance grep: - examples/triangle/build.zig.zon:8 §3.5 → ARCH-017 - bench/fixtures/synth_100/build.zig.zon:8 §3.5 → ARCH-017 - tests/etch/corpus/invalid/E0502_annotation_misapplied.etch:2 §2.9 → ARCH-006 Comment lines only. The fixture keeps its line count, so the E0502 span is unchanged; the corpus facade asserts the code alone either way. Fix-as-you-go on the first: "Phase 0 monolithique" → "monolithic". The language-normalization pass missed it through the same extension filter, and the line is being touched here anyway. --- bench/fixtures/synth_100/build.zig.zon | 2 +- examples/triangle/build.zig.zon | 2 +- tests/etch/corpus/invalid/E0502_annotation_misapplied.etch | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bench/fixtures/synth_100/build.zig.zon b/bench/fixtures/synth_100/build.zig.zon index d803c9b..83071be 100644 --- a/bench/fixtures/synth_100/build.zig.zon +++ b/bench/fixtures/synth_100/build.zig.zon @@ -5,7 +5,7 @@ .minimum_zig_version = "0.16.0", .dependencies = .{ .weld = .{ - // Path local — Phase 0 monolithic repo (cf. engine-spec.md §3.5), + // Path local — Phase 0 monolithic repo (cf. ARCH-017), // three levels up from bench/fixtures/synth_100/. .path = "../../..", }, diff --git a/examples/triangle/build.zig.zon b/examples/triangle/build.zig.zon index fcf6eb3..44aaf01 100644 --- a/examples/triangle/build.zig.zon +++ b/examples/triangle/build.zig.zon @@ -5,7 +5,7 @@ .minimum_zig_version = "0.16.0", .dependencies = .{ .weld = .{ - // Path local — Phase 0 monolithique (cf. engine-spec.md §3.5). + // Path local — Phase 0 monolithic (cf. ARCH-017). .path = "../..", }, }, diff --git a/tests/etch/corpus/invalid/E0502_annotation_misapplied.etch b/tests/etch/corpus/invalid/E0502_annotation_misapplied.etch index 33ee157..72d753f 100644 --- a/tests/etch/corpus/invalid/E0502_annotation_misapplied.etch +++ b/tests/etch/corpus/invalid/E0502_annotation_misapplied.etch @@ -1,5 +1,5 @@ // `@config` is a resource lifecycle annotation (serialized in scene files, -// not in saves — cf. RTTI `Lifecycle`, `engine-spec.md` §2.9). Applied to a +// not in saves — cf. RTTI `Lifecycle`, `ARCH-006`). Applied to a // component it must surface E0502 AnnotationMisapplied // (M0.8 D-S3-annot-applicability, `etch-resolver-types.md` §13.2). @config From 88c2b1c35801e374dc7d4d5963b652eacda707e4 Mon Sep 17 00:00:00 2001 From: Guy Senpai Date: Thu, 30 Jul 2026 23:48:16 +0200 Subject: [PATCH 04/12] docs: reconcile the spec references and quick links of CLAUDE.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three in-line references repaired and the whole § Quick links spec replaced by the text the chore brief supplies verbatim. - §26.1 → engine-platform.md (M1.0.15 boundary entry, the dangling pointer; §26 was dissolved). - §1.6 → ARCH-024; detail engine-c-bindings.md, and the closing "derogation in engine-spec.md" of the same rule goes to ARCH-024 and engine-c-bindings.md — a derogation is an amendment to the invariant. - The "top-level architecture, modules, roadmap (§22)" line is absorbed by the rewrite: the master carries no roadmap. The section listed 45 of the 75 corpus files while instructing the reader to stop and ask Guy for anything absent from it — a 40 % incomplete list generating unjustified stops. It now lists the corpus in reading order (corpus map → invariants → owner → constitution) and carries the referencing policy itself. Verified: the 75 filenames it lists are a bijection with the whitelist of engine-audit-checklist.md §4.1 — nothing invented, nothing missing. --- CLAUDE.md | 105 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 64 insertions(+), 41 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 983ef41..62ec1bd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -117,7 +117,7 @@ Hotfix milestones are merged to `main` without a tag (Guy decision, - **M1.0.12 scope boundary (concurrency algebra)** — the four constructs (`race`/`sync`/`branch`/`spawn { }`) parse, type-check, and execute as CHILD TASKS in the interpreter's pointer-stable monotonic pool (heap records, husk parking, no slot reuse — a pool index IS the Phase-1 `TaskHandle`, no generations), driven by-origin at the creating rule's position in creation order. **Recorded deviation (STOP round-trip 2026-07-02, `etch-resolver-types.md` §9.2 revision 2):** the brief's "calls inside the four construct bodies count as consumed launch sites" is SUPERSEDED — `await` is the SOLE call-grain consumer of the `{async}` effect (`E0905` applies recursively inside the construct bodies; the constructs relocate the suspension into a child task, they do not replace the `await`). Return asymmetry (Guy's ruling 2026-07-02): `return` legal only in a `race` branch (winner-return re-raised at the race site); `sync`/`branch`/`spawn` bodies reject it (`E0906`). Documented one-tick construct latency: a race/sync parent precedes its children in pool-creation order, so it resumes the tick AFTER its wake fires — which guarantees losers are canceled before their own wakes can fire (zero-admitted constructs have no latency). Cancellation is NON-transitive (Phase 1, `etch-bytecode.md` §9.5). Failed tasks (uncaught `throw` / runtime failure) park `.canceled` — never a race winner, never block a `sync` join, `await`ing them fails loud (§9.8 amended). Fix-as-you-go: `return await ` dropped its return at resume (M1.0.11 gap) — fixed. **Out (later):** `wait_unscaled` + timers (M1.0.13), `entity_event` (M1.0.14), entity-bound `async rule`, Phase-2 bytecode lowering (`etch-bytecode.md` §9.4), transitive cancellation (Phase 2+), task-pool slot reuse / generations (Phase-2 refcounted model), value-producing spawn bodies (no EBNF v0.6 value channel). - **M1.0.13 scope boundary (time subsystem and timers)** — the three builtin time resources (`GameTime`/`UnscaledTime`/`RealTime`) are auto-registered from the single `pub const builtin_resources` descriptor table in `types.zig` (the `TagSet` injection-point precedent — no `.etch` prelude, no separate module) and resolve AMBIENTLY (`get(GameTime).dt` needs no `when resource` clause — the spec examples read them without one). `await wait` re-plumbed onto scaled game time and `await wait_unscaled` onto unscaled time with **byte-identical wake ticks at `time_scale = 1`** — realized by the E5 recorded deviation: the two internal clock accumulators advance in TICK units (game `+= time_scale`, 0 under `paused`; unscaled `+= 1`; deadlines `clock + round(secs * 60)`), seconds derived only at resource publication (exact integer f64 sums at `scale = 1`; a seconds accumulator would accrue rounding that can shift a wake by one tick). Both waits KEEP the M1.0.11 literal-only Duration restriction; only the timer family evaluates a full `Duration` expression. The timer registry mirrors the M1.0.12 task-pool discipline (heap records, monotone pointer-stable, husk parking, `TimerHandle` = bare index, no generations) but is a DISTINCT mechanism — a timer is not a task: `cancel()` only, not awaitable, body is a synchronous context (E0901 on `await`/async calls inside). Timer callbacks fire at the head of `stepOnce` (after the clock advance and event-store clear, before rule dispatch) in registration order; `every` re-arms at a fixed period (no drift correction Phase 1). `Value` gained `timer_handle` + `duration` (`value.zig` — outside the brief's per-file list, justified: `Value.timer_handle` is the E6 deliverable and the `task_handle` precedent lives there). **Out (later, not debt):** `quantize` (stays reserved; needs the Sequencer/Pulse beat/bar clock — later Sequencer-adjacent milestone), `time.*` stdlib sugar (later stdlib milestone; `etch-stdlib.md §20` reconciliation deferred with it), `dt` as an injected rule parameter, per-entity `TimeDilation` / selective pause / `WorldClock` (need the phase scheduler), lifting `wait`'s literal-only restriction, Phase-2 bytecode lowering. - **M1.0.14 scope boundary (entity-scoped events + entity-bound async rules)** — `entity_event` = `global_event` + predicates (same wake path, same per-tick store, producer-before-awaiter order unchanged). Designated-field convention, filter semantics (equality-only, capture-once), and resume-unit are normative in `etch-reference-part1.md §9.4`; the four entity-bound lifecycle rulings in `§9.2`; realization in `§9.12` (Claude.ai KB re-uploads at open + at the E2 amendment). The M1.0.11/12 out-list mentions "entity-bound `async rule` → later" are **superseded** (delivered here — absorbed at the E2 round-trip: the construct is inexpressible end-to-end without an entity-scoped awaiter). String stabilization at the event/filter escape boundaries fixed a PRE-EXISTING observer-side use-after-free (fix-as-you-go, two STOP round-trips). **Out (later, NOT debt):** `future`/`Future` (the last await gap); implicit `event` binding / payload delivery at resume (unit is normative); entity-keyed event index (Phase-2 typed bus); emit-side targeting (grammar v0.6 frozen); non-equality filter predicates; cancel-on-despawn (Phase 2+); `dt`/non-entity rule-parameter injection; Phase-2 bytecode async lowering. -- **M1.0.15 scope boundary (test-runner + identity)** — `test "X" { }` executes end-to-end (parse+validate+register existed since M1.0.8; zero execution surface until now). **Resolves the M1.0.8 open residual** (a string-named `test "X"` sharing the identifier namespace via `registerSymbol` → `test "Foo"`/`component Foo` collision): test names now live in a SEPARATE `test_symbols` table; intra-namespace uniqueness reuses `E0101` with a contextual message (precedent: `collectImplMethods`, E1900 double-usage §24.1) — no new code minted for the duplicate. Semantics normative in `etch-reference-part2.md §32` (Phase-1 realization block, Claude.ai KB re-upload) + `etch-grammar.md §17` (`measure_expr` erratum). Realization: test bodies are SYNC (`await` → `E0901`); FRESH World per test (`bindToWorld`); `test_world()` is **mono-world** (repeated calls return the same handle — the multi-world shape is spec'd but not v0.6); `spawn_with` is an IMMEDIATE spawn (does not borrow the deferred structural-spawn command buffer — that is what lets it return a handle) reusing the shared observer-firing primitive; `measure` is the SOLE wall-clock surface, confined to test bodies (`E0910`) to keep gameplay deterministic. Runner is a library (`src/etch/test_runner.zig`) consumed in-proc by the `etch_test` shim + `zig build test-etch`; the future `weld test` (`engine-spec.md §26.1`) consumes the same library — no CLI-weld scaffolding minted for one subcommand. **Out (later, NOT debt):** multi-world (`test_world()` creating independent worlds); async test bodies (`await` stays `E0901`, spec'd v0.6 restriction — async exercised through rules + `tick(n)`); the `weld` CLI proper; test filtering flags (`--filter`, tag selection — `@only` covers focusing); GPU/session integration-test helpers (`create_test_session`, replay-driven runs — `engine-debug.md §13`, Phase 2+ module surface). **KB reconciliation done here (fix-as-you-go):** `etch-reference-part2.md §32` (dangling `engine-testing.md` ref removed → `engine-debug.md §13`); `engine-debug.md §13` realigned on §32 + grammar (`test_world`/`spawn_with`/`tick(n)`; the `capture_events` helper and a dedicated `integration_test` construct removed — never existed in EBNF v0.6); `etch-grammar.md §17` `measure_expr` erratum + `@tag(.perf)`; `engine-development-workflow.md §4.7` tag-scheme + multi-paragraph tag-message reconciliation. +- **M1.0.15 scope boundary (test-runner + identity)** — `test "X" { }` executes end-to-end (parse+validate+register existed since M1.0.8; zero execution surface until now). **Resolves the M1.0.8 open residual** (a string-named `test "X"` sharing the identifier namespace via `registerSymbol` → `test "Foo"`/`component Foo` collision): test names now live in a SEPARATE `test_symbols` table; intra-namespace uniqueness reuses `E0101` with a contextual message (precedent: `collectImplMethods`, E1900 double-usage §24.1) — no new code minted for the duplicate. Semantics normative in `etch-reference-part2.md §32` (Phase-1 realization block, Claude.ai KB re-upload) + `etch-grammar.md §17` (`measure_expr` erratum). Realization: test bodies are SYNC (`await` → `E0901`); FRESH World per test (`bindToWorld`); `test_world()` is **mono-world** (repeated calls return the same handle — the multi-world shape is spec'd but not v0.6); `spawn_with` is an IMMEDIATE spawn (does not borrow the deferred structural-spawn command buffer — that is what lets it return a handle) reusing the shared observer-firing primitive; `measure` is the SOLE wall-clock surface, confined to test bodies (`E0910`) to keep gameplay deterministic. Runner is a library (`src/etch/test_runner.zig`) consumed in-proc by the `etch_test` shim + `zig build test-etch`; the future `weld test` (`engine-platform.md`) consumes the same library — no CLI-weld scaffolding minted for one subcommand. **Out (later, NOT debt):** multi-world (`test_world()` creating independent worlds); async test bodies (`await` stays `E0901`, spec'd v0.6 restriction — async exercised through rules + `tick(n)`); the `weld` CLI proper; test filtering flags (`--filter`, tag selection — `@only` covers focusing); GPU/session integration-test helpers (`create_test_session`, replay-driven runs — `engine-debug.md §13`, Phase 2+ module surface). **KB reconciliation done here (fix-as-you-go):** `etch-reference-part2.md §32` (dangling `engine-testing.md` ref removed → `engine-debug.md §13`); `engine-debug.md §13` realigned on §32 + grammar (`test_world`/`spawn_with`/`tick(n)`; the `capture_events` helper and a dedicated `integration_test` construct removed — never existed in EBNF v0.6); `etch-grammar.md §17` `measure_expr` erratum + `@tag(.perf)`; `engine-development-workflow.md §4.7` tag-scheme + multi-paragraph tag-message reconciliation. - **M1.0.16 scope boundary (qualified imports + private visibility)** — the write-only `imported_aliases` binding (D-F since M1.0.7) is now consumed: `m.Type` resolves as a type-name at exact parity with the selective import form. **Re-scoped after an E1 blocker round-trip (Recorded deviation):** the original brief anchored acceptance on field-type / struct-literal positions, which do NOT consult imports even for the selective form (a `component` is no valid field type; a component struct-literal is rejected as "not a struct type") — acceptance re-anchored on the type-alias target (the one proven selective-resolution position, M1.0.7), and the grammar addition narrowed to TYPE POSITION ONLY. **Resolves the M1.0.8-deferred `private` items:** `etch-resolver-types.md §10.2` visibility inheritance (found STRUCTURAL — no active code; impls are never exported and a private type is unnameable cross-module via E0107) + `W0902 PrivateTypeInPublicImpl` (public trait impl for a private local type). **Finding (journaled):** of the three `imported_symbols` consult sites, only two (`namedTypeToResolved`, `validateTypeAliases`) are reachable by a `.path` node in type-position-only scope; `checkComponentInstance` is unreachable (component instances parse as bare `TYPE_IDENT`; qualified struct-literals are out of scope) — left intact to avoid unexercisable dead code. **Out (NOT debt):** expression-position qualified access (`m.Type { … }`, `m.func()`, `m.Type.CONST`) — not a proven selective capability, a separate import-usability concern; uppercase associated access `Vec3.UP` (a `TYPE_IDENT . TYPE_IDENT` gap unrelated to aliases); broad `private_in_public` signature leakage (Rust-style — §10.2 literal only); imported-trait-impl W0902 (imported-trait impls do not resolve today — pre-existing orthogonal gap). **KB reconciliation (Claude.ai re-upload):** `etch-grammar.md §2.1` (`qualified_path` production, type position); `etch-resolver-types.md §3.3` (`resolve_path` distinguishes absent E0104 / present-private E0107) + §3.1 note; `etch-diagnostics.md §25` (W0902 added to Phase 1 coverage). - **M1.0.17 scope boundary (resource collection fields)** — `T[]`/`[K: V]`/`Set` execute as `resource` fields in the tree-walker. **Decision A (persistent representation):** distinct persistent `Value` tags mirroring the `.string_run`/`.string_persistent` split (zone at the tag, no runtime discriminant); rejected the "persistent bit on the existing handle". **Decision a (Tier-0 drop registry):** `persistent.zig` exposes `DropFn` + `registerDrop`; the interp registers collection drops at init; `runDrop` never reinterprets a payload as an Etch type (Tier-0 stays Etch-decoupled). **Element bound:** value-POD + `string` + enum, on `resource` ONLY; **`Entity` element REFUSED** (E0222 — cross-reference-table remap not wired in persistent collections); nested collections refused (E0222). **Closures long-life REFUSED (no gap, NOT debt):** recon proved only synchronous same-body closure consumers; escaping is a Phase-2 HIR/bytecode lowering. **Recorded deviations (Claude.ai round-trips):** FROZEN element bound refined to exclude `Entity`; the brief's `.array` kind corrected to `.slice` (`T[]` is `.slice`); `descriptor.zig` NOT touched (resource field layout lives in `interp.compileTypeDecl`, shared with `scene_cook.zig`); **E5 gate added mid-milestone** (scene-cook Etch parity + Tier-0 loader guard — deferring the Etch renderers would re-open Etch after M1.0.x, forbidden); set `for-in` and a `.set_lit` renderer were forward-predicted then refuted by recon. **Out (frontiers, NOT debt):** full persistent-block reconstruction of a collection `resource` instance at scene-**load** = `src/core/scene/loader.zig`, Tier-0 → scene-serialization / asset-pipeline milestone (**M1.6**), never Etch; collection semantics in **codegen** = Phase 2 (`etch-ast-ir.md`). **Open decision (plan):** `for x in set` is type-rejected ("not in the M0.8 minimal subset") — C1.6 gap for a later M1.0.x milestone, or a stdlib feature outside C1.6? Unlocking touches every rule-arena set + the differential corpus, so it stays OUT of M1.0.17. New diagnostic: E0222. - **M1.1.0 scope boundary (Forge 3D foundations)**: `src/interfaces/PhysicsModule.zig` + the `PhysicsModule(Impl)` comptime wrapper + `core.ModuleContext` are DEFERRED to the milestone that wires forge_3d as a stepping module — instantiating the wrapper needs `ModuleContext`, whose spec carries an unresolved Tier-0→Tier-1 reference (`asset_loader: *AssetLoader`), and the repo precedent (RenderModule, C0.5) froze a module-root namespace, not a `src/interfaces/` file: the location ruling belongs to the interface-landing milestone. The day-1 contract is carried by the `api/` descriptor types (mirrored verbatim); when the interface file lands, declarations move there and `api/` re-exports — zero call sites. `Velocity` stays a core component (`api/` re-exports; moving it would invert core→Tier-1). `Mat4` and a math-level `Transform` pose type: excluded, purely additive (first consumers later). Shapes beyond sphere/box/capsule: `error.UnsupportedShape` until their sub-milestones (pre-freeze, additive). Descriptor validation policy (typed errors on degenerate mass/geometry) is a later milestone — M1.1.0 guards the dynamic path with a `mass > 0` debug assert only. Pending KB spec patch on `engine-tier-interfaces.md` §1 (ShapeType set, damping default, `foundation.math`, union-form `ShapeDescriptor`), produced at milestone close. @@ -147,49 +147,72 @@ Hotfix milestones are merged to `main` without a tag (Guy decision, - **Never use** `git commit --no-verify` or `git push --no-verify`. If a hook fails, fix the underlying cause — do not bypass. - **Conventional Commits** mandatory on every commit. Types: `feat`, `fix`, `perf`, `refactor`, `test`, `docs`, `chore`, `breaking`. Optional scope `[a-z0-9-]+`. Optional `!` for breaking change. Description 1–72 chars, lowercase first letter, no trailing period. The `commit-msg` hook enforces this locally; CI rejects offending commits. - **Squash-and-merge** as the default merge strategy on `main`. One milestone = one commit on `main`. -- **No external dependency** beyond the 7 authorized C keepers (cf. `engine-spec.md` §1.6): ONNX Runtime, Opus, Assimp, KTX/Basis Universal, libdatachannel, ACL compressor, HarfBuzz. Plus the standards adapted automatically (Vulkan/Wayland/OpenXR XML) and Apple frameworks where relevant. Anything else requires a dedicated derogation in `engine-spec.md`. +- **No external dependency** beyond the 7 authorized C keepers (cf. `ARCH-024`; detail `engine-c-bindings.md`): ONNX Runtime, Opus, Assimp, KTX/Basis Universal, libdatachannel, ACL compressor, HarfBuzz. Plus the standards adapted automatically (Vulkan/Wayland/OpenXR XML) and Apple frameworks where relevant. Anything else requires a dedicated derogation in `ARCH-024` and `engine-c-bindings.md`. ## Quick links spec -The full specification is in the claude.ai knowledge base. Files referenced by name (no repo path — `spec/` is not in the repo). - -Core docs (must read first for most milestones): - -- `engine-spec.md` — top-level architecture, modules, roadmap (§22) -- `engine-development-workflow.md` — milestone model, brief format, git conventions, review cycle -- `engine-zig-conventions.md` — Zig style for the whole engine (§17 build system, §18 migration policy) -- `engine-phase-0-criteria.md` — measurable exit criteria for Phase 0 - -Module / topic docs available in the knowledge base: - -- Tier 0 / API : `engine-tier-interfaces.md`, `engine-c-api.md` -- Modules Tier 1 : `engine-render.md`, `engine-physics-forge.md`, - `engine-physics-forge-2d.md`, `engine-audio-pulse.md`, - `engine-ai-cortex.md`, `engine-vfx-ember.md`, - `engine-animation-kinesis.md`, `engine-networking-relay.md`, - `engine-ui.md`, `engine-tools-editor.md`, `engine-sequencer.md`, - `engine-sprite.md`, `engine-input-system.md`, `engine-debug.md`, - `engine-media.md`, `engine-asset-pipeline.md` -- Subsystems: `engine-c-bindings.md`, `engine-ipc.md`, - `engine-ecs-internals.md`, `engine-coordinate-system.md`, - `engine-units.md`, `engine-color-picker.md`, - `engine-collaboration.md`, `engine-platform.md`, `engine-vr-ar.md`, - `engine-movement.md`, `engine-motion-design.md`, - `engine-scene-serialization.md`, `engine-gameplay-systems.md`, - `engine-mach-reference.md`, `engine-project-settings.md`, - `engine-directory-structure.md`, `engine-terminologie.md`, - `engine-phase-1-criteria.md` -- Etch : `etch-grammar.md`, `etch-visual-scripting.md` -- Templates : `brief-milestone_template.md`, - `prompt-claude-code_template.md` -- Mockups : `prompt-editor-mockups.md` - -The full set of `engine-*.md`, `etch-*.md`, `weld-*.md`, and template -files lives in the claude.ai project. **Never guess a spec filename.** -If a spec referenced in a brief or in conversation is not in the list -above and you are uncertain of its exact name, stop and ask Guy — do -not invent a plausible-looking filename. Hallucinated filenames cause -friction in subsequent sessions. +The full specification lives in the claude.ai knowledge base — 75 files +(54 `engine-*`, 17 `etch-*`, 4 others), referenced by name (no repo path — +`spec/` is not in the repo). + +Reading order is not free: it follows ownership. + +1. `engine-corpus-map.md` — which file owns which domain (§2) and what spec + status it carries (§1). Entry point of every recon. +2. `engine-invariants.md` — the 28 cross-cutting decisions, citable as + `ARCH-001` … `ARCH-028`. Where every `ARCH-nnn` used in this repo is + defined. +3. the owner document — all detailed normative content for its domain. +4. `engine-spec.md` — constitution only (architecture, ECS, tiers, plugins) + plus the canonical catalogue of the 18 Tier 1 modules. It holds no module + mini-spec and no roadmap. + +**Never cite `engine-spec.md §x`.** The master's numbering has shifted several +times and silently invalidated references — that is why the `ARCH-nnn` registry +exists. Cross-cutting decision → `ARCH-nnn`. Domain detail → canonical filename ++ named anchor. Roadmap → `engine-roadmap.md` + phase id. History → the relevant +archive. + +Process and conventions: `engine-development-workflow.md`, +`engine-zig-conventions.md`, `engine-audit-checklist.md`, +`engine-directory-structure.md`, `engine-terminologie.md`, `spec-changelog.md` + +Phases: `engine-roadmap.md`, `engine-phase-minus-1-archive.md`, +`engine-phase-0-criteria.md`, `engine-phase-0-plan.md`, +`engine-phase-1-criteria.md`, `engine-phase-1-plan.md`, +`engine-phase-2-criteria.md` + +Tier 0 and foundation: `engine-tier-interfaces.md`, `engine-c-api.md`, +`engine-c-bindings.md`, `engine-ecs-internals.md`, `engine-ipc.md`, +`engine-platform.md`, `engine-coordinate-system.md`, `engine-units.md`, +`engine-simd.md`, `engine-layout.md` + +Tier 1 owner documents: `engine-render.md`, `engine-physics-forge.md`, +`engine-physics-forge-2d.md`, `engine-audio-pulse.md`, `engine-ai-cortex.md`, +`engine-animation-kinesis.md`, `engine-vfx-ember.md`, +`engine-networking-relay.md`, `engine-ui.md`, `engine-tools-editor.md`, +`engine-sequencer.md`, `engine-sprite.md`, `engine-input-system.md`, +`engine-debug.md`, `engine-media.md`, `engine-asset-pipeline.md`, +`engine-synapse.md`, `engine-conduit.md`, `engine-git-integration.md` + +Etch — 17 files: `etch-grammar.md`, `etch-reference-part1.md`, +`etch-reference-part2.md`, `etch-reference-part3.md`, `etch-parser.md`, +`etch-ast-ir.md`, `etch-bytecode.md`, `etch-resolver-types.md`, +`etch-diagnostics.md`, `etch-stdlib.md`, `etch-memory-model.md`, +`etch-abi-zig.md`, `etch-validation-ecs.md`, `etch-style-guide.md`, +`etch-language-server.md`, `etch-visual-scripting.md`, `etch-crdt-ops.md` + +Cross-cutting topics: `engine-scene-serialization.md`, +`engine-gameplay-systems.md`, `engine-movement.md`, `engine-vr-ar.md`, +`engine-collaboration.md`, `engine-compression-zlc.md`, +`engine-motion-design.md`, `engine-project-settings.md`, +`engine-color-picker.md`, `engine-mach-reference.md` + +Templates and mockups: `brief-milestone_template.md`, +`prompt-claude-code_template.md`, `prompt-editor-mockups.md` + +**Never guess a spec filename.** The list above is the whole corpus; if a spec +named in a brief or in conversation is not in it, stop and ask Guy. ## Workflow reminder (Claude Code protocol) From 4efe8ad3b681642588ee02ba0f9eee1be2840b38 Mon Sep 17 00:00:00 2001 From: Guy Senpai Date: Fri, 31 Jul 2026 00:07:33 +0200 Subject: [PATCH 05/12] docs: use english word order in the two zon dependency comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "Path local" → "Local path" in examples/triangle/build.zig.zon and bench/fixtures/synth_100/build.zig.zon. French word order, not a spec citation, missed by the language-normalization pass through the same extension filter that hid the references repaired in the previous commit. Both lines were already being touched. --- bench/fixtures/synth_100/build.zig.zon | 2 +- examples/triangle/build.zig.zon | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bench/fixtures/synth_100/build.zig.zon b/bench/fixtures/synth_100/build.zig.zon index 83071be..2ac2a6a 100644 --- a/bench/fixtures/synth_100/build.zig.zon +++ b/bench/fixtures/synth_100/build.zig.zon @@ -5,7 +5,7 @@ .minimum_zig_version = "0.16.0", .dependencies = .{ .weld = .{ - // Path local — Phase 0 monolithic repo (cf. ARCH-017), + // Local path — Phase 0 monolithic repo (cf. ARCH-017), // three levels up from bench/fixtures/synth_100/. .path = "../../..", }, diff --git a/examples/triangle/build.zig.zon b/examples/triangle/build.zig.zon index 44aaf01..1502350 100644 --- a/examples/triangle/build.zig.zon +++ b/examples/triangle/build.zig.zon @@ -5,7 +5,7 @@ .minimum_zig_version = "0.16.0", .dependencies = .{ .weld = .{ - // Path local — Phase 0 monolithic (cf. ARCH-017). + // Local path — Phase 0 monolithic (cf. ARCH-017). .path = "../..", }, }, From feebb78bda0f53ed74e848103a40a9844584e123 Mon Sep 17 00:00:00 2001 From: Guy Senpai Date: Fri, 31 Jul 2026 00:13:04 +0200 Subject: [PATCH 06/12] docs: retarget spec references in the closed briefs and s5 record MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 52 reference repairs over 51 lines, across 21 of the 22 closed briefs and validation/s5-go-nogo.md. References only: verified mechanically that the set of numbers outside the reference tokens is identical line by line, so no figure, measurement, verdict, acceptance line or reading date moved. Cross-cutting decisions → ARCH ids: §2.9 → ARCH-006, §4 → ARCH-004, §3.5 → ARCH-017, §2 → ARCH-005, §1.6 → ARCH-024, §2.7 → ARCH-008, §2.8 → ARCH-011, §1.3 → ARCH-012, §1 → ARCH-001..006, and §3.1 read as "Tier 0 catalog" → ARCH-013 — the same number carried two subjects, and the root-file convention target applies only to the other one. Domain detail → the owner file: §19/§19.1 → engine-scene-serialization.md, §16 → engine-asset-pipeline.md, §5 → engine-render.md, §26.1 → engine-platform.md, §24.10 → etch-reference-part1.md. Roadmap and history split by what the citation designates, never by its number: a spike or the spike list → engine-phase-minus-1-archive.md, a roadmap line → engine-roadmap.md §3. Spike ids stay attached to the archive filename with no section number, since the archive is indexed by spike. S3:158's "§22 Layer 2" resolved by measurement, not by the number: the only "Couche/Layer 2" in the corpus is the parsing layer of the six-layer Etch Text panel, §23.10 of the 27-section master, migrated to engine-tools-editor.md. It takes the named anchor, like the root-file convention, because that file covers the whole editor. 29 lines keep an engine-spec.md citation by design and are enumerated in the E4 report: this chore's own brief, which must quote the form it forbids; the 14 "read " records, which state an act and would otherwise claim a milestone read files that did not exist on its date; the historical prose whose subject is the stale numbers themselves; M0.3:186, which records which files were patched; and the doctrine line of CLAUDE.md. A 30th, S0:258, is held back for review — same act as the 14, different form. --- briefs/M0.0-lint-custom.md | 2 +- briefs/M0.2-rtti-resources-events-bindgen.md | 18 +++++++++--------- briefs/M0.4-renderer-vulkan-forward-and-gal.md | 2 +- briefs/M0.7-ipc-scm-rights-windows-fuzz.md | 2 +- briefs/M0.8-full-grammar-v0.6.md | 4 ++-- briefs/M1.0.13-time-and-timers.md | 2 +- briefs/M1.0.15-test-runner.md | 2 +- briefs/M1.0.3-resource-nonpod-fields.md | 2 +- briefs/M1.0.4-scene-cook.md | 2 +- briefs/M1.0.5-scene-load.md | 6 +++--- briefs/M1.0.6-prefabs-crossrefs-extensions.md | 4 ++-- briefs/M1.1.0-forge-foundations.md | 2 +- briefs/M1.1.5-integration-euler.md | 2 +- briefs/S0-bootstrap.md | 10 +++++----- briefs/S1-mini-ecs.md | 2 +- briefs/S2-window-vulkan-triangle.md | 4 ++-- briefs/S3-etch-parser-subset.md | 2 +- briefs/S4-etch-tree-walking-interpreter.md | 4 ++-- briefs/S5-etch-codegen-zig.md | 14 +++++++------- briefs/S6-ipc-editor-runtime.md | 12 ++++++------ briefs/m0.6-assets.md | 2 +- validation/s5-go-nogo.md | 2 +- 22 files changed, 51 insertions(+), 51 deletions(-) diff --git a/briefs/M0.0-lint-custom.md b/briefs/M0.0-lint-custom.md index b52a2e8..3e929aa 100644 --- a/briefs/M0.0-lint-custom.md +++ b/briefs/M0.0-lint-custom.md @@ -35,7 +35,7 @@ First milestone of Phase 0. M0.0 absorbs two debts inherited from Phase −1: th - **Opt-out annotation `// weld-lint:allow-no-doc`** for trivial re-exports. Added only if real friction emerges — purely additive, zero refactoring cost in deferring (no call sites affected). - **Advanced lint rules** (ECS `extern struct` POD comptime check, strict doc-comment format, type-only imports, ABI stability check on `weld_c_api.api.zig`). Phase 0.1+. - **`bindgen-lint` rules** from `engine-c-bindings.md §9.2` beyond the four covered above (e.g. `AUTO-GENERATED` header check on `*_binding.zig` files themselves, all keepers have a `.api.zig`, no manual `.d.etch`). These require the bindgen pipeline itself, not present at M0.0. -- **Any spec patch outside the three KB files explicitly listed.** Phase −1 retrospective already covered `engine-spec.md §25.3` and `engine-development-workflow.md`. +- **Any spec patch outside the three KB files explicitly listed.** Phase −1 retrospective already covered `engine-roadmap.md` §3 and `engine-development-workflow.md`. - **Any modification of Tier 0 code** (ECS, jobs, IPC, platform). - **Any extension of the `c-api/` or `bindgen/` modules.** - **`spec/` directory addition to the repo** (cf. `engine-development-workflow.md §3.5` — to be re-evaluated separately if friction emerges). diff --git a/briefs/M0.2-rtti-resources-events-bindgen.md b/briefs/M0.2-rtti-resources-events-bindgen.md index cf43c1b..f9d18b6 100644 --- a/briefs/M0.2-rtti-resources-events-bindgen.md +++ b/briefs/M0.2-rtti-resources-events-bindgen.md @@ -22,7 +22,7 @@ M0.2 completes the Tier 0 of the Weld core: runtime reflection (native Weld RTTI - **Native Weld RTTI** in `src/core/rtti/`: comptime registry of components/resources/events/messages with metadata (`type_id`, `type_name`, `size`, `alignment`, `schema_hash`, `fields[]`, `category`, `lifecycle?`). Deterministic hash `xxHash32(@typeName(T))` for `type_id`, `xxHash64` over `(typeName, [(field.name, kind, count, offset)])` for `schema_hash`. Idempotent registration (same `type_id` + same `schema_hash` = silent; same `type_id` + different `schema_hash` = `error.SchemaMismatch`). - **S6 IPC swap**: `src/core/ipc/messages.zig` no longer imports `std.hash.Wyhash` directly; it consumes `core/rtti.computeSchemaHash(T)`. The call-site signature (`schemaHashOf(comptime T: type) u64`) is unchanged. The effective algorithm is strictly equivalent (same hashed tuple, same seed) — bytes emitted on the socket are identical. -- **Resource system** in `src/core/resources/`: singleton entities (consistent with `engine-spec.md` §2.9), API `setResource`/`getResource`/`getResourceMut`/`hasResource`/`removeResource`/`resourceChanged`. `HashMap(TypeId, EntityId)` lookup in `World.singleton_entities`. Exclusion from normal queries via the `is_resource` archetype flag. Tick-based change detection (reuses the M0.1 mechanism). Lifecycle tags (`@config`/`@state`/`@transient`) declarable via RTTI metadata (category `.resource`). +- **Resource system** in `src/core/resources/`: singleton entities (consistent with `ARCH-006`), API `setResource`/`getResource`/`getResourceMut`/`hasResource`/`removeResource`/`resourceChanged`. `HashMap(TypeId, EntityId)` lookup in `World.singleton_entities`. Exclusion from normal queries via the `is_resource` archetype flag. Tick-based change detection (reuses the M0.1 mechanism). Lifecycle tags (`@config`/`@state`/`@transient`) declarable via RTTI metadata (category `.resource`). - **Event system** in `src/core/events/`: one MPMC queue per event type, fixed ring buffer of 1024 entries by default (configurable via the `cap` parameter at registration), lifetimes `tick`/`phase`/`frame`, FIFO ordering per type (no cross-type guarantee), saturation = drop oldest FIFO + increment `drops: atomic.Value(u64)` + log warning if `drops/sec > 10`. Drain at the corresponding boundary, called by the scheduler. - **Unified bindgen** in `tools/bindgen/`: refactor of `tools/vk_gen` + `tools/wayland_gen` into a single generator with a formalized `.api.zig` format, validator, cross-api import resolver, idiomatic Zig emitter with dlopen support for 4 strategies (`dlopen`, `dlopen_loader_pattern`, `framework`, `static_link`). Adapters `vk_xml.zig` and `wayland_xml.zig`. 1:1 port — emitted bindings bit-for-bit identical to those on current `main`. - **Plugin loader skeleton** in `src/core/plugin_loader/`: `loadPlugin`/`unloadPlugin`/`lookupSymbol` wrapper on top of `platform.dynamic_loader`, `WeldAPI` struct with **final signatures** (consistent with `engine-c-api.md` §4), sub-APIs `WeldEcsAPI`/`WeldResourceAPI`/`WeldEventAPI`/`WeldMemoryAPI`/`WeldServiceAPI`/`WeldEditorAPI`/`WeldPlatformAPI` with final signatures and stub implementations returning `WELD_ERR_NOT_IMPLEMENTED`. Load-time validation: `weld_plugin_entry` symbol present, `api_version_min` compatible with `WELD_API_VERSION_MAJOR`. @@ -79,7 +79,7 @@ and waits. No anticipatory work on the next step is allowed while waiting for th **Deliverable.** - Edit `src/core/ipc/messages.zig`: remove the direct `std.hash.Wyhash` import, add the `core/rtti` import, `schemaHashOf(comptime T: type) u64` becomes an alias for `rtti.computeSchemaHash(T)`. Verify that the effective algorithm (hashed `(typeName, fields)` sequence, seed) is strictly equivalent — otherwise, adjust `hash.zig` to guarantee bit-for-bit equivalence. -- Edit `engine-spec.md §25.3` (S6 paragraph on comptime `schema_hash` via `std.hash.Wyhash`): update to reflect the effective swap (without changing the announced semantics — the algorithm stays comptime, deterministic, based on typeName + fields). +- Edit `engine-phase-minus-1-archive.md` (S6 paragraph on comptime `schema_hash` via `std.hash.Wyhash`): update to reflect the effective swap (without changing the announced semantics — the algorithm stays comptime, deterministic, based on typeName + fields). - No modification of `src/core/ipc/protocol.zig` or any other IPC file. **Local acceptance criteria.** @@ -188,7 +188,7 @@ Mandatory, complete reading before any production code is written (Step 2 of the 1. `engine-phase-0-plan.md` — section **M0.2** (canonical scope, deliverables, advanced criteria, absorbed Phase −1 debts, branch, tag). 2. `engine-phase-0-criteria.md` — sections **§ Bench methodology**, **§ Quantified non-regression gates**, **C0.1**, **C0.5**, **C0.10**. -3. `engine-spec.md` — sections **§1.6** (Tier 0/1 catalog, 7 C keepers), **§2.5** (RTTI / component registry), **§2.7** (versioned serialization), **§2.8** (memory model — for the resource context), **§2.9** (singleton-entity resources — acted, non-rediscussable decision), **§3.1** (Tier 0 catalog), **§25.3** (roadmap + S6 `schema_hash` Wyhash paragraph). +3. `engine-invariants.md` — **`ARCH-024`** (Tier 0/1 catalog, 7 C keepers; detail `engine-c-bindings.md`), **`ARCH-007`** (RTTI / component registry), **`ARCH-008`** (versioned serialization), **`ARCH-011`** (memory model — for the resource context), **`ARCH-006`** (singleton-entity resources — acted, non-rediscussable decision), **`ARCH-013`** (Tier 0 catalog); **`engine-roadmap.md` §3** (roadmap) and **`engine-phase-minus-1-archive.md`** (S6 `schema_hash` Wyhash paragraph). 4. `engine-ecs-internals.md` — sections **§5** (tick-based change detection — Resources consume it), **§8** (observers — consumed the RTTI in M0.1, check cross-step integration), **§12** (ECS comparison — Bevy 2026 singleton resources referenced). 5. `engine-tier-interfaces.md` — sections **§0** (principles, ModuleContext), **§1** through **§10** (interfaces already specified — for signature consistency in `WeldAPI`). 6. `engine-c-api.md` — sections **§0** (Tier 1 vs Tier 3), **§2** (fundamental types), **§3** (plugin lifecycle), **§4** (main API table), **§5** (WeldEcsAPI), **§6** (WeldResourceAPI), **§7** (WeldEventAPI), **§8** (WeldServiceAPI). The sections covering WeldMemoryAPI / WeldEditorAPI / WeldPlatformAPI complete the table to be reproduced as signatures. @@ -214,7 +214,7 @@ Mandatory, complete reading before any production code is written (Step 2 of the **E2 — S6 swap.** - `src/core/ipc/messages.zig` — edit — swap `std.hash.Wyhash` → `rtti.computeSchemaHash`. - `tests/core/rtti/ipc_compat_test.zig` — create — bit-for-bit equivalence for 5 known S6 messages. -- `engine-spec.md` — edit — §25.3 S6 paragraph on comptime `schema_hash` via `std.hash.Wyhash`: end-of-paragraph note indicating the effective swap in M0.2. +- `engine-phase-minus-1-archive.md` — edit — S6 paragraph on comptime `schema_hash` via `std.hash.Wyhash`: end-of-paragraph note indicating the effective swap in M0.2. **E3 — Resources.** - `src/core/resources/registry.zig` — create — `ResourceRegistry`. @@ -361,9 +361,9 @@ M0.2 smoke OK **Removal of `tools/vk_gen/` and `tools/wayland_gen/`.** In a separate commit after the effective empty-diff validation. If the intermediate E5 review requests adjustments, keeping the old directories allows a quick rollback. The removal is the last commit of E5 — not the first. -**Cross-doc audit.** The edit of `engine-spec.md §25.3` (S6 swap note) is the only spec modification expected in this milestone. If reading the specs in Step 2 reveals other inconsistencies (for example a reference to `tools/vk_gen/` in a file other than `engine-spec.md` or `engine-c-bindings.md`), that is a light blocker: log it, request a patch via this Claude.ai conversation, do not patch unilaterally. +**Cross-doc audit.** The edit of `engine-phase-minus-1-archive.md` (S6 swap note) is the only spec modification expected in this milestone. If reading the specs in Step 2 reveals other inconsistencies (for example a reference to `tools/vk_gen/` in a file other than `engine-spec.md` or `engine-c-bindings.md`), that is a light blocker: log it, request a patch via this Claude.ai conversation, do not patch unilaterally. -**No new C binding.** M0.2 introduces no keeper. The test stub plugin is pure Zig compiled into a `.so`/`.dll`, not a third-party C binding. The 7 Phase 1+ keepers (`engine-spec.md §1.6`) stay out of scope. +**No new C binding.** M0.2 introduces no keeper. The test stub plugin is pure Zig compiled into a `.so`/`.dll`, not a third-party C binding. The 7 Phase 1+ keepers (`ARCH-024`) stay out of scope. **No Zig version change.** Zig 0.16.x strict (patches accepted, minor forbidden). @@ -449,7 +449,7 @@ M0.2 smoke OK ## Acted deviations - E1 / rename — The brief listed `src/core/rtti.zig` as the "public re-export module" and `src/core/rtti/` as the directory of sub-files (two distinct entries). Initially implemented as-is at commit `dc76dc0`. Renamed to `src/core/rtti/root.zig` to align on the existing codebase convention (`src/core/ecs/root.zig`, `src/core/ecs/*.zig` — no parallel `src/core/ecs.zig`). Verbal decision from Guy 2026-05-22, tracked here. Strictly localized impact: `@import` in `src/core/root.zig` goes from `"rtti.zig"` to `"rtti/root.zig"`; the re-export's internal imports become relative to the directory (`"type_info.zig"` instead of `"rtti/type_info.zig"`). `build.zig` unchanged (the `tests/core/rtti/` tests do not reference the renamed path). CI gates green post-rename. -- **E2-bump** / **path 2 chosen — protocol version bump** (unblock from Guy 2026-05-22). The E2 §1 brief initially required a byte-for-byte equivalence between post-swap `messages.schemaHash` and legacy Wyhash. The blocker encountered (see "Blockers encountered" E2 / divergence) triggered a Claude.ai round-trip. Path 2 (protocol version bump) was chosen over path 1 (local Wyhash helper in `messages.zig`). Justification (verbatim Guy): (a) legacy Wyhash hashed a concatenated string, RTTI E1 hashes a structured tuple via xxHash64 — the two are incompatible AND the latter is better (structured serialization, fewer edge-case collisions); (b) preserving the equivalence via a local Wyhash helper = keeping a legacy algorithm alive just to match a convention with no production value, pure debt to remove later; (c) `engine-ipc.md §5.2` confirms: strictly incompatible versions, no negotiation. Bump = the intended mechanism; (d) guiding principle: "do not preserve a decision merely because we have already acted it. The cost of moving now is almost always lower than the cost of moving later." Accepted consequences: `WELD_IPC_PROTOCOL_VERSION` 1 → 2, breaks handshake compatibility with any prior S6 binary (acceptable — Phase 0, no binary in production). Revised E2 acceptance criterion: removal of the "byte-for-byte vs Wyhash" guard, replaced by RTTI golden values committed in `ipc_compat_test.zig` that detect any future drift. **E2 touch scope extended**: addition of `src/core/ipc/protocol.zig` (bump), `engine-spec.md §25.3` (effective swap note — patches prepared, applied by Guy in the KB in parallel with the commit), `engine-ipc.md §5.2` (effective bump note — likewise). +- **E2-bump** / **path 2 chosen — protocol version bump** (unblock from Guy 2026-05-22). The E2 §1 brief initially required a byte-for-byte equivalence between post-swap `messages.schemaHash` and legacy Wyhash. The blocker encountered (see "Blockers encountered" E2 / divergence) triggered a Claude.ai round-trip. Path 2 (protocol version bump) was chosen over path 1 (local Wyhash helper in `messages.zig`). Justification (verbatim Guy): (a) legacy Wyhash hashed a concatenated string, RTTI E1 hashes a structured tuple via xxHash64 — the two are incompatible AND the latter is better (structured serialization, fewer edge-case collisions); (b) preserving the equivalence via a local Wyhash helper = keeping a legacy algorithm alive just to match a convention with no production value, pure debt to remove later; (c) `engine-ipc.md §5.2` confirms: strictly incompatible versions, no negotiation. Bump = the intended mechanism; (d) guiding principle: "do not preserve a decision merely because we have already acted it. The cost of moving now is almost always lower than the cost of moving later." Accepted consequences: `WELD_IPC_PROTOCOL_VERSION` 1 → 2, breaks handshake compatibility with any prior S6 binary (acceptable — Phase 0, no binary in production). Revised E2 acceptance criterion: removal of the "byte-for-byte vs Wyhash" guard, replaced by RTTI golden values committed in `ipc_compat_test.zig` that detect any future drift. **E2 touch scope extended**: addition of `src/core/ipc/protocol.zig` (bump), `engine-phase-minus-1-archive.md` (effective swap note — patches prepared, applied by Guy in the KB in parallel with the commit), `engine-ipc.md §5.2` (effective bump note — likewise). - **E2-framing-fix** / `src/core/ipc/framing.zig:196` aligned on `protocol.WELD_IPC_PROTOCOL_VERSION` (unblock from Guy 2026-05-22, commit `1d9d186`). Latent S6 bug discovered during the 1 → 2 bump: line 196 hardcoded `@as(u16, 1)` while lines 183 and 199 of the same file already used `protocol.WELD_IPC_PROTOCOL_VERSION`. Substituted by the constant to align on the file's majority pattern — not a change of test intent, a fix of an S6 copy-paste oversight. Extends the E2 touch scope (initially `src/core/ipc/messages.zig` only) to `src/core/ipc/framing.zig` (1 line). ## Blockers encountered @@ -464,7 +464,7 @@ M0.2 smoke OK | `Heartbeat` | `0x32e19d009703d8b1` | `0x9f3fedfefae6683b` | | `LogMessage` | `0x7a828c2be968d129` | `0xa4b62ae89476bd45` | - Per the E2 directive §2 (Guy, 2026-05-22): **mandatory Case 2 blocker, Claude.ai round-trip**. I do **not** touch `src/core/rtti/hash.zig` (commit `dc76dc0`) to reconcile — that would break the consistency of the registry built in E1. The reconciliation, if needed, is done either (a) on the `messages.zig` side via a local helper that re-derives the legacy Wyhash key from the RTTI FieldDesc and applies Wyhash locally, or (b) via an explicit protocol version bump acted in a Claude.ai conversation. No third path. Checkpoint state at the blocking commit: `messages.zig` swap applied (visible on the wire but consistent editor↔runtime), byte-compat assertions failing red in `ipc_compat_test.zig`, no modification of `engine-spec.md §25.3` (the swap is not effective until the reconciliation is settled). **Resolved** by the unblock from Guy 2026-05-22 — path 2 chosen (protocol version bump). See "Acted deviations" §E2-bump. + Per the E2 directive §2 (Guy, 2026-05-22): **mandatory Case 2 blocker, Claude.ai round-trip**. I do **not** touch `src/core/rtti/hash.zig` (commit `dc76dc0`) to reconcile — that would break the consistency of the registry built in E1. The reconciliation, if needed, is done either (a) on the `messages.zig` side via a local helper that re-derives the legacy Wyhash key from the RTTI FieldDesc and applies Wyhash locally, or (b) via an explicit protocol version bump acted in a Claude.ai conversation. No third path. Checkpoint state at the blocking commit: `messages.zig` swap applied (visible on the wire but consistent editor↔runtime), byte-compat assertions failing red in `ipc_compat_test.zig`, no modification of `engine-phase-minus-1-archive.md` (the swap is not effective until the reconciliation is settled). **Resolved** by the unblock from Guy 2026-05-22 — path 2 chosen (protocol version bump). See "Acted deviations" §E2-bump. - **E2 / inline test `framing.zig:196` hardcodes the old version (2026-05-22 11:30)** — After applying path 2 (bump `WELD_IPC_PROTOCOL_VERSION` 1 → 2 in `protocol.zig`), 1 test fails: `src/core/ipc/framing.zig:196` contains `try std.testing.expectEqual(@as(u16, 1), h.version);` — line 196 hardcodes the literal value `1` instead of referring to the constant `protocol.WELD_IPC_PROTOCOL_VERSION` (the rest of the file uses the constant at lines 183, 199, etc.). 244/255 tests pass, 10 skipped, 1 failed. Per the E2 directive §5 ("If a test fails (for example a test that hardcodes the old value of WELD_IPC_PROTOCOL_VERSION or a Wyhash schema_hash), it is a Case 2 blocker — STOP, Claude.ai round-trip. Do not modify any tests/ipc/ test unilaterally."), **Case 2 blocker**. The test is inline in `src/core/ipc/framing.zig`, not in `tests/ipc/`, but the blocker-trigger rule is general ("a test that hardcodes the old value"). I do not apply the fix unilaterally even though the correct pattern (`protocol.WELD_IPC_PROTOCOL_VERSION` instead of `1`) is trivial and aligned with the rest of the file. **Resolved** by the unblock from Guy 2026-05-22 11:38 — explicit authorization to patch line 196. Fix applied in commit `1d9d186`. See "Acted deviations" §E2-framing-fix. @@ -473,7 +473,7 @@ M0.2 smoke OK - **What worked**: - **Tier 0 RTTI delivered clean** (4 files, 25 tests, FROZEN placed). The comptime builder traverses structs without any runtime `@TypeOf`. The component ↔ bytes round-trip test via FieldDesc passes — proof that the metadata is sufficient for fields-only dispatch. - **S6 IPC swap absorbed via protocol bump** (path 2 chosen after a Case 2 blocker). `WELD_IPC_PROTOCOL_VERSION` 1→2, RTTI golden values committed in `ipc_compat_test.zig`. Inherited `tests/ipc/` suite fully green without modification. The latent S6 bug `framing.zig:196` (hardcoding `@as(u16, 1)` instead of the constant) discovered as a by-product and fixed. - - **Resources as singleton entities** (consistent with `engine-spec.md §2.9`). Option (a) chosen (`is_singleton: bool` on Archetype) — 1 byte per archetype, modification localized to 3 sites (Archetype + Query.maybeRescan + ComptimeQuery.next). No conflict with M0.1. + - **Resources as singleton entities** (consistent with `ARCH-006`). Option (a) chosen (`is_singleton: bool` on Archetype) — 1 byte per archetype, modification localized to 3 sites (Archetype + Query.maybeRescan + ComptimeQuery.next). No conflict with M0.1. - **Events MPMC Vyukov-pattern** with atomic slot.seq + cursor epoch invalidation + per-lifetime drain. Scheduler integration at 3 sites (drainAtBoundary `.phase` after each phase + `.tick` + `.frame` at the end of `dispatchFrame`), 3 distinct boundaries even though tick=frame in Phase 0. - **Unified bindgen — "empty diff" mechanical criterion met** (E5). 6 files `tools/{vk_gen,wayland_gen}/` migrated to `tools/bindgen/adapters/{vk_xml,wayland_xml}/` via `git mv`. `zig build bindgen-verify` returns EC=0. The skeleton `core/{api_description,validator,resolver,emitter}.zig` is in place for Phase 1+ without being exercised in M0.2. - **Plugin loader skeleton delivered**: 79 callbacks across 7 sub-APIs (ECS 24 + Resource 8 + Event 6 + Service 2 + Memory 8 + Editor 17 + Platform 14), all `WELD_ERR_NOT_IMPLEMENTED` stubs. Load-time validation: `weld_plugin_entry` symbol present + `api_version_min <= 0`. 3 cross-platform stubs (.so/.dylib/.dll) built by `build.zig`. 18 tests green (6 + 12). diff --git a/briefs/M0.4-renderer-vulkan-forward-and-gal.md b/briefs/M0.4-renderer-vulkan-forward-and-gal.md index 76f7bb8..b29c177 100644 --- a/briefs/M0.4-renderer-vulkan-forward-and-gal.md +++ b/briefs/M0.4-renderer-vulkan-forward-and-gal.md @@ -77,7 +77,7 @@ M0.4 advances criterion C0.3 (minimal Vulkan forward renderer) by designing the 6. `engine-tier-interfaces.md` — the Tier 0 `RenderModule` interface. The GAL is a Tier 1 module that must respect this contract. 7. `engine-development-workflow.md` — §2.2 granularity, §3 brief format, §3.6.1 local cross-doc audit, §4.3 Conventional Commits, §4.5.1 semantic runtime CI validation + manual GPU gate (**first milestone applying it — read carefully**), §4.6 squash commit format (structured long body, 6 mandatory sections), §4.7 tag procedure. 8. `engine-zig-conventions.md` — Zig 0.16.x reminders (std.Io, allocators, Writer/Reader, naming), §14 isolation of `*_c` modules, §16 Weld conventions (POD `extern struct` ECS components). -9. `engine-spec.md` — §3.5 (in-tree as-if-lib, no separable lib extraction), §5 Render Module (GAL + V-Buffer + target pipeline recap), §22.2 S2 § Precisions (multi-GPU selection consistency, `--smoke-test` mode, committed SPIR-V). +9. `ARCH-017` (in-tree as-if-lib, no separable lib extraction), `engine-render.md` — Render Module (GAL + V-Buffer + target pipeline recap), `engine-phase-minus-1-archive.md` S2 § Precisions (multi-GPU selection consistency, `--smoke-test` mode, committed SPIR-V). 10. `engine-platform.md` — §4 Windowing (the GAL creates the Vulkan surface on top of the M0.3 Tier 0 Window interface, check the integration signature), §4 Input (not touched but general consistency). ## Files diff --git a/briefs/M0.7-ipc-scm-rights-windows-fuzz.md b/briefs/M0.7-ipc-scm-rights-windows-fuzz.md index 3ffdab4..e89a235 100644 --- a/briefs/M0.7-ipc-scm-rights-windows-fuzz.md +++ b/briefs/M0.7-ipc-scm-rights-windows-fuzz.md @@ -135,7 +135,7 @@ The milestone is split into **four steps (E1→E4) with a review checkpoint betw ## Notes - **E1→E4 split with checkpoints.** At the end of each Ei, signal Guy for review before continuing. Hard dependencies: E2 before E4 (`ProjectSaved` anchors `last_clean_line`); E3 before E4 (`crash_recovery`/`fuzz_1h` criteria must be green on Windows). E1 is independent of the others (can open). -- **The SCM_RIGHTS pivot is acted, not to be reopened.** Decided at S6 closure (cf. `engine-spec.md` S6 results). E1 implements it; it does not re-debate the choice. +- **The SCM_RIGHTS pivot is acted, not to be reopened.** Decided at S6 closure (cf. `engine-phase-minus-1-archive.md` S6 results). E1 implements it; it does not re-debate the choice. - **No new Tier 0 surface beyond the pivot.** `ShmRegion.fromFd` and the `ShmRegionsHandoff` message are the minimal materialization of the pivot (the editor keeps the fd, sends it, the runtime mmaps it). `sendWithHandles`/`recvWithHandles` have existed since S6 (G7-validated) — no addition. - **EINTR belongs to M0.5.** Do not re-fix it; only verify the retry holds on `sendmsg`/`recvmsg`/`*WithHandles` once promoted to the primary attach path. - **Minimal binary snapshot (option 1).** `SaveProject` does not write a `.scene.etch`. It persists just enough for replay to reload a reference point. The text writer and full project-settings serialization are out of Phase 0. diff --git a/briefs/M0.8-full-grammar-v0.6.md b/briefs/M0.8-full-grammar-v0.6.md index 5c92c18..dc0caf9 100644 --- a/briefs/M0.8-full-grammar-v0.6.md +++ b/briefs/M0.8-full-grammar-v0.6.md @@ -106,7 +106,7 @@ The work is delivered as **seven vertical stages** (parser→interpreter→codeg 14. `etch-style-guide.md` — Etch style for the reference file. 15. `engine-scene-serialization.md` — `scene`/`prefab` serialization, typed extensions. 16. `engine-ecs-internals.md` — archetype matching, deferred commands, event bus. -17. `engine-spec.md` — S3/S4/S5 results, §25 Etch pipeline. +17. `engine-phase-minus-1-archive.md` — S3/S4/S5 results; `engine-roadmap.md` §3 — Etch pipeline. 18. `engine-phase-0-criteria.md` — C0.2. 19. `engine-development-workflow.md` — brief/commit format, language criterion. 20. `engine-zig-conventions.md` — §13 module rooting (lazy analysis guard), naming, `extern struct` `u8`-not-`bool`. @@ -213,7 +213,7 @@ Read in the brief's "Documents to read first" order. Two docs were read to the s - 2026-06-06 — Step 1 (branch + brief). Branch `phase-0/etch/full-grammar-v0.6` created from up-to-date `main` (in sync with `origin/main`). Brief committed as the first commit, translated to English (see Acted deviations for Guy's authorization). Confirmed the delivered S3/S4/S5 layout against the real tree: `src/etch/` = lexer.zig, token.zig, parser.zig, ast.zig, types.zig, value.zig, interp.zig, ecs_bridge.zig, diagnostics.zig, root.zig + zig_codegen/ — matches the brief's "Files to create or modify" anchoring. - 2026-06-06 14:15 — Step 2 (spec ingestion). All 20 spec docs read in the brief's order. Two read to the scope the brief itself annotates: (a) `engine-spec.md` "(S3/S4/S5, §25)" → read §1–4 architecture + §24 Etch + §25 full roadmap (all S0–S6 spike results, Phase −1 closure, Phases 0–4) + §26–27 in full; §5–§23 (Tier 1 module designs: render/physics/audio/networking/AI/animation/UI/asset/sequencer/etc.) left unread as out-of-M0.8-scope — Level-B construct surfaces are covered by reference-part2 + validation-ecs. (b) `etch-bytecode.md` "read only to bound what is Phase 2" → read §1 + §17 + §18, confirming the whole bytecode VM / opcodes / async state machines are Phase 2 (engine), out of M0.8 scope. Both scopings follow the FROZEN doc-list annotations, not silent skimming; flagged here for review in case Claude.ai wants full cover-to-cover reads of either. -- 2026-06-06 14:15 — Key facts confirmed for scoping the stages: M0.8 backend = tree-walking interpreter (extends S4, `src/etch/interp.zig`) + direct AST→Zig codegen (extends S5, `src/etch/zig_codegen/`), NO HIR, NO VM. Async/generics are parse+interpret only (codegen Phase 2). The 12 debts (5×D-S3, 5×D-S4, 2×D-S5) match the S3/S4/S5 "Dette Phase 0.2" lists in `engine-spec.md §25` and `engine-phase-0-plan.md §M0.8`. The hard CI gate is `engine-zig-conventions.md §13` module rooting (no silently-skipped inline tests) + extern-struct-POD for component codegen. Incremental-compile < 2 s is the one tight gate (~1.9× S5 margin). +- 2026-06-06 14:15 — Key facts confirmed for scoping the stages: M0.8 backend = tree-walking interpreter (extends S4, `src/etch/interp.zig`) + direct AST→Zig codegen (extends S5, `src/etch/zig_codegen/`), NO HIR, NO VM. Async/generics are parse+interpret only (codegen Phase 2). The 12 debts (5×D-S3, 5×D-S4, 2×D-S5) match the S3/S4/S5 "Dette Phase 0.2" lists in `engine-phase-minus-1-archive.md` and `engine-phase-0-plan.md §M0.8`. The hard CI gate is `engine-zig-conventions.md §13` module rooting (no silently-skipped inline tests) + extern-struct-POD for component codegen. Incremental-compile < 2 s is the one tight gate (~1.9× S5 margin). - 2026-06-06 — E1 reconnaissance (parser front). Read the delivered `src/etch/` surface per the brief's "verify in code before scoping each stage": `root.zig` (108), `ast.zig` (791), `parser.zig` (1083). Findings: (1) AST already declares every v0.6 reserved kind (ItemKind/StmtKind/ExprKind/TypeNodeKind) — E1 *produces* the reserved variants + their side slabs/add-helpers, no enum redefinition. (2) Breaking-change target = `parser.ParseResult.diagnostic: ?Diagnostic` (consumed by `root.zig` `parseSource` + 2 tests). Recovery sync-point requires restructuring `Parser.parseFile` (l.194) to catch `ParseError` per top-level item, record the diagnostic, advance to next top-level keyword/EOF, resume. (3) D-S3-annot-field-access located: `parseAnnotationArg` (l.273) routes the ident arg through `continuePostfixAndBinary` (l.708, binary-only) instead of `parsePostfix` (l.751, the `.field`/`.get` chain) — matches the brief's exact fix description. (4) S3 corpus = ~40 `.etch` files under `tests/etch/corpus/{valid,invalid}/`. Still to read before implementing each layer: `lexer.zig`+`token.zig` (D-S3-doccomment, new operator tokens), `types.zig` (D-S3-annot-applicability + D-S3-resource-receiver + type-checker extension), `interp.zig`+`value.zig`+`ecs_bridge.zig` (interpret layer), `zig_codegen/*` (codegen layer). - 2026-06-06 — Reading-scope ruling confirmed by Guy (live decision): the two doc-list-annotated reads are correct as-is, no cover-to-cover required. (1) `engine-spec.md` §5–§23 = Tier 1 module runtime wiring, out of M0.8 scope; Level-B construct surface (parse + validation + IR) is covered by `etch-ast-ir.md §3.5` + `etch-reference-part2` + `etch-validation-ecs`; §1-4/§24/§25/§26-27 reads sufficient. (2) `etch-bytecode.md` §1/§17/§18 = exactly "bound what is Phase 2"; VM/opcodes/async state machines out of M0.8. E1 plan validated unchanged; GO to implement E1 as a single block (breaking interface → expr/control-flow foundations → 5 S3 debts → EBNF harness + tests), then STOP at the Review E1 gate. No sub-decomposition, no E2 start. diff --git a/briefs/M1.0.13-time-and-timers.md b/briefs/M1.0.13-time-and-timers.md index 5c16409..4aed5e9 100644 --- a/briefs/M1.0.13-time-and-timers.md +++ b/briefs/M1.0.13-time-and-timers.md @@ -54,7 +54,7 @@ Executed **gate by gate E1→E6**, in order. Each gate is pushed and reviewed on - **`quantize`** (`quantize_stmt`, `etch-grammar.md §4.3`; `etch-reference-part1.md §9.11`) — depends on a beat/bar musical clock (Sequencer/Pulse) absent from the Phase-1 runtime. `quantize` stays reserved in `non_s3_keywords`; its statement-head fail-loud stays (message re-pointed); `quantize_stmt` stays a payloadless placeholder. Assigned to a later Sequencer-adjacent milestone — a scope boundary, NOT parked debt. - **`time.*` stdlib sugar** (`etch-stdlib.md §20`: `time.dt`/`time.total`/`time.scale`/`time.unscaled_dt`/`time.fixed_dt`, lowering `time.X → get(GameTime|UnscaledTime).Y`) — an additive resolution add-on over the resources this milestone delivers; introducing it later touches no call site here. Later stdlib milestone. (`§20`'s `time.scale`/`time.frame_count` vs the real `time_scale`/`frame` field names is reconciled with the sugar, not now.) -- **`dt` as an injected rule parameter** (`rule tick(entity: Entity, dt: float)`, `engine-spec.md §24.10`) — additive injection over `GameTime.dt`, later milestone. +- **`dt` as an injected rule parameter** (`rule tick(entity: Entity, dt: float)`, `etch-reference-part1.md`) — additive injection over `GameTime.dt`, later milestone. - **Per-entity `TimeDilation`, selective pause (`@pause_group` / `PauseState`), `WorldClock`/day-night** (`engine-gameplay-systems.md`) — depend on the phase scheduler (per-rule dispatch metadata, per-entity `dt` in iteration) which does not exist in the tree-walker. - **Non-literal `Duration` for `await wait` / `await wait_unscaled`** — both keep the M1.0.11 literal-only restriction (non-literal Duration → fail-loud). Only the timer family evaluates a full `Duration` expression (its one-shot scheduling has no suspension reentry). Lifting `wait`'s restriction is a separate additive change, out of scope. - **New diagnostic codes** — none are minted (see § Notes). If a genuinely new validation surfaces mid-gate, STOP for a Claude.ai round-trip before minting `E0908`. diff --git a/briefs/M1.0.15-test-runner.md b/briefs/M1.0.15-test-runner.md index 28479e5..6074198 100644 --- a/briefs/M1.0.15-test-runner.md +++ b/briefs/M1.0.15-test-runner.md @@ -36,7 +36,7 @@ M1.0.8 delivered `test "X" { block }` as parse + symbol registration only: `inte - **Multi-world** (`test_world()` creating additional/independent worlds). The mono-world realization is a documented v0.6 spec restriction (part2 §32 normative block), not parked debt. - **Async test bodies.** `await` in a test body stays `E0901` — a spec'd v0.6 restriction. Async behavior is exercised through rules + `tick(n)`. -- **The `weld` CLI / `weld test` command.** Future consumer of this same library (`engine-spec.md §26.1`); no CLI-weld scaffolding in this milestone beyond the `etch_test` shim. +- **The `weld` CLI / `weld test` command.** Future consumer of this same library (`engine-platform.md`); no CLI-weld scaffolding in this milestone beyond the `etch_test` shim. - **`capture_events` helper and a dedicated `integration_test` construct** — removed from the KB by the M1.0.15 spec reconciliation; do not implement. - **Test filtering CLI flags** (`--filter`, tag selection). Purely additive; `@only` covers focusing in v0.6. - **`measure` outside test bodies** — rejected by design (`E0910`). diff --git a/briefs/M1.0.3-resource-nonpod-fields.md b/briefs/M1.0.3-resource-nonpod-fields.md index d123a4b..fa2423e 100644 --- a/briefs/M1.0.3-resource-nonpod-fields.md +++ b/briefs/M1.0.3-resource-nonpod-fields.md @@ -151,7 +151,7 @@ rule advance(dt: float) when resource GameState { **This is the "Option A alignment (tranche 7)".** The resource `string`/enum rejection in `validateFieldsInDecl` and its guard test are not a bug discovered late — they are deferred work named in the code (the guard-test comment and the `FieldDeclOrigin` doc comment). M1.0.3 closes it. Do not treat the flipped guard assertion as a regression: it is the intended resolution. -**The new `FieldKind`s are resource-only — the validator is the guarantee.** `string_`/`enum_` live in the shared `Registry`/`compileTypeDecl` path, but `fieldKindFromTypeName` emits them only for `reg_kind == .resource`, and `validateFieldsInDecl` rejects `string`/enum on `component`. The component SoA/POD invariant (`etch-reference-part1.md` §5.5, `engine-spec.md` §4) is untouched: no component can reach a non-POD `FieldKind`. State this explicitly in the registry field comment so a future reader does not mistake the new kinds for a component capability. +**The new `FieldKind`s are resource-only — the validator is the guarantee.** `string_`/`enum_` live in the shared `Registry`/`compileTypeDecl` path, but `fieldKindFromTypeName` emits them only for `reg_kind == .resource`, and `validateFieldsInDecl` rejects `string`/enum on `component`. The component SoA/POD invariant (`etch-reference-part1.md` §5.5, `ARCH-004`) is untouched: no component can reach a non-POD `FieldKind`. State this explicitly in the registry field comment so a future reader does not mistake the new kinds for a component capability. **Default strings are immortal, writes allocate.** A resource `string` field default is a compile-time literal → an immortal-interned slot (sentinel refcount `u32.max`, `etch-memory-model.md` §4.4), so no allocation happens at `addResource`. A runtime write (`get_mut(R).field = expr`) is the §6.7 promotion: copy the incoming rule-arena/literal bytes into a fresh persistent allocation (refcount 1), then `decref` the previous slot value (a no-op when it was the immortal default). This keeps `addResource` allocation-free and makes the only persistent allocations the user-written values. diff --git a/briefs/M1.0.4-scene-cook.md b/briefs/M1.0.4-scene-cook.md index 76861bb..9355979 100644 --- a/briefs/M1.0.4-scene-cook.md +++ b/briefs/M1.0.4-scene-cook.md @@ -63,7 +63,7 @@ This milestone **assembles** existing bricks — `descriptor.zig` (`Scene`/`Scen 3. `engine-asset-pipeline.md` — §6.3 (scene cooking steps), cooking cache. 4. `etch-reference-part2.md` — §30 (`prefab` `of` / `extends`) + scene / resource constructs. 5. `etch-grammar.md` — `scene_decl` / `prefab` productions. -6. `engine-spec.md` — §19 (scene serialization), §3.5 (in-tree discipline), §4 (component POD/SoA invariant). +6. `engine-scene-serialization.md` (scene serialization), `ARCH-017` (in-tree discipline), `ARCH-004` (component POD/SoA invariant). 7. `engine-zig-conventions.md` — §13 (test rooting / lazy-analysis guard — mandatory so `tests/scene/` actually runs), Zig 0.16.x conventions. ## Files to create or modify diff --git a/briefs/M1.0.5-scene-load.md b/briefs/M1.0.5-scene-load.md index 498d656..5ab391e 100644 --- a/briefs/M1.0.5-scene-load.md +++ b/briefs/M1.0.5-scene-load.md @@ -59,7 +59,7 @@ This milestone **assembles** existing bricks — `accessor.zig` (reused verbatim 1. `engine-scene-serialization.md` — §4 (`.scene.bin` layout + the runtime loading section — PRIMARY; note the pseudo-code's explicit "ECS instantiation surface reconfirmed at M1.0.5" banner — this brief is that reconfirmation), §2 (UUID + name identity), §5 (prefabs — to see the M1.0.6 boundary). 2. `engine-ecs-internals.md` — §10 (ECS serialization: Schema Registry, the spawn-all-then-remap identity pattern). -3. `engine-spec.md` — §19 (scene serialization), §3.5 (in-tree discipline). +3. `engine-scene-serialization.md` (scene serialization), `ARCH-017` (in-tree discipline). 4. `engine-zig-conventions.md` — §13 (test rooting / lazy-analysis guard — mandatory so `tests/scene/` actually runs), §19 (rules summary, POD `extern struct`). ## Files to create or modify @@ -160,7 +160,7 @@ This milestone **assembles** existing bricks — `accessor.zig` (reused verbatim - `f513782` — E3 scope amended (authorized by Claude.ai): the refcounted persistent heap moves `src/etch/persistent.zig` → `src/core/memory/persistent.zig` (Tier 0) so the `weld_core` loader can intern resource `string` fields without importing `weld_etch`. Root cause: M1.0.3 placed a tier-neutral heap (`runDrop` no-op) in `weld_etch`; the directory structure targets `src/core/memory` for persistent pools. `StringSlot` 16-byte layout unchanged — no format/ABI impact. - `02f8196` — E3 correction (b), authorized by Claude.ai: an out-of-range parent ordinal now returns `error.MalformedScene` (new `StructureError` — "invalid scene structure") instead of `error.CorruptScene` (reserved for content-hash mismatch). Adds a 4th loader error beyond the brief's original `CorruptScene`/`UnknownComponent`/`SchemaMismatch`, and **supersedes** the E2 micro-decision (b) logged 2026-06-27 17:20. Inline test forges a hash-valid `.scene.bin` with a parent ordinal past the UUID table → `error.MalformedScene`. -- `dc5d16e` — E3 resource install refined to **install-or-overwrite**, not a strict `addResource`. Discovered via T6: a running Etch program's `Interpreter.compile` pre-installs declared resources with their defaults, so loading a scene that sets the same resource hit `error.DuplicateResource`. Resolved per `engine-spec.md` §19.1 (scene `resources {…}` are *injected into the resource map at load*): the scene value is authoritative and overrides the compile-time default (`getMutResource` + memcpy when the resource is present, else `addResource`). Ownership stays clean — the overridden value's string blocks remain owned by their installer (the interp frees its compile-time defaults at teardown); the newly-interned blocks are owned by `LoadResult`. **Flag for review:** the brief's word was "addResource"; this is the spec-grounded completion for the program-then-scene runtime flow. +- `dc5d16e` — E3 resource install refined to **install-or-overwrite**, not a strict `addResource`. Discovered via T6: a running Etch program's `Interpreter.compile` pre-installs declared resources with their defaults, so loading a scene that sets the same resource hit `error.DuplicateResource`. Resolved per `engine-scene-serialization.md` (scene `resources {…}` are *injected into the resource map at load*): the scene value is authoritative and overrides the compile-time default (`getMutResource` + memcpy when the resource is present, else `addResource`). Ownership stays clean — the overridden value's string blocks remain owned by their installer (the interp frees its compile-time defaults at teardown); the newly-interned blocks are owned by `LoadResult`. **Flag for review:** the brief's word was "addResource"; this is the spec-grounded completion for the program-then-scene runtime flow. - `dc5d16e` — T6 integration test lives **inline in `src/etch/interp.zig`** (the brief explicitly permits this) rather than as `tests/scene/load_integration_test.zig`. Reason: the assertion counts emitted events by reading the interpreter's private per-tick `EventStore`, only reachable from within `interp.zig`. `tests/scene/load_integration_test.zig` is therefore not created. ## Blockers encountered @@ -170,7 +170,7 @@ This milestone **assembles** existing bricks — `accessor.zig` (reused verbatim ## Closing notes - **What worked**: The central hypothesis held — the M1.0.4 zero-copy `accessor` (reused **verbatim**) + the existing M0.1 spawn surface (`spawnDynamicWithValues`) + the M1.0.2 `ObserverRegistry` were sufficient to materialize a cooked scene into a live `World` with **no new ECS storage primitive**. The genuinely new code is small: `loader.zig` (open/verify + schema remap + per-entity instantiate + UUID map + two-phase lifecycle + resource interning) and one public observer entry (`dispatchOnSpawned`). The two-phase `on_spawned` (phase-1 direct spawn fires no observers; phase-2 dispatches per entity) gives the "all entities exist before any `on_spawned`" ordering cleanly. The persistent-heap move to Tier 0 was mechanical (the heap is tier-neutral, `runDrop` a no-op) and unblocked World-free resource-string interning. The gate split (E1 units → E2 instantiation → E3 resources/integration) kept each review tight. -- **What deviated from the original spec**: All traced in Accepted deviations. (1) E1/E2 structuring — `fs.mmapFile`'s *ownership* (`LoadResult.mmap`) is E2; E1 shipped byte-level units; `loadFromBytes` exposed as the byte-level core alongside `loadScene(path)`. (2) `error.MalformedScene` added (correction b, authorized) for a structurally-invalid scene, distinct from `CorruptScene`. (3) Persistent heap moved `src/etch/persistent.zig` → `src/core/memory/persistent.zig` (Claude.ai amendment). (4) Resource install is **install-or-overwrite** (spec-grounded, `engine-spec.md` §19.1) not strict `addResource`. (5) T6 lives inline in `interp.zig` (brief-permitted). (6) `core/root.zig` wired the `memory` submodule (not `build.zig` — single Zig module). +- **What deviated from the original spec**: All traced in Accepted deviations. (1) E1/E2 structuring — `fs.mmapFile`'s *ownership* (`LoadResult.mmap`) is E2; E1 shipped byte-level units; `loadFromBytes` exposed as the byte-level core alongside `loadScene(path)`. (2) `error.MalformedScene` added (correction b, authorized) for a structurally-invalid scene, distinct from `CorruptScene`. (3) Persistent heap moved `src/etch/persistent.zig` → `src/core/memory/persistent.zig` (Claude.ai amendment). (4) Resource install is **install-or-overwrite** (spec-grounded, `engine-scene-serialization.md`) not strict `addResource`. (5) T6 lives inline in `interp.zig` (brief-permitted). (6) `core/root.zig` wired the `memory` submodule (not `build.zig` — single Zig module). - **What to flag explicitly in review**: All reviewed and validated across the E1/E2/E3 gates (install-or-overwrite, T6 inline, `core/root.zig` wiring, MalformedScene). Nothing outstanding. - **Final measurements**: `scene_load_bench` (`zig build bench-scene-load -Doptimize=ReleaseFast`, **Apple M4 Pro**): **median 1.050 ms / 10 000 entities** (min 0.907, max 2.111; 50 runs, 3 warmup). Far under the spec ~10–50 ms/10k reference. **Decision: per-entity instantiation confirmed; bulk SoA column-copy is a genuine YAGNI — no bulk-spawn milestone scheduled.** The instantiate step keeps a clean internal boundary so a bulk path could later swap its body without touching `loadScene`'s signature or call sites. - **Residual risks / deliberately-left technical debt**: diff --git a/briefs/M1.0.6-prefabs-crossrefs-extensions.md b/briefs/M1.0.6-prefabs-crossrefs-extensions.md index e852003..cf3e462 100644 --- a/briefs/M1.0.6-prefabs-crossrefs-extensions.md +++ b/briefs/M1.0.6-prefabs-crossrefs-extensions.md @@ -36,7 +36,7 @@ This milestone **implements code surfaces the specs already define but the engin These were taken with Guy before coding. They are the contract; do not re-litigate. One open point (D-B note) is confirmed by reading at the head of E1. **D-A — `FieldKind.entity_` realizes the `Entity` type (component-side).** -8 B / 8-align slot = `EntityId`. Unassigned/dangling = `EntityId.dead`. **Component-only** — the exact mirror of `string_`/`enum_` (resource-only): `fieldKindFromTypeName` emits `.entity_` only for `reg_kind == .component` (resource→entity refs are a future additive milestone). Stays POD (8 B, no heap, no teardown) → the component SoA/POD invariant (`engine-spec.md §4`) is untouched. `sizeBytes`/`alignBytes`/`fromZigType` switches in `registry.zig` gain the case. +8 B / 8-align slot = `EntityId`. Unassigned/dangling = `EntityId.dead`. **Component-only** — the exact mirror of `string_`/`enum_` (resource-only): `fieldKindFromTypeName` emits `.entity_` only for `reg_kind == .component` (resource→entity refs are a future additive milestone). Stays POD (8 B, no heap, no teardown) → the component SoA/POD invariant (`ARCH-004`) is untouched. `sizeBytes`/`alignBytes`/`fromZigType` switches in `registry.zig` gain the case. **D-B — Cross-references Table (binary shape).** ``` @@ -133,7 +133,7 @@ Sparse (entities with no active extension are absent). The `§4` note's `u8` cou 3. `etch-reference-part2.md` — §30 (the `prefab` construct: §30.4 `of`-vs-`extends` differentiation, §30.5 additive-conflict validation). 4. `etch-reference-part1.md` — §3.2 / §5.5 (the `Entity` builtin type; POD-in-component rules). 5. `engine-asset-pipeline.md` — §6.3 (scene/prefab cooking; reconciled — prefab flattening is M1.0.6, the M1.0.4 cook rejects `instance of`). -6. `engine-spec.md` — §19 (scene serialization), §4 (component POD/SoA invariant — governs `FieldKind.entity_`), §3.5 (in-tree discipline). +6. `engine-scene-serialization.md` (scene serialization), `ARCH-004` (component POD/SoA invariant — governs `FieldKind.entity_`), `ARCH-017` (in-tree discipline). 7. `etch-resolver-types.md` — §14 (the `override` frontier — confirms `override` stays out-of-scope). 8. `engine-zig-conventions.md` — §13 (test rooting / lazy-analysis guard — so `tests/scene/` actually runs), §19 (POD `extern struct`, rules summary). diff --git a/briefs/M1.1.0-forge-foundations.md b/briefs/M1.1.0-forge-foundations.md index 056559c..d2c70a6 100644 --- a/briefs/M1.1.0-forge-foundations.md +++ b/briefs/M1.1.0-forge-foundations.md @@ -58,7 +58,7 @@ Organized in three gates (E1–E3). Gate protocol in the Claude Code prompt. ### E3 — `src/modules/forge/solvers_3d/forge_3d/` -- `forge_3d` skeleton, in-tree per `engine-spec.md` §3.5 / `engine-directory-structure.md` §622. Depends ONLY on `foundation/math/` and `src/modules/forge/api/` (plus `core.ecs` public component/entity types re-exported through `api/`). Zero coupling with other modules. +- `forge_3d` skeleton, in-tree per `ARCH-017` / `engine-directory-structure.md` §622. Depends ONLY on `foundation/math/` and `src/modules/forge/api/` (plus `core.ecs` public component/entity types re-exported through `api/`). Zero coupling with other modules. - `Real` comptime scalar: build option `physics_f64` (default false → `Real = f32`). Local aliases `Vec3r = math.Vec(3, Real)`, `Quatr`, `Mat3r`, `Aabbr`. All forge_3d code written against `Real` from day 1. - `ShapeStore`: `createShape(ShapeDescriptor) → ShapeId` / `destroyShape(ShapeId)` / getters. Free-list slot reuse (LIFO, deterministic), generation bump on destroy, stale-ID access rejected (safe getter returns null; debug assert on unchecked paths). M1.1.0 accepts sphere/box/capsule; any other variant → `error.UnsupportedShape`. Per shape, precomputes: local AABB, unit-mass local inertia diagonal. - `MotionProperties`: inv_mass (`Real`), local inverse inertia (`Mat3r`), linear/angular damping, gravity_factor. Static and kinematic bodies: inv_mass = 0, inv_inertia = zero. diff --git a/briefs/M1.1.5-integration-euler.md b/briefs/M1.1.5-integration-euler.md index f9e3b37..5a7a6c3 100644 --- a/briefs/M1.1.5-integration-euler.md +++ b/briefs/M1.1.5-integration-euler.md @@ -16,7 +16,7 @@ ## Context -This is the FIRST sub-milestone where bodies move. M1.1.0–4 laid the static machinery: the SoA `BodyManager` store (position, rotation, linear/angular velocity, `MotionProperties`), the broadphase BVH, and the narrowphase (GJK/EPA/manifold + analytic fast paths). No stepping exists yet — M1.1.0 explicitly deferred it (`PhysicsModule` freezes at M1.1.15). This milestone adds the integration stage the plan freezes at the M1.1.5 row of `engine-phase-1-plan.md` — "Integration : semi-implicit Euler + gravité + damping", demonstrator "Box en chute libre, trajectoire correcte" — landing it in `pipeline/integration.zig`, the file the spec directory tree (`engine-directory-structure.md`; `engine-spec.md §3.5`) reserves for "semi-implicit Euler, sleep, CCD" (only the Euler + gravity + damping part lands here; sleep is M1.1.8, CCD is later). +This is the FIRST sub-milestone where bodies move. M1.1.0–4 laid the static machinery: the SoA `BodyManager` store (position, rotation, linear/angular velocity, `MotionProperties`), the broadphase BVH, and the narrowphase (GJK/EPA/manifold + analytic fast paths). No stepping exists yet — M1.1.0 explicitly deferred it (`PhysicsModule` freezes at M1.1.15). This milestone adds the integration stage the plan freezes at the M1.1.5 row of `engine-phase-1-plan.md` — "Integration : semi-implicit Euler + gravité + damping", demonstrator "Box en chute libre, trajectoire correcte" — landing it in `pipeline/integration.zig`, the file the spec directory tree (`engine-directory-structure.md`; `ARCH-017`) reserves for "semi-implicit Euler, sleep, CCD" (only the Euler + gravity + damping part lands here; sleep is M1.1.8, CCD is later). It is FREE-FLIGHT integration only. Broadphase/narrowphase exist but are NOT invoked — there is no contact response (Sequential Impulses is M1.1.6). A dynamic body under gravity follows an unobstructed trajectory. Integration lives INSIDE `forge_3d` (a free function over the `BodyManager` SoA store); it is NOT wired into the `PhysicsModule` interface, whose `step` orchestration composes broadphase → narrowphase → solve → integrate at M1.1.15. diff --git a/briefs/S0-bootstrap.md b/briefs/S0-bootstrap.md index 601af93..9f33ad6 100644 --- a/briefs/S0-bootstrap.md +++ b/briefs/S0-bootstrap.md @@ -49,7 +49,7 @@ S0 is the first milestone of Phase −1 and the first milestone of the entire We ## Spec documents to read first -1. `engine-spec.md` — §22.3.0 (Phase −1 spike list, S0 entry) and §3.5 (in-tree default, no `spec/` in repo) +1. `engine-phase-minus-1-archive.md` (Phase −1 spike list, S0 entry) and `ARCH-017` (in-tree default, no `spec/` in repo) 2. `engine-development-workflow.md` — §2 (milestone model), §3 (brief format), §3.4 (`CLAUDE.md` lifecycle), §4 (git conventions: branches, tags, Conventional Commits, PRs, lefthook, squash-and-merge) 3. `engine-zig-conventions.md` — §17 (Zig version policy: 0.16.x strict, patches accepted, minor refused) 4. `engine-phase-0-criteria.md` — context on what comes next (informs `CLAUDE.md` content; no implementation impact at S0) @@ -232,9 +232,9 @@ None. Benchmark infrastructure is deferred to S1+. - The custom Zig linter mentioned in `engine-development-workflow.md` §4.5 (`zig build lint`, `zig build lint-commit`) does not exist at S0. The `commit-msg` hook uses `bash scripts/check-commit-msg.sh` instead. When the Zig linter ships in a later Phase 0 milestone, `lefthook.yml` and the workflow doc will be amended together. - `mlugg/setup-zig@v2` is the established standard for Zig in GitHub Actions and supports semver ranges (`0.16.x`). Patch pinning is intentionally avoided — the `build.zig` version guard enforces the minor invariant, patches are fluid. - Pinning `ubuntu-24.04` and `windows-2025` (not `*-latest`) is deliberate. `*-latest` aliases shift unpredictably and have caused silent CI breaks across the GitHub-hosted runners ecosystem; pinning gives the project explicit control over runner version bumps via dedicated PRs. -- `engine-spec.md` §22.3.0 originally listed `Fedora 44` as the Linux CI target. GitHub Actions does not provide a hosted Fedora runner; using `ubuntu-24.04` for CI is the pragmatic choice. Spec amendment to be applied by Guy in claude.ai knowledge base alongside this milestone. -- `engine-spec.md` §22.3.0 also listed `.vscode/` in the skeleton without specifying its content. The S0 scope refines this to a minimal project-level pair (`extensions.json` + `settings.json`), with personal IDE files explicitly gitignored. -- `CLAUDE.md` was not present in the original `engine-spec.md` §22.3.0 deliverables list. It is added at S0 because Claude Code reads it at every session start (`engine-development-workflow.md` §3.4); shipping S1 without it would mean a blind first session. +- `engine-phase-minus-1-archive.md` originally listed `Fedora 44` as the Linux CI target. GitHub Actions does not provide a hosted Fedora runner; using `ubuntu-24.04` for CI is the pragmatic choice. Spec amendment to be applied by Guy in claude.ai knowledge base alongside this milestone. +- `engine-phase-minus-1-archive.md` also listed `.vscode/` in the skeleton without specifying its content. The S0 scope refines this to a minimal project-level pair (`extensions.json` + `settings.json`), with personal IDE files explicitly gitignored. +- `CLAUDE.md` was not present in the original `engine-phase-minus-1-archive.md` deliverables list. It is added at S0 because Claude Code reads it at every session start (`engine-development-workflow.md` §3.4); shipping S1 without it would mean a blind first session. - `engine-zig-conventions.md` §17: minor version bumps require a dedicated migration milestone, never a silent change. The build-time guard enforces this. --- @@ -276,7 +276,7 @@ None. Benchmark infrastructure is deferred to S1+. *Modifications to the FROZEN SECTION made mid-milestone after a Claude.ai round-trip. Each deviation references the commit that enacts it. Empty at end of milestone is the nominal case.* -- `429de07` — `.github/workflows/ci.yml` step 2 pinned to `version: 0.16.0` instead of the brief's `version: 0.16.x` (cf. § Files to create or modify → `.github/workflows/ci.yml` specification, step 2). **Reason:** `mlugg/setup-zig@v2` — the action mandated by both the brief and `engine-spec.md` §22.3.0 — takes the version string literally and tries to fetch `zig-x86_64-{linux,windows}-0.16.x.tar.xz`, which 404s on every mirror. Tool capability fact, not a design choice. The brief's underlying intent (run on the latest 0.16 patch automatically) is preserved as future work once the action supports semver ranges, or via `version-file: build.zig.zon`. The `build.zig` minor-version guard continues to enforce the 0.16.x invariant on the local toolchain. Mechanical deviation — no Claude.ai round-trip; flagged here for review and surfaced in the PR's review notes. +- `429de07` — `.github/workflows/ci.yml` step 2 pinned to `version: 0.16.0` instead of the brief's `version: 0.16.x` (cf. § Files to create or modify → `.github/workflows/ci.yml` specification, step 2). **Reason:** `mlugg/setup-zig@v2` — the action mandated by both the brief and `engine-phase-minus-1-archive.md` — takes the version string literally and tries to fetch `zig-x86_64-{linux,windows}-0.16.x.tar.xz`, which 404s on every mirror. Tool capability fact, not a design choice. The brief's underlying intent (run on the latest 0.16 patch automatically) is preserved as future work once the action supports semver ranges, or via `version-file: build.zig.zon`. The `build.zig` minor-version guard continues to enforce the 0.16.x invariant on the local toolchain. Mechanical deviation — no Claude.ai round-trip; flagged here for review and surfaced in the PR's review notes. - `6cfaf07` — `src/main.zig` rewritten: print now includes the build mode via `@tagName(builtin.mode)` instead of the static phase tag, and the I/O path is condensed (single `print` + `flush` on a 128-byte stack buffer instead of the original 256-byte version with separate writer variable). The brief's "minimal hello-world" intent is preserved; the change is a refinement, not a scope expansion. Decided in Claude.ai post-review. diff --git a/briefs/S1-mini-ecs.md b/briefs/S1-mini-ecs.md index 632b297..dcd03be 100644 --- a/briefs/S1-mini-ecs.md +++ b/briefs/S1-mini-ecs.md @@ -52,7 +52,7 @@ Second spike of Phase −1. Validates the core architectural hypothesis of Weld' ## Documents to read first -1. `engine-spec.md` — §2 (ECS overview), §3.5 (in-tree extraction criterion), §22.3.0 sub-section S1 (canonical milestone definition). +1. `ARCH-005` (ECS overview; detail `engine-ecs-internals.md`), `ARCH-017` (in-tree extraction criterion), `engine-phase-minus-1-archive.md` S1 (canonical milestone definition). 2. `engine-ecs-internals.md` — §1 (architecture overview), §2 (chunk SoA layout), §4 (query compilation), §12 (comparison with other ECS). 3. `engine-zig-conventions.md` — full read; in particular naming, allocator passing, `extern struct` POD components, doc comments on public API, ban on `@cImport` outside `*_c` modules and on `usingnamespace`. 4. `engine-development-workflow.md` — §2 (milestone model), §3 (brief format), §4 (git conventions, hooks, squash-merge), §5 (Claude review cycle). diff --git a/briefs/S2-window-vulkan-triangle.md b/briefs/S2-window-vulkan-triangle.md index a0f29aa..54f2d58 100644 --- a/briefs/S2-window-vulkan-triangle.md +++ b/briefs/S2-window-vulkan-triangle.md @@ -64,7 +64,7 @@ Third spike of Phase −1. Validates the hypothesis that a Win32 window, a Wayla ## Spec documents to read first -1. `engine-spec.md` — §22.3 Phase −1 / S2 (full canonical definition including post-conversation refinements), §1 (overview), §3.5 (in-tree vs separable libs criterion), §1.6 (8 keepers list — none added in S2). +1. `engine-phase-minus-1-archive.md` Phase −1 / S2 (full canonical definition including post-conversation refinements), `ARCH-001`–`ARCH-006` (overview), `ARCH-017` (in-tree vs separable libs criterion), `ARCH-024` (8 keepers list — none added in S2). 2. `engine-c-bindings.md` — §1.1 to §1.4 (rationale registry-driven and S2 carve-out), §4.2 (idiomatic mapping rules — these are the conformance target for the S2 generators), §10.1 (S2→S3 sequencing). 3. `engine-platform.md` — §1 (architecture, what belongs to the platform layer), §2 windowing and HiDPI subsections. 4. `engine-mach-reference.md` — §5 (native windowing inspiration: Win32 ~800 lines direct, Wayland ~2100 lines + a tiny C callbacks file). Read as design inspiration, **not** as a code template — Weld's S2 attempts pure Zig callbacks via `callconv(.c)`. @@ -230,7 +230,7 @@ Vulkan validation layers and `VK_EXT_debug_utils` are activated only in Debug bu ### Reference reading order recommendation -Read `engine-spec.md` §22.3 / S2 in full first — the post-conversation precisions are the authoritative scope for this milestone. Then `engine-c-bindings.md` §4.2 to internalize the idiomatic mapping rules that the generators must produce. Then `engine-mach-reference.md` §5 for the design inspiration on the windowing layer (read for ideas, not for code patterns to copy: Mach-Core pulls in a tiny `wayland.c`; Weld attempts pure Zig `callconv(.c)` first, with the same C fallback documented here as a recovery path). +Read `engine-phase-minus-1-archive.md` S2 in full first — the post-conversation precisions are the authoritative scope for this milestone. Then `engine-c-bindings.md` §4.2 to internalize the idiomatic mapping rules that the generators must produce. Then `engine-mach-reference.md` §5 for the design inspiration on the windowing layer (read for ideas, not for code patterns to copy: Mach-Core pulls in a tiny `wayland.c`; Weld attempts pure Zig `callconv(.c)` first, with the same C fallback documented here as a recovery path). --- diff --git a/briefs/S3-etch-parser-subset.md b/briefs/S3-etch-parser-subset.md index 516a6c9..ac3b030 100644 --- a/briefs/S3-etch-parser-subset.md +++ b/briefs/S3-etch-parser-subset.md @@ -155,7 +155,7 @@ Five constructs only. Every position below tracks a decision taken in the conver ## Spec documents to read first -1. `engine-spec.md` — §22.3 sub-section S3 (canonical definition), §3.5 (in-tree Phase 1-4), §22 Layer 2 (parsing layer context). +1. `engine-phase-minus-1-archive.md` S3 (canonical definition), `ARCH-017` (in-tree Phase 1-4), `engine-tools-editor.md` § "Panneau Etch Text — éditeur de code", Layer 2 (parsing layer context). 2. `etch-grammar.md` — entire file, with special attention to §1 lexicon, §3 expressions and precedence, §5.4 component_decl, §5.5 resource_decl, §6 when clauses, §7 rule_decl, §19 ambiguities resolved. 3. `etch-reference-part1.md` — §2 lexicon, §3 type system primitives and engine builtins, §6 expressions and operator precedence. 4. `etch-ast-ir.md` — §1 pipeline overview, §3 entire (AST tabular layout, NodeId/StableId, kinds catalog, annotations, doc comments), §10 invariants. diff --git a/briefs/S4-etch-tree-walking-interpreter.md b/briefs/S4-etch-tree-walking-interpreter.md index f98413f..1c399d8 100644 --- a/briefs/S4-etch-tree-walking-interpreter.md +++ b/briefs/S4-etch-tree-walking-interpreter.md @@ -16,7 +16,7 @@ ## Context -Fifth Phase −1 derisking spike. Validates the hypothesis stated in `engine-spec.md` §22.3 / S4: that the AST emitted by S3 is correctly executable by a tree-walking interpreter, and that a functional bridge can be built between this interpreter and the comptime SoA archetype storage delivered by S1. The deliverable is twofold: (a) the interpreter itself and (b) a shared differential test harness that S5 (Etch → Zig codegen) will reuse verbatim to prove behavioural equivalence between the two backends. +Fifth Phase −1 derisking spike. Validates the hypothesis stated in `engine-phase-minus-1-archive.md` S4: that the AST emitted by S3 is correctly executable by a tree-walking interpreter, and that a functional bridge can be built between this interpreter and the comptime SoA archetype storage delivered by S1. The deliverable is twofold: (a) the interpreter itself and (b) a shared differential test harness that S5 (Etch → Zig codegen) will reuse verbatim to prove behavioural equivalence between the two backends. ## Scope @@ -73,7 +73,7 @@ The following are explicitly **not** to be touched, added, or extended in S4. Ea In the listed order. Mandatory before writing any production code — Claude Code ticks each entry in the LIVING SECTION with a real timestamp. -1. `engine-spec.md` — §22.3 / sub-section S4 (canonical scope), §22.3.0 (Phase −1 modus operandi), §3.5 (in-tree Phase 1-4, no separable libs). +1. `engine-phase-minus-1-archive.md` — S4 (canonical scope) and the Phase −1 modus operandi; `ARCH-017` (in-tree Phase 1-4, no separable libs). 2. `etch-grammar.md` — §3 (expressions, operators, precedence), §5 (constructs: component, resource, rule, when), §6 (when clause grammar), §18 (annotations — captured only, not honored at execution in S4), §19 (v0.6 design decisions). 3. `etch-reference-part1.md` — §3 (type system: polymorphic literal defaulting, int/float defaulting rules already applied by S3), §4 (variables, mutability, shadowing), §5 (memory model: surface invariants — S4 does not need the deeper internals), §6 (expressions: arithmetic semantics, division by zero, integer overflow, compound assignments, comparison, logical operators). 4. `etch-resolver-types.md` — §11 (const evaluation: contexts where const is required, defaulting rules), §12 (ECS rule validations: when clause compilation to archetype set, archetype matching), §19 (phasing — confirms S4 is Phase 0.5 boundary, AST-direct execution). diff --git a/briefs/S5-etch-codegen-zig.md b/briefs/S5-etch-codegen-zig.md index be53ad0..fcc8285 100644 --- a/briefs/S5-etch-codegen-zig.md +++ b/briefs/S5-etch-codegen-zig.md @@ -16,7 +16,7 @@ ## Context -S5 is the sixth and penultimate spike of Phase -1. It validates the structural shipping hypothesis of the Etch execution layer: **Etch → Zig source → compilation by Zig** is viable in terms of build time (cf. `engine-spec.md` §25.3 / S5). This is *the* major structural risk of Phase -1 — if compile times explode (e.g. > 1 minute for a single-line incremental edit), the shipping strategy is revisited before Phase 0.2 and alternatives (VM bytecode as primary backend, minimal Zig subset codegen, hybrid Zig release / bytecode dev) are arbitrated. S5 must therefore measure honestly, not work around the comptime path that is precisely what the spike is meant to exercise. The diff harness built in S4 (generic `Runner` interface + `runner_interp` over 20 differential programs) is reused as-is by plugging a new `runner_codegen` — this is the "design on day one, implement progressively" angle that S4 paid for. +S5 is the sixth and penultimate spike of Phase -1. It validates the structural shipping hypothesis of the Etch execution layer: **Etch → Zig source → compilation by Zig** is viable in terms of build time (cf. `engine-phase-minus-1-archive.md` S5). This is *the* major structural risk of Phase -1 — if compile times explode (e.g. > 1 minute for a single-line incremental edit), the shipping strategy is revisited before Phase 0.2 and alternatives (VM bytecode as primary backend, minimal Zig subset codegen, hybrid Zig release / bytecode dev) are arbitrated. S5 must therefore measure honestly, not work around the comptime path that is precisely what the spike is meant to exercise. The diff harness built in S4 (generic `Runner` interface + `runner_interp` over 20 differential programs) is reused as-is by plugging a new `runner_codegen` — this is the "design on day one, implement progressively" angle that S4 paid for. ## Scope @@ -25,7 +25,7 @@ S5 is the sixth and penultimate spike of Phase -1. It validates the structural s - Codegen invoked as an automatic step of `zig build`: input `.etch` files declared in `build.zig`, regenerated before Zig compile when source mtime/content hash has changed - Codegen cache keyed by xxHash of source `.etch` content, per-file granularity, stored at `zig-out/etch-gen/.cache/` - Generated programs consume the comptime archetype path of S1 (`world.query(.{T1, T2, ...})`, comptime monomorphisations), not the dynamic path of S4 — the spike must exercise the production target -- Component / resource Etch declarations mapped 1:1 to `extern struct` Zig declarations under matching names (no prefix), declared in the generated file; resources spawned as singleton entities at program init (consistent with `engine-spec.md` §2.9) +- Component / resource Etch declarations mapped 1:1 to `extern struct` Zig declarations under matching names (no prefix), declared in the generated file; resources spawned as singleton entities at program init (consistent with `ARCH-006`) - Type mapping fixed for S5 and Phase 0.2: `int` → `i64`, `float` → `f64`, `bool` → `bool`. Values in generated code are native Zig types, flow-typed from the S3 typechecker output — no `Value` tagged union on the hot path - Differential test harness reuse: a new `Runner` implementation `runner_codegen` plugged into the existing generic driver `tests/etch_interp/diff_runner.zig` (built in S4), consuming the same 20 sidecar pairs `tests/etch_interp/-*.{etch,input.json,expected.json}` without modification of the sidecar format - Differential test binary consolidating the 20 programs (one Zig module per cooked program, dispatched by program name), compiled statically — no JIT, no runtime `.so` loading @@ -56,7 +56,7 @@ S5 is the sixth and penultimate spike of Phase -1. It validates the structural s ## Spec documents to read first -1. `engine-spec.md` — §25.3 / S5 (canonical milestone definition), §25.3 / S4 (shared invariants reminder), §25.3 / S3 + S1 (delivered contracts), §3.5 (in-tree Phase 1-4) +1. `engine-phase-minus-1-archive.md` — S5 (canonical milestone definition), S4 (shared invariants reminder), S3 + S1 (delivered contracts); `ARCH-017` (in-tree Phase 1-4) 2. `etch-grammar.md` — §5 (component, resource, struct, enum), §6 (when clauses), §7 (rules), §19 (design decisions v0.6) — only as needed for the S3 subset 3. `etch-reference-part1.md` — §3 (type system), §5 (memory model surface), §8 (functions) — only as needed for S3 subset semantics 4. `etch-reference-part2.md` — §3 (component access patterns), §4 (resources) — only the parts in scope for S5 @@ -181,7 +181,7 @@ S5 is the sixth and penultimate spike of Phase -1. It validates the structural s - `zig build` clean, zero warning, on the Ubuntu 24.04 + Windows 2025 matrix (existing matrix unchanged) - `zig build test` green in `debug` and `ReleaseSafe` — within an accepted wall-clock budget for S5: **< 120 s on the CI matrix** (acknowledged tradeoff of the spike, recorded in § Notes) - `zig fmt --check` clean across all hand-written sources (generated Zig under `zig-out/etch-gen/` excluded from the check, as it lives in the build artifacts directory) -- `zig build lint` clean if the custom linter is present at the time the milestone is run; otherwise skipped (consistent with the post-S1 lint milestone deferral noted in `engine-spec.md` §25.3 / S0) +- `zig build lint` clean if the custom linter is present at the time the milestone is run; otherwise skipped (consistent with the post-S1 lint milestone deferral noted in `engine-phase-minus-1-archive.md` S0) - `commit-msg` hook green on all commits of the branch (Conventional Commits) - Benchmark CI artifact: `bench/results/S5-codegen-zig.md` committed and updated by the bench run @@ -203,7 +203,7 @@ For metric (b) the Zig cache is wiped via `rm -rf .zig-cache` (POSIX) or its Win ### Why the comptime archetype path, not the S4 dynamic path -The spec gate "no explosion of degenerate comptime monomorphisations" can only be measured by *generating* those monomorphisations. The dynamic path (registry, `query_dynamic`) is the documented post-spike fallback in case the comptime path explodes — it is not a way of avoiding the measurement. If the comptime path explodes, that is the spike outcome: stop, journal, return to Claude.ai for re-design. The spec text in `engine-spec.md` §25.3 / S5 (on failure, the shipping strategy is revisited before Phase 0.2 and alternatives are arbitrated then) places the arbitration *after* the spike, not during. +The spec gate "no explosion of degenerate comptime monomorphisations" can only be measured by *generating* those monomorphisations. The dynamic path (registry, `query_dynamic`) is the documented post-spike fallback in case the comptime path explodes — it is not a way of avoiding the measurement. If the comptime path explodes, that is the spike outcome: stop, journal, return to Claude.ai for re-design. The spec text in `engine-phase-minus-1-archive.md` S5 (on failure, the shipping strategy is revisited before Phase 0.2 and alternatives are arbitrated then) places the arbitration *after* the spike, not during. ### Why one Zig file per `.etch`, in `zig-out/etch-gen/` @@ -227,7 +227,7 @@ Per-declaration granularity (cache per component, per rule) is over-engineering ### Resources as singleton entities -Resources are spawned as singleton entities at program init, exposed via accessors generated for `get(R)` / `get_mut(R)` over the singleton handle. This is consistent with `engine-spec.md` §2.9 and reuses the S1 + S4 ECS APIs without introducing a separate resource storage. The generated `init(world)` function spawns one entity per resource declared in the cooked file and attaches the resource as a component on that entity. +Resources are spawned as singleton entities at program init, exposed via accessors generated for `get(R)` / `get_mut(R)` over the singleton handle. This is consistent with `ARCH-006` and reuses the S1 + S4 ECS APIs without introducing a separate resource storage. The generated `init(world)` function spawns one entity per resource declared in the cooked file and attaches the resource as a component on that entity. ### Generated file layout — readable, not minified @@ -251,7 +251,7 @@ The debts inherited from S2 (5), S3 (10), S4 (9) are listed in § Out-of-scope. ### Failure mode is a legitimate blocker -If during implementation the comptime path produces compile times that exceed the spec gates by an order of magnitude (e.g. cold > 60 s, incremental > 10 s) and the cause is clearly the codegen strategy (not, say, a bug in the bench harness or in the generated code), this is a legitimate blocker per `engine-development-workflow.md` §2.4: stop, journal under "Blockers encountered", return to Claude.ai. The spec arbitration of fallback strategies (`engine-spec.md` §25.3 / S5, alternatives to arbitrate) happens in a new conversation, with the bench numbers as input. Claude Code must not silently switch to the dynamic path to "make it pass." +If during implementation the comptime path produces compile times that exceed the spec gates by an order of magnitude (e.g. cold > 60 s, incremental > 10 s) and the cause is clearly the codegen strategy (not, say, a bug in the bench harness or in the generated code), this is a legitimate blocker per `engine-development-workflow.md` §2.4: stop, journal under "Blockers encountered", return to Claude.ai. The spec arbitration of fallback strategies (`engine-phase-minus-1-archive.md` S5, alternatives to arbitrate) happens in a new conversation, with the bench numbers as input. Claude Code must not silently switch to the dynamic path to "make it pass." --- diff --git a/briefs/S6-ipc-editor-runtime.md b/briefs/S6-ipc-editor-runtime.md index 5bf5e73..75aa85e 100644 --- a/briefs/S6-ipc-editor-runtime.md +++ b/briefs/S6-ipc-editor-runtime.md @@ -16,7 +16,7 @@ ## Context -S6 is the seventh and final spike of Phase -1. It validates the IPC editor↔runtime protocol specified in `engine-ipc.md` on a real two-process workload: an editor stub that spawns a runtime stub, exchanges typed framed messages over a Unix-domain socket / Win32 named pipe, shares a viewport framebuffer via POSIX shm / `CreateFileMapping`, and recovers from a `kill -9` of the runtime by detecting EOF, restarting, and re-handshaking. The hypothesis under test is that the wire protocol, the shared-memory layout, the handshake versioning, and the OS-handle passing primitives (`SCM_RIGHTS` on POSIX) all hold together as designed (cf. `engine-spec.md` §25.3 / S6). This is the last structural risk of Phase -1 — if IPC fails its gates, the two-process editor architecture is revised before Phase 0. +S6 is the seventh and final spike of Phase -1. It validates the IPC editor↔runtime protocol specified in `engine-ipc.md` on a real two-process workload: an editor stub that spawns a runtime stub, exchanges typed framed messages over a Unix-domain socket / Win32 named pipe, shares a viewport framebuffer via POSIX shm / `CreateFileMapping`, and recovers from a `kill -9` of the runtime by detecting EOF, restarting, and re-handshaking. The hypothesis under test is that the wire protocol, the shared-memory layout, the handshake versioning, and the OS-handle passing primitives (`SCM_RIGHTS` on POSIX) all hold together as designed (cf. `engine-phase-minus-1-archive.md` S6). This is the last structural risk of Phase -1 — if IPC fails its gates, the two-process editor architecture is revised before Phase 0. ## Scope @@ -47,7 +47,7 @@ S6 is the seventh and final spike of Phase -1. It validates the IPC editor↔run | `LogMessage` | R→E | unidirectional event | validates the event direction without ack | Total = 12 messages (the table sums 13 because `LogMessage` was added below the count). The fire-and-forget event direction is covered by `LogMessage`. `Echo` is transactional but cheap: the runtime stub replies immediately, no state change. -- **`ProtocolHello.capabilities: u32`** — bitflags, bit 0 = `GPU_SHARED_FB`. Published to 0 by the runtime stub in S6 (no GPU shared support). Reserved-for-future bits are zero. Posted now to stabilize the `schema_hash` of `ProtocolHello` against Phase 3 introduction (cf. `engine-spec.md` §25.3 / S6 and `engine-ipc.md` §4.7). +- **`ProtocolHello.capabilities: u32`** — bitflags, bit 0 = `GPU_SHARED_FB`. Published to 0 by the runtime stub in S6 (no GPU shared support). Reserved-for-future bits are zero. Posted now to stabilize the `schema_hash` of `ProtocolHello` against Phase 3 introduction (cf. `engine-phase-minus-1-archive.md` S6 and `engine-ipc.md` §4.7). - **Two binaries** at canonical locations per `engine-directory-structure.md` §9.1: - `src/editor/main.zig` — editor stub. Owns the listen socket and the shm region. Spawns the runtime via `platform.process.spawn_process` passing the socket path, the shm region name, and the editor PID in argv. Opens a window (reuses S2 `Window` + Vulkan setup), runs a fullscreen-quad blit pipeline that samples the viewport texture each frame and presents it. Drains the IPC inbox on its main thread. - `src/runtime/main.zig` — runtime stub. Connects to the socket, attaches the shm region, sends `ProtocolHello`, awaits `ProtocolHelloAck`. Renders a CPU-side moving color mire (gradient with frame-counter modulation) at 60 Hz into the viewport shm using the double-buffer atomics. Drains the IPC inbox on a dedicated reader thread that pushes into an MPSC queue consumed by the main loop. @@ -100,7 +100,7 @@ Total = 12 messages (the table sums 13 because `LogMessage` was added below the - **Job system S1 integration** — the IPC reader thread does **not** use the work-stealing scheduler. A dedicated OS thread is the right primitive; coupling to S1 would be gratuitous. - **Windows `sendWithHandles` / `recvWithHandles` implementation** via `DuplicateHandle`. Phase 3 (cf. `engine-ipc.md` §4.7). - **GPU shared framebuffer** per `engine-ipc.md` §4.7 — `VK_KHR_external_memory`, `ViewportConfig` / `ViewportTexturesShared`, exportable Vulkan semaphores. Phase 3. -- **GAL renderer abstraction** — S6 uses raw Vulkan exactly like S2 (cf. `engine-spec.md` §25.3 / S2 — no GAL before Phase 0.4). +- **GAL renderer abstraction** — S6 uses raw Vulkan exactly like S2 (cf. `engine-phase-minus-1-archive.md` S2 — no GAL before Phase 0.4). - **Inverse heartbeat** runtime→editor (cf. `engine-ipc.md` §6.3). - **CRDT op format coupling** — the wire `IpcMessage` is deliberately decoupled from `CrdtOp` in S6 (the format freeze is Phase 1 per `engine-collaboration.md`). - **Cross-endian support** — `comptime` panic if `builtin.cpu.arch.endian() != .little`. @@ -108,7 +108,7 @@ Total = 12 messages (the table sums 13 because `LogMessage` was added below the ## Spec documents to read first -1. `engine-spec.md` — §25.3 / S6 (canonical definition), §25.3 / S2 (design precisions — pattern for raw Vulkan + window reuse), §1.3 (process separation), §3.5 (in-tree Phase 1-4) +1. `engine-phase-minus-1-archive.md` — S6 (canonical definition), S2 (design precisions — pattern for raw Vulkan + window reuse); `ARCH-012` (process separation), `ARCH-017` (in-tree Phase 1-4) 2. `engine-ipc.md` — full document (§1 architecture, §2 transport, §3 messages and serialization, §4 shared memory including §4.7 GPU shared framebuffer Phase 3, §5 handshake and versioning, §6 heartbeat, §7 command-log replay, §8 security, §9 testing, §10 phasing) 3. `engine-tools-editor.md` — §2.2 threading model, §2.5 state management overview, §2.6 IPC dispatcher (especially §2.6.8 Phase 1 topics, §2.6.9 plugin MsgKind range), §2.7 crash recovery (especially §2.7.3 CommandLog and §2.7.4 best-effort replay — out of scope but read for context) 4. `engine-platform.md` — Process (spawn / wait / read_stdout), Memory (mmap, virtual_alloc), Threading (Mutex, atomics), FileSystem @@ -233,7 +233,7 @@ Target machine: dev-primary Apple Silicon ReleaseSafe (consistent with S1, S3, S ### Best-effort replay — recorded descope -The `engine-spec.md` §25.3 / S6 criterion calls for best-effort replay to remain functional after a `kill -9` of the runtime. In S6 this criterion is interpreted narrowly as **detection + restart + re-handshake + first post-restart command round-trips OK**. The replay of pending commands via a `CommandLog` and `SaveProject` ack mechanism (cf. `engine-tools-editor.md` §2.7.3 and `engine-ipc.md` §7) is **out of scope** for S6 and postponed to Phase 0.6. Rationale: the `CommandLog` depends on `SaveProject` acks which depend on a real save pipeline, none of which exist in Phase -1. Synthesizing a mini-CommandLog for S6 would be throwaway code. The hard part of the criterion — detecting the crash, killing orphan resources, spawning a new runtime, re-establishing the handshake, validating the connection is alive — is fully tested in G4 and `tests/ipc/crash_recovery.zig`. A design-precisions subsection is appended to `engine-spec.md` §25.3 / S6 in the same session to record this descope (pattern S2). +The `engine-phase-minus-1-archive.md` S6 criterion calls for best-effort replay to remain functional after a `kill -9` of the runtime. In S6 this criterion is interpreted narrowly as **detection + restart + re-handshake + first post-restart command round-trips OK**. The replay of pending commands via a `CommandLog` and `SaveProject` ack mechanism (cf. `engine-tools-editor.md` §2.7.3 and `engine-ipc.md` §7) is **out of scope** for S6 and postponed to Phase 0.6. Rationale: the `CommandLog` depends on `SaveProject` acks which depend on a real save pipeline, none of which exist in Phase -1. Synthesizing a mini-CommandLog for S6 would be throwaway code. The hard part of the criterion — detecting the crash, killing orphan resources, spawning a new runtime, re-establishing the handshake, validating the connection is alive — is fully tested in G4 and `tests/ipc/crash_recovery.zig`. A design-precisions subsection is appended to `engine-phase-minus-1-archive.md` S6 in the same session to record this descope (pattern S2). ### Two binaries at canonical locations @@ -241,7 +241,7 @@ The `engine-spec.md` §25.3 / S6 criterion calls for best-effort replay to remai ### No GAL, raw Vulkan -The fullscreen blit pipeline uses raw Vulkan exactly like S2 — no GAL abstraction. The GAL is designed in Phase 0.4 when a second backend is on the horizon (cf. `engine-spec.md` §25.3 / S2 design precisions). +The fullscreen blit pipeline uses raw Vulkan exactly like S2 — no GAL abstraction. The GAL is designed in Phase 0.4 when a second backend is on the horizon (cf. `engine-phase-minus-1-archive.md` S2 design precisions). ### Endianness invariant diff --git a/briefs/m0.6-assets.md b/briefs/m0.6-assets.md index 5938ab9..63b7810 100644 --- a/briefs/m0.6-assets.md +++ b/briefs/m0.6-assets.md @@ -81,7 +81,7 @@ Mandatory reads before any production code; Claude Code ticks each box in the LI 1. `engine-phase-0-plan.md` — § M0.6 — scope source of record. 2. `engine-asset-pipeline.md` — §1–10, especially §3 (the normative intermediate-format schema) — intermediate/runtime formats, importers, cookers, cache, registry, async loader. 3. `etch-grammar.md` — §21.4 — grammar of the `asset` construct (category 4, pipeline-generated). -4. `engine-spec.md` — §16 (Asset Pipeline) and §3.5 (in-tree as-if-lib discipline) — master alignment. +4. `engine-asset-pipeline.md` (Asset Pipeline) and `ARCH-017` (in-tree as-if-lib discipline) — master alignment. 5. `engine-simd.md` — §1–3 (module role, structure, two-level API), §7.1 (Asset Pipeline hot-path map), §9 (phasing: M0.6 adds the skeleton + `adler32` + `paeth_filter_decode`), §10-referenced `@Vector`-first / asm-second discipline. 6. `engine-zig-conventions.md` — §13 surface coverage (lazy analysis guard), module rooting rule, codecs in-tree convention, `root.zig` convention. 7. `engine-directory-structure.md` — `src/modules/asset_pipeline/` and `src/foundation/simd/` layout. diff --git a/validation/s5-go-nogo.md b/validation/s5-go-nogo.md index 54b8ac7..7a044d5 100644 --- a/validation/s5-go-nogo.md +++ b/validation/s5-go-nogo.md @@ -76,7 +76,7 @@ component is reachable by both keys. Tested in - `zig fmt --check` clean across hand-written sources (generated cooked.zig is under `zig-out/etch-bench/`, excluded by `.gitignore`). - `zig build lint` not present at the time of the milestone (deferred - per the post-S1 lint milestone note in `engine-spec.md` §25.3 / S0). + per the post-S1 lint milestone note in `engine-phase-minus-1-archive.md` S0). - `commit-msg` hook green on every commit of the branch (Conventional Commits). From 47611e293b6dfbd30c40cf153c8f46636db5963a Mon Sep 17 00:00:00 2001 From: Guy Senpai Date: Fri, 31 Jul 2026 00:31:16 +0200 Subject: [PATCH 07/12] docs: append the closing record to the chore brief MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Strictly append-only — 243 lines added, none removed, the frozen body untouched. Acted deviations A1 to A9, execution notes, closing notes. The two that change what the brief says rather than what it did: A2 — acceptance criterion 1 reads "→ zero lines" in the body. That is unreachable by construction, not by omission: a doctrine cannot forbid a citation form without quoting it, and a dated reading act cannot be repointed without making the record claim a milestone read a file that did not exist on its date. The amended form is 30 lines, each enumerated file:line with its exemption class, and no other — the number a consequence of the list, never the gate. A4 — "corrects a wrong-section reference" is refuted for the five §4 sites. Measured on the 27-section master: §4.3 lines 492-500 carry the POD rule in full, §2.1 only a one-line summary, and the body of ARCH-004 reproduces the former clause for clause. The five citations pointed at a real and correct location; only the qualification was wrong, and the target is unaffected. Also recorded: D8 restated by evidential function rather than template, with M0.8:215 reclassified; the six sections absent from the substitution table; §22 "Layer 2" resolved on the only Couche 2 in the corpus, against a precedent already closed in engine-corpus-map.md anomaly 10; the scope widening from 14 to 17 files and the two language fixes; the rendering conventions; and the KB debt on the §1.2 trap note, which states one of the three readings of §22. --- briefs/chore-spec-reference-reconciliation.md | 243 ++++++++++++++++++ 1 file changed, 243 insertions(+) diff --git a/briefs/chore-spec-reference-reconciliation.md b/briefs/chore-spec-reference-reconciliation.md index 991d4bb..608e6d3 100644 --- a/briefs/chore-spec-reference-reconciliation.md +++ b/briefs/chore-spec-reference-reconciliation.md @@ -209,3 +209,246 @@ Rationale for the split: E1 carries the semantic corrections and must be read cl - `CLAUDE.md` § *Current state* being stale by one milestone after every merge, and the two dead entries in § *Open / deferred decisions* (Codeberg, retired in July 2026 per `engine-development-workflow.md` §7.2; and the `engine-zig-conventions.md` `solvers_{2d,3d}` reconciliation, now paid). Same file, different fault class — owned by the M1.1.11.1 CLAUDE.md patch. - The `-Dphysics_f64=true` CI leg, absent from `.github/workflows/` across twelve forge milestones. Owned by whoever next opens `ci.yml`. - The residual wall-clock latency assertions (`tests/ipc/handshake.zig:135` and four others), same class as the four removed at M1.1.9. Owned by the next milestone that opens `tests/ipc/`. + +--- + +## Acted deviations + +Everything below was decided during execution, at a STOP point, and is recorded +here rather than by editing the frozen body above. + +### A1 — Scope widened from 14 to 17 source files + +The survey in *Context* was run with `--include=*.zig --include=*.md`, so three +sites carrying the same fault were invisible to it. They would have survived a +green acceptance grep, since criterion 1 filters on the same two extensions: + +- `examples/triangle/build.zig.zon:8` — `§3.5` → `ARCH-017` +- `bench/fixtures/synth_100/build.zig.zon:8` — `§3.5` → `ARCH-017` +- `tests/etch/corpus/invalid/E0502_annotation_misapplied.etch:2` — `§2.9` → + `ARCH-006`. The fixture keeps its line count, so the E0502 span is unchanged; + `tests/etch/corpus_facade.zig` asserts the code alone in any case. + +Two language residues sat on lines already being touched and went with them +(same extension blind spot, this time in the `chore-language-normalization` +pass): `Phase 0 monolithique` → `monolithic`, and `Path local` → `Local path` in +both `.zon` files. + +### A2 — Acceptance criterion 1 is amended, and this supersedes the body + +The body states criterion 1 as `→ zero lines`. **That is false and unreachable**, +by construction rather than by omission, and this append carries the amended +form: + +> The literal grep returns **30** lines, every one of them enumerated +> `file:line` with its exemption class, and no other. The number is a +> *consequence* of the list, not the gate. A 31st line is a defect; a 29th is a +> damaged record. + +Five classes, four of which the body could not have anticipated: + +| Class | Count | Lines | +|---|---|---| +| This chore's own brief — a doctrine cannot forbid a form without quoting it | 8 | `briefs/chore-spec-reference-reconciliation.md:1 :12 :21 :27 :99 :106 :135 :196` | +| Dated reading acts, checkbox form | 14 | `M0.2:390` `M0.4:304` `M0.8:207` `M1.0.4:144` `M1.0.5:145` `M1.0.6:223` `m0.6:198` `S0:250` `S1:154` `S2:245` `S3:300` `S4:228` `S5:266` `S6:295` | +| Dated reading acts, prose form | 2 | `S0:258` `M0.8:215` | +| Historical prose whose subject *is* the stale numbers | 4 | `M0.0:19` `:29` `:136` `M0.8:218` | +| Bare filename recording which files were patched | 1 | `M0.3:186` | +| The referencing policy itself | 1 | `CLAUDE.md:170` | + +The exemption of historical prose rests on `engine-audit-checklist.md` §1.1, +which excludes "une phrase explicitement historique" from the drift predicate — +not on a judgement call made here. + +### A3 — D8 is stated by function, not by form + +> Any line recording a **dated reading act** is exempt, whatever its shape — +> checkbox `- [x] … — read ` or prose `- — … read ( §x, +> …)`. Substituting the current target would make the record claim a milestone +> read a file that did not exist on its date. The exemption attaches to the +> evidential function, not to the template. + +Consequence: `S0:258` and `M0.8:215` are reading acts in prose, not historical +prose about numbering. `M0.8:215` was first classified as the latter and is +reclassified here. The total is unchanged; the decomposition is not. + +### A4 — "corrects a wrong-section reference" is REFUTED for the five `§4` sites + +The substitution table asserts that the five `§4` citations pointed at "Système +de plugins" for the POD invariant, "which lived in `§2.1`". Measured on the +27-section master — the numbering in force when those comments were written — +that is wrong. `§4.3` carries the POD rule **in full**, at lines 492–500: + +> `**Règle stricte 100% POD pour les composants :** les composants doivent être +> strictement Plain Old Data pour garantir que la migration par memcpy +> fonctionne toujours sans fuite mémoire ni double-free.` — followed by the +> allowed list, the fixed-buffer-or-POD-handle rule for dynamic data, and +> `Un composant non-POD ne compile pas — erreur, pas warning.` + +`§2.1` line 175 carries only a one-line summary. The body of `ARCH-004` +corroborates independently, reproducing that clause and not the summary: same +forbidden/allowed lists, same recursive `comptime` check, same "erreur, pas +avertissement". + +So `ARCH-004` was extracted from `§4.3`, the five citations pointed at a real +and correct location, and only the *qualification* was wrong — the target is +unaffected. `registry.zig:52`, `:65`, `:77`, `M1.0.3:154` and `M1.0.6:39` are +plain repairs, not corrections. The commit message of the source gate asserts +the refuted qualification; it is already pushed and stays as part of the record, +with the correction carried here and in the squash message. + +### A5 — Six sections absent from the substitution table + +Resolved at the STOP points, never by guessing: + +| Old | Subject in the 27-section master | Target | +|---|---|---| +| `§2.7` | Sérialisation versionnée | `ARCH-008` | +| `§2.8` | Gestion mémoire | `ARCH-011` | +| `§1.3` | Communication éditeur ↔ runtime | `ARCH-012` | +| `§1` | Architecture fondamentale | `ARCH-001`–`ARCH-006` | +| `§3.1` cited as "Tier 0 catalog" | Tier 0 — Noyau incompressible | `ARCH-013` | +| `§22 Layer 2` | see A6 | `engine-tools-editor.md`, named anchor | + +The `§3.1` row is the chore's own fault class caught in the act: one number, two +subjects. The table's `§3.1` target — the root-file convention of +`engine-zig-conventions.md` — is right for `rtti/root.zig:10`, which cites it +for a re-export convention, and wrong for `M0.2:191`, which cites it for the +Tier 0 catalogue. Only reading each line settles it. + +### A6 — `§22 Layer 2` (S3:158) resolved by measurement + +The only `Couche N` / `Layer N` decomposition in the whole corpus is line 2448 +of the 27-section master: + +> `**Couche 2 — Parsing (parser hybride LR(1) + Pratt, Zig natif in-tree)**` … +> `**Spec dédiée :** etch-parser.md`. + +It sits inside `### 23.10 Panneau Etch Text — Éditeur de code`, whose intro +reads "Il est construit en 6 couches" (Rendu, Text buffer, **Parsing**, +Intelligence, Bidirectionnalité, Collaboration). There is no layer +decomposition in `§24` Langage Etch nor in `§25` Stratégie & Roadmap, so neither +of the two readings first considered — Etch, or Roadmap — has a referent at all. + +The precedent was already closed: `engine-corpus-map.md` anomaly 10 records +`§22 Couche 3` in `etch-language-server.md` as a false target, corrected at lot +E towards `engine-tools-editor.md`; anomalies 8 and 19 record the migration of +`§23.10` itself. `engine-audit-checklist.md` §1.2 row 23 carries it too. + +`etch-parser.md` was considered and rejected: it is that layer's own dedicated +spec, but S3's word is "context" and the block cited is the description of the +stack. The pointer lands where the cited block lives. + +### A7 — `§25.3` split by what the citation designates + +`§25.3` was both the roadmap and the home of the Phase −1 spike paragraphs, and +the two go to different files. Sites naming a spike or the spike list → +`engine-phase-minus-1-archive.md`; sites naming a roadmap line → +`engine-roadmap.md` §3; `M0.2:191`, which names both in one parenthetical, gets +both. + +### A8 — `§24.10`: the table holds, and the measurement is recorded + +`§24.10` was "Standard Library", whose owner is `etch-stdlib.md`. Its Time +bullet reads literally `dt` (paramètre optionnel déclaré dans la signature de +rule, ex. `rule tick(entity: Entity, dt: float)`), so `M1.0.13:57` cited it +precisely. The retarget still goes to `etch-reference-part1.md`, per the table: +the pointer follows the current owner of the *assertion* — `dt` as an injected +rule parameter — not the historical descendant of the section. `etch-stdlib.md` +owns `time.dt`, a different surface. + +### A9 — Rendering conventions + +Three, applied uniformly and worth naming because they are the difference +between a repair and a reword: + +1. **Short `ARCH-nnn` form in code comments**, per `engine-invariants.md` §1.2 + ("Dans la prose, une référence courte suffit"). `; detail ` is added at + exactly two sites, where the sentence states content the registry explicitly + disclaims as detail: the 16 KiB chunk dimension (`chunk.zig`, disclaimed by + the admission test of §1.3) and the resource lifecycle-tag table + (`rtti/type_info.zig`). Citing the ARCH id alone there would have been a + *wrong* pointer, not merely a terse one. +2. **A spike id stays attached to the archive filename with no section number** — + `` `engine-phase-minus-1-archive.md` S6 `` — because the archive is indexed by + spike exactly as the roadmap is indexed by phase. Side effect: no `§` + reintroduced. +3. **A named anchor only where the filename designates nothing on its own**, as + the body prescribes for `§3.1`. Two files qualified: + `engine-zig-conventions.md` § "Fichier racine : `root.zig` (module) vs + `main.zig` (exécutable)" and `engine-tools-editor.md` § "Panneau Etch Text — + éditeur de code". The section numbers are dropped in both cases — a shifting + number is the failure mode being repaired, and "6 bis" has already moved once. + +Two textual side effects, both visible in the diff: the word "table" was dropped +from "§2.9 table" because `ARCH-006` carries none, and `rtti/root.zig` reads +"the Tier 0 convention of X" rather than "the X Tier 0 convention" because the +anchor is too long for the original word order. No other word moved anywhere. + +## Execution notes + +Gate by gate, each ending at a STOP with an explicit GO before the next. + +| Gate | Commit | Content | +|---|---|---| +| brief | `52f666a` | committed verbatim before any substitution | +| E1 | `31bff8e` | 18 references, 14 source/tool/example files | +| E1bis | `e4a605c` | 3 references outside the `.zig`/`.md` set (A1) | +| E1ter | `4efe8ad` | the second language fix (A1) | +| E2 | `88c2b1c` | `CLAUDE.md` — 4 references + § *Quick links spec* rewritten | +| E3 | `feebb78` | 52 references over 51 lines, 21 briefs + the S5 record | + +**77 references repaired.** `engine-spec.md` occurrences across the tree: 97 in +38 files at open, 37 in 18 files at close. Ten distinct ARCH ids used as +targets — `ARCH-004`, `-005`, `-006`, `-007`, `-008`, `-011`, `-012`, `-013`, +`-017`, `-024` — plus the `ARCH-001`–`ARCH-006` range at `S2:67`. + +**Verification that references alone moved.** For each of the 51 changed lines +in E3, the reference tokens (`§n.n`, `ARCH-nnn`, `*.md`) were neutralised and the +ordered sequence of every remaining number compared old against new: 51/51 +identical, including `S0:279`, which carries nineteen of them (`429de07`, +`0.16.0`, `404`, `x86_64`, …). Backtick and bold parity: no regression. E1 was +checked the same way at line granularity: 0 non-comment changed lines across all +14 files. No line was added or removed in any brief — 51 insertions against 51 +deletions. + +**`CLAUDE.md` § *Quick links spec*.** The 75 filenames it now lists were checked +against the whitelist of `engine-audit-checklist.md` §4.1 and form an exact +bijection with it — nothing invented, nothing missing. The section previously +listed 45 of 75 while instructing the reader to stop and ask Guy for anything +absent from it. + +**Gates**, all reproduced independently at close: `zig build lint` 0 · +`zig fmt --check src tests build.zig` 0 (and `examples tools bench` 0) · +`zig build test` 264/264 steps, 1523/1540 tests passed, 17 skipped · +`zig build test-forge-3d -Dphysics_f64=true` 4/4 steps, 356/356. Acceptance +criterion 2 returns zero; nothing outside `.md`/`.zig` still carries a citation. + +## Closing notes + +**Verbatim French spec citations — the count moves in two places, and the second +is the larger.** In *source*, the M1.1.10 language audit counted twelve; the two +named anchors of A9 add one each, taking the source-side count to **14**. In +*this brief*, the append above quotes considerably more: **20 French-bearing +lines**, measured with a Python detector and not a byte-wise grep class, carrying +some eighteen distinct fragments — section titles, the `§4.3` POD clause of A4, +the `Couche 2` block of A6, the `§24.10` Time bullet of A8, and one clause each +from `engine-invariants.md` §1.2 and `engine-audit-checklist.md` §1.1. Every one +is a verbatim citation, the category that audit established as licit, and they +are here deliberately: a measurement that cannot be re-read against its source +is an assertion. No French prose of my own was introduced — checked with the same +detector. Two French residues left the tree in the same pass ("monolithique", +"Path local"). + +**KB debt, owed by Claude.ai, deliberately not interleaved with this chore.** +The trap note of `engine-audit-checklist.md` §1.2 states only the Roadmap +reading of `§22`, while the corpus carries three: Roadmap (`§22.1`–`§22.4`), +Éditeur (`§22.10` and `§22 Couche N`, per A6), and Outils de Debug Avancés in +the 27-section numbering. Producing the amendment mid-chore is the exact +mechanism that manufactures the drift being paid off here. + +**No `weld_lint` rule**, per the body. The exit check is the amended criterion 1 +of A2, run by hand: the grep must return the 30 enumerated lines and nothing +else. Anything that changes that list — a new brief citing the old form, a +reading record edited — is a defect on one side or a damaged record on the +other, and both are visible from the same command. From 2b9c0cc1c3a83819d447fe57b911251237697c9b Mon Sep 17 00:00:00 2001 From: Guy Senpai Date: Fri, 31 Jul 2026 13:06:08 +0200 Subject: [PATCH 08/12] docs: put named anchors on every domain-detail spec target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit E5, on the four findings of the external review. Comment lines and markdown only; the frozen body of the brief is untouched, A2 and A9 are rewritten in place inside the append rather than patched by a second one. C1 — the body's anchor policy ("named anchors are deliberately not used except on §3.1") is WITHDRAWN: it contradicts engine-invariants.md §1.2, which prescribes a canonical file plus a named anchor for domain detail with no exception, and it left twenty-five pointers resolving to a whole document. Every anchor title was read in the owning document, never inferred from the subject, and the section number is dropped everywhere. Three exemptions stand, each motivated: the archive plus a spike id (the id IS the anchor), a bare ARCH-nnn (§1.2 allows the short form, and engine-invariants.md plus an id already IS the full link), and engine-roadmap.md, where §1.2 asks for a phase identifier — none of the three roadmap sites designates a single phase, so each takes the named anchor of the section it designates. C2 — 34 glossed ARCH references re-audited against the DECISION of the id cited, not the section it came from: 26 covered, 8 defects fixed. The count is 34 and not 25 because a motif keyed on a trailing parenthesis missed six glosses that precede the id or are the sentence's own subject. On M0.2:191 no word of a closed gloss is deleted — "Tier 0/1 catalog" moves to ARCH-013 + ARCH-014 and ARCH-024 keeps the keepers. S2:67's "8 keepers list" goes to the only document carrying the 8 → 7 transition. S0:52's "no spec/ in repo" is qualified as historical: spec/ appears nowhere in engine-directory-structure.md and the decision lives in CLAUDE.md, so no target is invented. Three of the eight REVOKE the GO given at the source gate, and the append records it: foundation/root.zig, scene/root.zig and scene/loader.zig cite the zero-coupling clause, which ARCH-017 does not carry but delegates — "couplage déclaré et acyclique (ARCH-016)". E1 verified them against the source text of the 27-section §3.5; the rule written afterwards asks about the decision of the cited id. The stricter rule wins, and the sites take ARCH-016, with ARCH-013 alongside where the sentence says "tier discipline". C3 — loader.zig:714 and M1.0.5:163/:173 take the anchor of the install-or-overwrite rule, which now exists in the amended KB. C4 — M0.2:364, a bare reference attributing a directory-tree detail to the master, goes to engine-directory-structure.md. Acceptance criterion 1 stays at exactly 30 lines, all enumerated with their class in A2. Criterion 2 zero; nothing outside .md/.zig. Gates: lint 0, fmt 0, test 264/264 steps and 1523/1540 tests, 17 skipped, test-forge-3d -Dphysics_f64=true 4/4 steps and 356/356. --- CLAUDE.md | 4 +- briefs/M0.0-lint-custom.md | 2 +- briefs/M0.2-rtti-resources-events-bindgen.md | 4 +- .../M0.4-renderer-vulkan-forward-and-gal.md | 2 +- briefs/M0.8-full-grammar-v0.6.md | 2 +- briefs/M1.0.13-time-and-timers.md | 2 +- briefs/M1.0.15-test-runner.md | 2 +- briefs/M1.0.4-scene-cook.md | 2 +- briefs/M1.0.5-scene-load.md | 6 +- briefs/M1.0.6-prefabs-crossrefs-extensions.md | 2 +- briefs/M1.1.5-integration-euler.md | 2 +- briefs/S0-bootstrap.md | 2 +- briefs/S1-mini-ecs.md | 2 +- briefs/S2-window-vulkan-triangle.md | 2 +- briefs/chore-spec-reference-reconciliation.md | 130 ++++++++++++++---- briefs/m0.6-assets.md | 2 +- src/core/ecs/chunk.zig | 2 +- src/core/rtti/type_info.zig | 5 +- src/core/scene/loader.zig | 10 +- src/core/scene/root.zig | 2 +- src/etch/test_runner.zig | 4 +- src/foundation/root.zig | 2 +- tools/etch_test/main.zig | 3 +- 23 files changed, 138 insertions(+), 58 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 62ec1bd..a27c7d4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -117,7 +117,7 @@ Hotfix milestones are merged to `main` without a tag (Guy decision, - **M1.0.12 scope boundary (concurrency algebra)** — the four constructs (`race`/`sync`/`branch`/`spawn { }`) parse, type-check, and execute as CHILD TASKS in the interpreter's pointer-stable monotonic pool (heap records, husk parking, no slot reuse — a pool index IS the Phase-1 `TaskHandle`, no generations), driven by-origin at the creating rule's position in creation order. **Recorded deviation (STOP round-trip 2026-07-02, `etch-resolver-types.md` §9.2 revision 2):** the brief's "calls inside the four construct bodies count as consumed launch sites" is SUPERSEDED — `await` is the SOLE call-grain consumer of the `{async}` effect (`E0905` applies recursively inside the construct bodies; the constructs relocate the suspension into a child task, they do not replace the `await`). Return asymmetry (Guy's ruling 2026-07-02): `return` legal only in a `race` branch (winner-return re-raised at the race site); `sync`/`branch`/`spawn` bodies reject it (`E0906`). Documented one-tick construct latency: a race/sync parent precedes its children in pool-creation order, so it resumes the tick AFTER its wake fires — which guarantees losers are canceled before their own wakes can fire (zero-admitted constructs have no latency). Cancellation is NON-transitive (Phase 1, `etch-bytecode.md` §9.5). Failed tasks (uncaught `throw` / runtime failure) park `.canceled` — never a race winner, never block a `sync` join, `await`ing them fails loud (§9.8 amended). Fix-as-you-go: `return await ` dropped its return at resume (M1.0.11 gap) — fixed. **Out (later):** `wait_unscaled` + timers (M1.0.13), `entity_event` (M1.0.14), entity-bound `async rule`, Phase-2 bytecode lowering (`etch-bytecode.md` §9.4), transitive cancellation (Phase 2+), task-pool slot reuse / generations (Phase-2 refcounted model), value-producing spawn bodies (no EBNF v0.6 value channel). - **M1.0.13 scope boundary (time subsystem and timers)** — the three builtin time resources (`GameTime`/`UnscaledTime`/`RealTime`) are auto-registered from the single `pub const builtin_resources` descriptor table in `types.zig` (the `TagSet` injection-point precedent — no `.etch` prelude, no separate module) and resolve AMBIENTLY (`get(GameTime).dt` needs no `when resource` clause — the spec examples read them without one). `await wait` re-plumbed onto scaled game time and `await wait_unscaled` onto unscaled time with **byte-identical wake ticks at `time_scale = 1`** — realized by the E5 recorded deviation: the two internal clock accumulators advance in TICK units (game `+= time_scale`, 0 under `paused`; unscaled `+= 1`; deadlines `clock + round(secs * 60)`), seconds derived only at resource publication (exact integer f64 sums at `scale = 1`; a seconds accumulator would accrue rounding that can shift a wake by one tick). Both waits KEEP the M1.0.11 literal-only Duration restriction; only the timer family evaluates a full `Duration` expression. The timer registry mirrors the M1.0.12 task-pool discipline (heap records, monotone pointer-stable, husk parking, `TimerHandle` = bare index, no generations) but is a DISTINCT mechanism — a timer is not a task: `cancel()` only, not awaitable, body is a synchronous context (E0901 on `await`/async calls inside). Timer callbacks fire at the head of `stepOnce` (after the clock advance and event-store clear, before rule dispatch) in registration order; `every` re-arms at a fixed period (no drift correction Phase 1). `Value` gained `timer_handle` + `duration` (`value.zig` — outside the brief's per-file list, justified: `Value.timer_handle` is the E6 deliverable and the `task_handle` precedent lives there). **Out (later, not debt):** `quantize` (stays reserved; needs the Sequencer/Pulse beat/bar clock — later Sequencer-adjacent milestone), `time.*` stdlib sugar (later stdlib milestone; `etch-stdlib.md §20` reconciliation deferred with it), `dt` as an injected rule parameter, per-entity `TimeDilation` / selective pause / `WorldClock` (need the phase scheduler), lifting `wait`'s literal-only restriction, Phase-2 bytecode lowering. - **M1.0.14 scope boundary (entity-scoped events + entity-bound async rules)** — `entity_event` = `global_event` + predicates (same wake path, same per-tick store, producer-before-awaiter order unchanged). Designated-field convention, filter semantics (equality-only, capture-once), and resume-unit are normative in `etch-reference-part1.md §9.4`; the four entity-bound lifecycle rulings in `§9.2`; realization in `§9.12` (Claude.ai KB re-uploads at open + at the E2 amendment). The M1.0.11/12 out-list mentions "entity-bound `async rule` → later" are **superseded** (delivered here — absorbed at the E2 round-trip: the construct is inexpressible end-to-end without an entity-scoped awaiter). String stabilization at the event/filter escape boundaries fixed a PRE-EXISTING observer-side use-after-free (fix-as-you-go, two STOP round-trips). **Out (later, NOT debt):** `future`/`Future` (the last await gap); implicit `event` binding / payload delivery at resume (unit is normative); entity-keyed event index (Phase-2 typed bus); emit-side targeting (grammar v0.6 frozen); non-equality filter predicates; cancel-on-despawn (Phase 2+); `dt`/non-entity rule-parameter injection; Phase-2 bytecode async lowering. -- **M1.0.15 scope boundary (test-runner + identity)** — `test "X" { }` executes end-to-end (parse+validate+register existed since M1.0.8; zero execution surface until now). **Resolves the M1.0.8 open residual** (a string-named `test "X"` sharing the identifier namespace via `registerSymbol` → `test "Foo"`/`component Foo` collision): test names now live in a SEPARATE `test_symbols` table; intra-namespace uniqueness reuses `E0101` with a contextual message (precedent: `collectImplMethods`, E1900 double-usage §24.1) — no new code minted for the duplicate. Semantics normative in `etch-reference-part2.md §32` (Phase-1 realization block, Claude.ai KB re-upload) + `etch-grammar.md §17` (`measure_expr` erratum). Realization: test bodies are SYNC (`await` → `E0901`); FRESH World per test (`bindToWorld`); `test_world()` is **mono-world** (repeated calls return the same handle — the multi-world shape is spec'd but not v0.6); `spawn_with` is an IMMEDIATE spawn (does not borrow the deferred structural-spawn command buffer — that is what lets it return a handle) reusing the shared observer-firing primitive; `measure` is the SOLE wall-clock surface, confined to test bodies (`E0910`) to keep gameplay deterministic. Runner is a library (`src/etch/test_runner.zig`) consumed in-proc by the `etch_test` shim + `zig build test-etch`; the future `weld test` (`engine-platform.md`) consumes the same library — no CLI-weld scaffolding minted for one subcommand. **Out (later, NOT debt):** multi-world (`test_world()` creating independent worlds); async test bodies (`await` stays `E0901`, spec'd v0.6 restriction — async exercised through rules + `tick(n)`); the `weld` CLI proper; test filtering flags (`--filter`, tag selection — `@only` covers focusing); GPU/session integration-test helpers (`create_test_session`, replay-driven runs — `engine-debug.md §13`, Phase 2+ module surface). **KB reconciliation done here (fix-as-you-go):** `etch-reference-part2.md §32` (dangling `engine-testing.md` ref removed → `engine-debug.md §13`); `engine-debug.md §13` realigned on §32 + grammar (`test_world`/`spawn_with`/`tick(n)`; the `capture_events` helper and a dedicated `integration_test` construct removed — never existed in EBNF v0.6); `etch-grammar.md §17` `measure_expr` erratum + `@tag(.perf)`; `engine-development-workflow.md §4.7` tag-scheme + multi-paragraph tag-message reconciliation. +- **M1.0.15 scope boundary (test-runner + identity)** — `test "X" { }` executes end-to-end (parse+validate+register existed since M1.0.8; zero execution surface until now). **Resolves the M1.0.8 open residual** (a string-named `test "X"` sharing the identifier namespace via `registerSymbol` → `test "Foo"`/`component Foo` collision): test names now live in a SEPARATE `test_symbols` table; intra-namespace uniqueness reuses `E0101` with a contextual message (precedent: `collectImplMethods`, E1900 double-usage §24.1) — no new code minted for the duplicate. Semantics normative in `etch-reference-part2.md §32` (Phase-1 realization block, Claude.ai KB re-upload) + `etch-grammar.md §17` (`measure_expr` erratum). Realization: test bodies are SYNC (`await` → `E0901`); FRESH World per test (`bindToWorld`); `test_world()` is **mono-world** (repeated calls return the same handle — the multi-world shape is spec'd but not v0.6); `spawn_with` is an IMMEDIATE spawn (does not borrow the deferred structural-spawn command buffer — that is what lets it return a handle) reusing the shared observer-firing primitive; `measure` is the SOLE wall-clock surface, confined to test bodies (`E0910`) to keep gameplay deterministic. Runner is a library (`src/etch/test_runner.zig`) consumed in-proc by the `etch_test` shim + `zig build test-etch`; the future `weld test` (`engine-platform.md` § "Build System — CLI `weld`") consumes the same library — no CLI-weld scaffolding minted for one subcommand. **Out (later, NOT debt):** multi-world (`test_world()` creating independent worlds); async test bodies (`await` stays `E0901`, spec'd v0.6 restriction — async exercised through rules + `tick(n)`); the `weld` CLI proper; test filtering flags (`--filter`, tag selection — `@only` covers focusing); GPU/session integration-test helpers (`create_test_session`, replay-driven runs — `engine-debug.md §13`, Phase 2+ module surface). **KB reconciliation done here (fix-as-you-go):** `etch-reference-part2.md §32` (dangling `engine-testing.md` ref removed → `engine-debug.md §13`); `engine-debug.md §13` realigned on §32 + grammar (`test_world`/`spawn_with`/`tick(n)`; the `capture_events` helper and a dedicated `integration_test` construct removed — never existed in EBNF v0.6); `etch-grammar.md §17` `measure_expr` erratum + `@tag(.perf)`; `engine-development-workflow.md §4.7` tag-scheme + multi-paragraph tag-message reconciliation. - **M1.0.16 scope boundary (qualified imports + private visibility)** — the write-only `imported_aliases` binding (D-F since M1.0.7) is now consumed: `m.Type` resolves as a type-name at exact parity with the selective import form. **Re-scoped after an E1 blocker round-trip (Recorded deviation):** the original brief anchored acceptance on field-type / struct-literal positions, which do NOT consult imports even for the selective form (a `component` is no valid field type; a component struct-literal is rejected as "not a struct type") — acceptance re-anchored on the type-alias target (the one proven selective-resolution position, M1.0.7), and the grammar addition narrowed to TYPE POSITION ONLY. **Resolves the M1.0.8-deferred `private` items:** `etch-resolver-types.md §10.2` visibility inheritance (found STRUCTURAL — no active code; impls are never exported and a private type is unnameable cross-module via E0107) + `W0902 PrivateTypeInPublicImpl` (public trait impl for a private local type). **Finding (journaled):** of the three `imported_symbols` consult sites, only two (`namedTypeToResolved`, `validateTypeAliases`) are reachable by a `.path` node in type-position-only scope; `checkComponentInstance` is unreachable (component instances parse as bare `TYPE_IDENT`; qualified struct-literals are out of scope) — left intact to avoid unexercisable dead code. **Out (NOT debt):** expression-position qualified access (`m.Type { … }`, `m.func()`, `m.Type.CONST`) — not a proven selective capability, a separate import-usability concern; uppercase associated access `Vec3.UP` (a `TYPE_IDENT . TYPE_IDENT` gap unrelated to aliases); broad `private_in_public` signature leakage (Rust-style — §10.2 literal only); imported-trait-impl W0902 (imported-trait impls do not resolve today — pre-existing orthogonal gap). **KB reconciliation (Claude.ai re-upload):** `etch-grammar.md §2.1` (`qualified_path` production, type position); `etch-resolver-types.md §3.3` (`resolve_path` distinguishes absent E0104 / present-private E0107) + §3.1 note; `etch-diagnostics.md §25` (W0902 added to Phase 1 coverage). - **M1.0.17 scope boundary (resource collection fields)** — `T[]`/`[K: V]`/`Set` execute as `resource` fields in the tree-walker. **Decision A (persistent representation):** distinct persistent `Value` tags mirroring the `.string_run`/`.string_persistent` split (zone at the tag, no runtime discriminant); rejected the "persistent bit on the existing handle". **Decision a (Tier-0 drop registry):** `persistent.zig` exposes `DropFn` + `registerDrop`; the interp registers collection drops at init; `runDrop` never reinterprets a payload as an Etch type (Tier-0 stays Etch-decoupled). **Element bound:** value-POD + `string` + enum, on `resource` ONLY; **`Entity` element REFUSED** (E0222 — cross-reference-table remap not wired in persistent collections); nested collections refused (E0222). **Closures long-life REFUSED (no gap, NOT debt):** recon proved only synchronous same-body closure consumers; escaping is a Phase-2 HIR/bytecode lowering. **Recorded deviations (Claude.ai round-trips):** FROZEN element bound refined to exclude `Entity`; the brief's `.array` kind corrected to `.slice` (`T[]` is `.slice`); `descriptor.zig` NOT touched (resource field layout lives in `interp.compileTypeDecl`, shared with `scene_cook.zig`); **E5 gate added mid-milestone** (scene-cook Etch parity + Tier-0 loader guard — deferring the Etch renderers would re-open Etch after M1.0.x, forbidden); set `for-in` and a `.set_lit` renderer were forward-predicted then refuted by recon. **Out (frontiers, NOT debt):** full persistent-block reconstruction of a collection `resource` instance at scene-**load** = `src/core/scene/loader.zig`, Tier-0 → scene-serialization / asset-pipeline milestone (**M1.6**), never Etch; collection semantics in **codegen** = Phase 2 (`etch-ast-ir.md`). **Open decision (plan):** `for x in set` is type-rejected ("not in the M0.8 minimal subset") — C1.6 gap for a later M1.0.x milestone, or a stdlib feature outside C1.6? Unlocking touches every rule-arena set + the differential corpus, so it stays OUT of M1.0.17. New diagnostic: E0222. - **M1.1.0 scope boundary (Forge 3D foundations)**: `src/interfaces/PhysicsModule.zig` + the `PhysicsModule(Impl)` comptime wrapper + `core.ModuleContext` are DEFERRED to the milestone that wires forge_3d as a stepping module — instantiating the wrapper needs `ModuleContext`, whose spec carries an unresolved Tier-0→Tier-1 reference (`asset_loader: *AssetLoader`), and the repo precedent (RenderModule, C0.5) froze a module-root namespace, not a `src/interfaces/` file: the location ruling belongs to the interface-landing milestone. The day-1 contract is carried by the `api/` descriptor types (mirrored verbatim); when the interface file lands, declarations move there and `api/` re-exports — zero call sites. `Velocity` stays a core component (`api/` re-exports; moving it would invert core→Tier-1). `Mat4` and a math-level `Transform` pose type: excluded, purely additive (first consumers later). Shapes beyond sphere/box/capsule: `error.UnsupportedShape` until their sub-milestones (pre-freeze, additive). Descriptor validation policy (typed errors on degenerate mass/geometry) is a later milestone — M1.1.0 guards the dynamic path with a `mass > 0` debug assert only. Pending KB spec patch on `engine-tier-interfaces.md` §1 (ShapeType set, damping default, `foundation.math`, union-form `ShapeDescriptor`), produced at milestone close. @@ -147,7 +147,7 @@ Hotfix milestones are merged to `main` without a tag (Guy decision, - **Never use** `git commit --no-verify` or `git push --no-verify`. If a hook fails, fix the underlying cause — do not bypass. - **Conventional Commits** mandatory on every commit. Types: `feat`, `fix`, `perf`, `refactor`, `test`, `docs`, `chore`, `breaking`. Optional scope `[a-z0-9-]+`. Optional `!` for breaking change. Description 1–72 chars, lowercase first letter, no trailing period. The `commit-msg` hook enforces this locally; CI rejects offending commits. - **Squash-and-merge** as the default merge strategy on `main`. One milestone = one commit on `main`. -- **No external dependency** beyond the 7 authorized C keepers (cf. `ARCH-024`; detail `engine-c-bindings.md`): ONNX Runtime, Opus, Assimp, KTX/Basis Universal, libdatachannel, ACL compressor, HarfBuzz. Plus the standards adapted automatically (Vulkan/Wayland/OpenXR XML) and Apple frameworks where relevant. Anything else requires a dedicated derogation in `ARCH-024` and `engine-c-bindings.md`. +- **No external dependency** beyond the 7 authorized C keepers (cf. `ARCH-024`; detail `engine-c-bindings.md` § "Liste des `.api.zig` manuels"): ONNX Runtime, Opus, Assimp, KTX/Basis Universal, libdatachannel, ACL compressor, HarfBuzz. Plus the standards adapted automatically (Vulkan/Wayland/OpenXR XML) and Apple frameworks where relevant. Anything else requires a dedicated derogation in `ARCH-024` and `engine-c-bindings.md` § "Liste des `.api.zig` manuels". ## Quick links spec diff --git a/briefs/M0.0-lint-custom.md b/briefs/M0.0-lint-custom.md index 3e929aa..7cc1c5e 100644 --- a/briefs/M0.0-lint-custom.md +++ b/briefs/M0.0-lint-custom.md @@ -35,7 +35,7 @@ First milestone of Phase 0. M0.0 absorbs two debts inherited from Phase −1: th - **Opt-out annotation `// weld-lint:allow-no-doc`** for trivial re-exports. Added only if real friction emerges — purely additive, zero refactoring cost in deferring (no call sites affected). - **Advanced lint rules** (ECS `extern struct` POD comptime check, strict doc-comment format, type-only imports, ABI stability check on `weld_c_api.api.zig`). Phase 0.1+. - **`bindgen-lint` rules** from `engine-c-bindings.md §9.2` beyond the four covered above (e.g. `AUTO-GENERATED` header check on `*_binding.zig` files themselves, all keepers have a `.api.zig`, no manual `.d.etch`). These require the bindgen pipeline itself, not present at M0.0. -- **Any spec patch outside the three KB files explicitly listed.** Phase −1 retrospective already covered `engine-roadmap.md` §3 and `engine-development-workflow.md`. +- **Any spec patch outside the three KB files explicitly listed.** Phase −1 retrospective already covered `engine-roadmap.md` § "Contenu des phases" and `engine-development-workflow.md`. - **Any modification of Tier 0 code** (ECS, jobs, IPC, platform). - **Any extension of the `c-api/` or `bindgen/` modules.** - **`spec/` directory addition to the repo** (cf. `engine-development-workflow.md §3.5` — to be re-evaluated separately if friction emerges). diff --git a/briefs/M0.2-rtti-resources-events-bindgen.md b/briefs/M0.2-rtti-resources-events-bindgen.md index f9d18b6..c8619d4 100644 --- a/briefs/M0.2-rtti-resources-events-bindgen.md +++ b/briefs/M0.2-rtti-resources-events-bindgen.md @@ -188,7 +188,7 @@ Mandatory, complete reading before any production code is written (Step 2 of the 1. `engine-phase-0-plan.md` — section **M0.2** (canonical scope, deliverables, advanced criteria, absorbed Phase −1 debts, branch, tag). 2. `engine-phase-0-criteria.md` — sections **§ Bench methodology**, **§ Quantified non-regression gates**, **C0.1**, **C0.5**, **C0.10**. -3. `engine-invariants.md` — **`ARCH-024`** (Tier 0/1 catalog, 7 C keepers; detail `engine-c-bindings.md`), **`ARCH-007`** (RTTI / component registry), **`ARCH-008`** (versioned serialization), **`ARCH-011`** (memory model — for the resource context), **`ARCH-006`** (singleton-entity resources — acted, non-rediscussable decision), **`ARCH-013`** (Tier 0 catalog); **`engine-roadmap.md` §3** (roadmap) and **`engine-phase-minus-1-archive.md`** (S6 `schema_hash` Wyhash paragraph). +3. `engine-invariants.md` — **`ARCH-024`** (7 C keepers; detail `engine-c-bindings.md` § "Liste des `.api.zig` manuels"), **`ARCH-007`** (RTTI / component registry), **`ARCH-008`** (versioned serialization), **`ARCH-011`** (memory model — for the resource context), **`ARCH-006`** (singleton-entity resources — acted, non-rediscussable decision), **`ARCH-013`** + **`ARCH-014`** (Tier 0/1 catalog); **`engine-roadmap.md` § "Contenu des phases"** (roadmap) and **`engine-phase-minus-1-archive.md`** (S6 `schema_hash` Wyhash paragraph). 4. `engine-ecs-internals.md` — sections **§5** (tick-based change detection — Resources consume it), **§8** (observers — consumed the RTTI in M0.1, check cross-step integration), **§12** (ECS comparison — Bevy 2026 singleton resources referenced). 5. `engine-tier-interfaces.md` — sections **§0** (principles, ModuleContext), **§1** through **§10** (interfaces already specified — for signature consistency in `WeldAPI`). 6. `engine-c-api.md` — sections **§0** (Tier 1 vs Tier 3), **§2** (fundamental types), **§3** (plugin lifecycle), **§4** (main API table), **§5** (WeldEcsAPI), **§6** (WeldResourceAPI), **§7** (WeldEventAPI), **§8** (WeldServiceAPI). The sections covering WeldMemoryAPI / WeldEditorAPI / WeldPlatformAPI complete the table to be reproduced as signatures. @@ -361,7 +361,7 @@ M0.2 smoke OK **Removal of `tools/vk_gen/` and `tools/wayland_gen/`.** In a separate commit after the effective empty-diff validation. If the intermediate E5 review requests adjustments, keeping the old directories allows a quick rollback. The removal is the last commit of E5 — not the first. -**Cross-doc audit.** The edit of `engine-phase-minus-1-archive.md` (S6 swap note) is the only spec modification expected in this milestone. If reading the specs in Step 2 reveals other inconsistencies (for example a reference to `tools/vk_gen/` in a file other than `engine-spec.md` or `engine-c-bindings.md`), that is a light blocker: log it, request a patch via this Claude.ai conversation, do not patch unilaterally. +**Cross-doc audit.** The edit of `engine-phase-minus-1-archive.md` (S6 swap note) is the only spec modification expected in this milestone. If reading the specs in Step 2 reveals other inconsistencies (for example a reference to `tools/vk_gen/` in a file other than `engine-directory-structure.md` § "Structure interne du repo Weld lui-même" or `engine-c-bindings.md`), that is a light blocker: log it, request a patch via this Claude.ai conversation, do not patch unilaterally. **No new C binding.** M0.2 introduces no keeper. The test stub plugin is pure Zig compiled into a `.so`/`.dll`, not a third-party C binding. The 7 Phase 1+ keepers (`ARCH-024`) stay out of scope. diff --git a/briefs/M0.4-renderer-vulkan-forward-and-gal.md b/briefs/M0.4-renderer-vulkan-forward-and-gal.md index b29c177..3833245 100644 --- a/briefs/M0.4-renderer-vulkan-forward-and-gal.md +++ b/briefs/M0.4-renderer-vulkan-forward-and-gal.md @@ -77,7 +77,7 @@ M0.4 advances criterion C0.3 (minimal Vulkan forward renderer) by designing the 6. `engine-tier-interfaces.md` — the Tier 0 `RenderModule` interface. The GAL is a Tier 1 module that must respect this contract. 7. `engine-development-workflow.md` — §2.2 granularity, §3 brief format, §3.6.1 local cross-doc audit, §4.3 Conventional Commits, §4.5.1 semantic runtime CI validation + manual GPU gate (**first milestone applying it — read carefully**), §4.6 squash commit format (structured long body, 6 mandatory sections), §4.7 tag procedure. 8. `engine-zig-conventions.md` — Zig 0.16.x reminders (std.Io, allocators, Writer/Reader, naming), §14 isolation of `*_c` modules, §16 Weld conventions (POD `extern struct` ECS components). -9. `ARCH-017` (in-tree as-if-lib, no separable lib extraction), `engine-render.md` — Render Module (GAL + V-Buffer + target pipeline recap), `engine-phase-minus-1-archive.md` S2 § Precisions (multi-GPU selection consistency, `--smoke-test` mode, committed SPIR-V). +9. `ARCH-017` (in-tree as-if-lib, no separable lib extraction), `engine-render.md` § "Architecture" — Render Module (GAL + V-Buffer + target pipeline recap), `engine-phase-minus-1-archive.md` S2 § Precisions (multi-GPU selection consistency, `--smoke-test` mode, committed SPIR-V). 10. `engine-platform.md` — §4 Windowing (the GAL creates the Vulkan surface on top of the M0.3 Tier 0 Window interface, check the integration signature), §4 Input (not touched but general consistency). ## Files diff --git a/briefs/M0.8-full-grammar-v0.6.md b/briefs/M0.8-full-grammar-v0.6.md index dc0caf9..8b7bbc1 100644 --- a/briefs/M0.8-full-grammar-v0.6.md +++ b/briefs/M0.8-full-grammar-v0.6.md @@ -106,7 +106,7 @@ The work is delivered as **seven vertical stages** (parser→interpreter→codeg 14. `etch-style-guide.md` — Etch style for the reference file. 15. `engine-scene-serialization.md` — `scene`/`prefab` serialization, typed extensions. 16. `engine-ecs-internals.md` — archetype matching, deferred commands, event bus. -17. `engine-phase-minus-1-archive.md` — S3/S4/S5 results; `engine-roadmap.md` §3 — Etch pipeline. +17. `engine-phase-minus-1-archive.md` — S3/S4/S5 results; `engine-roadmap.md` § "Carte globale des phases par module" — Etch pipeline. 18. `engine-phase-0-criteria.md` — C0.2. 19. `engine-development-workflow.md` — brief/commit format, language criterion. 20. `engine-zig-conventions.md` — §13 module rooting (lazy analysis guard), naming, `extern struct` `u8`-not-`bool`. diff --git a/briefs/M1.0.13-time-and-timers.md b/briefs/M1.0.13-time-and-timers.md index 4aed5e9..9e551f7 100644 --- a/briefs/M1.0.13-time-and-timers.md +++ b/briefs/M1.0.13-time-and-timers.md @@ -54,7 +54,7 @@ Executed **gate by gate E1→E6**, in order. Each gate is pushed and reviewed on - **`quantize`** (`quantize_stmt`, `etch-grammar.md §4.3`; `etch-reference-part1.md §9.11`) — depends on a beat/bar musical clock (Sequencer/Pulse) absent from the Phase-1 runtime. `quantize` stays reserved in `non_s3_keywords`; its statement-head fail-loud stays (message re-pointed); `quantize_stmt` stays a payloadless placeholder. Assigned to a later Sequencer-adjacent milestone — a scope boundary, NOT parked debt. - **`time.*` stdlib sugar** (`etch-stdlib.md §20`: `time.dt`/`time.total`/`time.scale`/`time.unscaled_dt`/`time.fixed_dt`, lowering `time.X → get(GameTime|UnscaledTime).Y`) — an additive resolution add-on over the resources this milestone delivers; introducing it later touches no call site here. Later stdlib milestone. (`§20`'s `time.scale`/`time.frame_count` vs the real `time_scale`/`frame` field names is reconciled with the sugar, not now.) -- **`dt` as an injected rule parameter** (`rule tick(entity: Entity, dt: float)`, `etch-reference-part1.md`) — additive injection over `GameTime.dt`, later milestone. +- **`dt` as an injected rule parameter** (`rule tick(entity: Entity, dt: float)`, `etch-reference-part1.md` § "`async fn` et `async rule`") — additive injection over `GameTime.dt`, later milestone. - **Per-entity `TimeDilation`, selective pause (`@pause_group` / `PauseState`), `WorldClock`/day-night** (`engine-gameplay-systems.md`) — depend on the phase scheduler (per-rule dispatch metadata, per-entity `dt` in iteration) which does not exist in the tree-walker. - **Non-literal `Duration` for `await wait` / `await wait_unscaled`** — both keep the M1.0.11 literal-only restriction (non-literal Duration → fail-loud). Only the timer family evaluates a full `Duration` expression (its one-shot scheduling has no suspension reentry). Lifting `wait`'s restriction is a separate additive change, out of scope. - **New diagnostic codes** — none are minted (see § Notes). If a genuinely new validation surfaces mid-gate, STOP for a Claude.ai round-trip before minting `E0908`. diff --git a/briefs/M1.0.15-test-runner.md b/briefs/M1.0.15-test-runner.md index 6074198..3c6f9ce 100644 --- a/briefs/M1.0.15-test-runner.md +++ b/briefs/M1.0.15-test-runner.md @@ -36,7 +36,7 @@ M1.0.8 delivered `test "X" { block }` as parse + symbol registration only: `inte - **Multi-world** (`test_world()` creating additional/independent worlds). The mono-world realization is a documented v0.6 spec restriction (part2 §32 normative block), not parked debt. - **Async test bodies.** `await` in a test body stays `E0901` — a spec'd v0.6 restriction. Async behavior is exercised through rules + `tick(n)`. -- **The `weld` CLI / `weld test` command.** Future consumer of this same library (`engine-platform.md`); no CLI-weld scaffolding in this milestone beyond the `etch_test` shim. +- **The `weld` CLI / `weld test` command.** Future consumer of this same library (`engine-platform.md` § "Build System — CLI `weld`"); no CLI-weld scaffolding in this milestone beyond the `etch_test` shim. - **`capture_events` helper and a dedicated `integration_test` construct** — removed from the KB by the M1.0.15 spec reconciliation; do not implement. - **Test filtering CLI flags** (`--filter`, tag selection). Purely additive; `@only` covers focusing in v0.6. - **`measure` outside test bodies** — rejected by design (`E0910`). diff --git a/briefs/M1.0.4-scene-cook.md b/briefs/M1.0.4-scene-cook.md index 9355979..a845e86 100644 --- a/briefs/M1.0.4-scene-cook.md +++ b/briefs/M1.0.4-scene-cook.md @@ -63,7 +63,7 @@ This milestone **assembles** existing bricks — `descriptor.zig` (`Scene`/`Scen 3. `engine-asset-pipeline.md` — §6.3 (scene cooking steps), cooking cache. 4. `etch-reference-part2.md` — §30 (`prefab` `of` / `extends`) + scene / resource constructs. 5. `etch-grammar.md` — `scene_decl` / `prefab` productions. -6. `engine-scene-serialization.md` (scene serialization), `ARCH-017` (in-tree discipline), `ARCH-004` (component POD/SoA invariant). +6. `engine-scene-serialization.md` § "Architecture" (scene serialization), `ARCH-017` (in-tree discipline), `ARCH-004` (component POD/SoA invariant). 7. `engine-zig-conventions.md` — §13 (test rooting / lazy-analysis guard — mandatory so `tests/scene/` actually runs), Zig 0.16.x conventions. ## Files to create or modify diff --git a/briefs/M1.0.5-scene-load.md b/briefs/M1.0.5-scene-load.md index 5ab391e..368af29 100644 --- a/briefs/M1.0.5-scene-load.md +++ b/briefs/M1.0.5-scene-load.md @@ -59,7 +59,7 @@ This milestone **assembles** existing bricks — `accessor.zig` (reused verbatim 1. `engine-scene-serialization.md` — §4 (`.scene.bin` layout + the runtime loading section — PRIMARY; note the pseudo-code's explicit "ECS instantiation surface reconfirmed at M1.0.5" banner — this brief is that reconfirmation), §2 (UUID + name identity), §5 (prefabs — to see the M1.0.6 boundary). 2. `engine-ecs-internals.md` — §10 (ECS serialization: Schema Registry, the spawn-all-then-remap identity pattern). -3. `engine-scene-serialization.md` (scene serialization), `ARCH-017` (in-tree discipline). +3. `engine-scene-serialization.md` § "Architecture" (scene serialization), `ARCH-017` (in-tree discipline). 4. `engine-zig-conventions.md` — §13 (test rooting / lazy-analysis guard — mandatory so `tests/scene/` actually runs), §19 (rules summary, POD `extern struct`). ## Files to create or modify @@ -160,7 +160,7 @@ This milestone **assembles** existing bricks — `accessor.zig` (reused verbatim - `f513782` — E3 scope amended (authorized by Claude.ai): the refcounted persistent heap moves `src/etch/persistent.zig` → `src/core/memory/persistent.zig` (Tier 0) so the `weld_core` loader can intern resource `string` fields without importing `weld_etch`. Root cause: M1.0.3 placed a tier-neutral heap (`runDrop` no-op) in `weld_etch`; the directory structure targets `src/core/memory` for persistent pools. `StringSlot` 16-byte layout unchanged — no format/ABI impact. - `02f8196` — E3 correction (b), authorized by Claude.ai: an out-of-range parent ordinal now returns `error.MalformedScene` (new `StructureError` — "invalid scene structure") instead of `error.CorruptScene` (reserved for content-hash mismatch). Adds a 4th loader error beyond the brief's original `CorruptScene`/`UnknownComponent`/`SchemaMismatch`, and **supersedes** the E2 micro-decision (b) logged 2026-06-27 17:20. Inline test forges a hash-valid `.scene.bin` with a parent ordinal past the UUID table → `error.MalformedScene`. -- `dc5d16e` — E3 resource install refined to **install-or-overwrite**, not a strict `addResource`. Discovered via T6: a running Etch program's `Interpreter.compile` pre-installs declared resources with their defaults, so loading a scene that sets the same resource hit `error.DuplicateResource`. Resolved per `engine-scene-serialization.md` (scene `resources {…}` are *injected into the resource map at load*): the scene value is authoritative and overrides the compile-time default (`getMutResource` + memcpy when the resource is present, else `addResource`). Ownership stays clean — the overridden value's string blocks remain owned by their installer (the interp frees its compile-time defaults at teardown); the newly-interned blocks are owned by `LoadResult`. **Flag for review:** the brief's word was "addResource"; this is the spec-grounded completion for the program-then-scene runtime flow. +- `dc5d16e` — E3 resource install refined to **install-or-overwrite**, not a strict `addResource`. Discovered via T6: a running Etch program's `Interpreter.compile` pre-installs declared resources with their defaults, so loading a scene that sets the same resource hit `error.DuplicateResource`. Resolved per `engine-scene-serialization.md` § "Resources de scène — install-or-overwrite" (scene `resources {…}` are *injected into the resource map at load*): the scene value is authoritative and overrides the compile-time default (`getMutResource` + memcpy when the resource is present, else `addResource`). Ownership stays clean — the overridden value's string blocks remain owned by their installer (the interp frees its compile-time defaults at teardown); the newly-interned blocks are owned by `LoadResult`. **Flag for review:** the brief's word was "addResource"; this is the spec-grounded completion for the program-then-scene runtime flow. - `dc5d16e` — T6 integration test lives **inline in `src/etch/interp.zig`** (the brief explicitly permits this) rather than as `tests/scene/load_integration_test.zig`. Reason: the assertion counts emitted events by reading the interpreter's private per-tick `EventStore`, only reachable from within `interp.zig`. `tests/scene/load_integration_test.zig` is therefore not created. ## Blockers encountered @@ -170,7 +170,7 @@ This milestone **assembles** existing bricks — `accessor.zig` (reused verbatim ## Closing notes - **What worked**: The central hypothesis held — the M1.0.4 zero-copy `accessor` (reused **verbatim**) + the existing M0.1 spawn surface (`spawnDynamicWithValues`) + the M1.0.2 `ObserverRegistry` were sufficient to materialize a cooked scene into a live `World` with **no new ECS storage primitive**. The genuinely new code is small: `loader.zig` (open/verify + schema remap + per-entity instantiate + UUID map + two-phase lifecycle + resource interning) and one public observer entry (`dispatchOnSpawned`). The two-phase `on_spawned` (phase-1 direct spawn fires no observers; phase-2 dispatches per entity) gives the "all entities exist before any `on_spawned`" ordering cleanly. The persistent-heap move to Tier 0 was mechanical (the heap is tier-neutral, `runDrop` a no-op) and unblocked World-free resource-string interning. The gate split (E1 units → E2 instantiation → E3 resources/integration) kept each review tight. -- **What deviated from the original spec**: All traced in Accepted deviations. (1) E1/E2 structuring — `fs.mmapFile`'s *ownership* (`LoadResult.mmap`) is E2; E1 shipped byte-level units; `loadFromBytes` exposed as the byte-level core alongside `loadScene(path)`. (2) `error.MalformedScene` added (correction b, authorized) for a structurally-invalid scene, distinct from `CorruptScene`. (3) Persistent heap moved `src/etch/persistent.zig` → `src/core/memory/persistent.zig` (Claude.ai amendment). (4) Resource install is **install-or-overwrite** (spec-grounded, `engine-scene-serialization.md`) not strict `addResource`. (5) T6 lives inline in `interp.zig` (brief-permitted). (6) `core/root.zig` wired the `memory` submodule (not `build.zig` — single Zig module). +- **What deviated from the original spec**: All traced in Accepted deviations. (1) E1/E2 structuring — `fs.mmapFile`'s *ownership* (`LoadResult.mmap`) is E2; E1 shipped byte-level units; `loadFromBytes` exposed as the byte-level core alongside `loadScene(path)`. (2) `error.MalformedScene` added (correction b, authorized) for a structurally-invalid scene, distinct from `CorruptScene`. (3) Persistent heap moved `src/etch/persistent.zig` → `src/core/memory/persistent.zig` (Claude.ai amendment). (4) Resource install is **install-or-overwrite** (spec-grounded, `engine-scene-serialization.md` § "Resources de scène — install-or-overwrite") not strict `addResource`. (5) T6 lives inline in `interp.zig` (brief-permitted). (6) `core/root.zig` wired the `memory` submodule (not `build.zig` — single Zig module). - **What to flag explicitly in review**: All reviewed and validated across the E1/E2/E3 gates (install-or-overwrite, T6 inline, `core/root.zig` wiring, MalformedScene). Nothing outstanding. - **Final measurements**: `scene_load_bench` (`zig build bench-scene-load -Doptimize=ReleaseFast`, **Apple M4 Pro**): **median 1.050 ms / 10 000 entities** (min 0.907, max 2.111; 50 runs, 3 warmup). Far under the spec ~10–50 ms/10k reference. **Decision: per-entity instantiation confirmed; bulk SoA column-copy is a genuine YAGNI — no bulk-spawn milestone scheduled.** The instantiate step keeps a clean internal boundary so a bulk path could later swap its body without touching `loadScene`'s signature or call sites. - **Residual risks / deliberately-left technical debt**: diff --git a/briefs/M1.0.6-prefabs-crossrefs-extensions.md b/briefs/M1.0.6-prefabs-crossrefs-extensions.md index cf3e462..896e07f 100644 --- a/briefs/M1.0.6-prefabs-crossrefs-extensions.md +++ b/briefs/M1.0.6-prefabs-crossrefs-extensions.md @@ -133,7 +133,7 @@ Sparse (entities with no active extension are absent). The `§4` note's `u8` cou 3. `etch-reference-part2.md` — §30 (the `prefab` construct: §30.4 `of`-vs-`extends` differentiation, §30.5 additive-conflict validation). 4. `etch-reference-part1.md` — §3.2 / §5.5 (the `Entity` builtin type; POD-in-component rules). 5. `engine-asset-pipeline.md` — §6.3 (scene/prefab cooking; reconciled — prefab flattening is M1.0.6, the M1.0.4 cook rejects `instance of`). -6. `engine-scene-serialization.md` (scene serialization), `ARCH-004` (component POD/SoA invariant — governs `FieldKind.entity_`), `ARCH-017` (in-tree discipline). +6. `engine-scene-serialization.md` § "Architecture" (scene serialization), `ARCH-004` (component POD/SoA invariant — governs `FieldKind.entity_`), `ARCH-017` (in-tree discipline). 7. `etch-resolver-types.md` — §14 (the `override` frontier — confirms `override` stays out-of-scope). 8. `engine-zig-conventions.md` — §13 (test rooting / lazy-analysis guard — so `tests/scene/` actually runs), §19 (POD `extern struct`, rules summary). diff --git a/briefs/M1.1.5-integration-euler.md b/briefs/M1.1.5-integration-euler.md index 5a7a6c3..cbe14c2 100644 --- a/briefs/M1.1.5-integration-euler.md +++ b/briefs/M1.1.5-integration-euler.md @@ -16,7 +16,7 @@ ## Context -This is the FIRST sub-milestone where bodies move. M1.1.0–4 laid the static machinery: the SoA `BodyManager` store (position, rotation, linear/angular velocity, `MotionProperties`), the broadphase BVH, and the narrowphase (GJK/EPA/manifold + analytic fast paths). No stepping exists yet — M1.1.0 explicitly deferred it (`PhysicsModule` freezes at M1.1.15). This milestone adds the integration stage the plan freezes at the M1.1.5 row of `engine-phase-1-plan.md` — "Integration : semi-implicit Euler + gravité + damping", demonstrator "Box en chute libre, trajectoire correcte" — landing it in `pipeline/integration.zig`, the file the spec directory tree (`engine-directory-structure.md`; `ARCH-017`) reserves for "semi-implicit Euler, sleep, CCD" (only the Euler + gravity + damping part lands here; sleep is M1.1.8, CCD is later). +This is the FIRST sub-milestone where bodies move. M1.1.0–4 laid the static machinery: the SoA `BodyManager` store (position, rotation, linear/angular velocity, `MotionProperties`), the broadphase BVH, and the narrowphase (GJK/EPA/manifold + analytic fast paths). No stepping exists yet — M1.1.0 explicitly deferred it (`PhysicsModule` freezes at M1.1.15). This milestone adds the integration stage the plan freezes at the M1.1.5 row of `engine-phase-1-plan.md` — "Integration : semi-implicit Euler + gravité + damping", demonstrator "Box en chute libre, trajectoire correcte" — landing it in `pipeline/integration.zig`, the file the spec directory tree (`engine-directory-structure.md` § "Structure interne du repo Weld lui-même") reserves for "semi-implicit Euler, sleep, CCD" (only the Euler + gravity + damping part lands here; sleep is M1.1.8, CCD is later). It is FREE-FLIGHT integration only. Broadphase/narrowphase exist but are NOT invoked — there is no contact response (Sequential Impulses is M1.1.6). A dynamic body under gravity follows an unobstructed trajectory. Integration lives INSIDE `forge_3d` (a free function over the `BodyManager` SoA store); it is NOT wired into the `PhysicsModule` interface, whose `step` orchestration composes broadphase → narrowphase → solve → integrate at M1.1.15. diff --git a/briefs/S0-bootstrap.md b/briefs/S0-bootstrap.md index 9f33ad6..4c1df9a 100644 --- a/briefs/S0-bootstrap.md +++ b/briefs/S0-bootstrap.md @@ -49,7 +49,7 @@ S0 is the first milestone of Phase −1 and the first milestone of the entire We ## Spec documents to read first -1. `engine-phase-minus-1-archive.md` (Phase −1 spike list, S0 entry) and `ARCH-017` (in-tree default, no `spec/` in repo) +1. `engine-phase-minus-1-archive.md` (Phase −1 spike list, S0 entry) and `ARCH-017` (in-tree default; "no `spec/` in repo" — historical, no KB owner) 2. `engine-development-workflow.md` — §2 (milestone model), §3 (brief format), §3.4 (`CLAUDE.md` lifecycle), §4 (git conventions: branches, tags, Conventional Commits, PRs, lefthook, squash-and-merge) 3. `engine-zig-conventions.md` — §17 (Zig version policy: 0.16.x strict, patches accepted, minor refused) 4. `engine-phase-0-criteria.md` — context on what comes next (informs `CLAUDE.md` content; no implementation impact at S0) diff --git a/briefs/S1-mini-ecs.md b/briefs/S1-mini-ecs.md index dcd03be..6db2782 100644 --- a/briefs/S1-mini-ecs.md +++ b/briefs/S1-mini-ecs.md @@ -52,7 +52,7 @@ Second spike of Phase −1. Validates the core architectural hypothesis of Weld' ## Documents to read first -1. `ARCH-005` (ECS overview; detail `engine-ecs-internals.md`), `ARCH-017` (in-tree extraction criterion), `engine-phase-minus-1-archive.md` S1 (canonical milestone definition). +1. `engine-ecs-internals.md` § "Architecture" (ECS overview), `ARCH-005`, `ARCH-017` (in-tree extraction criterion), `engine-phase-minus-1-archive.md` S1 (canonical milestone definition). 2. `engine-ecs-internals.md` — §1 (architecture overview), §2 (chunk SoA layout), §4 (query compilation), §12 (comparison with other ECS). 3. `engine-zig-conventions.md` — full read; in particular naming, allocator passing, `extern struct` POD components, doc comments on public API, ban on `@cImport` outside `*_c` modules and on `usingnamespace`. 4. `engine-development-workflow.md` — §2 (milestone model), §3 (brief format), §4 (git conventions, hooks, squash-merge), §5 (Claude review cycle). diff --git a/briefs/S2-window-vulkan-triangle.md b/briefs/S2-window-vulkan-triangle.md index 54f2d58..3213aa6 100644 --- a/briefs/S2-window-vulkan-triangle.md +++ b/briefs/S2-window-vulkan-triangle.md @@ -64,7 +64,7 @@ Third spike of Phase −1. Validates the hypothesis that a Win32 window, a Wayla ## Spec documents to read first -1. `engine-phase-minus-1-archive.md` Phase −1 / S2 (full canonical definition including post-conversation refinements), `ARCH-001`–`ARCH-006` (overview), `ARCH-017` (in-tree vs separable libs criterion), `ARCH-024` (8 keepers list — none added in S2). +1. `engine-phase-minus-1-archive.md` Phase −1 / S2 (full canonical definition including post-conversation refinements), `ARCH-001`–`ARCH-006` (overview), `ARCH-017` (in-tree vs separable libs criterion), `engine-c-bindings.md` § "Liste des `.api.zig` manuels" (8 keepers list — none added in S2). 2. `engine-c-bindings.md` — §1.1 to §1.4 (rationale registry-driven and S2 carve-out), §4.2 (idiomatic mapping rules — these are the conformance target for the S2 generators), §10.1 (S2→S3 sequencing). 3. `engine-platform.md` — §1 (architecture, what belongs to the platform layer), §2 windowing and HiDPI subsections. 4. `engine-mach-reference.md` — §5 (native windowing inspiration: Win32 ~800 lines direct, Wayland ~2100 lines + a tiny C callbacks file). Read as design inspiration, **not** as a code template — Weld's S2 attempts pure Zig callbacks via `callconv(.c)`. diff --git a/briefs/chore-spec-reference-reconciliation.md b/briefs/chore-spec-reference-reconciliation.md index 608e6d3..0bc2803 100644 --- a/briefs/chore-spec-reference-reconciliation.md +++ b/briefs/chore-spec-reference-reconciliation.md @@ -245,7 +245,7 @@ form: > *consequence* of the list, not the gate. A 31st line is a defect; a 29th is a > damaged record. -Five classes, four of which the body could not have anticipated: +Six classes, five of which the body could not have anticipated: | Class | Count | Lines | |---|---|---| @@ -357,34 +357,105 @@ the pointer follows the current owner of the *assertion* — `dt` as an injected rule parameter — not the historical descendant of the section. `etch-stdlib.md` owns `time.dt`, a different surface. -### A9 — Rendering conventions - -Three, applied uniformly and worth naming because they are the difference -between a repair and a reword: - -1. **Short `ARCH-nnn` form in code comments**, per `engine-invariants.md` §1.2 - ("Dans la prose, une référence courte suffit"). `; detail ` is added at - exactly two sites, where the sentence states content the registry explicitly - disclaims as detail: the 16 KiB chunk dimension (`chunk.zig`, disclaimed by - the admission test of §1.3) and the resource lifecycle-tag table - (`rtti/type_info.zig`). Citing the ARCH id alone there would have been a - *wrong* pointer, not merely a terse one. -2. **A spike id stays attached to the archive filename with no section number** — - `` `engine-phase-minus-1-archive.md` S6 `` — because the archive is indexed by - spike exactly as the roadmap is indexed by phase. Side effect: no `§` - reintroduced. -3. **A named anchor only where the filename designates nothing on its own**, as - the body prescribes for `§3.1`. Two files qualified: - `engine-zig-conventions.md` § "Fichier racine : `root.zig` (module) vs - `main.zig` (exécutable)" and `engine-tools-editor.md` § "Panneau Etch Text — - éditeur de code". The section numbers are dropped in both cases — a shifting - number is the failure mode being repaired, and "6 bis" has already moved once. +### A9 — Rendering conventions, and the anchor policy of the body WITHDRAWN + +The body states that "named anchors are deliberately not used except on `§3.1`". +**That policy is withdrawn.** It contradicts `engine-invariants.md` §1.2, which +prescribes "fichier canonique + ancre nommée" for domain detail with no +exception, and it would have left twenty-five pointers resolving to a whole +document. Every domain-detail target now carries a named anchor, **read in the +owning document and never inferred from the subject**, with the section number +dropped everywhere — a shifting number is the failure mode being repaired, and +"6 bis" has already moved once. + +| Target | Anchor, read | Sites | +|---|---|---| +| `engine-platform.md` | § "Build System — CLI `weld`" | `test_runner.zig:20` · `tools/etch_test:4` · `CLAUDE.md:120` · `M1.0.15:39` | +| `engine-scene-serialization.md` | § "Resources de scène — install-or-overwrite" | `loader.zig:714` · `M1.0.5:163` · `:173` | +| `engine-scene-serialization.md` | § "Architecture" | `M1.0.4:66` · `M1.0.5:62` · `M1.0.6:136` | +| `engine-ecs-internals.md` | § "Archetype Chunk Layout (SoA par composant)" | `chunk.zig:56` | +| `engine-ecs-internals.md` | § "Architecture" | `S1:55` | +| `engine-c-bindings.md` | § "Liste des `.api.zig` manuels" | `CLAUDE.md:150` ×2 · `M0.2:191` · `S2:67` | +| `engine-render.md` | § "Architecture" | `M0.4:80` | +| `engine-asset-pipeline.md` | § "Architecture" | `m0.6:84` | +| `etch-reference-part1.md` | § "`async fn` et `async rule`" | `M1.0.13:57` | +| `engine-roadmap.md` | § "Contenu des phases" | `M0.0:38` · `M0.2:191` | +| `engine-roadmap.md` | § "Carte globale des phases par module" | `M0.8:109` | +| `engine-directory-structure.md` | § "Structure interne du repo Weld lui-même" | `M0.2:364` · `M1.1.5:19` | +| `engine-zig-conventions.md` | § "Fichier racine : `root.zig` (module) vs `main.zig` (exécutable)" | `rtti/root.zig:10` | +| `engine-tools-editor.md` | § "Panneau Etch Text — éditeur de code" | `S3:158` | + +The last two were already anchored and carried no number, so the withdrawal took +nothing back from them. + +Three exemptions, each motivated rather than granted: + +1. **The archive plus a spike id** — `` `engine-phase-minus-1-archive.md` S6 `` — + because the archive is indexed by spike: the id *is* the anchor. Side effect: + no `§` reintroduced anywhere on those 27 sites. +2. **A bare `ARCH-nnn`**, because §1.2 explicitly allows the short form in prose, + and because `engine-invariants.md` followed by an id already *is* the full + `engine-invariants.md#arch-nnn` link its preamble rule asks for — the `#` is a + rendering detail. Nothing to add on `M0.2:191`. +3. **`engine-roadmap.md`**, where §1.2 asks for a phase identifier rather than a + section number. None of the three roadmap sites designates a single phase — + `M0.0:38` and `M0.2:191` designate the roadmap's phase content, `M0.8:109` the + per-module map — so each takes the named anchor of the section it designates, + which is §1.2's primary rule rather than a departure from it. + +One `; detail ` survives, on `chunk.zig`: the 16 KiB chunk dimension is +content `ARCH-005` explicitly disclaims in the admission test of §1.3, so the id +alone would have been a *wrong* pointer, not merely a terse one. The second such +pointer, on `rtti/type_info.zig:48`, is **removed**: the resource lifecycle tags +are stated in `ARCH-006`'s own decision ("Trois tags de cycle de vie — `@config`, +`@state`, `@transient` — déterminent le comportement de sérialisation et de +réplication"), so it is not disclaimed detail and needs no owner. It also has no +anchor available — the tag table is not in `engine-ecs-internals.md`, and +`engine-project-settings.md` was not in the attached set. Two textual side effects, both visible in the diff: the word "table" was dropped from "§2.9 table" because `ARCH-006` carries none, and `rtti/root.zig` reads "the Tier 0 convention of X" rather than "the X Tier 0 convention" because the anchor is too long for the original word order. No other word moved anywhere. +### A10 — The gloss audit, and the one arbitration that had to be revoked + +Every `ARCH-nnn` introduced was re-audited against the **decision** of the id +cited, read in `engine-invariants.md` §2 — not against the section the id was +extracted from. Of **34** glossed references, **26** are covered and **8** were +defects. The count of glossed references is 34 and not 25: a grep motif keyed on +a parenthesis immediately following the id missed six sites whose gloss precedes +it or is the sentence's own subject. (An earlier statement of this arithmetic +said 25 covered and 9 defects; 34 = 26 + 8 is the correct decomposition.) + +The eight, and what each one shows: + +| Site | Gloss | Why it failed | +|---|---|---| +| `M0.2:191` | `ARCH-024` "Tier 0/1 catalog, 7 C keepers" | `ARCH-024` carries only the keepers. The catalogue half goes to `ARCH-013` + `ARCH-014`, which is where the Tier 0 model and the canonical Tier 1 list live — so no word of a closed record's gloss is deleted to make a target fit | +| `S2:67` | `ARCH-024` "8 keepers list" | `ARCH-024` fixes 7, going to 6. No id carries 8; the only document that carries the 8 → 7 transition is `engine-c-bindings.md`, in the Phase −1 tree-sitter note of § "Liste des `.api.zig` manuels". The gloss is the record and does not change | +| `S0:52` | `ARCH-017` "in-tree default, no `spec/` in repo" | `ARCH-017` carries the in-tree default and the absence of `libs/`, nothing about `spec/`. Verified: `spec/` appears nowhere in `engine-directory-structure.md`. There is no KB owner — the decision lives in `CLAUDE.md` § *Open / deferred decisions* — so the clause is qualified as historical rather than given an invented target | +| `M1.1.5:19` | `ARCH-017` "the spec directory tree … reserves for 'semi-implicit Euler, sleep, CCD'" | `ARCH-017` carries no directory tree at all: the bullets of `§3.5` that named `src/modules/forge/solvers_{2d,3d}//` — a path itself made stale by the M1.1.1 flatten — did not survive into the invariant. `engine-directory-structure.md`, already cited on the same line, is the owner, and the `ARCH-017` half is dropped as a mis-attribution | +| `S1:55` | `ARCH-005` "ECS overview" | An overview of the ECS spans `ARCH-003` to `ARCH-010`. The gloss reattaches to the owner, `engine-ecs-internals.md` § "Architecture"; `ARCH-005` stays alongside, unglossed | +| `foundation/root.zig:3` | `ARCH-017` "sibling submodules with no mutual dependency" | → `ARCH-016` | +| `scene/root.zig:8` | `ARCH-017` "tier discipline, imports `weld_core` only, never `weld_etch`" | → `ARCH-013` + `ARCH-016` | +| `scene/loader.zig:12` | `ARCH-017` "Tier discipline: imports `weld_core` internals only" | → `ARCH-013` + `ARCH-016` | + +**The last three revoke the GO given at the source gate**, and that is worth +recording plainly because it is the only arbitration of this chore that had to be +taken back. At E1 the three were verified against the *source text* of the +27-section `§3.5`, whose "Discipline d'API in-tree" literally requires "Zero +coupling avec d'autres modules Weld", and `ARCH-017` was accepted on that +measurement. The rule written afterwards is stricter and asks whether the gloss +is covered by the **decision of the id cited** — and `ARCH-017` does not carry the +coupling clause, it *delegates* it: "couplage déclaré et acyclique (`ARCH-016`)". +The two readings cannot both hold; the property of the cited id is what a reader +resolves, so the stricter rule wins and `ARCH-016` — "un module n'accède qu'à +`foundation`, au Tier 0, et aux modules explicitement déclarés dans son +`b.addModule`" — is what those three sentences actually assert. What made the +revocation possible was raising the tension rather than silently applying +whichever rule was most recent. + ## Execution notes Gate by gate, each ending at a STOP with an explicit GO before the next. @@ -397,11 +468,20 @@ Gate by gate, each ending at a STOP with an explicit GO before the next. | E1ter | `4efe8ad` | the second language fix (A1) | | E2 | `88c2b1c` | `CLAUDE.md` — 4 references + § *Quick links spec* rewritten | | E3 | `feebb78` | 52 references over 51 lines, 21 briefs + the S5 record | +| E5 | this commit | named anchors (A9), the 8 gloss defects (A10), `M0.2:364` (A5) | **77 references repaired.** `engine-spec.md` occurrences across the tree: 97 in -38 files at open, 37 in 18 files at close. Ten distinct ARCH ids used as +38 files at open, 37 in 18 files at close. Twelve distinct ARCH ids used as targets — `ARCH-004`, `-005`, `-006`, `-007`, `-008`, `-011`, `-012`, `-013`, -`-017`, `-024` — plus the `ARCH-001`–`ARCH-006` range at `S2:67`. +`-014`, `-016`, `-017`, `-024` — plus the `ARCH-001`–`ARCH-006` range at +`S2:67`. `ARCH-014` and `ARCH-016` enter at E5 through the gloss audit (A10), +not through the substitution table. + +**A2 and A9 were rewritten in place** rather than corrected by a second append: +the body's anchor policy is withdrawn, so the section that recorded it had to +state the withdrawal, and a patch of a patch would have left two contradictory +renderings of the same rule in the same file. The frozen body is untouched +either way — only the append changed. **Verification that references alone moved.** For each of the 51 changed lines in E3, the reference tokens (`§n.n`, `ARCH-nnn`, `*.md`) were neutralised and the diff --git a/briefs/m0.6-assets.md b/briefs/m0.6-assets.md index 63b7810..fe79066 100644 --- a/briefs/m0.6-assets.md +++ b/briefs/m0.6-assets.md @@ -81,7 +81,7 @@ Mandatory reads before any production code; Claude Code ticks each box in the LI 1. `engine-phase-0-plan.md` — § M0.6 — scope source of record. 2. `engine-asset-pipeline.md` — §1–10, especially §3 (the normative intermediate-format schema) — intermediate/runtime formats, importers, cookers, cache, registry, async loader. 3. `etch-grammar.md` — §21.4 — grammar of the `asset` construct (category 4, pipeline-generated). -4. `engine-asset-pipeline.md` (Asset Pipeline) and `ARCH-017` (in-tree as-if-lib discipline) — master alignment. +4. `engine-asset-pipeline.md` § "Architecture" (Asset Pipeline) and `ARCH-017` (in-tree as-if-lib discipline) — master alignment. 5. `engine-simd.md` — §1–3 (module role, structure, two-level API), §7.1 (Asset Pipeline hot-path map), §9 (phasing: M0.6 adds the skeleton + `adler32` + `paeth_filter_decode`), §10-referenced `@Vector`-first / asm-second discipline. 6. `engine-zig-conventions.md` — §13 surface coverage (lazy analysis guard), module rooting rule, codecs in-tree convention, `root.zig` convention. 7. `engine-directory-structure.md` — `src/modules/asset_pipeline/` and `src/foundation/simd/` layout. diff --git a/src/core/ecs/chunk.zig b/src/core/ecs/chunk.zig index 30ca5b0..0d4ff58 100644 --- a/src/core/ecs/chunk.zig +++ b/src/core/ecs/chunk.zig @@ -54,7 +54,7 @@ const Tick = tick_mod.Tick; /// Total chunk size — locked to 16 KiB to fit comfortably in L1D on modern /// x86-64, Apple Silicon, and ARM Cortex CPUs (cf. `ARCH-005`; detail -/// `engine-ecs-internals.md`). +/// `engine-ecs-internals.md` § "Archetype Chunk Layout (SoA par composant)"). pub const ChunkSize: usize = 16 * 1024; /// Required alignment of the chunk and of every SoA column within it. diff --git a/src/core/rtti/type_info.zig b/src/core/rtti/type_info.zig index b455e8e..6b1e184 100644 --- a/src/core/rtti/type_info.zig +++ b/src/core/rtti/type_info.zig @@ -45,9 +45,8 @@ pub const Category = enum(u8) { /// FROZEN — see engine-phase-0-criteria.md C0.5 (M0.2) /// Lifecycle hint for resources. Drives the serialization / -/// replication policy (cf. `ARCH-006`; detail `engine-ecs-internals.md`). -/// Only carries meaning when `TypeInfo.category == .resource`; `null` for -/// the other categories. +/// replication policy (cf. `ARCH-006`). Only carries meaning when +/// `TypeInfo.category == .resource`; `null` for the other categories. pub const Lifecycle = enum(u8) { /// `@config` — serialized in scene files, not in saves, not /// replicated. diff --git a/src/core/scene/loader.zig b/src/core/scene/loader.zig index 02a8d87..c6b5336 100644 --- a/src/core/scene/loader.zig +++ b/src/core/scene/loader.zig @@ -10,7 +10,7 @@ //! `world.addResource`). //! //! Tier discipline: imports `weld_core` internals only — never `weld_etch` -//! (`ARCH-017`). The cook driver's Etch coupling lives in +//! (`ARCH-013` + `ARCH-016`). The cook driver's Etch coupling lives in //! `src/etch/scene_cook.zig`; the loader consumes only the neutral byte image. //! //! ## Stages (gate-split, see `briefs/M1.0.5-scene-load.md`) @@ -712,10 +712,10 @@ pub fn runtimeDeactivate(world: *World, gpa: std.mem.Allocator, entity: EntityId /// transactional (D2). An empty string keeps the zeroed slot (`ptr == 0`). /// /// Scene resources are *injected into the resource map at load* -/// (`engine-scene-serialization.md`): the scene value is authoritative and -/// overrides a value the running program already installed (e.g. a declared -/// resource's defaults) rather than erroring; the overridden value's old -/// string blocks are decreffed at commit. +/// (`engine-scene-serialization.md` § "Resources de scène — install-or-overwrite"): +/// the scene value is authoritative and overrides a value the running program +/// already installed (e.g. a declared resource's defaults) rather than +/// erroring; the overridden value's old string blocks are decreffed at commit. fn loadResources( world: *World, gpa: std.mem.Allocator, diff --git a/src/core/scene/root.zig b/src/core/scene/root.zig index 5e96f2c..78c37f6 100644 --- a/src/core/scene/root.zig +++ b/src/core/scene/root.zig @@ -5,7 +5,7 @@ //! verbatim by the M1.0.5 loader). //! //! **Imports `weld_core` internals only — never `weld_etch`** (tier discipline, -//! `ARCH-017` / the M1.0.4 brief Notes). The Etch coupling +//! `ARCH-013` + `ARCH-016` / the M1.0.4 brief Notes). The Etch coupling //! (descriptors, const-eval, `writeValueAsBytes`) lives in //! `src/etch/scene_cook.zig`, which consumes this surface. diff --git a/src/etch/test_runner.zig b/src/etch/test_runner.zig index 1a475fd..8b656a2 100644 --- a/src/etch/test_runner.zig +++ b/src/etch/test_runner.zig @@ -18,8 +18,8 @@ //! //! `RunReport` OWNS its strings (an internal arena); the caller need only keep //! `ast` alive for the duration of `run`. The `weld test` CLI -//! (`engine-platform.md`) will consume this same library — the `etch_test` -//! shim (E5) is its Phase-1 driver. +//! (`engine-platform.md` § "Build System — CLI `weld`") will consume this same +//! library — the `etch_test` shim (E5) is its Phase-1 driver. const std = @import("std"); const weld_core = @import("weld_core"); diff --git a/src/foundation/root.zig b/src/foundation/root.zig index 6fd515b..2187f50 100644 --- a/src/foundation/root.zig +++ b/src/foundation/root.zig @@ -1,6 +1,6 @@ //! Foundation — transversal sibling submodules consumed across the engine. //! -//! Per `ARCH-017` and `engine-simd.md` §4, `math` and `simd` are +//! Per `ARCH-016` and `engine-simd.md` §4, `math` and `simd` are //! sibling submodules with no mutual dependency. M0.6 shipped `simd` (the //! batched-kernel module); M1.1.0 adds `math` — its first consumer is Forge 3D. diff --git a/tools/etch_test/main.zig b/tools/etch_test/main.zig index 2fb6e2a..6d5c5ec 100644 --- a/tools/etch_test/main.zig +++ b/tools/etch_test/main.zig @@ -1,7 +1,8 @@ //! `etch_test` — thin CLI shim over the M1.0.15 Etch test runner //! (`weld_etch.test_runner`). The shim owns arg parsing + file I/O + report //! printing only; parse + type-check + run all live in the library, which -//! `weld test` (`engine-platform.md`) will consume through the same entry. +//! `weld test` (`engine-platform.md` § "Build System — CLI `weld`") will +//! consume through the same entry. //! //! CLI: //! etch_test [ ...] From 229b13dea59d69eb37d5c0a476028cbb6a99acaf Mon Sep 17 00:00:00 2001 From: Guy Senpai Date: Fri, 31 Jul 2026 16:02:57 +0200 Subject: [PATCH 09/12] docs: drop the out-of-scope invariant and qualify two stale glosses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit E6, six edits on the review's four findings. Comment lines and markdown only; the frozen body untouched, A9 and A10 updated in place and A11 added inside the append. F2 — `ARCH-016` carries **`Portée` : Tier 1, Tier 3**, and `foundation` and `core/scene` are Tier 0, so the id was out of scope at all three coupling sites. Third occurrence of one cause: an id validated on its decision text alone — ARCH-024 without its perimeter, ARCH-017 on a clause it delegates, ARCH-016 on a decision that describes the constraint exactly while its scope excludes the tier. A citation is admissible only if the decision AND the Portée both hold, and that is now the rule in A10. No invariant carries the Tier 0 import graph, so the remedy is to stop citing rather than hunt a third id — the call already made on rtti/type_info.zig:48. foundation/root.zig keeps engine-simd.md §4, which carries the sister-module clause on the same line; the two scene files keep ARCH-013 (Portée: Global) for tier membership and state their import constraint uncited. Sweep over every id used as a target, Portée against the tier of the cited subject: ARCH-016 was the only violation. ARCH-014 surfaces mechanically and is resolved by reading — it is cited for the Tier 1 half of "Tier 0/1 catalog". F3 and F4 — two glosses true when written and false against today's corpus: M0.2:364's two-document whitelist (vk_gen is named in five KB files today) and M1.1.5:19's "reserves for semi-implicit Euler, sleep, CCD" (the tree now has integration.zig for Euler and CCD plus a separate sleep.zig). Retargeting them to the living owner makes the gloss testable against that owner, and it fails — repairing the pointer would have manufactured the contradiction. Both are qualified as historical instead, the recorded words unmoved; the C4 retarget of the previous gate is annulled. Rule in A11, a generalisation of drift pattern D4. F5 — engine-render.md carries four headings titled "Architecture", so that anchor localises nothing there: M0.4:80 cites § "GPU Abstraction Layer (GAL)" and § "V-Buffer (Visibility Buffer)", the two sections its own gloss designates. Every anchor in A9 was then counted in its owning document — all eleven resolve to exactly one heading. One site, not a class. F1 was not a defect: the install-or-overwrite anchor exists in the amended file and the three references stand. Criterion 1 stays at exactly 30. Criterion 2 zero. Gates: lint 0, fmt 0, test 264/264 steps and 1523/1540 tests with 17 skipped, test-forge-3d -Dphysics_f64=true 4/4 steps and 356/356. --- briefs/M0.2-rtti-resources-events-bindgen.md | 2 +- .../M0.4-renderer-vulkan-forward-and-gal.md | 2 +- briefs/M1.1.5-integration-euler.md | 2 +- briefs/chore-spec-reference-reconciliation.md | 90 ++++++++++++++++--- src/core/scene/loader.zig | 2 +- src/core/scene/root.zig | 2 +- src/foundation/root.zig | 2 +- 7 files changed, 83 insertions(+), 19 deletions(-) diff --git a/briefs/M0.2-rtti-resources-events-bindgen.md b/briefs/M0.2-rtti-resources-events-bindgen.md index c8619d4..573cf68 100644 --- a/briefs/M0.2-rtti-resources-events-bindgen.md +++ b/briefs/M0.2-rtti-resources-events-bindgen.md @@ -361,7 +361,7 @@ M0.2 smoke OK **Removal of `tools/vk_gen/` and `tools/wayland_gen/`.** In a separate commit after the effective empty-diff validation. If the intermediate E5 review requests adjustments, keeping the old directories allows a quick rollback. The removal is the last commit of E5 — not the first. -**Cross-doc audit.** The edit of `engine-phase-minus-1-archive.md` (S6 swap note) is the only spec modification expected in this milestone. If reading the specs in Step 2 reveals other inconsistencies (for example a reference to `tools/vk_gen/` in a file other than `engine-directory-structure.md` § "Structure interne du repo Weld lui-même" or `engine-c-bindings.md`), that is a light blocker: log it, request a patch via this Claude.ai conversation, do not patch unilaterally. +**Cross-doc audit.** The edit of `engine-phase-minus-1-archive.md` (S6 swap note) is the only spec modification expected in this milestone. If reading the specs in Step 2 reveals other inconsistencies (for example a reference to `tools/vk_gen/` in a file other than `engine-spec.md` or `engine-c-bindings.md` — historical whitelist), that is a light blocker: log it, request a patch via this Claude.ai conversation, do not patch unilaterally. **No new C binding.** M0.2 introduces no keeper. The test stub plugin is pure Zig compiled into a `.so`/`.dll`, not a third-party C binding. The 7 Phase 1+ keepers (`ARCH-024`) stay out of scope. diff --git a/briefs/M0.4-renderer-vulkan-forward-and-gal.md b/briefs/M0.4-renderer-vulkan-forward-and-gal.md index 3833245..f3709e7 100644 --- a/briefs/M0.4-renderer-vulkan-forward-and-gal.md +++ b/briefs/M0.4-renderer-vulkan-forward-and-gal.md @@ -77,7 +77,7 @@ M0.4 advances criterion C0.3 (minimal Vulkan forward renderer) by designing the 6. `engine-tier-interfaces.md` — the Tier 0 `RenderModule` interface. The GAL is a Tier 1 module that must respect this contract. 7. `engine-development-workflow.md` — §2.2 granularity, §3 brief format, §3.6.1 local cross-doc audit, §4.3 Conventional Commits, §4.5.1 semantic runtime CI validation + manual GPU gate (**first milestone applying it — read carefully**), §4.6 squash commit format (structured long body, 6 mandatory sections), §4.7 tag procedure. 8. `engine-zig-conventions.md` — Zig 0.16.x reminders (std.Io, allocators, Writer/Reader, naming), §14 isolation of `*_c` modules, §16 Weld conventions (POD `extern struct` ECS components). -9. `ARCH-017` (in-tree as-if-lib, no separable lib extraction), `engine-render.md` § "Architecture" — Render Module (GAL + V-Buffer + target pipeline recap), `engine-phase-minus-1-archive.md` S2 § Precisions (multi-GPU selection consistency, `--smoke-test` mode, committed SPIR-V). +9. `ARCH-017` (in-tree as-if-lib, no separable lib extraction), `engine-render.md` § "GPU Abstraction Layer (GAL)" + § "V-Buffer (Visibility Buffer)" — Render Module (GAL + V-Buffer + target pipeline recap), `engine-phase-minus-1-archive.md` S2 § Precisions (multi-GPU selection consistency, `--smoke-test` mode, committed SPIR-V). 10. `engine-platform.md` — §4 Windowing (the GAL creates the Vulkan surface on top of the M0.3 Tier 0 Window interface, check the integration signature), §4 Input (not touched but general consistency). ## Files diff --git a/briefs/M1.1.5-integration-euler.md b/briefs/M1.1.5-integration-euler.md index cbe14c2..4a4400d 100644 --- a/briefs/M1.1.5-integration-euler.md +++ b/briefs/M1.1.5-integration-euler.md @@ -16,7 +16,7 @@ ## Context -This is the FIRST sub-milestone where bodies move. M1.1.0–4 laid the static machinery: the SoA `BodyManager` store (position, rotation, linear/angular velocity, `MotionProperties`), the broadphase BVH, and the narrowphase (GJK/EPA/manifold + analytic fast paths). No stepping exists yet — M1.1.0 explicitly deferred it (`PhysicsModule` freezes at M1.1.15). This milestone adds the integration stage the plan freezes at the M1.1.5 row of `engine-phase-1-plan.md` — "Integration : semi-implicit Euler + gravité + damping", demonstrator "Box en chute libre, trajectoire correcte" — landing it in `pipeline/integration.zig`, the file the spec directory tree (`engine-directory-structure.md` § "Structure interne du repo Weld lui-même") reserves for "semi-implicit Euler, sleep, CCD" (only the Euler + gravity + damping part lands here; sleep is M1.1.8, CCD is later). +This is the FIRST sub-milestone where bodies move. M1.1.0–4 laid the static machinery: the SoA `BodyManager` store (position, rotation, linear/angular velocity, `MotionProperties`), the broadphase BVH, and the narrowphase (GJK/EPA/manifold + analytic fast paths). No stepping exists yet — M1.1.0 explicitly deferred it (`PhysicsModule` freezes at M1.1.15). This milestone adds the integration stage the plan freezes at the M1.1.5 row of `engine-phase-1-plan.md` — "Integration : semi-implicit Euler + gravité + damping", demonstrator "Box en chute libre, trajectoire correcte" — landing it in `pipeline/integration.zig`, the file the spec directory tree (as read at M1.1.5 — historical) reserves for "semi-implicit Euler, sleep, CCD" (only the Euler + gravity + damping part lands here; sleep is M1.1.8, CCD is later). It is FREE-FLIGHT integration only. Broadphase/narrowphase exist but are NOT invoked — there is no contact response (Sequential Impulses is M1.1.6). A dynamic body under gravity follows an unobstructed trajectory. Integration lives INSIDE `forge_3d` (a free function over the `BodyManager` SoA store); it is NOT wired into the `PhysicsModule` interface, whose `step` orchestration composes broadphase → narrowphase → solve → integrate at M1.1.15. diff --git a/briefs/chore-spec-reference-reconciliation.md b/briefs/chore-spec-reference-reconciliation.md index 0bc2803..d5ad81a 100644 --- a/briefs/chore-spec-reference-reconciliation.md +++ b/briefs/chore-spec-reference-reconciliation.md @@ -376,18 +376,26 @@ dropped everywhere — a shifting number is the failure mode being repaired, and | `engine-ecs-internals.md` | § "Archetype Chunk Layout (SoA par composant)" | `chunk.zig:56` | | `engine-ecs-internals.md` | § "Architecture" | `S1:55` | | `engine-c-bindings.md` | § "Liste des `.api.zig` manuels" | `CLAUDE.md:150` ×2 · `M0.2:191` · `S2:67` | -| `engine-render.md` | § "Architecture" | `M0.4:80` | +| `engine-render.md` | § "GPU Abstraction Layer (GAL)" + § "V-Buffer (Visibility Buffer)" | `M0.4:80` | | `engine-asset-pipeline.md` | § "Architecture" | `m0.6:84` | | `etch-reference-part1.md` | § "`async fn` et `async rule`" | `M1.0.13:57` | | `engine-roadmap.md` | § "Contenu des phases" | `M0.0:38` · `M0.2:191` | | `engine-roadmap.md` | § "Carte globale des phases par module" | `M0.8:109` | -| `engine-directory-structure.md` | § "Structure interne du repo Weld lui-même" | `M0.2:364` · `M1.1.5:19` | | `engine-zig-conventions.md` | § "Fichier racine : `root.zig` (module) vs `main.zig` (exécutable)" | `rtti/root.zig:10` | | `engine-tools-editor.md` | § "Panneau Etch Text — éditeur de code" | `S3:158` | The last two were already anchored and carried no number, so the withdrawal took nothing back from them. +**An anchor must be unique in its file, and that is checked, not assumed.** +`engine-render.md` carries **four** headings titled "Architecture" (lines 29, 634, +1036, 1351), so § "Architecture" localises nothing there — `M0.4:80` cites the two +named sections its own gloss designates instead. Every anchor in the table above +was then counted mechanically in its owning document: all eleven resolve to +exactly one heading, including the three remaining § "Architecture" targets, which +are unique in `engine-scene-serialization.md`, `engine-ecs-internals.md` and +`engine-asset-pipeline.md`. One site, not a class. + Three exemptions, each motivated rather than granted: 1. **The archive plus a spike id** — `` `engine-phase-minus-1-archive.md` S6 `` — @@ -437,13 +445,14 @@ The eight, and what each one shows: | `S0:52` | `ARCH-017` "in-tree default, no `spec/` in repo" | `ARCH-017` carries the in-tree default and the absence of `libs/`, nothing about `spec/`. Verified: `spec/` appears nowhere in `engine-directory-structure.md`. There is no KB owner — the decision lives in `CLAUDE.md` § *Open / deferred decisions* — so the clause is qualified as historical rather than given an invented target | | `M1.1.5:19` | `ARCH-017` "the spec directory tree … reserves for 'semi-implicit Euler, sleep, CCD'" | `ARCH-017` carries no directory tree at all: the bullets of `§3.5` that named `src/modules/forge/solvers_{2d,3d}//` — a path itself made stale by the M1.1.1 flatten — did not survive into the invariant. `engine-directory-structure.md`, already cited on the same line, is the owner, and the `ARCH-017` half is dropped as a mis-attribution | | `S1:55` | `ARCH-005` "ECS overview" | An overview of the ECS spans `ARCH-003` to `ARCH-010`. The gloss reattaches to the owner, `engine-ecs-internals.md` § "Architecture"; `ARCH-005` stays alongside, unglossed | -| `foundation/root.zig:3` | `ARCH-017` "sibling submodules with no mutual dependency" | → `ARCH-016` | -| `scene/root.zig:8` | `ARCH-017` "tier discipline, imports `weld_core` only, never `weld_etch`" | → `ARCH-013` + `ARCH-016` | -| `scene/loader.zig:12` | `ARCH-017` "Tier discipline: imports `weld_core` internals only" | → `ARCH-013` + `ARCH-016` | +| `foundation/root.zig:3` | `ARCH-017` "sibling submodules with no mutual dependency" | → no id at all (see below) | +| `scene/root.zig:8` | `ARCH-017` "tier discipline, imports `weld_core` only, never `weld_etch`" | → `ARCH-013` alone | +| `scene/loader.zig:12` | `ARCH-017` "Tier discipline: imports `weld_core` internals only" | → `ARCH-013` alone | + +**The last three revoke the GO given at the source gate** — twice over, as it +turned out, and that is worth recording plainly. -**The last three revoke the GO given at the source gate**, and that is worth -recording plainly because it is the only arbitration of this chore that had to be -taken back. At E1 the three were verified against the *source text* of the +*First revocation.* At E1 the three were verified against the *source text* of the 27-section `§3.5`, whose "Discipline d'API in-tree" literally requires "Zero coupling avec d'autres modules Weld", and `ARCH-017` was accepted on that measurement. The rule written afterwards is stricter and asks whether the gloss @@ -456,6 +465,58 @@ resolves, so the stricter rule wins and `ARCH-016` — "un module n'accède qu' revocation possible was raising the tension rather than silently applying whichever rule was most recent. +*Second revocation — `ARCH-016` does not apply either.* Its **`Portée`** field +reads "Tier 1, Tier 3". `foundation` and `core/scene` are Tier 0, so the id was +out of scope at all three sites. The cause is the same one three times running, +and naming it is the point of this entry: an id had been validated on its +**decision text alone** — `ARCH-024` on its subject without its perimeter, +`ARCH-017` on a clause it delegates, `ARCH-016` on a decision that describes the +constraint exactly while its scope excludes the tier. **A citation is admissible +only if the decision AND the `Portée` both hold.** + +No invariant carries the Tier 0 import graph, so the remedy is to **stop citing** +for that half rather than find a third id — the same call already made on +`rtti/type_info.zig:48`. A comment may state a local discipline without invoking +an invariant. `foundation/root.zig` keeps `engine-simd.md` §4, which carries the +sister-module clause on the same line; the two scene files keep `ARCH-013` +(`Portée : Global`) for tier membership, and their import constraint stays stated +by the sentence, uncited. + +The sweep this produced was run over every id used as a target, cross-checking +each `Portée` against the tier of the cited subject. Six registry entries have a +`Portée` that excludes Tier 0 (`ARCH-014`, `-015`, `-016`, `-019`, `-020`, `-021`); +two of them are used here. `ARCH-016` was the only violation. `ARCH-014` surfaces +as a mechanical candidate and is resolved by reading: it is cited for the **Tier +1** half of "Tier 0/1 catalog", so its scope is satisfied, with `ARCH-013` +(Global) carrying the Tier 0 half on the same line. + +### A11 — A stale gloss is QUALIFIED as historical, never retargeted + +Two sites carried a gloss that was true when written and is false against today's +corpus. Retargeting them to the living owner makes the gloss *testable* against +that owner — and it fails, so repairing the pointer would have manufactured the +contradiction: + +- `M0.2:364` whitelisted two documents for a `tools/vk_gen/` reference. Today + `vk_gen` is named in five KB files. The C4 retarget of the previous gate is + **annulled**: the recorded text is restored verbatim and marked "historical + whitelist". +- `M1.1.5:19` states that the directory tree reserves one file for "semi-implicit + Euler, sleep, CCD". Today `engine-directory-structure.md` gives + `integration.zig # semi-implicit Euler (M1.1.5), CCD` with a **separate** + `sleep.zig`. The pointer is dropped for that half and the claim marked "as read + at M1.1.5 — historical". + +**Rule, a generalisation of drift pattern D4:** when the gloss of a closed record +is stale against the living owner, qualify it as historical; do not retarget. +Retargeting would make the record assert a falsehood about today's corpus, while +the qualification tells the truth about both dates. The recorded words do not +move in either case — only a short marker is added. + +This also settles a residual the previous gate left implicit: `engine-directory- +structure.md` is no longer a target of this chore at all, and `ARCH-016` is no +longer used anywhere in the tree. + ## Execution notes Gate by gate, each ending at a STOP with an explicit GO before the next. @@ -468,16 +529,19 @@ Gate by gate, each ending at a STOP with an explicit GO before the next. | E1ter | `4efe8ad` | the second language fix (A1) | | E2 | `88c2b1c` | `CLAUDE.md` — 4 references + § *Quick links spec* rewritten | | E3 | `feebb78` | 52 references over 51 lines, 21 briefs + the S5 record | -| E5 | this commit | named anchors (A9), the 8 gloss defects (A10), `M0.2:364` (A5) | +| E5 | `2b9c0cc` | named anchors (A9), the 8 gloss defects (A10) | +| E6 | this commit | `ARCH-016` out of scope (A10), two stale glosses qualified (A11), the render anchor (A9) | **77 references repaired.** `engine-spec.md` occurrences across the tree: 97 in 38 files at open, 37 in 18 files at close. Twelve distinct ARCH ids used as targets — `ARCH-004`, `-005`, `-006`, `-007`, `-008`, `-011`, `-012`, `-013`, -`-014`, `-016`, `-017`, `-024` — plus the `ARCH-001`–`ARCH-006` range at -`S2:67`. `ARCH-014` and `ARCH-016` enter at E5 through the gloss audit (A10), -not through the substitution table. +`-014`, `-017`, `-024` — plus the `ARCH-001`–`ARCH-006` range at `S2:67`. +`ARCH-014` enters at E5 through the gloss audit (A10), not through the +substitution table; `ARCH-016` entered there too and left again at E6 on its +`Portée` (A10), so it is used nowhere in the tree. -**A2 and A9 were rewritten in place** rather than corrected by a second append: +**A2, A9 and A10 were rewritten in place** rather than corrected by further +appends: the body's anchor policy is withdrawn, so the section that recorded it had to state the withdrawal, and a patch of a patch would have left two contradictory renderings of the same rule in the same file. The frozen body is untouched diff --git a/src/core/scene/loader.zig b/src/core/scene/loader.zig index c6b5336..2a80073 100644 --- a/src/core/scene/loader.zig +++ b/src/core/scene/loader.zig @@ -10,7 +10,7 @@ //! `world.addResource`). //! //! Tier discipline: imports `weld_core` internals only — never `weld_etch` -//! (`ARCH-013` + `ARCH-016`). The cook driver's Etch coupling lives in +//! (`ARCH-013`). The cook driver's Etch coupling lives in //! `src/etch/scene_cook.zig`; the loader consumes only the neutral byte image. //! //! ## Stages (gate-split, see `briefs/M1.0.5-scene-load.md`) diff --git a/src/core/scene/root.zig b/src/core/scene/root.zig index 78c37f6..bd5a873 100644 --- a/src/core/scene/root.zig +++ b/src/core/scene/root.zig @@ -5,7 +5,7 @@ //! verbatim by the M1.0.5 loader). //! //! **Imports `weld_core` internals only — never `weld_etch`** (tier discipline, -//! `ARCH-013` + `ARCH-016` / the M1.0.4 brief Notes). The Etch coupling +//! `ARCH-013` / the M1.0.4 brief Notes). The Etch coupling //! (descriptors, const-eval, `writeValueAsBytes`) lives in //! `src/etch/scene_cook.zig`, which consumes this surface. diff --git a/src/foundation/root.zig b/src/foundation/root.zig index 2187f50..3ca4eec 100644 --- a/src/foundation/root.zig +++ b/src/foundation/root.zig @@ -1,6 +1,6 @@ //! Foundation — transversal sibling submodules consumed across the engine. //! -//! Per `ARCH-016` and `engine-simd.md` §4, `math` and `simd` are +//! Per `engine-simd.md` §4, `math` and `simd` are //! sibling submodules with no mutual dependency. M0.6 shipped `simd` (the //! batched-kernel module); M1.1.0 adds `math` — its first consumer is Forge 3D. From 155c0f6a09de49c1490cd7037cf5cc5d8b5631b7 Mon Sep 17 00:00:00 2001 From: Guy Senpai Date: Fri, 31 Jul 2026 17:47:47 +0200 Subject: [PATCH 10/12] docs: make the two sibling tier-discipline comments agree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit E7, three edits on the review's three findings. Comment lines and markdown only; A9 updated in place inside the append, frozen body untouched. F2 — scene/root.zig and scene/loader.zig both declare that the module imports weld_core only and never weld_etch. After E6 removed ARCH-016, the first read "(tier discipline, ARCH-013 / the M1.0.4 brief Notes)" while the second read "(ARCH-013)" placed after the import sentence, where a bare id reads as covering the import rule — which E6 had just declared it does not. The divergence between the two mattered more than either variant: loader.zig now carries its sibling's form. Citing where a decision was taken is not the same act as citing a normative owner, and the provenance is worth keeping, so the M1.0.4 brief is named rather than the sentence left uncited. F3 — foundation/root.zig:3 kept `engine-simd.md` §4 while C1 requires a named anchor on every domain-detail target. It takes § "Relation avec `foundation/math/`" and the number falls. F1 was not a repository defect, but it exposed an unfalsifiable claim in the append: "all eleven anchors resolve to exactly one heading" was true of the files supplied to the gate and unverifiable by anyone else. A9 now states which two anchors rest on a provenance the next reader cannot reproduce — § "Resources de scène — install-or-overwrite", counted in an amended file the published corpus does not yet carry, and § "Relation avec `foundation/math/`", whose uniqueness is the reviewer's measurement since engine-simd.md was not in the supplied set. The first makes re-uploading that file a precondition of merging rather than a follow-up: three references pointing at an anchor published nowhere is the fault this chore exists to repair. Criterion 1 stays at exactly 30 — none of the three edits carries a `§` towards the master. Criterion 2 zero. Gates: lint 0, fmt 0, test 264/264 steps and 1523/1540 tests with 17 skipped, test-forge-3d -Dphysics_f64=true 4/4 steps and 356/356. --- briefs/chore-spec-reference-reconciliation.md | 33 ++++++++++++++++++- src/core/scene/loader.zig | 5 +-- src/foundation/root.zig | 4 +-- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/briefs/chore-spec-reference-reconciliation.md b/briefs/chore-spec-reference-reconciliation.md index d5ad81a..7b328be 100644 --- a/briefs/chore-spec-reference-reconciliation.md +++ b/briefs/chore-spec-reference-reconciliation.md @@ -383,6 +383,7 @@ dropped everywhere — a shifting number is the failure mode being repaired, and | `engine-roadmap.md` | § "Carte globale des phases par module" | `M0.8:109` | | `engine-zig-conventions.md` | § "Fichier racine : `root.zig` (module) vs `main.zig` (exécutable)" | `rtti/root.zig:10` | | `engine-tools-editor.md` | § "Panneau Etch Text — éditeur de code" | `S3:158` | +| `engine-simd.md` | § "Relation avec `foundation/math/`" | `foundation/root.zig:3` | The last two were already anchored and carried no number, so the withdrawal took nothing back from them. @@ -396,6 +397,24 @@ exactly one heading, including the three remaining § "Architecture" targets, wh are unique in `engine-scene-serialization.md`, `engine-ecs-internals.md` and `engine-asset-pipeline.md`. One site, not a class. +**Two of the anchors are asserted on a provenance the next reader cannot +reproduce, and that is stated rather than smoothed over.** The count above was +run against the twelve files supplied to the gate, so: + +- § "Resources de scène — install-or-overwrite" was counted in the **amended** + `engine-scene-serialization.md` produced for this gate. The published corpus + carries zero occurrences of it until that file is re-uploaded, so the three + references pointing at it (`loader.zig:714`, `M1.0.5:163`, `:173`) resolve only + once it is. **Re-uploading it — to the knowledge base and to the `weld-spec/` + mirror — is a precondition of merging, not a follow-up:** three references + pointing at an anchor published nowhere is precisely the fault this chore + repairs. +- § "Relation avec `foundation/math/`" on `engine-simd.md` was **not** measured + here: that file was not in the supplied set. Its title and its uniqueness come + from the reviewer's own reading (`engine-simd.md:102`). Recorded as a citation + of someone else's measurement, which is a weaker warrant than the other eleven + and should be re-counted at the next corpus audit. + Three exemptions, each motivated rather than granted: 1. **The archive plus a spike id** — `` `engine-phase-minus-1-archive.md` S6 `` — @@ -421,6 +440,17 @@ réplication"), so it is not disclaimed detail and needs no owner. It also has n anchor available — the tag table is not in `engine-ecs-internals.md`, and `engine-project-settings.md` was not in the attached set. +**Two sibling comments state the same rule and must attribute it the same way.** +`scene/root.zig` and `scene/loader.zig` both declare that the module imports +`weld_core` only and never `weld_etch`. After the `ARCH-016` removal of A10, the +first read "(tier discipline, `ARCH-013` / the M1.0.4 brief Notes)" while the +second read "(`ARCH-013`)" placed *after* the import sentence — where the bare id +reads as covering the import rule, which A10 had just declared it does not. The +divergence between the two mattered more than either variant: `loader.zig` now +carries the same form as its sibling. Citing where a decision was taken is not +the same act as citing a normative owner, and the provenance is worth keeping — +so the M1.0.4 brief is named rather than the sentence left uncited. + Two textual side effects, both visible in the diff: the word "table" was dropped from "§2.9 table" because `ARCH-006` carries none, and `rtti/root.zig` reads "the Tier 0 convention of X" rather than "the X Tier 0 convention" because the @@ -530,7 +560,8 @@ Gate by gate, each ending at a STOP with an explicit GO before the next. | E2 | `88c2b1c` | `CLAUDE.md` — 4 references + § *Quick links spec* rewritten | | E3 | `feebb78` | 52 references over 51 lines, 21 briefs + the S5 record | | E5 | `2b9c0cc` | named anchors (A9), the 8 gloss defects (A10) | -| E6 | this commit | `ARCH-016` out of scope (A10), two stale glosses qualified (A11), the render anchor (A9) | +| E6 | `229b13d` | `ARCH-016` out of scope (A10), two stale glosses qualified (A11), the render anchor (A9) | +| E7 | this commit | the two sibling comments agree, the `engine-simd.md` anchor, the anchor-count caveats (A9) | **77 references repaired.** `engine-spec.md` occurrences across the tree: 97 in 38 files at open, 37 in 18 files at close. Twelve distinct ARCH ids used as diff --git a/src/core/scene/loader.zig b/src/core/scene/loader.zig index 2a80073..82c34ac 100644 --- a/src/core/scene/loader.zig +++ b/src/core/scene/loader.zig @@ -10,8 +10,9 @@ //! `world.addResource`). //! //! Tier discipline: imports `weld_core` internals only — never `weld_etch` -//! (`ARCH-013`). The cook driver's Etch coupling lives in -//! `src/etch/scene_cook.zig`; the loader consumes only the neutral byte image. +//! (`ARCH-013` / the M1.0.4 brief Notes). The cook driver's Etch coupling +//! lives in `src/etch/scene_cook.zig`; the loader consumes only the neutral +//! byte image. //! //! ## Stages (gate-split, see `briefs/M1.0.5-scene-load.md`) //! * **E1 (here)** — open + integrity check + schema-identity remap. The two diff --git a/src/foundation/root.zig b/src/foundation/root.zig index 3ca4eec..dc0b5e1 100644 --- a/src/foundation/root.zig +++ b/src/foundation/root.zig @@ -1,7 +1,7 @@ //! Foundation — transversal sibling submodules consumed across the engine. //! -//! Per `engine-simd.md` §4, `math` and `simd` are -//! sibling submodules with no mutual dependency. M0.6 shipped `simd` (the +//! Per `engine-simd.md` § "Relation avec `foundation/math/`", `math` and `simd` +//! are sibling submodules with no mutual dependency. M0.6 shipped `simd` (the //! batched-kernel module); M1.1.0 adds `math` — its first consumer is Forge 3D. /// General-purpose math types (Vec/Quat/Mat3/Aabb, generic over the scalar; From a5755f9f62a1739fc517c2645e581a06c25f9e32 Mon Sep 17 00:00:00 2001 From: Guy Senpai Date: Fri, 31 Jul 2026 20:00:56 +0200 Subject: [PATCH 11/12] docs: correct the anchor count and re-measure it on the synced mirror MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit E8, markdown only, and only inside the append — the frozen body and A2 are untouched. A2's thirty enumerated lines were verified against the tree and not reopened. The claim "all eleven resolve to exactly one heading" was E5's count, before E6 split the render row in two and E7 added `engine-simd.md`. The table holds fifteen anchors over fourteen rows, and both numbers are now written; "the last two were already anchored" is replaced by naming `engine-zig-conventions.md` and `engine-tools-editor.md`, since a positional reference into a table that has grown twice is this chore's own fault in miniature. The two provenance caveats are closed rather than restated: the anchors this work introduced are synchronised into the `weld-spec/` mirror, so the count was re-run there and all fifteen now rest on the same warrant — measured in the published corpus, none cited from someone else's reading. That re-run first returned 14 of 15, reporting § "Panneau Etch Text — éditeur de code" absent. The heading is present, `## 6 bis. Panneau Etch Text — éditeur de code`; the count was wrong, its ordinal strip choking on "6 bis" — the very number the same paragraph notes has already moved once. The corrected pass also checks that a deliberately wrong anchor still returns zero, so absence and normalisation bug are distinguishable. A measurement that cannot fail on purpose is not a measurement. `loader.zig:714` → `:716`, the citation's real line. Two further staleness points that E7 introduced and left behind: A10 said the code keeps `engine-simd.md` §4, which E7 replaced with the named anchor, and said the scene files' import constraint stays uncited, which E7 attributed to the M1.0.4 brief Notes. Both corrected to what the tree says. Criterion 1 stays at exactly 30; no edit touches a line carrying a `§` towards the master. Criterion 2 zero. lint 0, fmt 0. --- briefs/chore-spec-reference-reconciliation.md | 64 ++++++++++--------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/briefs/chore-spec-reference-reconciliation.md b/briefs/chore-spec-reference-reconciliation.md index 7b328be..86c2c8b 100644 --- a/briefs/chore-spec-reference-reconciliation.md +++ b/briefs/chore-spec-reference-reconciliation.md @@ -371,7 +371,7 @@ dropped everywhere — a shifting number is the failure mode being repaired, and | Target | Anchor, read | Sites | |---|---|---| | `engine-platform.md` | § "Build System — CLI `weld`" | `test_runner.zig:20` · `tools/etch_test:4` · `CLAUDE.md:120` · `M1.0.15:39` | -| `engine-scene-serialization.md` | § "Resources de scène — install-or-overwrite" | `loader.zig:714` · `M1.0.5:163` · `:173` | +| `engine-scene-serialization.md` | § "Resources de scène — install-or-overwrite" | `loader.zig:716` · `M1.0.5:163` · `:173` | | `engine-scene-serialization.md` | § "Architecture" | `M1.0.4:66` · `M1.0.5:62` · `M1.0.6:136` | | `engine-ecs-internals.md` | § "Archetype Chunk Layout (SoA par composant)" | `chunk.zig:56` | | `engine-ecs-internals.md` | § "Architecture" | `S1:55` | @@ -385,35 +385,37 @@ dropped everywhere — a shifting number is the failure mode being repaired, and | `engine-tools-editor.md` | § "Panneau Etch Text — éditeur de code" | `S3:158` | | `engine-simd.md` | § "Relation avec `foundation/math/`" | `foundation/root.zig:3` | -The last two were already anchored and carried no number, so the withdrawal took -nothing back from them. +`engine-zig-conventions.md` and `engine-tools-editor.md` were already anchored +and carried no number, so the withdrawal took nothing back from them. They are +named rather than pointed at by position: the table has since grown twice, and a +positional reference into a growing table is this chore's own fault in miniature. **An anchor must be unique in its file, and that is checked, not assumed.** `engine-render.md` carries **four** headings titled "Architecture" (lines 29, 634, 1036, 1351), so § "Architecture" localises nothing there — `M0.4:80` cites the two named sections its own gloss designates instead. Every anchor in the table above -was then counted mechanically in its owning document: all eleven resolve to -exactly one heading, including the three remaining § "Architecture" targets, which -are unique in `engine-scene-serialization.md`, `engine-ecs-internals.md` and -`engine-asset-pipeline.md`. One site, not a class. - -**Two of the anchors are asserted on a provenance the next reader cannot -reproduce, and that is stated rather than smoothed over.** The count above was -run against the twelve files supplied to the gate, so: - -- § "Resources de scène — install-or-overwrite" was counted in the **amended** - `engine-scene-serialization.md` produced for this gate. The published corpus - carries zero occurrences of it until that file is re-uploaded, so the three - references pointing at it (`loader.zig:714`, `M1.0.5:163`, `:173`) resolve only - once it is. **Re-uploading it — to the knowledge base and to the `weld-spec/` - mirror — is a precondition of merging, not a follow-up:** three references - pointing at an anchor published nowhere is precisely the fault this chore - repairs. -- § "Relation avec `foundation/math/`" on `engine-simd.md` was **not** measured - here: that file was not in the supplied set. Its title and its uniqueness come - from the reviewer's own reading (`engine-simd.md:102`). Recorded as a citation - of someone else's measurement, which is a weaker warrant than the other eleven - and should be re-counted at the next corpus audit. +was then counted mechanically in its owning document. The table holds **fifteen +anchors over fourteen rows** — the `engine-render.md` row carries two — and **all +fifteen resolve to exactly one heading**, including the three remaining +§ "Architecture" targets, which are unique in `engine-scene-serialization.md`, +`engine-ecs-internals.md` and `engine-asset-pipeline.md`. One site, not a class. + +The count was run against the `weld-spec/` mirror after the two anchors this work +introduced were synchronised into it, so **all fifteen rest on the same warrant** +— counted here, in the published corpus, not cited from someone else's reading. +That closes two caveats an earlier revision of this section carried: the +install-or-overwrite anchor, which the corpus did not yet publish, and the +`engine-simd.md` anchor, which had been attested by the reviewer rather than +measured. + +**The instrument was proven before its result was trusted.** A first count +returned 14 of 15, reporting § "Panneau Etch Text — éditeur de code" absent. The +heading is present — `## 6 bis. Panneau Etch Text — éditeur de code` — and the +count was wrong: the ordinal strip choked on "6 bis", which is exactly the number +this section notes has already moved once. The corrected pass also verifies that a +deliberately wrong anchor still returns zero, so the tool can distinguish absence +from a normalisation bug. A measurement that cannot fail on purpose is not a +measurement. Three exemptions, each motivated rather than granted: @@ -507,10 +509,11 @@ only if the decision AND the `Portée` both hold.** No invariant carries the Tier 0 import graph, so the remedy is to **stop citing** for that half rather than find a third id — the same call already made on `rtti/type_info.zig:48`. A comment may state a local discipline without invoking -an invariant. `foundation/root.zig` keeps `engine-simd.md` §4, which carries the -sister-module clause on the same line; the two scene files keep `ARCH-013` -(`Portée : Global`) for tier membership, and their import constraint stays stated -by the sentence, uncited. +an invariant. `foundation/root.zig` keeps `engine-simd.md`, which carries the +sister-module clause on the same line and takes its named anchor at E7; the two +scene files keep `ARCH-013` (`Portée : Global`) for tier membership, and their +import constraint is attributed to the M1.0.4 brief Notes rather than to an +invariant — see the sibling-comment paragraph of A9 for the final form. The sweep this produced was run over every id used as a target, cross-checking each `Portée` against the tier of the cited subject. Six registry entries have a @@ -561,7 +564,8 @@ Gate by gate, each ending at a STOP with an explicit GO before the next. | E3 | `feebb78` | 52 references over 51 lines, 21 briefs + the S5 record | | E5 | `2b9c0cc` | named anchors (A9), the 8 gloss defects (A10) | | E6 | `229b13d` | `ARCH-016` out of scope (A10), two stale glosses qualified (A11), the render anchor (A9) | -| E7 | this commit | the two sibling comments agree, the `engine-simd.md` anchor, the anchor-count caveats (A9) | +| E7 | `155c0f6` | the two sibling comments agree, the `engine-simd.md` anchor (A9) | +| E8 | this commit | the anchor count corrected to fifteen and re-measured against the synchronised mirror (A9) | **77 references repaired.** `engine-spec.md` occurrences across the tree: 97 in 38 files at open, 37 in 18 files at close. Twelve distinct ARCH ids used as From 6dda33f171b2236b955ddeccbf780ddaf2303d9e Mon Sep 17 00:00:00 2001 From: Guy Senpai Date: Fri, 31 Jul 2026 22:58:10 +0200 Subject: [PATCH 12/12] fix(ci): drop the zig cache from the debug legs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `build-and-test (windows-2025, Debug)` was killed at its 20-minute ceiling on two consecutive runs — 25m0s then 25m1s, reproducible, not variance — which left `ci-gate` red and branch protection blocking the merge. The cause is the cache, not the work. Against the last green run of the same leg the work is flat: `zig build` ~3 min, `zig build test` ~8 min. What grew is `Save Zig cache (post-build)`: 39s → 5m23s → 7m39s. Cache steps consumed ~8m40s for ~10m52s of useful work, and the bench.yml log on the same head names the mechanism — `Zig cache exceeded 2147483648 bytes (was 6214569092); purged contents before save`, 6.2 GB purged to a 2 GB cap on every save. The two failures had different victims: the first died in the final save with every build and test step green, the second lost `zig build test` to the ceiling. That is budget exhaustion, not a defect. The three cache steps — restore, post-build save, final save — are now conditioned on `matrix.mode == 'ReleaseSafe'`. The final save keeps `always()` so a red ReleaseSafe leg still saves what it compiled. The budget was deliberately NOT raised. The comment block being edited records the same assumption breaking three times already: 10 → 20 at M0.1, 20 → 40 at the M0.8 close, 40 → 55 at the cache refresh chore. A fourth raise buys one milestone. A cache that does not fit under its own cap is not a cache, it is a tax, and the Debug legs pay it for nothing — a cold Debug leg is ~11 min of work, ~45 % inside its budget on the slowest runner. ReleaseSafe keeps the cache: its near-cold recompile is the case the cache was added for and what the 55-minute budget covers. The verification surface is unchanged. The Debug legs run the same `zig fmt --check`, `zig build`, `zig build test`, `zig build test-etch` and `zig build bindgen-verify` against the same sources; Zig's cache is content-hashed and self-invalidating, so a cold leg compiles the same program. Recorded as A12 in the chore's append, which amends acceptance criterion 5 — "comments and markdown only" meant "no semantic change to the engine", and no `.zig` file is in this diff. The timing artefact gains `cache_enabled` beside `cache_matched_key`: a Debug leg now reports `none` by design, and without that line a future reader would diagnose a deliberately cold leg as a broken cache. --- .github/workflows/ci.yml | 35 ++++++++++++- briefs/chore-spec-reference-reconciliation.md | 49 ++++++++++++++++++- 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 44e2fb1..3fe4fc9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -114,6 +114,27 @@ jobs: # windows-2025 ReleaseSafe recompile now exceeds 40 min. The restore/save # split below fixes the refresh mechanism; 55 min covers the residual # near-cold case on the slowest runner (Debug still fits in 20). + # E9 of the spec-reference reconciliation chore — the Zig cache is now + # restricted to the ReleaseSafe legs, and "Debug still fits in 20" is + # retired. It fell to the cost of SAVING, not of building. Measured on + # windows-2025 / Debug over two consecutive runs, both killed at the 20-min + # ceiling (25m0s then 25m1s — reproducible, not variance): the work is FLAT + # against the last green run of the same leg — `zig build` ~3 min, + # `zig build test` ~8 min — while `Save Zig cache (post-build)` went + # 39s -> 5m23s -> 7m39s. Cache steps ate ~8m40s for ~10m52s of useful work, + # and the bench.yml log on the same head names the mechanism: `Zig cache + # exceeded 2147483648 bytes (was 6214569092); purged contents before save` + # — 6.2 GB purged down to a 2 GB cap on every save. The two failures had + # different victims (the first died in the final save with every build and + # test step green, the second lost `zig build test` to the ceiling), which + # is what budget exhaustion looks like rather than a defect. + # The budget was NOT raised. This very comment block records the assumption + # breaking three times already — 10 -> 20 at M0.1, 20 -> 40 at M0.8 close, + # 40 -> 55 at the cache refresh chore — and a cache that does not fit under + # its own cap is not a cache, it is a tax. A cold Debug leg is ~11 min of + # work, ~45 % inside the 20-min budget on the slowest runner. ReleaseSafe + # keeps the cache: its near-cold recompile is what the 55-min budget exists + # for, and it is the leg the cache was added for in the first place. timeout-minutes: ${{ matrix.mode == 'ReleaseSafe' && 55 || 20 }} steps: - uses: actions/checkout@v6 @@ -156,7 +177,12 @@ jobs: # §7.3 whitelist note: actions/cache/restore@v5 and actions/cache/save@v5 # are sub-actions of the already-whitelisted actions/cache@v5 (same # repo, same major, same portability notes). + # E9 — ReleaseSafe legs only, cf. the budget comment above: on Debug the + # save cost exceeded the build it protected and pushed the job past its + # ceiling. The Debug legs now run fully cold by design, so their build and + # test wall-times are the honest cold numbers rather than a cache lottery. - name: Restore Zig cache + if: matrix.mode == 'ReleaseSafe' id: zig-cache uses: actions/cache/restore@v5 with: @@ -193,6 +219,7 @@ jobs: # zon-level prefix fallback of the restore step matches both, newest # first. - name: Save Zig cache (post-build) + if: matrix.mode == 'ReleaseSafe' uses: actions/cache/save@v5 with: path: .zig-cache @@ -234,7 +261,10 @@ jobs: # With the restore/save split, `cache-hit` is true only on an exact # primary-key hit — per-sha, effectively never — so the diagnostic # signal is `cache_matched_key`: which fallback level actually seeded - # the run ('none' = fully cold). + # the run ('none' = fully cold). E9 — `cache_enabled` is reported + # alongside it, because since the cache is ReleaseSafe-only a Debug leg + # reports 'none' by design; without that line a future reader would read + # a deliberately cold leg as a broken cache. - name: Write CI timing report if: always() shell: bash @@ -247,6 +277,7 @@ jobs: echo "mode=${{ matrix.mode }}" echo "zig_version=${{ env.ZIG_VERSION }}" echo "cache_key=zig-${{ matrix.os }}-${{ matrix.mode }}-${{ env.ZIG_VERSION }}-${{ hashFiles('build.zig.zon') }}-${{ github.sha }}" + echo "cache_enabled=${{ matrix.mode == 'ReleaseSafe' }}" echo "cache_hit=${{ steps.zig-cache.outputs.cache-hit || 'false' }}" echo "cache_matched_key=${{ steps.zig-cache.outputs.cache-matched-key || 'none' }}" echo "build_seconds=${BUILD_SECONDS:-NA}" @@ -270,7 +301,7 @@ jobs: # newer than the post-build save of the same run, so the zon-level # prefix fallback serves it first to the next run. - name: Save Zig cache (final) - if: always() + if: always() && matrix.mode == 'ReleaseSafe' uses: actions/cache/save@v5 with: path: .zig-cache diff --git a/briefs/chore-spec-reference-reconciliation.md b/briefs/chore-spec-reference-reconciliation.md index 86c2c8b..b97b3ac 100644 --- a/briefs/chore-spec-reference-reconciliation.md +++ b/briefs/chore-spec-reference-reconciliation.md @@ -550,6 +550,52 @@ This also settles a residual the previous gate left implicit: `engine-directory- structure.md` is no longer a target of this chore at all, and `ARCH-016` is no longer used anywhere in the tree. +### A12 — Acceptance criterion 5 is amended: the Debug legs lose the Zig cache + +The body states criterion 5 as "`git diff` contains only comment lines (`//`, +`///`, `//!`) and markdown. No `.zig` statement, declaration or signature +changed." E9 edits `.github/workflows/ci.yml` — neither a comment line nor +markdown — so the criterion as written is violated, and the deviation is recorded +here rather than smoothed over. + +**What the criterion meant** is that the chore changes nothing semantic about the +engine. That still holds exactly: removing a cache from a workflow changes how +fast CI runs, not what it verifies. The Debug legs execute the same `zig fmt +--check`, `zig build`, `zig build test`, `zig build test-etch` and +`zig build bindgen-verify` against the same sources; the verification surface is +identical, byte for byte. Zig's cache is content-hashed and self-invalidating, so +a cold leg and a warm leg compile the same program. Criterion 5 is amended to +"no semantic change to the engine, and no `.zig` file touched" — the second half +still holds literally: **no `.zig` file is in the E9 diff.** + +**Why the edit was necessary rather than optional.** `ci-gate` was red and branch +protection blocks the merge, so the chore could not land. `build-and-test +(windows-2025, Debug)` was killed at its 20-minute ceiling on two consecutive +runs (25m0s, 25m1s — reproducible, not variance), and the measurement isolates the +cause: against the last green run of the same leg the work is flat — `zig build` +~3 min, `zig build test` ~8 min — while `Save Zig cache (post-build)` went 39s → +5m23s → 7m39s. Cache steps consumed ~8m40s for ~10m52s of useful work. The +`bench.yml` log on the same head names the mechanism: `Zig cache exceeded +2147483648 bytes (was 6214569092); purged contents before save` — 6.2 GB purged +to a 2 GB cap on every save. The two failures had different victims (the first +died in the final save with every build and test step green; the second lost +`zig build test` to the ceiling), which is budget exhaustion rather than a defect. + +**Why the budget was not raised instead.** The comment block being edited records +that same assumption breaking three times already — 10 → 20 at M0.1, 20 → 40 at +the M0.8 close, 40 → 55 at the cache refresh chore. A fourth raise would buy one +more milestone. A cache that does not fit under its own cap is not a cache, it is +a tax, and the Debug legs are the ones paying it for nothing: a cold Debug leg is +~11 min of work, ~45 % inside its budget on the slowest runner. ReleaseSafe keeps +the cache — its near-cold recompile is the case the cache was added for, and what +the 55-minute budget exists to cover. + +One addition beyond the fix: the timing artefact now reports `cache_enabled` +next to `cache_matched_key`. Since the cache is ReleaseSafe-only, a Debug leg +reports `cache_matched_key=none` **by design**, and without that line a future +reader would diagnose a deliberately cold leg as a broken cache — the exact +class of misreading this chore exists to prevent. + ## Execution notes Gate by gate, each ending at a STOP with an explicit GO before the next. @@ -565,7 +611,8 @@ Gate by gate, each ending at a STOP with an explicit GO before the next. | E5 | `2b9c0cc` | named anchors (A9), the 8 gloss defects (A10) | | E6 | `229b13d` | `ARCH-016` out of scope (A10), two stale glosses qualified (A11), the render anchor (A9) | | E7 | `155c0f6` | the two sibling comments agree, the `engine-simd.md` anchor (A9) | -| E8 | this commit | the anchor count corrected to fifteen and re-measured against the synchronised mirror (A9) | +| E8 | `a5755f9` | the anchor count corrected to fifteen and re-measured against the synchronised mirror (A9) | +| E9 | this commit | the Zig cache restricted to the ReleaseSafe legs, unblocking `ci-gate` (A12) | **77 references repaired.** `engine-spec.md` occurrences across the tree: 97 in 38 files at open, 37 in 18 files at close. Twelve distinct ARCH ids used as