From d5895c0bd8094ea99e4dc7499babe48af7d71196 Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Thu, 22 Oct 2026 08:59:19 +0200 Subject: [PATCH 1/4] Rule cuts: apply the approved cut list, rule text 11,914 -> 9,492 words (@architect) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Applies section 4 of docs/retrospectives/2026-10-22-merge-331-and-cut-list.md (branch worktree-worktree-retro-cut-1022, 6bff3b46), all items, and its section 3 proposals: - CLAUDE.md section 2 gains "Rule text pays for itself" (29 words). - The restart bullet in .claude/REQUIRED-READING.md now reads: fast-forward the main checkout and restart before the next spawn. Items 2.2-2.6 (template skill text on libraries the repo does not use) are applied, and "or `click`" goes with them (typer is the CLI library). "Ruled" items keep the ruled rule in its one place; only the repeat is cut. 3.12 (plot bullet) keeps Ola's phrases. Not to the letter: - python-development: section 4 is cut but sections 5 and 6 keep their numbers, because docs/increments/42-readable-docstrings.md cites section 6. - PRINCIPLES.md B3 pointed at the cut A1 and B1; it now points at .claude/REQUIRED-READING.md, Claims. - docs/increments/h2-citation-pinning.md: its example citation `PRINCIPLES.md §E1` becomes §A6, so check_citations.py stays green. python3 tools/rule_sizes.py, before (base 94a157b2): 1255 CLAUDE.md 2014 .claude/REQUIRED-READING.md 1410 docs/increments/README.md 1396 docs/PRINCIPLES.md 236 .claude/briefs/common.md 541 .claude/agents/architect.md 241 .claude/agents/developer.md 482 .claude/agents/orchestrator.md 774 .claude/agents/perf.md 604 .claude/agents/reviewer.md 960 .claude/agents/tester.md 545 .claude/skills/computational-geometry/SKILL.md 392 .claude/skills/geospatial-data-formats/SKILL.md 334 .claude/skills/modern-cxx/SKILL.md 730 .claude/skills/python-development/SKILL.md 11914 total after: 1187 CLAUDE.md 1884 .claude/REQUIRED-READING.md 1007 docs/increments/README.md 437 docs/PRINCIPLES.md 236 .claude/briefs/common.md 495 .claude/agents/architect.md 211 .claude/agents/developer.md 482 .claude/agents/orchestrator.md 635 .claude/agents/perf.md 570 .claude/agents/reviewer.md 813 .claude/agents/tester.md 346 .claude/skills/computational-geometry/SKILL.md 249 .claude/skills/geospatial-data-formats/SKILL.md 308 .claude/skills/modern-cxx/SKILL.md 632 .claude/skills/python-development/SKILL.md 9492 total (-2,422) Checks: test_brief, test_spawn_rule_text, test_rule_sizes, test_settings_wiring, test_session_state, test_ci_changes, test_check_citations_pinning and test_guard_*: 2450 passed. check_citations.py exit 0; check_code_history.py clean. Left dangling, outside this branch's write limit: references to cut PRINCIPLES entries in .claude/hooks/guard_push.py (lines 4 and 461; the ask message cites "PRINCIPLES.md E1") and in test docstrings (plyread.py, vtkread.py, test_io_ply.py, test_io_vtk_legacy.py, test_always_xy.py, test_geotiff_fixtures.py: A1, A3, A4, B1). Co-Authored-By: Claude Opus 5.5 --- .claude/REQUIRED-READING.md | 45 ++--- .claude/agents/architect.md | 7 +- .claude/agents/developer.md | 7 +- .claude/agents/perf.md | 36 ++-- .claude/agents/reviewer.md | 5 +- .claude/agents/tester.md | 30 ++-- .../skills/computational-geometry/SKILL.md | 21 +-- .../skills/geospatial-data-formats/SKILL.md | 14 +- .claude/skills/modern-cxx/SKILL.md | 1 - .claude/skills/python-development/SKILL.md | 10 +- CLAUDE.md | 22 +-- docs/PRINCIPLES.md | 160 +----------------- docs/increments/README.md | 62 ++----- docs/increments/h2-citation-pinning.md | 2 +- 14 files changed, 84 insertions(+), 338 deletions(-) diff --git a/.claude/REQUIRED-READING.md b/.claude/REQUIRED-READING.md index 18646ff42..d4656e034 100644 --- a/.claude/REQUIRED-READING.md +++ b/.claude/REQUIRED-READING.md @@ -6,12 +6,11 @@ order as the sections here. ## Before you act in a cold or resumed session -"Ok, let's continue" is not an instruction. Neither is `git log`. Before the -first subagent spawn, the first commit, or the first edit, in this order: +Before the first subagent spawn, the first commit, or the first edit, in this +order: -1. The recap from `tools/session_state.py`: the round recap (last landed, in - flight, running background jobs, decisions waiting on Ola, next ROADMAP - items), `.claude/current-task/` and the predecessor's last human turns, +1. The recap from `tools/session_state.py`: the round recap, + `.claude/current-task/` and the predecessor's last human turns, **including prompts Ola queued and the harness absorbed mid-turn** (they never appear as normal turns). The `SessionStart` hook puts it in context on startup, resume, `/clear`, compaction and fork; if it is missing, run @@ -20,8 +19,7 @@ first subagent spawn, the first commit, or the first edit, in this order: file or PR is still pending, and an earlier pending turn outranks your reconstruction of "what comes next". Start no background job before the running ones are listed. -3. If anything is still ambiguous after both, **ask Ola one question**. A - question costs a line; a guessed round costs ~150k tokens. +3. If anything is still ambiguous after both, **ask Ola one question**. Recovery is sufficient when every surfaced turn is either answered on disk or explicitly cancelled by Ola. `git log` plus `docs/increments/` is not @@ -44,8 +42,8 @@ sufficient: it shows what was finished, never what was asked. lost session, the next session does, after step 1 has printed it. - **One session per working tree.** Nothing assigns `session.md`, so concurrent sessions need separate worktrees. -- **After merging a change to `CLAUDE.md` or `.claude/agents/`, restart - before spawning the changed persona**. +- **After merging a change to `CLAUDE.md` or `.claude/agents/`, fast-forward + the main checkout and restart before the next spawn**. - **A subagent whose product is a file creates that file first and writes incrementally**, so a death leaves something to resume. A persona's prompt names the output path. @@ -58,8 +56,7 @@ before writing, judging or planning any code. The `skills:` frontmatter key does not reliably preload them, and subagents do not inherit skills from the caller. -Also read `docs/increments/README.md` (the protocol and a round's cost -constraints) and the increment file for whatever you are working on. +Read the increment file for whatever you are working on. **Ideas and idea-stage research live in `../rasputin_scratch/ideas/` (beside the main checkout): private to Ola, outside git; commit nothing from @@ -76,15 +73,12 @@ in your handback under *Ideas*; the main session adds it. - **Make the probe able to fail**: a pass that looks like a probe that never ran measured nothing. Time a hang from outside the process. - **Do not write "X is verified by Y" until Y has been run against a broken - X.** Verify a gate by planting what it forbids: append the prohibited - directive to `CMakeLists.txt`, run `python3 tools/check_prohibited_deps.py`, - confirm it fails naming it, restore the file. + X.** Verify a gate by planting what it forbids. - Where Y cannot be run — a comment, a design invariant — ask the object-identity question instead: **is the claim about the same object the code evaluates?** - When a claim's truth depends on the state of the tree, **write the rule and - the command that resolves it, not the resolved value** (no "current anchor" - hash, no count of rounds or commits). + the command that resolves it, not the resolved value**. - This binds claims about behaviour — what a script does, what a suite covers, what a file says — not design opinions, which have no command to run. If you cannot run it, state the mechanism rather than the incident. @@ -142,18 +136,13 @@ round of findings (a verdict other than `APPROVED`, ## The harness -Active in `.claude/settings.json`: `guard_push.py` asks before any act -that publishes, writes to the forge, rewrites history or writes refs, -remotes or git config, and before `--no-verify` (its code lists them; not -`gh pr close` or `gh pr comment`); `guard_governance.py` asks before any write -to a file that states rules or to a `tools/` file named after a standard-library -module, a copy or move into their directories included (`cp json.py tools`, -with or without the trailing `/`), and a path is judged also with `.`, -`..` and a doubled `/` resolved (`tools/./json.py`); not a write to a -whole governed directory (`rm -r .claude/hooks`), -except an absolute path in a session scratchpad, written by Edit or -Write or by a shell line that is one plain command (no `&&`, `;`, pipe, -substitution or interpreter program). `guard_push.py` passes no git write in +Active in `.claude/settings.json`: `guard_push.py` asks before any act that +publishes, writes to the forge, rewrites history or writes refs, remotes or +git config, or uses `--no-verify` (its code lists them); `guard_governance.py` +asks before any write to a file that states rules or to a `tools/` file named +after a standard-library module (its code lists what it resolves), except an +absolute scratchpad path written by Edit or Write or one plain shell command. +`guard_push.py` passes no git write in a scratchpad repository. `guard_unattended.py` refuses `AskUserQuestion`, permission prompts and configuration changes while diff --git a/.claude/agents/architect.md b/.claude/agents/architect.md index 29dbce52f..5a3431855 100644 --- a/.claude/agents/architect.md +++ b/.claude/agents/architect.md @@ -10,7 +10,7 @@ tools: Read, Grep, Glob, Bash, Write, Edit, Skill, WebSearch, WebFetch, Monitor See `.claude/REQUIRED-READING.md`, and load it before acting. -You are the Principal Systems Architect for the terrain-meshing engine. Your primary responsibility is to enforce structural integrity, component swappability, and strict boundaries across the C++ and Python ecosystems. You evaluate proposals against long-term scalability and testability. +You rule on structure, boundaries and testability, before any production file exists. ## 1. Core Architectural Pillars * **Separation of Concerns (SoC):** Ensure mathematical geometry (C++), geospatial I/O (Python/Shapely), runtime orchestration (Async Python), and configuration (Pydantic) never bleed into each other. @@ -27,7 +27,7 @@ You are the Principal Systems Architect for the terrain-meshing engine. Your pri When asked to evaluate or design a feature, you must judge it against these explicit criteria: 1. **Data vs. Execution Separation:** Are configuration parameters (Pydantic) cleanly separated from the algorithms executing them? 2. **State Mutability:** Is state isolated? (e.g., does the triangulation kernel maintain hidden global state, or is it pure and thread-safe?) -3. **Dependency Gravity:** Does a change introduce massive dependencies? (Enforce the prohibited dependencies of `CLAUDE.md` §2 fiercely.) +3. **Dependency Gravity:** Does a change add a heavy dependency? 4. **Async-Readiness:** Can this architectural layout run non-blocking inside a desktop GUI backend or an API worker? ## 4. Literature and Novelty (before the design) @@ -40,6 +40,5 @@ written before the design. Its rules are `docs/increments/README.md`, step 1 * **Fixtures:** each test a design specifies carries its fixture's measured figures; an amendment recomputes them for each existing test it changes. * **Estimates:** a cost estimate counts each driver it does not know with a scratch probe; a timing gate's factor sums the estimate of each new term in the gated call, plus noise. * **Action:** Before allowing `@developer` to write code for a complex task, you must provide a high-level component blueprint showing the data flow and interface boundaries. -* **Ideas:** before a design, read the private ideas file; name each entry - the design takes up or overlaps, and hand back each idea it leaves out. +* **Ideas:** a design names each ideas-file entry it takes up or overlaps. diff --git a/.claude/agents/developer.md b/.claude/agents/developer.md index 83eddfb88..eb4d67d87 100644 --- a/.claude/agents/developer.md +++ b/.claude/agents/developer.md @@ -22,11 +22,8 @@ You are an expert C++ and Python engineer. the specification. Where it and the tests disagree, the tests win and you report the discrepancy rather than resolving it silently. - **Stay under the ceiling in `CLAUDE.md` §2**, and report your actual count - against the increment doc's estimate; if you overrun, say so. `@reviewer` - reconciles the two (`reviewer.md` §5, check 3). + against the increment doc's estimate; if you overrun, say so. - **Verify before reporting**: Release, the compiler gate (`CLAUDE.md` §4), and the governance gates in `tools/`; Debug+asan/ubsan once, at the green step (CI runs it on every PR push), and TSan only when a threaded test changed. For - anything touching floating-point geometry, also build under `-ffp-contract=off`: - a compiler that contracts a determinant to a single fma can make a test pass - on one platform and fail on another. + anything touching floating-point geometry, also build under `-ffp-contract=off`. diff --git a/.claude/agents/perf.md b/.claude/agents/perf.md index 229817a3d..8032a84be 100644 --- a/.claude/agents/perf.md +++ b/.claude/agents/perf.md @@ -16,9 +16,7 @@ figures only. ## 1. What you own * **The acceptance run** (`docs/increments/README.md`, "Acceptance: an increment - that touches refine or mesh code" — the one statement of the rule): the 1 m - benchmark and a thread-scaling sweep, compared with the previous increment's - run. + that touches refine or mesh code" — the one statement of the rule). * **`tools/bench.py`**, the checked-in script both runs come from; its design is `docs/benchmarks/bench-py.md`. It is code under `tools/`, so a change to it follows the TDD loop: a failing test in `tests/python/test_bench.py` first, @@ -36,9 +34,6 @@ figures only. rebuild and reinstall the extension first, per `.claude/REQUIRED-READING.md` ("Stale artifacts"); a run against a stale `.so` measures the previous increment. -* **Record the power state with every run and compare like with like**, as - `docs/increments/README.md`, *Acceptance*, states. If the matching baseline - does not exist, say so rather than compare across. * **Compare like with like on bounds checks too**: judge `--hardening on` (the default) only against hardened runs and `--hardening off` only against unchecked ones; when a baseline of the matching mode is missing, measure the @@ -52,16 +47,12 @@ figures only. machine, the thread counts, the DEM, the domain and the tolerance. * **Record quality with speed:** worst angle, max vertex degree, the tolerance check and the Delaunay check. A faster run that loses quality is a regression. -* **Sanitize a scratch build before you run it for numbers.** A simulation or - instrumentation patch that changes C++ runs once sanitized on a small case - before any Release, timed or full-size run: `-fsanitize=address,undefined` - (as `build-san` builds) where it can run, which is a C++ driver or test - binary. ASan cannot be loaded into the Python process here (the harness - strips `DYLD_INSERT_LIBRARIES`), so a run through `_core` uses - `-fsanitize=undefined` with libc++'s extensive hardening mode instead. A run - that crashed produces no numbers. A deliberate crash (a plant) stays out of - a script's default loop: a crash in a Python process shows as a crash dialog - on the owner's screen. +* **Sanitize a scratch build before you run it for numbers:** a C++ patch for + a timed or full-size run first runs once on a small case under + `-fsanitize=address,undefined` (a C++ driver or test binary), or + `-fsanitize=undefined` with libc++ extensive hardening through `_core`, where + ASan cannot load. A crashed run has no numbers; a deliberate crash stays out + of a script's default loop. ## 3. Evidence * Commit the evidence under `docs/benchmarks//`: a `README.md` with the @@ -69,19 +60,16 @@ figures only. Evidence left in `/tmp` or a scratchpad is lost with the session. * Large outputs (meshes) stay out of the repository; say where they were and how to regenerate them. -* A plot comparing observed and modelled series has three lines: the observed - series, the best result so far, and the run under investigation, unless Ola - asks for more for that plot. Legend entries are plain words: the best line - is marked as the best, and each modelled line gets a reasonable explanation - of what that run is. When plain words make the legend too long, the - descriptions go in a caption at the base of the plot, like a figure legend. +* A plot of observed against modelled series has three lines unless Ola asks + for more: observed, the best so far and the run under investigation. + Plain-word legends mark the best and give each run a reasonable explanation; + when too long, they go in a caption at the base, like a figure legend. * A table scoring a modelled series against an observed one shows KGE with its parts r, α and β, NSE, and the half-water date (the day half the period's flow has passed). ## 4. Reporting * Hand back figures you measured, with their power state, in a table against - the baseline. No figure from a run still in progress, and no cause you have - not measured: "thought to be" is the honest wording until a profile says so. + the baseline. An unprofiled cause is "thought to be". * Verdict: `ACCEPTED` or `REGRESSION` (naming the measure and the size), or `NO BASELINE` when the matching power-state baseline is missing. diff --git a/.claude/agents/reviewer.md b/.claude/agents/reviewer.md index 19cd9def9..22ac4b07b 100644 --- a/.claude/agents/reviewer.md +++ b/.claude/agents/reviewer.md @@ -10,12 +10,11 @@ tools: Read, Grep, Glob, Bash, Skill, WebSearch, WebFetch, Monitor See `.claude/REQUIRED-READING.md`, and load it before acting. -You are the Senior Code Reviewer and quality gatekeeper for the terrain-meshing project. Your mandate is the checks no gate makes: CI status, the size ceiling, leftover red-step scaffolding, prose claims that the change made false, and the mutant kill record of a suite named invariant-critical. Readability, typing and style are the gates' (`CLAUDE.md` §4); do not spend the round on them. (There are no sections 2-4; section numbers are kept stable for citations.) +Your mandate is the checks no gate makes: CI status, the size ceiling, leftover red-step scaffolding, prose claims that the change made false, and the mutant kill record of a suite named invariant-critical. Readability, typing and style are the gates' (`CLAUDE.md` §4); do not spend the round on them. ## 1. Strict Structural Constraints * **The LOC Ceiling:** Reject a PR over the ceiling of `CLAUDE.md` §2, measured - with `python3 tools/count_loc.py `, and say how to split it; - reconcile the count against the estimate (check 3 below). + with `python3 tools/count_loc.py `, and say how to split it. ## 5. Review Execution & Feedback Loop diff --git a/.claude/agents/tester.md b/.claude/agents/tester.md index 251382efc..b264c2b99 100644 --- a/.claude/agents/tester.md +++ b/.claude/agents/tester.md @@ -10,26 +10,23 @@ tools: Read, Grep, Glob, Bash, Write, Edit, Skill, Monitor See `.claude/REQUIRED-READING.md`, and load it before acting. -You are the Lead QA and Testing Engineer for the terrain-meshing engine. Your absolute mandate is to enforce a rigorous, resilient, and deterministic testing culture across both the modern C++ core and the async Python layer. You hold the line at the coverage floor `testing.md` defines and at absolute -correctness. +You write the failing suites and hold the coverage floor `testing.md` defines. ## 1. Core Testing Mandates & Coverage -* **Coverage Floor:** Every pull request must keep line coverage at or above - the floor `testing.md` defines, project-wide, which is what `--cov-fail-under` - enforces; a per-module floor is a review obligation, not a machine-checked one, and - `--cov=tin_engine` measures the Python layer only, not the C++ core. Anything lower needs a justification in the PR. Beyond the floor, - what matters is named coverage: every documented invariant and every - specially-handled condition (NaN, NoData, empty input, single-element input, - boundary intersection) has a test that names it. -* **Determinism:** Tests must be 100% deterministic. Eliminate any race conditions in async Python loops, and enforce strict bitwise consistency or acceptable floating-point tolerances (using `pytest.approx` or custom numerical predicates) in C++. -* **Numeric bounds:** Every absolute bound in an assertion states the scale it assumes and the largest input it was checked at. -* **Zero Flakiness:** Flaky tests are a blocking bug. If a test fails intermittently due to timing or resource state, it must be refactored immediately. +* **Coverage Floor:** keep project-wide line coverage at or above + `testing.md`'s floor (`--cov-fail-under`, Python only); a per-module floor + is checked in review, and anything lower is justified in the PR. Every + documented invariant and every specially-handled condition (NaN, NoData, + empty or single-element input, boundary intersection) has a test that names + it. +* **Determinism:** no races, no flaky test (a blocking bug); bitwise results or + a stated tolerance. Every absolute bound states the scale it assumes and the + largest input it was checked at. * **Lists:** a red step that adds a command or module greps the tests for each list of them it must join, and adds it there. * **Scaffolding:** after green, remove red-step scaffolding, red notes included, before review. * **Choices beyond the design:** list every choice your tests pin that the - increment file leaves open, under the handback heading "Pinned or assumed - beyond the design"; `@architect` confirms or rules on each before green + increment file leaves open, in the handback; `@architect` confirms or rules on each before green (on the short path, only a pin that changes the design: `docs/increments/README.md`). * **Mutants:** for a suite the increment file names invariant-critical, run its mutants as your own task, planted in a scratch copy made by @@ -42,7 +39,7 @@ correctness. ## 2. Test Architecture Tiers * **C++ Core Unit Tests:** Use a modern testing framework (e.g., Catch2). Focus on micro-benchmarks, exact geometric predicates, and verifying that C++20 concepts hold under tight memory limits. * **Python Unit & Async Tests:** Enforce `pytest` and `pytest-asyncio`. Test asynchronous execution pipelines, coroutine streaming, and ensure that non-blocking blocks (`asyncio.to_thread`) release the event loop correctly. -* **System & Integration Tests:** Orchestrate end-to-end flows. Test the ingestion of a geospatial dataset (e.g., GeoJSON/XML), through Pybind11, into the C++ mesh generation, and out to a multi-tiered metadata 2D surface. +* **System & Integration Tests:** end to end, from input files through the C++ core to the written mesh. ## 3. Advanced & Domain-Specific Testing Rules @@ -55,8 +52,7 @@ You must aggressively test the computational geometry core against adversarial e * **Non-finite Input:** NaN and infinite coordinates and elevations. ### C. Data Source Ingestion Validation -Only for an increment that reads external input. (There is no section B; -letters are kept stable for citations.) +Only for an increment that reads external input. * **CRS Misalignment:** Test what happens when an XML breakline file uses a different CRS than the base GeoJSON polygon. Ensure the system safely rejects it or transforms it via `pyproj` cleanly. * **Corrupted Rasters:** Feed the TIFF parser truncated, missing, or misaligned raster windows to verify safe, non-crashing async exceptions. * **Schema Drift:** Enforce strict Pydantic V2 error raising when custom XML or GeoJSON attributes deviate from the schema. diff --git a/.claude/skills/computational-geometry/SKILL.md b/.claude/skills/computational-geometry/SKILL.md index 235b4bf77..699f0847c 100644 --- a/.claude/skills/computational-geometry/SKILL.md +++ b/.claude/skills/computational-geometry/SKILL.md @@ -21,27 +21,13 @@ Core domain constraints and architecture for the C++ terrain-meshing engine (CDT * **Parallel Suitability:** Favor data-parallel layouts (Structure of Arrays where applicable), regular memory access, and branch-minimized paths. Avoid pointer-heavy graph structures and recursive walks. ## 3. Robustness & Numerics -* **Dependencies:** the prohibited list is `CLAUDE.md` §2. * **Filtered Arithmetic:** 1. Evaluate orientation, incircle, and intersections using fast hardware floats. 2. Determine numerical ambiguity. 3. Fall back to exact/arbitrary-precision filtering *only* when the float sign/ordering is unsafe. -* **Degeneracy Handling:** Actively handle non-general positions (duplicate/coincident vertices, long collinear sequences, cocircular points, and acute angles) through explicit test cases. -* **Test in the producer's relation, not the prose one:** an oracle must be - written in the relation the code under test actually used. The operative test: - *does this check use the relation the producer used, or the one that reads more - naturally in prose?* In a snapping module those differ on ~93 % of the input — - a snapped node is *near* a segment, not *on* it — so an exact-incidence oracle - over snapped output is red on correct output, and an oracle re-derived from the - producer's own bookkeeping is a self-confirming invariant that cannot fail when - the thing it checks is broken. Borrow the producer's *predicate*, never its - records. Worked example, where each occurrence was introduced by the fix to the - previous: `docs/increments/05-noder.md`, guarantees 14 and 15. The parent rule - carries the evidence, so it is not repeated here. This is the - geometry instance of one general rule, not a rule of its own — the same defect - reaches gate scripts and comments, where "the producer's relation" has no - meaning; `.claude/REQUIRED-READING.md` states it there as *is the claim about - the same object the code evaluates?* +* **Degeneracy Handling:** handle the cases of `tester.md` §3A, and acute angles. +* **Oracles use the producer's predicate, never its records** (the general + rule: `.claude/REQUIRED-READING.md`, *Claims*). ## 4. Prior Art Before Design * **Start from the literature:** the rule is `docs/increments/README.md`, @@ -50,6 +36,5 @@ Core domain constraints and architecture for the C++ terrain-meshing engine (CDT Delaunay refinement, and Üngör for off-centres. ## 5. Change & Review Boundaries -* **Change Limit:** see the ceiling in `CLAUDE.md` §2. * Every major algorithmic change must explicitly document its impact on locality, parallelization, and numerical robustness. diff --git a/.claude/skills/geospatial-data-formats/SKILL.md b/.claude/skills/geospatial-data-formats/SKILL.md index f8bcd7068..6f4f9d360 100644 --- a/.claude/skills/geospatial-data-formats/SKILL.md +++ b/.claude/skills/geospatial-data-formats/SKILL.md @@ -8,14 +8,12 @@ description: Geospatial ingestion and CRS handling in Python. Use when reading o Enforces high-performance geospatial data ingestion and processing using Shapely, PyProj, and clean metadata models. ## 1. Dependency Boundaries -* **Prohibited:** the list is `CLAUDE.md` §2; this section says what to use instead. * **Allowed Libraries:** Use python-native and lightweight C-bound libraries: - * **Vector:** `shapely` (for geometry and predicates) and `geojson` or `ujson` (for fast JSON parsing). + * **Vector:** `shapely`. * **Raster/TIN:** direct binary parsing, or a pure-Python reader such as `tifffile`. The project's own reader will live under `src_python/tin_engine/io/`. * **Projections:** `pyproj` (for CRS definition and transformations). ## 2. Ingestion & Performance (Large Datasets) -* **GeoJSON:** Process large GeoJSON files using streaming or iterative parsing (e.g., `ijson`) to keep memory footprints low. Convert geometries directly into `shapely` objects. * **TIN in TIFF:** Handle large GeoTIFF-based TIN surfaces efficiently. Read by window/tile so chunks are processed asynchronously, without loading the entire raster into memory. * **Custom XML Parsers:** Build light, iterative XML parsers using `xml.etree.ElementTree.iterparse` to ingest custom XML terrain formats line-by-line, enforcing strict schema validation. @@ -24,14 +22,6 @@ Enforces high-performance geospatial data ingestion and processing using Shapely * **Validation:** Reject any geospatial input that lacks a verifiable CRS (EPSG code, WKT, or PROJ string). * **PyProj Integration:** Use `pyproj.CRS` to validate incoming coordinate systems and `pyproj.Transformer` (with `always_xy=True`) for all reprojections before passing data to the C++ core. -## 4. Output Architecture (Surfaces & Metadata) -* **2D Surface Generation:** Outputs must represent valid 2D planar subdivisions or meshed surfaces mapped to `shapely` geometry collections. -* **Granular Metadata:** Every output must encapsulate metadata at three explicit tiers via Pydantic models: - * **Global Level:** CRS, overall bounding box (`bbox`), total element counts, and transformation history. - * **Edge Level:** Structural topology tags (e.g., hard breakline, soft constraint, outer boundary). - * **Cell/Triangle Level:** Reference DTM error metrics, slope, aspect, and source file identifiers. - -## 5. Testing & Change Limits +## 4. Testing * **Mocking I/O:** Test geospatial ingestion using small, hand-crafted GeoJSON snippets, micro-TIFFs, and minimal XML strings. -* **Change Limit:** see the ceiling in `CLAUDE.md` §2. diff --git a/.claude/skills/modern-cxx/SKILL.md b/.claude/skills/modern-cxx/SKILL.md index 0e1215c54..4b833f263 100644 --- a/.claude/skills/modern-cxx/SKILL.md +++ b/.claude/skills/modern-cxx/SKILL.md @@ -20,7 +20,6 @@ Enforces C++20/C++23 standards, zero-overhead abstractions, component swappabili ## 2. Memory, Data Layout & Execution * **Ownership:** Enforce RAII. Use smart pointers (`std::unique_ptr`) only for resource boundaries; use stack allocation, string views (`std::string_view`), and spans (`std::span`) for zero-copy data passing. -* **Asynchrony & Streaming:** Use C++20 **Coroutines** (`std::generator`, `co_yield`, `co_await`) for lazy terrain sampling, heavy batch processing, or streaming mesh updates to/from Python without massive allocations. * **Memory Locality:** Prefer contiguous memory layouts (`std::vector`). Avoid pointer-chasing topologies; represent mesh connectivity via indices into contiguous arrays. ## 3. Python Bindings (Pybind11) diff --git a/.claude/skills/python-development/SKILL.md b/.claude/skills/python-development/SKILL.md index 72ba1e117..17a0c995c 100644 --- a/.claude/skills/python-development/SKILL.md +++ b/.claude/skills/python-development/SKILL.md @@ -9,7 +9,6 @@ Enforces cutting-edge, safe, and testable Python 3.12+ architecture for orchestr ## 1. Data Integrity & Validation (Pydantic V2) * **Strict Modeling:** Every external data input, configuration option, and API boundary must use **Pydantic models** (`pydantic.BaseModel`). -* **Settings Management:** Use `pydantic-settings` for application and environment configuration. * **Type Safety:** Enforce strict runtime and static type hinting. Leverage `Annotated` types for precise validation criteria (e.g., coordinate boundaries, file path validation). ## 2. Asynchronous & Decoupled Architecture @@ -18,21 +17,14 @@ Enforces cutting-edge, safe, and testable Python 3.12+ architecture for orchestr * **Frontend-Backend Separation:** Keep the business logic completely decoupled from UI or CLI elements. Expose generic, stable APIs that can be wrapped by either a command-line interface or a WebSocket/REST API. ## 3. CLI Engineering & Security -* **Modern CLI:** Use `typer` or `click` to build user-friendly, structured CLI applications for generating TIN models. +* **Modern CLI:** Use `typer` to build user-friendly, structured CLI applications for generating TIN models. * **Security & Input Validation:** * Validate all file paths before ingestion; reject symlink exploits and path traversal attacks (use `pathlib.Path.resolve()`). * Never use `eval()`, `exec()`, or insecure YAML/Pickle parsing. Use `pydantic` or `json` for serialization. * Sanitise input coordinates and parameters at the CLI boundary before passing them to Pybind11. -## 4. Testability & Quality Assurance -* **Test Architecture:** Maintain 100% testable logic. Use `pytest` for all unit and integration/system-level tests. -* **Unit Level:** Mock the Pybind11 geometry engine using standard fixtures to isolate pure Python logic, DTM parsing, and CLI formatting. -* **System Level:** Implement complete integration tests that orchestrate the pipeline from a mock DTM file input to a generated TIN output. -* **Async Testing:** Use `pytest-asyncio` for verifying asynchronous execution loops and non-blocking tasks. - ## 5. Pragmatic Code Design * **Pragmatic Polymorphism:** Prefer composition over inheritance. Use structural subtyping (`typing.Protocol`) or simple functional abstractions instead of heavy object-oriented hierarchies. -* **Change Limit:** see the ceiling in `CLAUDE.md` §2. ## 6. Docstrings and Comments Docstrings and comments describe the code as it is, for a reader who has never diff --git a/CLAUDE.md b/CLAUDE.md index 74e09a619..02b2d54a4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -20,6 +20,9 @@ file's `description` says what the persona does. `# fmt: off` is allowed, provided the packed lines stay readable and the review says why each new region is packed. This is the only statement of the rule; everywhere else points here. +* **Rule text pays for itself:** a change that adds words to the files + `tools/rule_sizes.py` counts cuts as many in the same change, unless Ola + explicitly allows the growth. * **Prohibited Dependencies:** Never introduce `CGAL`, `GDAL`, `OGR`, `Fiona`, `Rasterio` (it wraps GDAL), or external `date` libraries. Enforced by `tools/check_prohibited_deps.py` over imports, includes, declared @@ -34,11 +37,7 @@ Before acting on this repository — in the main session as well as in any perso that starts cold or resumes after a context loss runs the recovery steps at the top of `.claude/REQUIRED-READING.md` **before** its first spawn, commit or edit; that is the only statement of the rule and of where in-flight state lives. -Every code alteration must execute this strict pipeline, dispatched by the main session: -1. `@architect` defines interfaces and types. -2. `@tester` writes failing unit/async test cases *first* (including happy path and edge cases). -3. `@developer` writes the minimal code needed to pass the active tests. -4. `@tester` and `@reviewer` validate results and type consistency before merge readiness. +Every code alteration follows the step order below, dispatched by the main session. ### The main session dispatches The main session, started with no agent name, is the dispatcher: it spawns @@ -74,9 +73,8 @@ the personas. Its rules: file and writes no code. * **Recap each round.** At every new round or increment, run `python3 tools/session_state.py` and open with its recap. -* **Milestone updates.** After each milestone, give Ola a one-line log of - where the round stands (e.g. "`@tester` has 8 failing tests; on to - `@developer`"). +* **Milestone updates.** After each milestone, one line to Ola on where the + round stands. * **Report only finished, verified results, briefly.** Answer Ola's question first, then stop. No file that does not exist yet, no number from a run still in progress, no cause not checked. No unasked images, no undefined @@ -87,8 +85,7 @@ the personas. Its rules: that increment, and its lessons recorded); the morning after each unattended night (idle time, guard refusals and false positives, work done out of role); and for a research round, weekly or when Ola asks. - `tools/duties.py` shows which are owed (recap, every prompt, Ola's status - line). + `tools/duties.py` shows which are owed. ## 4. Operational Commands @@ -129,10 +126,7 @@ CI is authoritative: local green does not mean the branch is green, so gh pr checks # must be green; required for merge on master ``` `CI result` is green only when every gating job passed, or was skipped on a -prose-only change; it is meant to be the one required check on `master`, and -`gh api repos/expertanalytics/rasputin/branches/master/protection/required_status_checks` -shows whether it is yet. While the per-job checks are still required, a -prose-only PR waits on matrix checks that never report. A PR that changes +prose-only change. A PR that changes only prose (Markdown at the root or under `docs/`, less the files the suites read; `tools/ci_changes.py` decides) skips the C++, sanitizer and Python jobs and runs only the governance gates, so its green says nothing about code. diff --git a/docs/PRINCIPLES.md b/docs/PRINCIPLES.md index 5421adf8c..94991fef6 100644 --- a/docs/PRINCIPLES.md +++ b/docs/PRINCIPLES.md @@ -14,66 +14,10 @@ Designs live in `docs/increments/NN-*.md` and hold no findings. **Origin** is a reference, not an account. Commits and files, no retelling. -**Last exercised** is set by the retrospective, not by whoever writes or edits a -principle. A principle not exercised in three increments is reviewed for -retirement at the next retrospective. - --- ## A. Evidence -### A1 — Run the command before writing the claim - -**Rule.** Run the check, then write the claim. Naming a check is not -performing one, and neither is being confident about it. -**Apply.** Any sentence of the form "X does Y" about a script, a suite, a gate -or a file — and any claim about what the user has asked for or ruled, which has -a command too. See A6. -**Origin.** `debd8d1`, corrected by `0fa05e3`. -**Status.** In force. Last exercised: 5c. - -### A2 — Derive the probe set from the code as fixed, not the bug as found - -**Rule.** Choose the probes from what the code accepts **now**, not from what -broke. Re-running the old probes tests the old code. -**Apply.** After any change to an input domain. -**Origin.** `fdbd532`. -**Status.** In force. Last exercised: 5b. - -### A3 — Make the probe able to fail - -**Rule.** Before trusting a pass, check the probe could have failed. A pass -that looks the same as a probe that never ran measured nothing. -**Apply.** Prefer a probe whose pass and its own absence look different. Time a -hang from outside the process. -**Origin.** `b0bf129`. Recurrence: 5b's ring search, `05b-noder-driver.md`, -"The bounded ring search". -**Status.** In force. Last exercised: 5b. - -### A4 — Do not write "X is verified by Y" until Y has run against a broken X - -**Rule.** As stated. -**Apply.** Before crediting any test, gate or probe with covering anything. -**Origin.** `7ece838`. -**Status.** In force. Last exercised: 5c. - -### A5 — Four stale artifacts can make a run meaningless - -**Rule.** Rebuild and reinstall before any `pytest` that measures C++. -`touch` after any `cp` restore. Read `cmake --build`'s exit status before you -read `ctest`'s summary. A clean-looking run proves nothing until you have. -**Apply.** Four artifacts go stale silently: `pytest` does not rebuild the -extension, `cmake --build` can skip a `cp` restore, `ctest` reports the previous -binary after a failed build, and Python imports a stale `.pyc` when mtime and -size are unchanged. -**Origin.** `docs/retrospectives/2026-09-27-required-reading-incidents.md` -carries the reproductions of the first three (the rules are in -`.claude/REQUIRED-READING.md`); `2026-09-22-increments-7-5b-5c.md` names the -fourth. -**Status.** In force. Last exercised: 5c. - ---- - ### A6 — A rule in a governance file is evidence someone wrote it **Rule.** A rule in a repository document is evidence an agent wrote a sentence, @@ -83,91 +27,34 @@ citing it to the user or enforcing it in a gate. identity. The `Co-Authored-By` line and the transcript store are the evidence, and `git log -S` finds the commit. A prohibition is never a design opinion for this purpose, because provenance always has a command to run. -**Origin.** `docs/retrospectives/2026-09-24-unauthored-rules.md`, which measures -48 of 48 governance commits as agent-written. +**Origin.** `docs/retrospectives/2026-09-24-unauthored-rules.md`. **Status.** In force. ## B. Claims -### B1 — A claim names the object the code evaluates - -**Rule.** Where there is nothing to run, ask one question: is this claim about -the same object the code evaluates? -**Apply.** Comments, design invariants, error messages, commit messages. -**Origin.** `1807e73`, `dd67a68`, `7473712`, `d86668f`. -**Status.** In force. Last exercised: 5c. - -### B2 — Write the rule and the command, not the resolved value - -**Rule.** When a claim's truth depends on the state of the tree, write what -resolves it, not what it resolved to. A resolved value is a citation with an -invisible expiry date. -**Apply.** Line numbers, commit hashes, counts, file lists. Prefer a section -name, a symbol, or a `grep`/`git grep` that returns the set. -**Origin.** `docs/increments/07-edge-properties.md`, its reconciliation section; -`docs/retrospectives/2026-09-22-increments-7-5b-5c.md`. -**Status.** In force. Last exercised: 5c. - ### B3 — A correction is a change and can carry the next defect -**Rule.** Apply A1 and B1 to the correction itself, before committing it. +**Rule.** Check the correction itself as you would the claim it corrects +(`.claude/REQUIRED-READING.md`, *Claims*), before committing it. **Apply.** Especially when the correction is *about* accuracy, and in a governance cleanup, where the mandate to delete false rules is what licenses writing one. **Origin.** `docs/retrospectives/2026-09-22-increments-7-5b-5c.md` for the citation cases; `2026-09-24-unauthored-rules.md` for the chain of five commits in which each repair carried the next defect. -**Status.** In force. Last exercised: 5c. +**Status.** In force. ### B4 — An estimate is not a measurement and must not be reported as one **Rule.** Label which is which, every time both appear. **Apply.** Reconciliations, LOC tables, cost sections. **Origin.** `docs/increments/05c-noder-wiring.md`, its reconciliation section. -**Status.** In force. Last exercised: 5c. - ---- - -## C. The increment loop - -### C1 — Design, red, green, review, in that order - -**Rule.** `@architect` designs; `@tester` commits a failing suite; `@developer` -makes it pass; `@reviewer` audits. Detail in `docs/increments/README.md`. -**Status.** In force. Last exercised: 5c. - -### C2 — The red commit touches no production file; the green commit touches no test file - -**Rule.** As stated. Verified per commit, on master, after every merge. -**Apply.** `git show --name-only`. Merge commits, never squashes: a squash -destroys the trace irreversibly. -**Origin.** `docs/increments/README.md`. -**Status.** In force. Last exercised: 5c. - -### C3 — A documentation defect found during an increment is fixed in that PR, or it is not recorded - -**Rule.** As stated. No debt ledgers. -**Origin.** `docs/increments/README.md`, cost constraints. -**Status.** In force. Last exercised: 5c. - -### C4 — The merge updates the roadmap row, in the same PR - -**Rule.** As stated. -**Origin.** `docs/increments/README.md`, the merge section; PR #78. -**Status.** In force. Last exercised: 5c (first application). +**Status.** In force. --- ## D. Working with agents -### D1 — One agent at a time - -**Rule.** Dispatch serially. Disjoint files do not make parallel dispatch safe. -**Apply.** Queue the work and say what is queued. Prefer doing small mechanical -fixes inline over spawning an agent for each. -**Origin.** `docs/retrospectives/2026-09-22-increments-7-5b-5c.md`. -**Status.** In force. Last exercised: 5c. - ### D2 — Verify a handback; do not relay it **Rule.** Re-run the cheap claims in any report before acting on them or @@ -175,16 +62,7 @@ repeating them. **Apply.** Especially numbers, and especially when the report contradicts something you believe. **Origin.** `docs/retrospectives/2026-09-22-increments-7-5b-5c.md`. -**Status.** In force. Last exercised: 5c. - -### D4 — Finish an open PR before starting new work - -**Rule.** Merge it, or say why it is parked. Green and waiting is not finished, -and neither is opened. Report its checks until it settles. -**Apply.** Before branching anything new, `gh pr list --state open`. A PR of -yours in that list is the next task, ahead of whatever seems more interesting. -**Origin.** `docs/retrospectives/2026-09-22-increments-7-5b-5c.md`. -**Status.** In force. Last exercised: not yet. +**Status.** In force. ### D3 — A compromise between two correct principles goes to `@orchestrator` @@ -192,28 +70,4 @@ yours in that list is the next task, ahead of whatever seems more interesting. that are each correct is lifted, not settled in the main session and not handed to a persona chosen ad hoc. **Origin.** `docs/increments/05c-noder-wiring.md`, section 2. -**Status.** In force. Last exercised: 5c. - ---- - -## E. Boundaries - -### E1 — The working tree is yours; the remote is the user's - -**Rule.** Editing, spawning, building, testing and committing run on an -instruction's momentum. `git push`, `gh pr create`, `gh pr merge` -(which enqueues; one yes covers one enqueue), any force-push or history rewrite, and any change to permissions need a fresh yes. -**Apply.** One named act, one occurrence. Ask again if the tree has changed. -**Status.** In force. Last exercised: 5c. - -### E3 — The I/O boundary - -**Rule.** The C++ core never opens a file, sees a path or links a codec, and CRS -never crosses into it. `CLAUDE.md` §2 is the statement; this is the pointer. -**Origin.** `CLAUDE.md` §2; `docs/increments/05c-noder-wiring.md`, prior art. -**Status.** In force. Last exercised: 5c. - -### E4 — Under the production-line ceiling per PR - -**Rule.** `CLAUDE.md` §2 is the statement; this is the pointer. -**Status.** In force. Last exercised: 5c. +**Status.** In force. diff --git a/docs/increments/README.md b/docs/increments/README.md index 5f373f9fa..f433a0a87 100644 --- a/docs/increments/README.md +++ b/docs/increments/README.md @@ -2,10 +2,7 @@ One file per increment of the post-CGAL core, written by `@architect` before `@tester` writes anything, and read by every persona that works on that -increment. The directory also holds the occasional audit — a question about -work already shipped, answered with evidence and dated to the commit that -answered it, rather than a design for work not yet done. Such a file says so -in its own status line; `kernel-sufficiency-audit.md` is the first. +increment. An audit of shipped work may live here; its status line says so. ## The loop @@ -20,24 +17,15 @@ Per `CLAUDE.md` §3, with the artifact each step produces: citation, and say what differs from it. Design with the literature, not against it: every departure says why, and a departure that drops the method's guarantees is named as such. If the increment claims something - new (for example an exact sup-norm guarantee on a DEM with constraints), - say what was searched and what was found; a novelty claim is not made + new, say what was searched and what was found; a novelty claim is not made without that check. The option to publish is kept open, so an unchecked claim is a defect, not a detail. - *Legacy.* What the legacy tree holds on this increment's subject, and either - what is being carried across or why nothing is. "Nothing" is a legitimate - answer, but it is an answer, with the grep that reached it, not an omission - — and the section pastes the command **with the file list it returned**, - because a cited grep and an unrun one look identical on the page. - The legacy tree no longer ships in the working tree; it is kept at the - `legacy-archive` tag, so the grep runs against the tag - (`git grep -n legacy-archive -- legacy`). - Where the answer is not "nothing", `@architect` reads the archived source - from the `legacy-archive` tag and reports intent before `@tester` is spawned - — before, because a suite written against re-derived intent pins the - re-derivation, and a domain constant guessed wrong is then guarded by a test - that agrees with it. + *Legacy.* What the `legacy-archive` tag holds on the subject, with the + command and the files it returned + (`git grep -n legacy-archive -- legacy`), and what is carried + across or why nothing is. Where something is, `@architect` reads that source + and states its intent before `@tester` starts. `@reviewer` reviews the design before `@tester` starts (on the short path, below, with the code), and again when a design update adds a step. 2. `@tester` reads it and writes a failing suite. No production code. The @@ -68,13 +56,9 @@ the increment file from `@tester`'s and `@developer`'s handbacks or the commits they name; no separate `@architect` step. -Steps 2 and 3 are not strictly once each. A ruling can land after the red -commit, and the suite that encodes it is still `@tester`'s to write; so can a -retuned constant, when implementation shows it depends on the platform. Such -amendments land as their own commit with the reason in the message, never -folded into the green one. The rule is not "tests are frozen after red"; it is -"`@developer` does not edit tests, and no test change hides inside an -implementation commit". +A ruling or a platform-retuned constant after the red commit is `@tester`'s +amendment, in its own commit with the reason. `@developer` never edits tests, +and no test change hides in an implementation commit. **The merge updates `ROADMAP.md`'s row for that increment**, in the same PR, before the merge rather than after it. @@ -97,8 +81,7 @@ the brief says so, and `@reviewer` may send it back to the full path. row together, before the push. Never dropped: the red commit ahead of the green one, a green commit with no -test file, `@reviewer` before every push, Ola's yes for each push, and the -prompt on a governed file. +test file, and the approval boundary (`.claude/REQUIRED-READING.md`). ## Acceptance: an increment that touches refine or mesh code @@ -122,9 +105,6 @@ newer one's baseline. Only runs in `bench.py`'s format are baselines. ## Cost constraints -These exist because a round that costs three times what it needs to is a round -that gets skipped next time. - **Reference, do not restate.** The increment files are the source of truth, and `.claude/REQUIRED-READING.md` rules on what is currently being asked. Prompts point at them. If a fact is wrong there, fix the file rather than @@ -133,23 +113,7 @@ transcript. **Mutation testing is required only for the invariant-critical suite of an increment**, named as such in its increment file with the mutation targets its -kill record must cover. Writing a throwaway implementation, compiling the suite -against it, and killing deliberate mutants catches defects a passing suite -hides. It is also the slowest part of a round. Spend it where the topology -decisions are, not on value types whose failure mode is a typo. - -**Template cross products are opt-in, not default.** Running every algorithm -as a `TEMPLATE_TEST_CASE` over every kernel × every model multiplies each -compile in the edit-validate loop. Use the full cross product only where an -instantiation proves something a later increment depends on — the increment -file says which. - -**Match the model to the work — but the applicable surface is small.** Use a -smaller model only for a value-type header with no kernel parameter and no -exactness claim; otherwise do not spend the round setting it up. Predicate, -kernel and topology work is not delegable downward. +kill record must cover. **A documentation defect found during an increment is fixed in that increment's -PR, or it is not recorded.** A "documentation debt" section is a ledger nobody -settles: it converts a one-line fix into a permanent entry and gives false -assurance that the rest is clean. Fix it, or leave it to be found. +PR, or it is not recorded.** No debt ledger. diff --git a/docs/increments/h2-citation-pinning.md b/docs/increments/h2-citation-pinning.md index e631d59c3..b6b0b9a00 100644 --- a/docs/increments/h2-citation-pinning.md +++ b/docs/increments/h2-citation-pinning.md @@ -106,7 +106,7 @@ spaces or tabs, then `§` **immediately** followed by `ID` = `\d+(\.\d+)*[A-Z]?` | `[A-Z]{1,2}\d+` | `[A-Z]`, not followed by `\w`. So `` `CLAUDE.md` §2 ``, `` `tester.md` §3C ``, `` `generic-harness.md` §3.2. `` -(the final period is punctuation) and `PRINCIPLES.md §E1` are ID citations. +(the final period is punctuation) and `PRINCIPLES.md §A6` are ID citations. `§ Constraint` (space after `§`) and "`CLAUDE.md` section 2" are not citations and are ignored. From 98d2a5edf825b9d68ff013f7e4612ce1cd45e43a Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Thu, 22 Oct 2026 11:41:35 +0200 Subject: [PATCH 2/4] guard_push: cite REQUIRED-READING.md's approval boundary, not the cut PRINCIPLES.md E1 (@developer) The rule cut (d5895c0b) removed PRINCIPLES.md E1 ("the working tree is yours; the remote is the user's"); it now lives in .claude/REQUIRED-READING.md, "Before you publish: the approval boundary". The module docstring and the ask reason Ola sees on a push now point there. Wording and behaviour otherwise unchanged. Grep of the tree for references to cut entries (A1-A5, B1, B2, C1-C4, D1, D4, E1, E3, E4): the only explicit ones outside tests/ were these two. Bare letter-number ids such as (D2), (E1) in src_python/, tests/cpp and most of tests/python are increment design ruling ids, not PRINCIPLES.md. Left for @tester (tests/python, explicit `PRINCIPLES.md` citations): test_io_ply.py:13 (B1), :31 (A1, A4), :58 (A3, bare), :311 (A4); test_io_vtk_legacy.py:15 (B1); vtkread.py:6 (B1); plyread.py:5 (B1); test_geotiff_fixtures.py:11 (A3); test_always_xy.py:8 (A3). guard_governance.py:14 mentions PRINCIPLES.md as a file, no entry; left. Checks: tests/python/test_guard*.py 1970 passed (coverage floor not applicable to a subset); check_code_history.py clean; check_citations.py clean; count_loc.py HEAD: 0 net production lines (.claude/ not counted). Co-Authored-By: Claude Opus 5.5 --- .claude/hooks/guard_push.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.claude/hooks/guard_push.py b/.claude/hooks/guard_push.py index fd8cfc617..cce335010 100755 --- a/.claude/hooks/guard_push.py +++ b/.claude/hooks/guard_push.py @@ -1,10 +1,10 @@ #!/usr/bin/env python3 """PreToolUse: the working tree is the agent's, the remote is the user's. -PRINCIPLES.md E1 states this and the user stated it directly -- "ensure one agent -working at the time, and alert me before pushing". It held because the agent -remembered to ask. That is not a mechanism; it is a habit, and the same session -has forgotten comparable ones. +.claude/REQUIRED-READING.md (the approval boundary) states this and the user +stated it directly -- "ensure one agent working at the time, and alert me before +pushing". It held because the agent remembered to ask. That is not a mechanism; +it is a habit, and the same session has forgotten comparable ones. `gh pr create`, `gh pr merge` and `git push` all publish, and `gh pr update-branch` writes to a pull request's branch (docs/increments/h10-merge-queue.md §4a). So @@ -458,9 +458,9 @@ def main() -> int: if grant is not None: why += f"; granted branch {grant.branch}, but {grant.why}" reason = ( - "This reaches beyond the working tree: " + why + ".\nPRINCIPLES.md E1 -- the " - "working tree is the agent's, the remote is the user's. Approval of an earlier " - "push does not carry to this one." + "This reaches beyond the working tree: " + why + ".\n.claude/REQUIRED-READING.md, " + "the approval boundary -- the working tree is the agent's, the remote is the " + "user's. Approval of an earlier push does not carry to this one." ) emit(event, "ask", reason, command, why) except Exception as error: # a crash must not turn an ask into a pass From c5c81160f7fa3adc6259ae4d68037cd50d7edcca Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Thu, 22 Oct 2026 11:43:59 +0200 Subject: [PATCH 3/4] tests: repoint citations of cut PRINCIPLES.md entries to REQUIRED-READING.md (@tester) The rule cut removed PRINCIPLES.md A1, A3, A4 (run the check first; make the probe able to fail; no "verified by" until run against a broken X) and B1 (a claim names the object the code evaluates). All four now live in .claude/REQUIRED-READING.md, *Claims*. Test docstrings and comments citing them now point there: - test_io_ply.py:13 (B1), :31 (A1, A4), :311 (A4) - test_io_vtk_legacy.py:15 (B1); vtkread.py:6 (B1); plyread.py:5 (B1) - test_geotiff_fixtures.py:11 (A3); test_always_xy.py:8 (A3, rewrapped) Bare "(A3)" pointers dropped where the sentence stands alone: test_io_ply.py:58 and :207, and test_io_vtk_legacy.py:154 (the last two were not on the developer's list; found by grep). Grep of tests/ for the other cut ids (A2, A5, B2, C1-C4, D1, D4, E1, E3, E4): every remaining hit is an increment design or review ruling id, not PRINCIPLES.md. Comment and docstring changes only; no test behaviour. Checks: the four touched test files, 347 passed; ruff check and format clean; check_code_history.py exit 0; check_citations.py clean. Co-Authored-By: Claude Opus 5.5 --- tests/python/plyread.py | 2 +- tests/python/test_always_xy.py | 7 ++++--- tests/python/test_geotiff_fixtures.py | 2 +- tests/python/test_io_ply.py | 10 +++++----- tests/python/test_io_vtk_legacy.py | 4 ++-- tests/python/vtkread.py | 2 +- 6 files changed, 14 insertions(+), 13 deletions(-) diff --git a/tests/python/plyread.py b/tests/python/plyread.py index aee7853ee..3cddb0e74 100644 --- a/tests/python/plyread.py +++ b/tests/python/plyread.py @@ -2,7 +2,7 @@ This module exists so that increment 10's round trip goes through a parser that **reads the header it was handed** instead of re-deriving the writer's byte -offsets (`PRINCIPLES.md` B1, and `10-mesh-output.md`'s "Testing" section). The +offsets (`.claude/REQUIRED-READING.md`, *Claims*, and `10-mesh-output.md`'s "Testing" section). The distinction is the whole value of the suite: a reader that computed "vertices start at 3 * 8 * N bytes from the end of the header, because that is what `write_ply` computes" would agree with the writer's arithmetic bug and report a diff --git a/tests/python/test_always_xy.py b/tests/python/test_always_xy.py index 9a5edd4dc..27513ba43 100644 --- a/tests/python/test_always_xy.py +++ b/tests/python/test_always_xy.py @@ -5,9 +5,10 @@ same transform puts a point 6 000 km away and says nothing (prior art §5.1). Today `src_python/` has no transformer at all, so the check against the real -tree passes vacuously. Per `PRINCIPLES.md` A3 the checker is therefore also run -against planted violations in `tmp_path`, which it must catch; and a -compliant plant, which it must pass, so it cannot pass by flagging everything. +tree passes vacuously. Per `.claude/REQUIRED-READING.md`, *Claims*, the +checker is therefore also run against planted violations in `tmp_path`, +which it must catch; and a compliant plant, which it must pass, so it cannot +pass by flagging everything. It was also shown failing against a violation planted inside `src_python/` itself, in the commit that added it (see the commit message). diff --git a/tests/python/test_geotiff_fixtures.py b/tests/python/test_geotiff_fixtures.py index 7d7dea424..82c8fb30a 100644 --- a/tests/python/test_geotiff_fixtures.py +++ b/tests/python/test_geotiff_fixtures.py @@ -8,7 +8,7 @@ 1. its `witness` holds, so the named defect is really in the bytes; 2. the valid baseline does *not* satisfy that witness, so the witness can - fail (`PRINCIPLES.md` A3) and the defect is not shared with the baseline. + fail (`.claude/REQUIRED-READING.md`, *Claims*) and the defect is not shared with the baseline. """ from __future__ import annotations diff --git a/tests/python/test_io_ply.py b/tests/python/test_io_ply.py index 411aae067..48277e31f 100644 --- a/tests/python/test_io_ply.py +++ b/tests/python/test_io_ply.py @@ -10,7 +10,7 @@ class it exists to catch is the one a count assertion cannot see: a wrong reads the header it is handed and takes every size from a declaration in the blob. It never asks the writer where anything is. That is deliberate and it is the difference between a test and a restatement of the implementation -(`PRINCIPLES.md` B1). +(`.claude/REQUIRED-READING.md`, *Claims*). Committed red at `1d4ec8b`, when the intended failure was `ModuleNotFoundError: tin_engine.io` -- the package did not exist, and @@ -28,7 +28,7 @@ class it exists to catch is the one a count assertion cannot see: a wrong 1. That QGIS or ParaView opens the result. Nobody here can run either, and ruling 2 forbids the claim until a person has opened a file and said so in - the record (`PRINCIPLES.md` A1, A4). What the suite asserts is what the + the record (`.claude/REQUIRED-READING.md`, *Claims*). What the suite asserts is what the bytes are. 2. `cli_kit.destination`'s refusals. They are covered in `test_cli_kit.py`; re-testing them here would pin one boundary in two places. @@ -55,7 +55,7 @@ class it exists to catch is the one a count assertion cannot see: a wrong #: A millimetre-resolved easting at UTM 33N magnitudes -- the road-crosses-river #: shape's origin is 430_000 -- carried by ruling 2 as the probe that float32 cannot survive. -#: `TestDoublePrecision` proves the probe can fail before trusting it (A3). +#: `TestDoublePrecision` proves the probe can fail before trusting it. EASTING = 430_000.001 #: A z the caller supplied and nothing snapped. The grid rounds x and y; ruling @@ -204,7 +204,7 @@ class TestDoublePrecision: """Ruling 2: float32 is a corruption, and this is the probe that says so.""" def test_the_probe_can_fail(self) -> None: - # A3. Written before the round trip is trusted: if EASTING survived a + # Written before the round trip is trusted: if EASTING survived a # float32 trip, the test below would pass against the dtype it exists # to refuse. The float32 step at this magnitude is 2**19 * 2**-24. assert float(np.float32(EASTING)) != EASTING @@ -308,7 +308,7 @@ def test_the_vertex_blocks_are_not_empty(self, surface: bytes) -> None: class TestTheGuardsThatHadNeverRun: """The two guards beyond what the rest of this suite asks for. - `PRINCIPLES.md` A4: neither may be credited with covering anything until it + `.claude/REQUIRED-READING.md`, *Claims*: neither may be credited with covering anything until it has run against a broken input. Both were the only uncovered production lines in the tree until these tests existed. diff --git a/tests/python/test_io_vtk_legacy.py b/tests/python/test_io_vtk_legacy.py index 65a565920..2f62de3d9 100644 --- a/tests/python/test_io_vtk_legacy.py +++ b/tests/python/test_io_vtk_legacy.py @@ -12,7 +12,7 @@ Everything goes through `vtkread`, a parser written for the suite that takes every size from a declaration in the blob and refuses whatever it does not -recognise (`PRINCIPLES.md` B1). No `vtk` import appears here: this file runs in +recognise (`.claude/REQUIRED-READING.md`, *Claims*). No `vtk` import appears here: this file runs in the plain dev environment, and the design's pure half must not depend on the test-only extra. @@ -151,7 +151,7 @@ class TestPoints: """Ruling 2: double end to end, bit for bit.""" def test_the_probe_can_fail(self) -> None: - # A3: if float32 held these, the round trip below would not refute it. + # If float32 held these, the round trip below would not refute it. assert float(np.float32(EASTING)) != EASTING assert float(np.float32(NORTHING)) != NORTHING diff --git a/tests/python/vtkread.py b/tests/python/vtkread.py index 1a03f2bd1..932c02212 100644 --- a/tests/python/vtkread.py +++ b/tests/python/vtkread.py @@ -3,7 +3,7 @@ Increment 13's pure suite goes through this parser for the same reason increment 10's goes through `plyread`: it **reads the file it was handed**, and takes every count, type and width from a declaration in the blob. It never asks -the writer where anything is (`PRINCIPLES.md` B1). +the writer where anything is (`.claude/REQUIRED-READING.md`, *Claims*). It is deliberately narrow and deliberately strict. It knows the subset of legacy VTK 4.2 that `13-bundled-mesh.md` rules on -- `DATASET POLYDATA`, a From beffc4422dc87bb46c3516d140664f223a762322 Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Thu, 22 Oct 2026 12:16:58 +0200 Subject: [PATCH 4/4] rule cuts: restore click, repoint cut PRINCIPLES.md citations (@architect) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes the two points of @reviewer's round 1 on c5c81160, plus the suggestion taken. 1. python-development/SKILL.md §3: "`typer` or `click`" restored; click is used in src_python/tin_engine/mcp/schema.py. 2. Citations of PRINCIPLES.md entries this branch cut now point at the pinned old text, docs/PRINCIPLES.md@94a157b2: (ID kept in brackets), since they are records: - 08-crossing-gallery.md: C3 (147-151), C4 (153-159), A3 (43-51) - 10-mesh-output.md: A1+A4 (25-58), B1 (92-98) - h10-merge-queue.md: E1 (201-207) - 29-nve-reference-catchments.md: E4 (216-219) - research/generic-harness.md: D1 (163-169), A5 (60-75), E1, and line 145's A5/E3/E4 (60-75, 209-219) - 42-readable-docstrings.md: B1 (92-98), found by the extra grep 11-raster-ingestion-prior-art.md: "and PRINCIPLES.md E3" dropped; the sentence already cites the rule's home, CLAUDE.md §2. Left as is: generic-harness.md "PRINCIPLES A1-E4" (proposal text about ID labels, not a citation of an entry) and "Rename E4" in its step table (a plan record). 3. geospatial-data-formats/SKILL.md: "## 4. Testing" back to "## 5.", keeping section numbers as python-development does. Checks: check_citations.py clean; check_code_history.py clean; the eight test files that read rule files, 1128 passed (the subset's coverage floor message is expected on a partial run). rule_sizes.py: 9494 words of rule text, -2220 since f5c0889b. Co-Authored-By: Claude Opus 5.5 --- .claude/skills/geospatial-data-formats/SKILL.md | 2 +- .claude/skills/python-development/SKILL.md | 2 +- docs/increments/08-crossing-gallery.md | 6 +++--- docs/increments/10-mesh-output.md | 4 ++-- docs/increments/11-raster-ingestion-prior-art.md | 2 +- docs/increments/29-nve-reference-catchments.md | 2 +- docs/increments/42-readable-docstrings.md | 2 +- docs/increments/h10-merge-queue.md | 2 +- docs/research/generic-harness.md | 8 ++++---- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.claude/skills/geospatial-data-formats/SKILL.md b/.claude/skills/geospatial-data-formats/SKILL.md index 6f4f9d360..81fc9fda3 100644 --- a/.claude/skills/geospatial-data-formats/SKILL.md +++ b/.claude/skills/geospatial-data-formats/SKILL.md @@ -22,6 +22,6 @@ Enforces high-performance geospatial data ingestion and processing using Shapely * **Validation:** Reject any geospatial input that lacks a verifiable CRS (EPSG code, WKT, or PROJ string). * **PyProj Integration:** Use `pyproj.CRS` to validate incoming coordinate systems and `pyproj.Transformer` (with `always_xy=True`) for all reprojections before passing data to the C++ core. -## 4. Testing +## 5. Testing * **Mocking I/O:** Test geospatial ingestion using small, hand-crafted GeoJSON snippets, micro-TIFFs, and minimal XML strings. diff --git a/.claude/skills/python-development/SKILL.md b/.claude/skills/python-development/SKILL.md index 17a0c995c..0da0f0c52 100644 --- a/.claude/skills/python-development/SKILL.md +++ b/.claude/skills/python-development/SKILL.md @@ -17,7 +17,7 @@ Enforces cutting-edge, safe, and testable Python 3.12+ architecture for orchestr * **Frontend-Backend Separation:** Keep the business logic completely decoupled from UI or CLI elements. Expose generic, stable APIs that can be wrapped by either a command-line interface or a WebSocket/REST API. ## 3. CLI Engineering & Security -* **Modern CLI:** Use `typer` to build user-friendly, structured CLI applications for generating TIN models. +* **Modern CLI:** Use `typer` or `click` to build user-friendly, structured CLI applications for generating TIN models. * **Security & Input Validation:** * Validate all file paths before ingestion; reject symlink exploits and path traversal attacks (use `pathlib.Path.resolve()`). * Never use `eval()`, `exec()`, or insecure YAML/Pickle parsing. Use `pydantic` or `json` for serialization. diff --git a/docs/increments/08-crossing-gallery.md b/docs/increments/08-crossing-gallery.md index 0ac50cad7..99b9196de 100644 --- a/docs/increments/08-crossing-gallery.md +++ b/docs/increments/08-crossing-gallery.md @@ -288,7 +288,7 @@ says what the geometry is and asserts nothing about the landscape. The fixture's own `description` string is untouched at the geometry level but carries the same false noun (*"two lake holes"*) and is corrected with it. -Per `PRINCIPLES.md` C3 this lands in this PR or it is not recorded. +Per `docs/PRINCIPLES.md@94a157b2:147-151` (C3) this lands in this PR or it is not recorded. ## What 5b and 5c made false in `06-cdt-viewer.md` @@ -333,7 +333,7 @@ changed between two commits is real — restated under the new name. above it that explains why it grew. - `docs/increments/06-cdt-viewer.md` — the four corrections above. - `docs/increments/08-crossing-gallery.md` — this file. -- `ROADMAP.md` — the row for increment 8, per `PRINCIPLES.md` C4. +- `ROADMAP.md` — the row for increment 8, per `docs/PRINCIPLES.md@94a157b2:153-159` (C4). - `tests/python/test_viz_svg.py`, `tests/python/test_cli_draw.py`. Explicitly out of scope: any C++ file, `bindings/`, `_core.pyi`, @@ -364,7 +364,7 @@ count assertion passes on a node constructed in the wrong place. The predicted positions are `(200,250)`; `(700,250)`; `(250,250)` and `(450,250)`, plus `ORIGIN`. -**The probe must be able to fail** (`PRINCIPLES.md` A3). The controlled negative +**The probe must be able to fail** (`docs/PRINCIPLES.md@94a157b2:43-51`, A3). The controlled negative for the crossing assertion already exists in the suite: `test_viz_svg.py::TestGalleryPredicates` self-tests `properly_cross` against a shared endpoint. Extend that habit — assert that `road-enters-forest`'s road diff --git a/docs/increments/10-mesh-output.md b/docs/increments/10-mesh-output.md index 474c7ccbb..0e7b66518 100644 --- a/docs/increments/10-mesh-output.md +++ b/docs/increments/10-mesh-output.md @@ -96,7 +96,7 @@ PLY. **Which makes one thing worth verifying before it is claimed.** Nothing in this record has been opened in QGIS, because no session here can run QGIS. So: no test and no document may say "QGIS opens this" until someone has opened one -(`PRINCIPLES.md` A1, A4). What the suite can assert is what the bytes are. What +(`docs/PRINCIPLES.md@94a157b2:25-58`, A1, A4). What the suite can assert is what the bytes are. What a person must do once, by hand, is open a written file in QGIS and in ParaView and say so here, with the version. If `double` is refused, the origin-offset fallback above is the answer and it gets written up here with the failure that @@ -329,7 +329,7 @@ What the suite pins: writer's own logic. The test parses the header it was handed, unpacks the body by the header's own declarations, and compares arrays with `assert_array_equal`. A test that re-derives offsets the same way the writer - did would agree with the writer's bug (`PRINCIPLES.md` B1). + did would agree with the writer's bug (`docs/PRINCIPLES.md@94a157b2:92-98`, B1). - **ASCII and binary produce the same geometry.** Parse both, compare arrays. This is what catches one mode drifting when the other is edited. - **`double` really is 8 bytes on the wire**, checked by writing a coordinate diff --git a/docs/increments/11-raster-ingestion-prior-art.md b/docs/increments/11-raster-ingestion-prior-art.md index b379c11fb..0fb812ac6 100644 --- a/docs/increments/11-raster-ingestion-prior-art.md +++ b/docs/increments/11-raster-ingestion-prior-art.md @@ -651,7 +651,7 @@ writing a stale value under a fresh key name. 1. **`make_mesh(raster_data, polygon, proj4_str)`** — `legacy/bindings.cpp:187-194`, reached from `legacy/rasputin/mesh.py:43`. A CRS string as a C++ parameter. - Forbidden by `CLAUDE.md` §2 and `docs/PRINCIPLES.md` E3. Nothing in the new + Forbidden by `CLAUDE.md` §2. Nothing in the new `raster/` module has a CRS field, and nothing should acquire one. 2. **Pillow as the GeoTIFF decoder.** Not prohibited, but `CLAUDE.md` §2's Core Stack names `tifffile`, and Pillow forced three of the defects above: the diff --git a/docs/increments/29-nve-reference-catchments.md b/docs/increments/29-nve-reference-catchments.md index 33d6a17c4..38869dccd 100644 --- a/docs/increments/29-nve-reference-catchments.md +++ b/docs/increments/29-nve-reference-catchments.md @@ -3312,7 +3312,7 @@ Fixes for round 5, by `@architect` in the commit that records it: (1) option (a) 1. **The records are word for word.** I took both handbacks from the `SubagentHandback` calls in the transcripts: round 4 from subagent `aa531c608e07d23ea`, round 5 from `aadbad41b7cbf3dc0`. For each round I took the recorded block from "#### Result" up to the "Fixes for round N" paragraph and raised unindented `#### ` headings back to `## `. Then I ran `diff` against the handback. Both diffs are empty (round 4: 74 lines on each side; round 5: 73 lines on each side). Each handback has 6 level-2 headings, so the heading change really was tested. The level-4 lines inside round 5's indented code block stayed as the handback has them, as the record's header says. Ola's quotation in the round-5 fix paragraph, "net, and write it into CLAUDE.md", matches Ola's message in this session's transcript exactly. 2. **`CLAUDE.md` §2.** - *Does it state the rule the reviews used?* Yes. It defines net lines as added minus removed, with the same exclusions on both sides. It takes the lines from the PR's `git diff -U0` hunks against its base, judging added lines at the new revision and removed lines at the old one. That is the method above, and it gives the same 691. - - *Is it still the only statement of the rule?* Yes. The paragraph still ends "This is the only statement of the rule; everywhere else points here". I searched every rule file for the ceiling. `.claude/agents/reviewer.md`, `developer.md` and `tester.md`, the three skills that mention it, and `docs/PRINCIPLES.md` E4 all point to §2 and do not repeat the number or the unit. Nothing conflicts with it. `docs/increments/README.md` does not state it. No `tools/` script counts lines, so no code can disagree with it. + - *Is it still the only statement of the rule?* Yes. The paragraph still ends "This is the only statement of the rule; everywhere else points here". I searched every rule file for the ceiling. `.claude/agents/reviewer.md`, `developer.md` and `tester.md`, the three skills that mention it, and `docs/PRINCIPLES.md@94a157b2:216-219` (E4) all point to §2 and do not repeat the number or the unit. Nothing conflicts with it. `docs/increments/README.md` does not state it. No `tools/` script counts lines, so no code can disagree with it. - *The gap @architect left open (a multi-line string that is not raw and not a docstring):* it does not matter for this PR. As worded, such lines are not blank lines, comments, docstrings or raw-string bodies, so they count. The only open question is a blank line inside such a string. In the PR's ten production files, all 103 multi-line strings are docstrings: a tokenizer pass found none elsewhere and no triple-quoted f-string. Leaving it open is fine until a PR has such a string. 3. **ROADMAP row 29, the status line, citations.** - Row 29 is still `ROADMAP.md:54`. diff --git a/docs/increments/42-readable-docstrings.md b/docs/increments/42-readable-docstrings.md index 62a462cf7..f7dba30d3 100644 --- a/docs/increments/42-readable-docstrings.md +++ b/docs/increments/42-readable-docstrings.md @@ -503,7 +503,7 @@ counted lines and no C++): full suite, `mypy`, `ruff`, `check_citations.py`. 4. `@reviewer`: reads the diff as a reader new to the project. The question for every rewritten reason: is it about the object the code evaluates - (`docs/PRINCIPLES.md`, B1)? A rewrite that dropped a reason along with its + (`docs/PRINCIPLES.md@94a157b2:92-98`, B1)? A rewrite that dropped a reason along with its label is a finding. PR 1 takes the full path: `@tester` writes the checker's failing tests first. diff --git a/docs/increments/h10-merge-queue.md b/docs/increments/h10-merge-queue.md index f4e4b15cc..dab352478 100644 --- a/docs/increments/h10-merge-queue.md +++ b/docs/increments/h10-merge-queue.md @@ -120,7 +120,7 @@ practice. The places that describe merging: only prints a warning (the queue's method wins); `-d`/`--delete-branch` is refused. Never pass `--admin`: it bypasses the queue." - **`.claude/REQUIRED-READING.md`, approval boundary** (the "A fresh yes" - paragraph) and **`docs/PRINCIPLES.md` E1**: after `gh pr merge` add "(which + paragraph) and **`docs/PRINCIPLES.md@94a157b2:201-207` (E1)**: after `gh pr merge` add "(which enqueues; one yes covers one enqueue)". `gh pr merge` on a PR whose checks have not passed turns on auto-merge into the queue (`gh pr merge --help`, gh 2.101.0); that is the same act and needs the same yes. diff --git a/docs/research/generic-harness.md b/docs/research/generic-harness.md index 6063da792..8ea1d70f3 100644 --- a/docs/research/generic-harness.md +++ b/docs/research/generic-harness.md @@ -142,7 +142,7 @@ one home: | `tools/check_detria_boundary.py` | Profile | a later "confinement" gate could generalise it, but not from one instance (§5b) | | `tools/bench.py` | Profile | — | | `docs/increments/README.md` | Generic, with profile paragraphs | see 1.4 | -| `docs/PRINCIPLES.md` | Generic index | A5 (stale artifacts, C++-specific), E3 (I/O boundary) and E4 (ceiling) are profile pointers | +| `docs/PRINCIPLES.md` | Generic index | A5 (stale artifacts, C++-specific), E3 (I/O boundary) and E4 (ceiling), `docs/PRINCIPLES.md@94a157b2:60-75` and `docs/PRINCIPLES.md@94a157b2:209-219`, are profile pointers | | `testing.md` | **Profile** | its conventions are generic and go into a template skeleton: `[live]`/`[planned]` markers, "never lose a bug twice", and the characterisation-test exception to "we do not test third-party code" | | `.claude/current-task/` protocol | Generic | the owner marker only | | `.gitignore` lines (`.claude/current-task/`, `.claude/worktrees/`) | Generic | — | @@ -171,7 +171,7 @@ one home: | Acceptance: refine or mesh code | **Profile**. Generic shape: the profile may declare an acceptance run with trigger paths, a tool, an environment probe and an evidence directory | | Cost: reference, do not restate; mutation testing; model matching; documentation defects | Generic; the examples (predicates, kernels) are profile | | Cost: template cross products | Profile (C++) | -| Cost: "Independent suites run as parallel agents" | **Contradicts PRINCIPLES D1**; see §6, defect 1 | +| Cost: "Independent suites run as parallel agents" | **Contradicts `docs/PRINCIPLES.md@94a157b2:163-169` (D1)**; see §6, defect 1 | ### 1.5 Survey: rules stated more than once (checked on this branch) @@ -315,7 +315,7 @@ Two carriers, with no overlap between them: | Roadmap | `session_state.py` (`ROADMAP.md`; "shipped", "landed", "unscheduled") | `profile.roadmap` | as today | | Governed rule files | `guard_governance.py` `GOVERNED` | the generic manifest ∪ `profile.rule_files` | adds `testing.md` and `.claude/skills/` (§6, defect 3) | | Skills to invoke | `REQUIRED-READING.md`, `developer.md` | `profile.skills` and one line in `CLAUDE.md` | the four | -| Stale artifacts | `REQUIRED-READING.md`, `PRINCIPLES` A5 | a profile section of `CLAUDE.md` (§7, step 5) | four items | +| Stale artifacts | `REQUIRED-READING.md`, `docs/PRINCIPLES.md@94a157b2:60-75` (A5) | a profile section of `CLAUDE.md` (§7, step 5) | four items | | Legacy archive | `check_citations.py`, `README` step 1 | `profile.legacy = {prefix, tag}` | `legacy/`, `legacy-archive` | | Persona path classes (R-B) | the audit's §7 table | `profile.paths.{production,tests,design,evidence}` (§6) | `src/ include/ bindings/ src_python/`, `tests/`, `docs/increments/`, `docs/benchmarks/` | @@ -375,7 +375,7 @@ of the rules it indexes (1.5). Each entry becomes one table row: `ID | title | home (file § heading) | check (a command, or "object-identity question")`. The IDs and the A-E families -stay, so `guard_push.py`'s "PRINCIPLES.md E1" and every other ID citation keep +stay, so `guard_push.py`'s "PRINCIPLES.md E1" (`docs/PRINCIPLES.md@94a157b2:201-207`) and every other ID citation keep resolving. A5, E3 and E4 are profile rows: rasputin's index keeps them, and the generic template does not ship them.