Skip to content

Commit 4eaa768

Browse files
aram-devdocsclaude
andcommitted
docs: fix runbook markdown lint + regenerate AI config mirrors
Add blank lines around tables (MD058) and backtick-wrap _TYPE_ALIASES (MD037); regenerate .cursor/rules/*.mdc mirrors for the three new .agents/rules files (sync-agent-configs). Fixes the Lint Markdown and Validate AI Config Sync checks. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 536f17b commit 4eaa768

9 files changed

Lines changed: 124 additions & 2 deletions

File tree

.cursor/rules/perf-work.mdc

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
description: Performance Work Rules (ENG2)
3+
globs:
4+
alwaysApply: true
5+
---
6+
7+
<!-- AUTO-GENERATED by scripts/sync-agent-configs.py. Edit .agents/rules/perf-work.md, then rerun the generator. -->
8+
# Performance Work Rules (ENG2)
9+
10+
Applies to any change under `**/rendering/**`, `**/graphics/**`, `**/benches/**`, `scripts/bench-gate.py`, or any issue labeled `area:performance`.
11+
12+
## Definition of done (non-negotiable)
13+
14+
A performance change is done only when BOTH hold:
15+
16+
1. **A named scripted scene hits its numeric target** — S1–S5/H1/cull_scaling from `docs/src/runbook/perf-dod.md`, captured via `scripts/perf-capture` (3-run median on Apple M-series/Metal) or the CI `scripts/bench-gate.py` ratio-gate for CI-runnable benches.
17+
2. **A phase counter attributes the cost.** The relevant GPU/CPU phase timing is non-zero and accounts for the frame.
18+
19+
**A counter that reads zero means the instrumentation is broken, never that the work is free.** GPU cost surfaces at surface-acquire/present, not in a CPU command-record timer — measure it with a real GPU timestamp (QuerySet), do not infer it.
20+
21+
## Rules
22+
23+
- No optimization without a measurement first. Post the before-number, then the after-number, on the issue.
24+
- Never silently cap coverage (top-N, sampling, windowing) to hit a number — `log`/document what was dropped. A scene-windowing workaround is a consumer band-aid, not an engine fix.
25+
- The target is **O(visible), not O(total)**: per-frame CPU cost must not scale with total scene/entity count. If it does, that is the bug, regardless of FPS.
26+
- Steady-state render frame (no scene mutation) must perform **0 heap allocations** in the render path — assert it with the alloc-budget harness (ENG2-P0-15), do not eyeball it.
27+
- Aim for headroom, not the 60 FPS floor: the v2 targets are 120+ FPS for tens of thousands of visible entities.
28+
29+
## Before closing
30+
31+
Run the relevant bench-gate scene, attach the capture (scene, numbers, phase breakdown, delta vs baseline). See `docs/src/runbook/perf-dod.md` for scenes and targets and `[perf-work]`↔`[render-v2]`↔`[testing-v2]` for the companion rules.

.cursor/rules/render-v2.mdc

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
description: Render Core v2 Rules (ENG2 Phases 2 & 6)
3+
globs:
4+
alwaysApply: true
5+
---
6+
7+
<!-- AUTO-GENERATED by scripts/sync-agent-configs.py. Edit .agents/rules/render-v2.md, then rerun the generator. -->
8+
# Render Core v2 Rules (ENG2 Phases 2 & 6)
9+
10+
Applies to `**/graphics/**` and the renderer paths. These invariants supersede the performance/submission guidance in `graphics-patterns.md` (the backend-isolation and Renderer-trait rules there still hold). Full design: `docs/src/runbook/phases/phase-2.md` and RFC-0005.
11+
12+
## Submission model
13+
14+
- **O(visible), never O(total).** No per-frame iteration over the entire scene-object set. Cull against a spatial index (grid/BVH) *before* any transform/uniform/encode work. A culled object must pay zero per-frame cost.
15+
- **Dense storage, not hash-map iteration.** Scene objects live in slot arrays with generational handles, iterated contiguously — not a per-frame walk of a `FxHashMap`.
16+
- **Instancing by identity.** Draws are grouped by `(mesh id, material id)` and submitted instanced by default — including `CreatePlane`/`CreateCube`/`CreateSphere`/`CreateCylinder` primitives, not only `InstantiateModel`. A config flag that claims to enable instancing must actually drive the instanced path or not exist.
17+
18+
## Uniforms & buffers
19+
20+
- **Per-frame constants uploaded once** (camera, lights, fog) into one bind group. **Per-object data goes in dynamic-offset slots** — never copy a full multi-KB uniform block per draw, never issue one `write_buffer` per draw. Coalesce writes into one upload per frame.
21+
- **Persistent, dirty-tracked instance/transform buffers.** Upload only changed ranges. Never rebuild the whole buffer, and never CPU-pre-transform vertices into a static VBO that must be re-baked on any change.
22+
- Do not clone per-instance CPU vertex data for instances that share a mesh.
23+
24+
## Data seam
25+
26+
- Transforms are a **structure-of-arrays column shared between ECS and the renderer** (ENG2-P2-13). The renderer consumes dirty ranges from that column; it does not receive per-entity transforms across FFI one at a time.
27+
28+
## Every render/UI/particle change
29+
30+
- Add or update a **story** in the scene gallery (ENG2-P0-14) with a golden image and metric budget — see `[testing-v2]`.
31+
- Meet the perf definition of done — see `[perf-work]` and `docs/src/runbook/perf-dod.md`.
32+
- Raw GPU calls stay in `libs/graphics/backend/` (unchanged boundary).

.cursor/rules/testing-v2.mdc

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
description: Testing v2 Rules (ENG2)
3+
globs:
4+
alwaysApply: true
5+
---
6+
7+
<!-- AUTO-GENERATED by scripts/sync-agent-configs.py. Edit .agents/rules/testing-v2.md, then rerun the generator. -->
8+
# Testing v2 Rules (ENG2)
9+
10+
Applies to all ENG2 work. Full strategy: `docs/src/runbook/testing-strategy.md`. Extends `testing.md` (GL-context and unit conventions there still hold).
11+
12+
## Spec-first (TDD)
13+
14+
- Every behavioral issue gets a spec test at `goud_engine/tests/spec/eng2_p<N>_<nn>_<slug>.rs` encoding its Acceptance Criteria as executable assertions. The spec **is** the gate evidence.
15+
- Write the spec RED before the implementation; make it GREEN; then REFACTOR. A reviewer confirms it was red-before-green.
16+
- The issue's Verification section names the spec path. Do not close an issue whose spec test does not exist or does not assert its acceptance criteria.
17+
18+
## Tiers — put each test at the right level
19+
20+
- **Unit** — pure logic, no GPU. Design v2 render/ECS logic (culling, instancing-grouping, sort-key, propagation) to be unit-testable without a GPU context.
21+
- **Isolation** — one subsystem behind its null/provider boundary (`NullBackend`, null net/audio, in-memory VFS). If the seam to mock a dependency is missing, add the null/mock provider.
22+
- **Integration** — cross-layer in-process; assert exact draw/culled/visible counts (generalize `tests/renderer3d_frame_counts.rs`).
23+
- **E2E** — SDK-driven headless engine (ENG2-P0-17); C#/TS/Python boot a real engine, run a scripted game, assert counts/world-hash/flat-RSS.
24+
25+
## Story gallery
26+
27+
- Every render/UI/particle/animation change adds or updates a **story** (ENG2-P0-14): scene + camera + knobs + expected invariants + golden image. CI renders it headless, diffs the golden, checks metric budgets, and publishes the HTML gallery.
28+
- Changing a golden requires the documented golden-update approval — do not silently overwrite baselines.
29+
30+
## Determinism, property, fuzz
31+
32+
- Sim/scheduling changes run the determinism fixture (3 seeds × 10k ticks, hash-compare). Do not introduce nondeterminism (unordered iteration, wall-clock reads, FP drift).
33+
- Component-store and command-decoder changes add/extend `proptest` + `cargo-fuzz` targets (ENG2-P0-18, P3-09).
34+
35+
## Don't game the gate
36+
37+
Never weaken an assertion, widen a tolerance, or skip a scene to make a gate pass. If a gate is wrong, fix the gate in the same PR with justification.

docs/src/runbook/phase-specs.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ CI runners cannot measure Metal. Two enforcement layers:
141141

142142
| ID | Title | Grp | Effort | Blocked by |
143143
|---|---|---|---|---|
144-
| ENG2-P1-03 | Generate ffi_mapping.json from the Rust manifest; delete the 30-entry _TYPE_ALIASES drift table | A | M ||
144+
| ENG2-P1-03 | Generate ffi_mapping.json from the Rust manifest; delete the 30-entry `_TYPE_ALIASES` drift table | A | M ||
145145
| ENG2-P1-04 | Per-SDK export-parity CI gate: diff each generated SDK against the manifest | A | M | P1-03 |
146146
| ENG2-P1-05 | Bring wasm/web bridge under manifest codegen + coverage gate; wasm-pack modernization (#425) | B | L | P1-03 |
147147

@@ -393,20 +393,23 @@ CI runners cannot measure Metal. Two enforcement layers:
393393
**Goal:** Four sprite renderers become one instanced core; 2D camera and culling move engine-side. 8 issues.
394394

395395
### Batch 6.0 — Decision record
396+
396397
| ID | Title | Grp | Effort | Blocked by |
397398
|---|---|---|---|---|
398399
| ENG2-P6-01 | RFC-0008: Unified instanced 2D sprite core || S | Phase 4 gate |
399400

400401
- Four parallel renderers today: immediate FFI (~15 GL-state calls/sprite, `ffi/renderer/draw/internal.rs:107-132`), batched FFI (full rebuild/sort/upload per call, `draw/batch.rs:171-343`), retained ECS SpriteBatch (unreachable from FFI, `rendering/sprite_batch/batch.rs`), WASM batcher (`wasm/sprite_renderer/renderer_core.rs`). Zero GPU instancing anywhere — every sprite CPU-expanded to 4 vertices.
401402

402403
### Batch 6.1 — Core
404+
403405
| ID | Title | Grp | Effort | Blocked by |
404406
|---|---|---|---|---|
405407
| ENG2-P6-02 | Instanced sprite core: static unit quad + dirty-tracked per-instance buffer (incl. flip flags, #424) || L | P6-01 |
406408

407409
- Per-instance {transform, size, uv-rect, color, tex-index, flags}; persistent buffers, changed-only uploads (replaces `batch.rs:234-242` CPU corner rotation + `batch.rs:355-417` per-frame vertex gen). Reuses Phase 2 instancing patterns behind the backend trait.
408410

409411
### Batch 6.2 — Path unification (3 groups, parallel)
412+
410413
| ID | Title | Grp | Effort | Blocked by |
411414
|---|---|---|---|---|
412415
| ENG2-P6-03 | Route FFI sprite paths through the core; delete immediate + rebuild-per-call paths | A | M | P6-02 |
@@ -416,6 +419,7 @@ CI runners cannot measure Metal. Two enforcement layers:
416419
- **P6-05:** FFI batch sets only `u_viewport` (`draw/batch.rs:355`); Throne does world→screen math + visibility in C# per sprite (`GoudRenderPort.cs:275`). Camera uniform + grid/quadtree culling before submission; #554's configurable origin lands in the camera.
417420

418421
### Batch 6.3 — 2D scale features (3 groups, parallel)
422+
419423
| ID | Title | Grp | Effort | Blocked by |
420424
|---|---|---|---|---|
421425
| ENG2-P6-06 | Chunked tilemap renderer from TileLayer gids | A | M | P6-02 |
@@ -439,6 +443,7 @@ CI runners cannot measure Metal. Two enforcement layers:
439443
**Goal:** The built-but-unwired services become real: async assets, mipmaps, one audio stack, one 2D physics, retained UI, multi-window. 6 issues.
440444

441445
### Batch 7.1 — Assets & media (3 groups, parallel)
446+
442447
| ID | Title | Grp | Effort | Blocked by |
443448
|---|---|---|---|---|
444449
| ENG2-P7-01 | Wire async asset pipeline into the runtime; background decode incl. RGB→RGBA off main thread | A | M | Phase 4 gate |
@@ -450,6 +455,7 @@ CI runners cannot measure Metal. Two enforcement layers:
450455
- **P7-03:** `assets/audio_manager/` (2832 LOC) vs `libs/providers/impls/rodio_audio.rs:118-138` — two full rodio wrappers, two unsafe Send/Sync blocks; keep the provider-trait one, bridge asset data through it.
451456

452457
### Batch 7.2 — Physics, UI, windowing (3 groups, parallel)
458+
453459
| ID | Title | Grp | Effort | Blocked by |
454460
|---|---|---|---|---|
455461
| ENG2-P7-04 | Consolidate 2D physics on rapier2d; delete the custom ECS solver | A | M | P3-07 |
@@ -474,6 +480,7 @@ CI runners cannot measure Metal. Two enforcement layers:
474480
**Goal:** "Any game" credibility: navigation, particles, save/load, procedural primitives, terrain. Every capability ships FFI + 10-SDK parity + integration test + docs page (per the Phase 1 gates, now cheap). 11 issues.
475481

476482
### Batch 8.1 — Highest-demand capabilities (4 groups, parallel)
483+
477484
| ID | Title | Grp | Effort | Blocked by |
478485
|---|---|---|---|---|
479486
| ENG2-P8-01 | Grid A* + flow fields reusing ecs/spatial_grid; nav FFI surface (#546) | A | L | P5-03 |
@@ -487,6 +494,7 @@ CI runners cannot measure Metal. Two enforcement layers:
487494
- **P8-04:** No `rand`/`noise` in engine deps; cross-language determinism requires an engine-owned seeded PRNG (ties to P5-04 hashing).
488495

489496
### Batch 8.2 — Gameplay services (4 groups, parallel)
497+
490498
| ID | Title | Grp | Effort | Blocked by |
491499
|---|---|---|---|---|
492500
| ENG2-P8-05 | Engine event bus with FFI subscription (#547) | A | M ||
@@ -498,6 +506,7 @@ CI runners cannot measure Metal. Two enforcement layers:
498506
- **P8-08:** Deliberately late: Phases 2/3/6 already deleted much cgmath-using code; migrate the remainder onto `core/math`, drop the advisory-ignored dep.
499507

500508
### Batch 8.3 — Big-world (2 groups)
509+
501510
| ID | Title | Grp | Effort | Blocked by |
502511
|---|---|---|---|---|
503512
| ENG2-P8-09 | Navmesh generation/query for 3D navigation | A | L | P8-01 |
@@ -519,6 +528,7 @@ CI runners cannot measure Metal. Two enforcement layers:
519528
**Goal:** Every kept platform is CI-real; developers can see and author scenes; examples prove the v2 API. 9 issues.
520529

521530
### Batch 9.1 — Platforms & tooling (4 groups, parallel)
531+
522532
| ID | Title | Grp | Effort | Blocked by |
523533
|---|---|---|---|---|
524534
| ENG2-P9-01 | Scene inspector MVP (egui or web client over the MCP relay): entity tree + live component editing | A | L | P7-06 |
@@ -533,6 +543,7 @@ CI runners cannot measure Metal. Two enforcement layers:
533543
- **P9-05:** `providers/impls/webrtc_network/mod.rs:1-17` is custom UDP+STUN labeled WebRTC, `net-webrtc=[]` pulls no crate; ~7-8k LOC networking has zero production consumers — decision record on tiering (default-feature status) + honest naming.
534544

535545
### Batch 9.2 — Examples, docs, agent configs (4 groups, parallel)
546+
536547
| ID | Title | Grp | Effort | Blocked by |
537548
|---|---|---|---|---|
538549
| ENG2-P9-06 | Compiled Rust examples in the engine crate (currently zero .rs examples) | A | M ||

docs/src/runbook/phases/phase-1.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ _Authoritative spec. See also the [phase index](../phase-index.md), [perf-dod](.
2222

2323
| ID | Title | Grp | Effort | Blocked by |
2424
|---|---|---|---|---|
25-
| ENG2-P1-03 | Generate ffi_mapping.json from the Rust manifest; delete the 30-entry _TYPE_ALIASES drift table | A | M ||
25+
| ENG2-P1-03 | Generate ffi_mapping.json from the Rust manifest; delete the 30-entry `_TYPE_ALIASES` drift table | A | M ||
2626
| ENG2-P1-04 | Per-SDK export-parity CI gate: diff each generated SDK against the manifest | A | M | P1-03 |
2727
| ENG2-P1-05 | Bring wasm/web bridge under manifest codegen + coverage gate; wasm-pack modernization (#425) | B | L | P1-03 |
2828

docs/src/runbook/phases/phase-6.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,23 @@ _Authoritative spec. See also the [phase index](../phase-index.md), [perf-dod](.
99
**Goal:** Four sprite renderers become one instanced core; 2D camera and culling move engine-side. 8 issues.
1010

1111
### Batch 6.0 — Decision record
12+
1213
| ID | Title | Grp | Effort | Blocked by |
1314
|---|---|---|---|---|
1415
| ENG2-P6-01 | RFC-0008: Unified instanced 2D sprite core || S | Phase 4 gate |
1516

1617
- Four parallel renderers today: immediate FFI (~15 GL-state calls/sprite, `ffi/renderer/draw/internal.rs:107-132`), batched FFI (full rebuild/sort/upload per call, `draw/batch.rs:171-343`), retained ECS SpriteBatch (unreachable from FFI, `rendering/sprite_batch/batch.rs`), WASM batcher (`wasm/sprite_renderer/renderer_core.rs`). Zero GPU instancing anywhere — every sprite CPU-expanded to 4 vertices.
1718

1819
### Batch 6.1 — Core
20+
1921
| ID | Title | Grp | Effort | Blocked by |
2022
|---|---|---|---|---|
2123
| ENG2-P6-02 | Instanced sprite core: static unit quad + dirty-tracked per-instance buffer (incl. flip flags, #424) || L | P6-01 |
2224

2325
- Per-instance {transform, size, uv-rect, color, tex-index, flags}; persistent buffers, changed-only uploads (replaces `batch.rs:234-242` CPU corner rotation + `batch.rs:355-417` per-frame vertex gen). Reuses Phase 2 instancing patterns behind the backend trait.
2426

2527
### Batch 6.2 — Path unification (3 groups, parallel)
28+
2629
| ID | Title | Grp | Effort | Blocked by |
2730
|---|---|---|---|---|
2831
| ENG2-P6-03 | Route FFI sprite paths through the core; delete immediate + rebuild-per-call paths | A | M | P6-02 |
@@ -32,6 +35,7 @@ _Authoritative spec. See also the [phase index](../phase-index.md), [perf-dod](.
3235
- **P6-05:** FFI batch sets only `u_viewport` (`draw/batch.rs:355`); Throne does world→screen math + visibility in C# per sprite (`GoudRenderPort.cs:275`). Camera uniform + grid/quadtree culling before submission; #554's configurable origin lands in the camera.
3336

3437
### Batch 6.3 — 2D scale features (3 groups, parallel)
38+
3539
| ID | Title | Grp | Effort | Blocked by |
3640
|---|---|---|---|---|
3741
| ENG2-P6-06 | Chunked tilemap renderer from TileLayer gids | A | M | P6-02 |

docs/src/runbook/phases/phase-7.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ _Authoritative spec. See also the [phase index](../phase-index.md), [perf-dod](.
99
**Goal:** The built-but-unwired services become real: async assets, mipmaps, one audio stack, one 2D physics, retained UI, multi-window. 6 issues.
1010

1111
### Batch 7.1 — Assets & media (3 groups, parallel)
12+
1213
| ID | Title | Grp | Effort | Blocked by |
1314
|---|---|---|---|---|
1415
| ENG2-P7-01 | Wire async asset pipeline into the runtime; background decode incl. RGB→RGBA off main thread | A | M | Phase 4 gate |
@@ -20,6 +21,7 @@ _Authoritative spec. See also the [phase index](../phase-index.md), [perf-dod](.
2021
- **P7-03:** `assets/audio_manager/` (2832 LOC) vs `libs/providers/impls/rodio_audio.rs:118-138` — two full rodio wrappers, two unsafe Send/Sync blocks; keep the provider-trait one, bridge asset data through it.
2122

2223
### Batch 7.2 — Physics, UI, windowing (3 groups, parallel)
24+
2325
| ID | Title | Grp | Effort | Blocked by |
2426
|---|---|---|---|---|
2527
| ENG2-P7-04 | Consolidate 2D physics on rapier2d; delete the custom ECS solver | A | M | P3-07 |

docs/src/runbook/phases/phase-8.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ _Authoritative spec. See also the [phase index](../phase-index.md), [perf-dod](.
99
**Goal:** "Any game" credibility: navigation, particles, save/load, procedural primitives, terrain. Every capability ships FFI + 10-SDK parity + integration test + docs page (per the Phase 1 gates, now cheap). 11 issues.
1010

1111
### Batch 8.1 — Highest-demand capabilities (4 groups, parallel)
12+
1213
| ID | Title | Grp | Effort | Blocked by |
1314
|---|---|---|---|---|
1415
| ENG2-P8-01 | Grid A* + flow fields reusing ecs/spatial_grid; nav FFI surface (#546) | A | L | P5-03 |
@@ -22,6 +23,7 @@ _Authoritative spec. See also the [phase index](../phase-index.md), [perf-dod](.
2223
- **P8-04:** No `rand`/`noise` in engine deps; cross-language determinism requires an engine-owned seeded PRNG (ties to P5-04 hashing).
2324

2425
### Batch 8.2 — Gameplay services (4 groups, parallel)
26+
2527
| ID | Title | Grp | Effort | Blocked by |
2628
|---|---|---|---|---|
2729
| ENG2-P8-05 | Engine event bus with FFI subscription (#547) | A | M ||
@@ -33,6 +35,7 @@ _Authoritative spec. See also the [phase index](../phase-index.md), [perf-dod](.
3335
- **P8-08:** Deliberately late: Phases 2/3/6 already deleted much cgmath-using code; migrate the remainder onto `core/math`, drop the advisory-ignored dep.
3436

3537
### Batch 8.3 — Big-world (2 groups)
38+
3639
| ID | Title | Grp | Effort | Blocked by |
3740
|---|---|---|---|---|
3841
| ENG2-P8-09 | Navmesh generation/query for 3D navigation | A | L | P8-01 |

docs/src/runbook/phases/phase-9.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ _Authoritative spec. See also the [phase index](../phase-index.md), [perf-dod](.
1111
**Goal:** Every kept platform is CI-real; developers can see and author scenes; examples prove the v2 API. 9 issues.
1212

1313
### Batch 9.1 — Platforms & tooling (4 groups, parallel)
14+
1415
| ID | Title | Grp | Effort | Blocked by |
1516
|---|---|---|---|---|
1617
| ENG2-P9-01 | Scene inspector MVP (egui or web client over the MCP relay): entity tree + live component editing | A | L | P7-06 |
@@ -25,6 +26,7 @@ _Authoritative spec. See also the [phase index](../phase-index.md), [perf-dod](.
2526
- **P9-05:** `providers/impls/webrtc_network/mod.rs:1-17` is custom UDP+STUN labeled WebRTC, `net-webrtc=[]` pulls no crate; ~7-8k LOC networking has zero production consumers — decision record on tiering (default-feature status) + honest naming.
2627

2728
### Batch 9.2 — Examples, docs, agent configs (4 groups, parallel)
29+
2830
| ID | Title | Grp | Effort | Blocked by |
2931
|---|---|---|---|---|
3032
| ENG2-P9-06 | Compiled Rust examples in the engine crate (currently zero .rs examples) | A | M ||

0 commit comments

Comments
 (0)