Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
env:
CARGO_TERM_COLOR: always
# Turn adjustable rustc lint warnings into hard errors for local crates across
# the build/test/docs compile lanes (respected as of Rust 1.97; MSRV is 1.97.0).
# the build/test/docs compile lanes (respected as of Rust 1.97; MSRV is 1.97.1).
# Complements clippy's `-D warnings` and the workspace deny lint tables.
CARGO_BUILD_WARNINGS: deny

Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ members = [
[workspace.package]
version = "0.1.0"
edition = "2024"
rust-version = "1.97.0"
rust-version = "1.97.1"
authors = ["Marat Bukharov <marat.buharov@gmail.com>"]
license = "MIT OR Apache-2.0"

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,10 @@ pipeline, oracle, feature extraction, policy) are still `todo!()`. See the
```sh
cargo build # whole workspace
cargo run -p gp-game # run the graphite-gp binary (scaffold banner)
cargo test # 97 gp-core + 2 gp-gen tests green (supercover + corridor-graph + Size/Rect + overflow-safety + typed legal_mask + track-artifact contract + sim step + lap counter + crash rule + collisions + seeded RNG)
cargo test # 103 workspace tests green (97 gp-core; 2 gp-gen; 2 gp-render: tessellation smoke + golden guard; 2 doc-tests)
```

MSRV: **Rust 1.97.0**. CI (GitHub Actions, `ubuntu-latest`) runs format, build,
MSRV: **Rust 1.97.1**. CI (GitHub Actions, `ubuntu-latest`) runs format, build,
test, clippy (`-D warnings`), and docs on every push/PR to `main`, plus an
advisory Miri lane; the workspace lint policy (`clippy::pedantic`/`nursery` =
`deny`) lives in the root `Cargo.toml` + `clippy.toml` (see
Expand Down
2 changes: 1 addition & 1 deletion ai-docs/context.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Dependency edges: `gen · render · ai → core`; `game → all`; `core` depends
- Code: **scaffold + geom physics primitives** — module structure, `TrackArtifact` type, and stub APIs in place. `crates/core/src/geom/` (split into `mod.rs` + a private `graph.rs`) implements the exact integer `supercover` predicate (full §3 C4 test table) plus the corridor-graph helpers: 4-conn `flood_fill` / `component_count`, `bounded_complement_components` (the §2 Ф4 infield-hole test), in-`D` geodesic BFS (`CorridorScratch::geodesic_bfs` reusable-scratch visitor + eager `geodesic_layers`), and `walls_from_boundary` (Ф7 dual edges). The box/index math is factored into `Size { width, height }` (unsigned) + `Rect { origin, size }` value types; `Corridor` is `{ rect, cells }` with **unsigned** dimensions — negative dims are unrepresentable, so `Corridor::new` needs no `assert!` and **gp-core has zero production panics** (`Rect::index` is total via `checked_sub` + `usize::try_from`). Every integer op is now **overflow- and signedness-safe by construction** (issue #48): `Point::neighbors4` saturates, `legal_move` / `walls_from_boundary` use `checked_add`, and each deliberately-retained bounded raw op carries a documented domain bound + covering test. `Wall` is `{ cell, side: Side }` (4-way outward `Side`). `legal_mask` returns a typed `enumflags2::BitFlags<Action>` set (re-exported from `gp-core::sim` so consumers need no direct `enumflags2` dep) rather than a positional `[bool; 5]` — issue #51. The **`TrackArtifact` contract is now finalized** (issue #6): `SField` distance/gradient/tangent accessors (`t̂ = normalize(∇s)`, gate cells → `race_dir`, gradient barred at the gate cut), `StartGrid` (distinct `v=(0,0)` positions, front-to-back along `−race_dir`), the `TimingGate` half-grid segment on `StartFinish` (`separates` barrier + explicit `forward: Side`), and `Centerline::at` arc-length sampling (wrap-around + total, no panic) — contract types + read accessors on hand-filled fixtures; the block-1 generator that populates them stays `todo!`. `sim::step` — the pure accelerate-then-advance state advance (`(vx',vy') = v + accel`, then `pos += (vx',vy')`) — is implemented as an infallible `const fn` on the assumed-legal domain (no legality check; that stays `legal_move`'s sole job), joining the already-live `legal_move` / `legal_mask` path (issue #7). `LapCounter::register_move` — the signed **half-open S/F crossing test** over the half-grid timing gate (`+1` forward / `−1` reverse, ≤1 event/move via a doubled integer perpendicular coordinate whose odd `GATE_LINE` value is unreachable by real `Point`s), with the `legal_move`-first valid-finish conjunction expressed at the call site — is implemented (issue #8). `sim::resolve_crash` — the finalized **quench-with-scrub crash rule** (a search dead-end where `legal_mask` is empty): respawn at the last swept cell still in `D` (a projection-ordered `supercover` walk, no second geometry path), zero the into-wall velocity component and damp the along-wall component to `⌊t/2⌋` (a concave corner zeros both), plus a one-tick forced-`Coast` **scrub** marker and a whole-vector-halving **fail-safe** (guarded by an `L∈D` termination check; inherits `supercover`'s bounded-chord precondition) that never yields a penalty-free controlled `v=0` — is implemented as a pure `resolve_crash(d, s) -> CrashOutcome` whose `action_mask` / (total) `consume_scrub` make the scrub tick unit-testable inside `gp-core` (issue #9). `sim::resolve_collisions` — the **same-final-cell collision resolver** (issues #10 + #49): cars ending a turn on the same cell are bucketed, a seeded `rand_chacha` ChaCha8 RNG (canonical `sort_unstable_by_key(|g| g[0])` order → `groups.shuffle` → per-group shuffle → `u32` equidistant tie-draw, cross-arch-reproducible) picks the winner + displacement order, and each loser teleports to the nearest free cell via the in-`D` geodesic BFS (velocity retained, no supercover/lap-counter touch, single occupied-after-each pass). Per a **product-owner amendment (2026-07-16)** the predicate is same-final-cell **only** — the swap/pass-through detector (`docs/design.md` [D1]/[N2]) was dropped, so swaps / mid-segment / orthogonal crossings ending on **distinct** cells are allowed, and the two canonical docs were corrected to match. The tie-draw is total (`u32::try_from(..).unwrap_or(u32::MAX)`) so **gp-core keeps zero production panics**. A follow-up **PR #68 review amendment** changed `resolve_collisions` to take a **caller-owned physics RNG handle** (`rng: &mut ChaCha8Rng`, drawn from but never re-seeded) rather than a per-call `seed: u64`, establishing a **per-domain seeded-RNG architecture** — one long-lived stream each for physics / track generation / AI learning / AI inference (the AI streams and the block-3b game-loop stream ownership are deferred). Issue #49 also adds the `rand` + `rand_chacha` (`0.10`, `default-features = false` ⇒ no `getrandom`/OS entropy) stack to **both** `gp-core` and `gp-gen`, with a seeded `GenParams::rng() -> ChaCha8Rng` (gp-gen's generator stochastic usage stays deferred — #49 remains open for that remainder). **97 gp-core + 2 gp-gen unit tests.** The `gen` / `render` / `ai` algorithms are still `todo!()` (marked `TODO(<block>)`). Whole workspace builds clean.
- **Visual base:** the Claude Design "Graphite GP Design System" is imported to [`docs/design-system/`](../docs/design-system/IMPORT.md) and adopted as the canonical visual language; render target is a **native Rust GUI** (design tokens/components are a spec to port, not runnable web code). The backend is **eframe/egui 0.35** (#11) — chosen because its widget layer makes #13–#16 token-styling tasks rather than from-scratch toolkit work; rationale + rejected alternatives in [`key-decisions.md`](key-decisions.md).
- **Golden-image testing (block 2, from #11):** `gp-render` ships a committed golden PNG (`crates/render/tests/snapshots/placeholder.png` — the repo's **first binary asset**, plain git, no LFS) rendered offscreen through `egui_kittest`'s wgpu/Vulkan harness. Three mechanisms guard it, each catching what the others cannot: **exact compare** (`threshold(0.0)` + `failed_pixel_count_threshold(0)` — both must be overridden; the `0.6` default silently permits colour distance) catches *regressions*; **AC9 pixel probes** catch *degeneracy*; and the **`image-check` subagent** (spawned by `code-writer` at mint/regen only, **never CI**) catches *a golden that was wrong from birth*. Exactness is bit-exact **in flat regions only** — `egui_kittest` hardcodes dify's `detect_anti_aliased_pixels`, so AA-classified edge pixels differ silently with no diff artifact; accepted, revisit when dx12/metal lanes join the CI matrix. Adapter is asserted `DeviceType::Cpu` (llvmpipe), so a bit-exact golden can never be minted against a discrete GPU.
- **CI / tooling:** GitHub Actions CI (`ubuntu-latest`) is in place — `changes`-gated format/build/test/clippy/docs + advisory Miri + `-pass` branch-protection gates, sccache, and a mandatory Linux software-Vulkan env-init in the `test` job (ready for the block-2 wgpu/Vulkan renderer). Plus Dependabot (cargo + github-actions), MSRV bumped to **1.97.0** (`resolver = "3"` retained — virtual workspace), `CARGO_BUILD_WARNINGS=deny`, and a strict workspace lint policy (`clippy::pedantic`/`nursery` = `deny`, `missing_docs`/`broken_intra_doc_links` = `deny`, `clippy::arithmetic_side_effects` = `deny` — issue #48, machine-enforcing integer-overflow safety) in the root `Cargo.toml` + `clippy.toml`, each crate opting in via `[lints] workspace = true`. See [`code-style.md`](code-style.md) § Linter posture.
- **CI / tooling:** GitHub Actions CI (`ubuntu-latest`) is in place — `changes`-gated format/build/test/clippy/docs + advisory Miri + `-pass` branch-protection gates, sccache, and a mandatory Linux software-Vulkan env-init in the `test` job (ready for the block-2 wgpu/Vulkan renderer). Plus Dependabot (cargo + github-actions), MSRV **1.97.1** (`resolver = "3"` retained — virtual workspace), `CARGO_BUILD_WARNINGS=deny`, and a strict workspace lint policy (`clippy::pedantic`/`nursery` = `deny`, `missing_docs`/`broken_intra_doc_links` = `deny`, `clippy::arithmetic_side_effects` = `deny` — issue #48, machine-enforcing integer-overflow safety) in the root `Cargo.toml` + `clippy.toml`, each crate opting in via `[lints] workspace = true`. See [`code-style.md`](code-style.md) § Linter posture.
- **Next implementation step: block 3a is COMPLETE** — `supercover` (#4), the corridor-graph helpers (#5), the `TrackArtifact` contract (#6), `sim::step` + `legal_move` / `legal_mask` (#7), `LapCounter::register_move` (#8), `resolve_crash` (#9), and `resolve_collisions` (#10) are all landed; **zero open `block:core` issues**. Per the §6 build order (`3a → (1 ∥ 2) → 4`), the next units are **block 1 (`gp-gen`)** — the Ф1–Ф7 generator pipeline (#24–#34) that populates the `TrackArtifact` contract, starting at Ф1 coarse-block ring (#24); its seeded `GenParams::rng()` already exists (#49, whose remainder is the generator's stochastic usage) — and, in parallel, **block 2 (`gp-render`)**, whose foundational GUI-backend scaffold (#11) is now **landed** — eframe/egui 0.35 picked, window/loop standing in `gp-game`, `render_frame` re-signed to take `&egui::Painter`, and the golden-image harness in place; block 2 continues at the design tokens (#12), which also owns the real font faces (#11 deliberately loads none — see [`key-decisions.md`](key-decisions.md)).
- **Not 3a** (a recurring mis-attribution — both are tracked under later blocks): the **passability oracle** is block 1 (`gp-gen` Ф5a #28 / Ф5b #29), and the **tick/turn driver loop** — ordering `legal_move` → `step` → `register_move` → crash/collision resolution, then reading `laps()`/`raw()` for the win check — is **block:game** (#43, the full game loop). 3a ships the pure per-move primitives; sequencing them into a running game is block 3b/game.

Expand Down
2 changes: 1 addition & 1 deletion ai-docs/key-decisions.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ The finalized design and its full rationale live in [`docs/design.md`](../docs/d

**Context.** Issue #11 is the foundational decision unit for Block 2 — `gp-render` ([`docs/design.md`](../docs/design.md) §4 *Рендеринг + UX*, §6 *Архитектура*); the other twelve `block:render` issues (#12–#23) all sit downstream of the pick. The design system to port (`docs/design-system/`) specifies flat vector geometry on a lattice, hairline pencil borders, crisp radii, and no photographic imagery. The downstream units cover **13 components** (#13–#16) across **4 screens** (#19–#22).

**Decision.** **eframe/egui 0.35** — `egui` is the draw layer, `eframe` the window/loop shell. Verified live 2026-07-16: `egui` and `eframe` `max_stable_version` = **0.35.0**; MSRV 1.92 ≤ the workspace's `rust-version = 1.97.0`; eframe's **default** renderer is wgpu (glow is opt-in), which matches the CI `WGPU_BACKEND=vulkan` env as-is.
**Decision.** **eframe/egui 0.35** — `egui` is the draw layer, `eframe` the window/loop shell. Verified live 2026-07-16: `egui` and `eframe` `max_stable_version` = **0.35.0**; MSRV 1.92 ≤ the workspace's `rust-version = 1.97.1`; eframe's **default** renderer is wgpu (glow is opt-in), which matches the CI `WGPU_BACKEND=vulkan` env as-is.

**(a) Why it fits the aesthetic.** egui's `Painter` draws the design system's vocabulary directly — flat fills, strokes, and lines on a lattice — with no photographic or raster-asset pipeline to fight. Verified against `egui-0.35.0/src/painter.rs`: `rect_filled` (:397) and `rect_stroke` (:406) give crisp shapes and radii; `line_segment` (:318) and `hline` (:332) give hairline strokes; `hline` + `circle_filled` (:356) give the graph-paper ruling + dot motif.

Expand Down
6 changes: 6 additions & 0 deletions ai-docs/learnings.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,9 @@ Append-only correction/validation log — the feed for `/improve`. See AGENTS.md
**Rule:** When adding a test that touches **FFI, a GPU, `dlopen`, or any foreign function** (wgpu / vulkan / a `-sys` crate / `libloading`), gate it with `#[cfg_attr(miri, ignore = "<why>")]` **in the same commit**. Miri aborts the `--workspace` run at the first unsupported op and cargo's fail-fast drops every phase queued behind it, so the blast radius is the offending crate's whole binary plus the later phases (doc-tests especially) — and any crate whose unittest binary happens to be scheduled after it. **Measure the blast radius from the log before claiming it**; crates scheduled earlier complete normally, and asserting otherwise puts a mechanism that does not exist into the feed `/improve` escalates into AGENTS.md. Prefer the **per-test attribute** over a crate-level `--exclude` in `ci.yml`: it keeps that crate's non-FFI tests under Miri (`tessellation_smoke` really does run), states the reason at the site where a future reader hits it, and prints "ignored, <reason>" in the run output instead of silently omitting the test. Verify with the *exact* CI invocation (`MIRIFLAGS=-Zmiri-tree-borrows cargo +nightly miri test --workspace`) rather than a narrower `-p` run — `rustup component add miri --toolchain nightly` is a one-time install. **An `continue-on-error: true` job failing is still a regression**: it does not block merge, so the only thing standing between it and permanent decay is noticing. Check advisory checks after any PR that adds a new dependency class.
**Kind:** correction
**Escalated?** no

### 2026-07-16 — documentation — fetched the issue when the user linked a COMMENT; then acknowledged two verbal corrections without propagating either into the artifact
**What happened:** The user reported the 1.97.0 miscompile with a **precise anchor**: `https://github.com/rust-lang/rust/issues/159035#issuecomment-4976844186`. I ran `WebFetch` on the issue URL **with the anchor stripped**. The tool summarised the issue **body** — accurate on 2026-07-09, listing "1.98.0 beta (affected), 1.99.0 nightly (affected)" and stating no fix release was known — and I wrote that into `README.md` + `ai-docs/context.md` as present-tense fact ("an LLVM bug also present in 1.98 beta / 1.99 nightly"). **The comment the user linked contained the refutation**: *"The beta backport was done in #159072, and the stable backport was done in #159288. The 1.97.1 release is on its way. Thus, closing this issue as resolved."* The user then said **"nightly already patched"** and **"1.98 stable is not released yet"** — I replied "consistent with your claim" and "fair", **and edited nothing**: the README kept asserting the opposite for two more turns until `self-review` caught it as a `major`. Two distinct failures: (a) fetching the *container* when the user cited an *anchor* — their link was precise and I widened it into a stale summary; (b) treating a user correction as **conversationally settled** rather than as a **defect in the work product**. (b) is the worse one: they corrected me directly, twice, and the artifact still contradicted them.
**Rule:** **When a user cites a URL with a `#fragment`, fetch THAT anchor and read it — the fragment is the citation, the page is merely where it lives.** A user who links a specific comment has usually already found the answer and is handing it to you; re-deriving it from the page body substitutes stale context for their current evidence. Corollary, and the sharper half: **a verbal acknowledgement is not a fix.** When a user corrects a fact — especially mid-flow, especially when you've already written that fact into a file — the correction is a **work item**, not a conversational beat: `grep` the artifact for the wrong claim and edit it in the same turn you say "you're right". Tell: any reply containing "fair", "good point", "consistent with", or "that closes it" that is **not accompanied by an Edit** to whatever asserts the now-refuted thing. Issue *bodies* are frozen at filing time while *comments* carry the resolution — for any bug-tracker citation, check the closing comment and the issue state before rendering the body's claims as current. Same family as [[2026-07-16 — search — find/ls proves on-disk presence, NOT git-tracked status]] and [[2026-07-16 — search — git status is BLIND to ignored files]]: asserting from a source that cannot see the thing asked about — here the source was stale rather than blind, and the live one was in the user's own link.
**Kind:** correction
**Escalated?** no
Loading