30 Commits
Author SHA1 Message Date
jpmschweitzerandClaude Opus 5 338644b409 refactor(tooling): T-1286 — generate, pr and dev become reach domains
Twelve scripts retired, three domains registered. `reach` now covers nine.

generate: `generate-brands` and `generate-corporations` were the second and
third copies of the same 24-line build-if-missing-then-exec bash `tooling/atlas`
carried, so they collapsed into `core.process.cargo_binary` rather than being
ported. `import_economics` shelled out to the first of those, so it now calls
that helper — `generated_brands.toml` comes back byte-identical, and the stamp
registry swaps the retired wrapper for `core/process.py`.

pr: `watchlist-diff` derives its watched set from `generator_sources.py` instead
of restating it, so it cannot drift from the stamp check.

dev: the environment scripts split decision from performing, per D-263's
guarded-exec rule. `godot_plan()` and `worktree_plan()` decide what would
happen; `install_godot()`, `install_rust()` and `setup_worktree()` do it.
`tooling/test_environment.py` pins the version pin, both override precedences,
the already-current skip, the platform refusal and both worktree refusals —
none of them performed. `make setup` now installs reach first, since the
targets that install rust and godot are reach verbs.

Two live bugs found while porting:

- The clerk read its decision index from `decisions/README.md`, a path that
  stopped existing when the DQR tree moved to `governance/`. Every clerk agent
  has been grepping blind; its prompt pointed at the same dead directory.
- The conformance exec-check matched any `x.system()` regardless of receiver,
  so `platform.system()` read as `os.system()`. Narrowed and re-proved against
  a real mutant.

`process.run` gains `input=`, `timeout=` and a `ProcessTimeout` subclass so a
killed run stays distinguishable from a verdict. The pre-push hook no longer
merges the clerk's stderr into its stdout — under streaming the last merged
line is a JSONL event, which would read as an unrecognised verdict and block.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 17:00:46 +02:00
jpmschweitzerandClaude Opus 5 7f20bd303b feat(config): T-1282 — the validate domain, and a move that broke a root
reach validate content / checklist / ron / name-collisions. The three old
scripts are retired, their make targets with them.

Print statements go through the logging sink rather than a collector. The
validators emit their findings as console events as they run, so a long content
validation streams instead of going quiet and dumping at the end — the message
strings and their order are unchanged, only the destination. That also
satisfies the conformance rule forbidding print() in the package, which is what
forced the question.

validate-ron was three languages deep: bash dispatching on a flag, a Python
heredoc doing collision detection, cargo run for schema validation. Logic
embedded in a shell string cannot be imported, tested, or found by anything
that indexes Python, so it became Python; the cargo call became a guarded exec.
It also split into two verbs, because --check-name-collisions answered a
different question from the default path: whether the SET of cultures is
coherent, versus whether ONE file is well-formed.

The move broke something, quietly, which is the point of doing these one at a
time. validate-checklist computed ROOT as Path(__file__).parent.parent — the
repo root while it lived at tooling/validate-checklist, and tooling/domains
once moved. Both its schema and gauntlet paths silently repointed at nothing,
the gauntlet directory "did not exist", and it reported success having checked
zero files. Caught by running it beside the original: old exit 1, new exit 0.
Now config.repo_root(), and load_schema raises ReachError instead of calling
sys.exit, which a service must not do.

Parity on the live tree: content reproduces the original byte for byte
including its counts, name-collisions likewise. Tests pin what those runs
cannot reach — the detection path, since the repo currently has no collisions,
and the argument errors.

Two things found and left alone: validate-content FAILS on the live tree with
13 missing schemas, pre-existing and unrelated to this port; and the ticket's
claim that validate-content sits in the pre-commit hook is wrong — that hook
runs only check-fact-ids and pql decisions validate, so there was no shared
edit to coordinate.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 12:48:56 +02:00
jpmschweitzerandClaude Opus 5 cb5d3f1335 refactor(config): T-1281 — the check domain retires its five scripts
All five gates are ported, tested against their failure paths, and the
originals are gone. reach check is the only way to run them.

Parity first, then deletion. Every case in test_check.py began as a parity case
running the new implementation beside the script it replaced; that evidence is
in the ticket. With the scripts retired there is nothing left to compare
against, so the assertions become the spec and the file drops its "_parity"
name. A parity test is scaffolding with a defined lifetime — keeping one after
its subject is deleted would mean keeping the subject alive to be compared
with, which is the opposite of a migration.

Two gates could not be parity-tested in a fixture at all, and both reasons are
findings rather than obstacles. canvas-version: canvas_sources globs from a
__file__ root while the service resolves git through config.repo_root(), so a
fixture would diff one tree and glob another — real history is used instead,
including two genuine instances of the regression the gate exists to catch.
systems-db-stamp: generator_sources raises at IMPORT time when the economy-db
tree is absent, so the old script died before reaching any logic in every
fixture. The ported service imports it lazily and after the absent/unstamped
checks, which is exactly why those states are testable now and were not before.

Hooks rewired: pre-commit runs reach check fact-ids, pre-push runs the other
four. Both pass --no-input, because a hook has no TTY and a prompt there does
not wait, it crashes. Both guard on `command -v reach` and skip with a message
rather than blocking every commit on a missing tool.

Make targets are RETIRED, not wrapped, per the D-263 split — with the mapping
left as a comment where they used to be. Wrapping would leave two ways to
invoke each gate, and reach --help would stop being the answer to "what tooling
exists" while the Makefile remained a competing index. pre-pr-validate and
pre-pr-content keep their orchestration role and lose the individual target.

Sprint archives and workshop notes still name the old paths and are left alone:
they record what was true when written.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 20:41:33 +02:00
jpmschweitzerandClaude Opus 5 1eb30a1460 chore(config): T-1263 — one permission rule for the whole tool surface
Bash(reach) and Bash(reach *) join .claude/settings.json beside the pql pair.
Two entries, not the one the ticket asked for: a rule ending in " *" does not
match the bare word, and bare `reach` is a real invocation now that it prints
the domain list. pql, make, cargo test and ruff check each carry a bare-form
entry alongside the wildcard for exactly this reason, and adding only the
wildcard would have left `reach` prompting while `reach check ...` did not.

This is the line Q-124 was actually filed about. Ten hand-written
Bash(tooling/...) entries each cover a single script and every unlisted tool
prompts; one command with subcommands is one rule covering everything. The ten
stay for now — the old scripts are still the working tools until T-1253.

On verification, since the ticket warned specifically against declaring this
done on the wrong evidence: real calls run clean, but that is NOT proof the
rule matched. The same calls succeeded before the rule existed — there was no
Bash(reach ...) entry in either settings file and no blanket grant — so the
session was already permitting them and the observation cannot distinguish "the
rule matched" from "the rule was never consulted". settings.json is read at
session start, so this cannot be self-verified from the session that wrote it.
Proof is a later session, in a prompting mode, where reach runs without asking.

One accepted limitation, documented rather than worked around: rules
prefix-match the whole command string, so an env-prefixed call like
SR_REPO_ROOT=... reach ... will still prompt. An environment override is a real
departure from normal invocation; the ordinary form is what needs to be
frictionless.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 13:52:45 +02:00
jpmschweitzerandClaude Opus 5 f4cca69cab feat(config): T-1261 — reach is a bare name on PATH, in every context
`uv tool install --editable` puts the executable in ~/.local/bin rather than
.venv/bin, which is the difference between a command that works everywhere and
one that works only under an activated venv. Agents and git hooks never
activate one.

Verified in the three contexts that matter, with a negative control so the
passes discriminate: a stripped non-interactive shell, a REAL git hook process
(via git -c core.hooksPath ... hook run pre-push, not a simulation), and an
agent Bash call — all with VIRTUAL_ENV unset. With ~/.local/bin removed from
PATH the same check reports NOT-FOUND, so this is not passing because a venv
happens to be active.

Found a silent interpreter fork while doing it, which is this initiative's own
failure mode wearing a different hat. uv tool install without --python picked
CPython 3.11 for the tool environment while .venv and system python are 3.14 —
uv selects the lowest interpreter satisfying requires-python. reach would have
run on one interpreter and the test scripts on another, with different wheels
for numpy/scipy/PIL, and future 3.12+ syntax would break the tool while the
venv stayed green. PYTHON_VERSION now pins both.

make setup-venv is rebuilt on uv, per the T-1258 finding that it called
.venv/bin/pip against a venv that has no pip. The first fix was wrong too:
plain `uv venv` fails on an existing venv, so the target was not idempotent
where the version it replaced had been. Caught by running it twice instead of
dry-running it — which is how the original rotted unnoticed.

make install-reach self-checks that reach is actually on PATH afterwards
rather than assuming it. make reach-repoint gives a name to the situation
where uv keeps resolving a deleted worktree: reach still runs, edits in the
main checkout do nothing, and there is no error message.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 14:36:31 +02:00
jpmschweitzerandClaude Opus 5 48fee8a0b6 feat(config): make the canvas-generation/version pairing a gate, not a habit (T-1242)
project.yaml's version is the Atlas disk cache's only invalidation signal, and
nothing enforced that changing canvas GENERATION also moved it. It broke five
times -- 0.4.2 lake_margin_q, 0.4.3 coast_warp_px, 0.4.4 the extent inversion,
0.4.5 the Global sentinel, 0.4.6 one-course-per-river -- each bumped only after
someone noticed a wrong map. The failure is invisible to its author: it needs a
warm cache to reproduce, so a cold checkout looks fine. T-1239 is the last one,
and it took eight days.

tooling/canvas_sources.py is the path registry; tooling/check-canvas-version
rejects a push that touches those paths without moving project.yaml's version
line. Wired into the pre-push hook, `make check-canvas-version`, and, for the
parsing units, `make test-tooling`.

Verified against real history rather than a synthetic branch: run over
4e503c356 -- the commit that actually caused T-1239 -- the gate rejects and names
the three files. Run over the commits that DID bump (bdea71953, 39f0fd8c5, and
T-1239's own fix), it passes.

The registry is globbed, not hand-listed. step_canvas.rs imports ten sibling
modules and those import more, so a traced closure would be stale within a month,
and stale here is silent. It over-includes on purpose: a false positive costs one
bump and one round of cache misses, a false negative costs another week of a
wrong map -- the ticket's own ruling.

Two deliberate calls worth naming. The registry includes ITSELF, which closes the
narrowing hole: remove a path and change that same path in one push, and the gate
still fires because the registry file is in the set. And there is no override
flag -- it would be reached for exactly when someone is certain their change is
harmless, which is the reasoning behind all five regressions.

Version bumped 0.4.6 -> 0.4.7 with NO canvas-generation change: self-inclusion
means adding the registry trips its own rule. Spent rather than special-cased,
because the first exception is how a rule like this dies.

The units cover the property no branch run can show -- that editing project.yaml's
comment block, which quotes old version NUMBERS directly above the field, is not
a bump -- plus a registry-coverage test naming the files each of the five known
regressions touched, so a future narrowing past them fails loudly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 00:06:32 +02:00
jpmschweitzerandClaude Opus 5 086d9ed56e fix(client): bake the version into the build, so an export can invalidate its cache (T-1241)
current_schema_version() line-scanned res://../project.yaml at runtime. That
resolves to the repo root in a dev run and to nothing in an exported build, so a
shipped game got the "?.?.?" fallback every time. Since that tag is the Atlas
disk cache's ONLY invalidation signal, every exported build stamped and compared
the same sentinel: a canvas cached by one build would be served by every later
build, forever. T-1239 is what that failure looks like once it happens.

loading_screen.gd carried a byte-for-byte copy of the same function, so the
version shown to the player was "?.?.?" in exactly the builds where a version
string is worth showing. Both call sites now share client/scripts/build_version.gd,
which reads application/config/version out of ProjectSettings — a value Godot
bakes into the PCK, identical in the editor and in an export by construction
rather than by luck. No file IO, no fallback branch.

project.yaml stays the source of truth (CLAUDE.md); client/project.godot mirrors
it. A mirror nobody checks would be worse than the bug it replaces -- the old
code failed loudly everywhere, a stale mirror fails silently -- so
tooling/check-client-version compares the two and the pre-push hook runs it
unconditionally. Not gated on "were those files in this push": drift persists on
main once introduced, and gating would let an existing drift ride along.

The test this replaces asserted that current_schema_version() did not return its
fallback, and passed -- in the one environment where the code under test worked.
Three tests now pin the property that actually matters: a real version, sourced
from the baked setting, matching project.yaml.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 23:35:33 +02:00
jpmschweitzerandClaude Fable 5 a45d4a5f58 feat(client): capture harness redesigned for the stepped Atlas — (body, rung) goldens (T-1157)
_setup_atlas_golden_shot and _run_atlas_matrix rebuilt on the real D-255
surface: nav.push('regional') through AtlasApp's own body-selection tail,
then StepCanvasViewer.jump_to at a fixed center (inventory item 5) — no
shim over the retired continuous-zoom API. is_pending()-aware bounded
settle (mirroring atlas_agent_driver.gd) and view-transform logging
(inventory item 3) wired into both capture paths. The 12 z2_0/z4_0/zfit
goldens are replaced by 12 (body, rung) goldens captured live against
fresh --test-mode servers; atlas_shots.json/visual.json re-keyed;
atlas_gen_open's stale gen_l1_* overlay ids fixed to gen_dw_temp.

Verification: two consecutive District runs byte-identical, and a
cold-vs-warm disk-cache invariance proof on both terrain draw branches
(Global/NEAREST, District/LINEAR) — byte-identical either way, so
capture output does not depend on the shared user://atlas_cache state.
DEVOPS.md's real-rendering exception note now records the fold-target
mapping for the smoke file T-1182 already deleted (Global + District
goldens exercise its two real-pixel draw branches). Old legacy-tracked
.import sidecars go with their PNGs; new goldens ship bare per
.gitignore's client/**/*.import rule.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 22:13:37 +02:00
jpmschweitzer 493a7345d3 fix(client): T-1153/T-1152 round 4 — mosaic canvas-local frame, offset recompute on rung crossing, tile texture RID lifetime; real-driver draw smoke
Three live-found rendering bugs, all invisible to green unit suites:
- _draw_tile_mosaic placed tiles from absolute district (0,0); canvas-
  local (0,0) is held_center - held_n/2 everywhere else, and tile-mode
  held_n is the whole-body extent — the entire mosaic drew tens of
  thousands of px off-canvas. New pure district_to_canvas_local() +
  viewer accessors route every tile through the shared frame.
- _maybe_reselect_rung updated held_n across crossings without
  recomputing _view_offset — the single-window composite landed off-
  canvas the moment any crossing happened (why District/Quarter were
  black too). New pure recompute_offset_for_held_n_change().
- _build_tile_texture created an unstored ImageTexture per _draw,
  racing the RenderingServer's deferred upload — CPU pixels correct,
  screen white. Per-tile-index texture cache, same reference-identity
  discipline as the single-window _cached_texture.

Structural close of the twice-bitten 'nothing asserts pixels' gap:
test_atlas_window_overlay_draw_smoke.gd renders overlay output into a
SubViewport and asserts visible pixels for both modes — runs under a
real driver (invocation documented in DEVOPS.md, visual_capture
precedent; migration to the T-1157 harness noted on that ticket), skips
loud-but-green under the gate's headless run (verified green-with-skips
AND genuinely red with detection forced off). +7 geometry/crossing
tests, all revert-verified. Targeted suites 270 green; gdlint clean.
2026-07-22 14:41:00 +02:00
jpmschweitzerandClaude Fable 5 346d87df7a chore(meta): docs/build sweep + tooling test gate (T-1069, T-1066)
- make test-tooling: planet-gen determinism guard + import_economics
  --dry-run, wired into pre-push on TOOLING_CHANGED; ruff widened to
  E4/E7/E9/F/W (90 safe auto-fixes applied; E402/E702/F841 ignored with
  documented counts)
- one-generator reality fixed in DEVOPS.md, asset-pipeline rule, CLAUDE.md
  (import_economics sole generator since #951/D-223); dead check-protocol
  target deleted; DEVOPS hook/config sections rewritten from the actual
  hook sources; team-patterns gate description updated (client+tooling)
- project.yaml: 0.2.0 → 0.4.0 per the 0.{phase}.{n} scheme, description
  refreshed from the v0.1 Sova narration to cascade reality
- stale comment sweep: voxel.rs stub claims (all 8 families implemented),
  cascade.rs TODO recited to T-1044, main.rs D-192 handshake claim,
  relationships.rs/chunk_streaming.rs version targets → phase language
- gitignore: client/settings.db* e2e-run artifacts

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 16:22:55 +02:00
jpmschweitzerandClaude Opus 4.8 ccc194d5f9 fix(meta): address PR #155 review — DEVOPS layout + common.py cleanup
Hoshe (QA): docs/DEVOPS.md Repository Layout still listed `decisions/` — corrected to
`governance/` (the DQR tree) and added a `.pql/` entry for the planning store.

Tyre (architecture, non-blocking): tooling/db/common.py docstring named deleted scripts
as consumers and `resolve_db_path`/`load_config`/`get_connection` were dead settledreach.db
code. Trimmed common.py to just `ensure_venv` (the only symbol any kept connector imports)
and rewrote the docstring to name the real consumers.

ruff clean; common.py parses; ensure_venv intact.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 23:06:39 +02:00
jpmschweitzerandClaude Opus 4.8 5a399c7924 chore(meta): retire legacy SQLite ticket/decision tooling (pql migration phase 6)
The pql cutover is stable, so remove the superseded SQLite planning tooling. Surgical
— only the ticket/decision/raw-SQL scripts (all settledreach.db-bound and replaced by
pql) are deleted; the asset/audio/wiki connectors and shared common.py stay.

Removed:
- tooling/db/{ticket,decision,decisions-sync,decisions_sync.py,sqlite-query,sqlite-exec,
  sqlite-init,sqlite-seed,sqlite_connector.py}
- tooling/{db-backup,db-install} + docs/backups/settledreach.db.backup (the binary-DB
  backup ritual; tickets now live in the git-tracked .pql/changelog/)
- tooling/check-decision-ids (dead stub, superseded by `pql decisions validate`)
- Makefile db-backup/db-install targets; SR_DB_PATH + tooling/db/{ticket,sqlite-*,
  decision*} entries from .claude/settings.json (audio entries kept)

Updated docs to pql: DEVOPS.md (SQLite Access + Decisions System → pql), project
structure, ticket-cli closing note, asset-pipeline raw-SQL warning.

Kept (verified still imported by the asset connectors via common.ensure_venv): common.py,
config.json, audio/image/trellis/wiki connectors. The live settledreach.db file
(gitignored, repo-parent) is left on disk as a cold rollback only.

ruff clean; pql decisions validate ok (357 decisions / 1013 tickets).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 22:13:29 +02:00
jpmschweitzerandClaude Opus 4.6 5079d84405 docs(ci): document source-canonical asset pipeline rule
Adds `.claude/rules/asset-pipeline.md` describing the full pipeline —
generators, meta stamp, pre-push hook, how to make DB/schema changes,
and why direct systems.db edits are forbidden. CLAUDE.md and DEVOPS.md
point at the rules file; sprint-start template now reminds teammates
to read it before touching DB sources.

CLAUDE.md also gains the missing `decision show` row in the CLI tool
table (companion to #723's `decision show` subcommand).

Refs: #859 #723

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-21 17:35:12 +02:00
jpmschweitzerandClaude Opus 4.6 d20a3a1354 chore(db): remove Qdrant semantic search infrastructure (#816)
The Qdrant index (commonwealth collection, 475 points) was stale —
pointing at old worktree paths from previous sprints with no
maintenance. Grep covers all current search needs.

Removed: qdrant_connector.py, wrapper scripts (qdrant-search,
qdrant-index, qdrant-health, qdrant-count), /docs-search skill,
Qdrant/Ollama config entries, and all active references in agents,
rules, briefings, DEVOPS, CLAUDE.md, and TEAM.md. The commonwealth
collection was dropped from tower-of-joy:6333.

Historical references in discussion archives and sprint briefings
are preserved as-is.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 08:52:50 +02:00
jpmschweitzerandClaude Opus 4.6 5683b1dc4b chore(config): fix skill contradictions, gitignore .obsidian, docs cleanup
- sprint-start: remove three-tier severity (critical/warning/suggestion),
  align with pr-review policy (every comment is actionable)
- sprint-start: resolve {team_scope_dirs} dangling placeholder
- .gitignore: add .obsidian/ directory
- DEVOPS.md: remove "pending setup" from gdlint (now enforced)
- Remove GEMINI-SCAN.md one-off scan artifact

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 15:16:51 +02:00
jpmschweitzerandClaude Opus 4.6 a5ce7fa603 docs(docs): add frontmatter to DEVOPS.md and scratchpad.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 00:15:38 +01:00
jpmschweitzerandClaude Opus 4.6 ccff595101 fix(ci): address PR #74 review comments
Add missing db/connectors/decision permission in settings.json and
update DEVOPS.md layout table to reflect connector move. Filed #568
for Sprint 22 symlink removal.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 17:51:09 +01:00
jpmschweitzerandClaude Opus 4.6 bc226d8baf chore(db): move db/connectors/ to tooling/db/ (#274)
Consolidates all connector scripts under tooling/ per project
structure conventions. Symlink at db/connectors → tooling/db/
preserves backwards compatibility (remove after Sprint 22).

Updated references in CLAUDE.md, Makefile, DEVOPS.md, all skill
files, agent files, rules, schema comments, and Sprint 21
briefings. Python scripts updated with correct SCHEMA_PATH
(now relative to WORKTREE_ROOT/db/schema.sql).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 11:08:00 +01:00
jpmschweitzerandClaude Opus 4.6 4f21465dbf feat(ci): test runner scripts and Makefile integration (#270)
Six test runner scripts at tests/: run-rust, run-godot, run-ipc-fixtures,
run-ipc-protocol, run-ipc-integration, run-all. Plus run-ipc-benchmark
for Layer 3 timing. All produce structured JSON stdout, support --filter,
and exit 0/non-zero. Makefile targets updated to delegate to scripts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 12:47:58 +01:00
jpmschweitzerandClaude Opus 4.6 f0bab3999d chore(skills): rename all skills to domain-action convention
Adopts the whatsinagame naming pattern where the domain comes
first and the action second (e.g. pr-review, sprint-start,
audio-gen). Updated all cross-references in settings, agents,
docs, and inter-skill references.

12 renames: commit→git-commit, create-skill→skill-create,
gen-audio→audio-gen, gen-image→image-gen, plan-sprint→sprint-plan,
push-pr→pr-push, render-sprite→sprite-gen, review-pr→pr-review,
search-docs→docs-search, start-sprint→sprint-start,
start-workshop→workshop-start.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 12:51:56 +01:00
jpmschweitzerandClaude Opus 4.6 d8d005169a feat(ci): add golden file targets and checklist schema (#486, #497)
Golden targets: make golden-diff shows color diff if simulation output
changed, make golden-update regenerates and stages for review.

Checklist schema: JSON Schema for 7 condition types evaluable from
ObserverSnapshot. Per-room YAML checklists for 3 Gauntlet rooms plus
cross-room checks. Validation script + make checklist-validate/generate.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 10:44:33 +01:00
jpmschweitzerandClaude Opus 4.6 3ccac5432b fix(ci): address PR #31 review — harden cross-encoder fixture pipeline
- Fail on encode errors instead of silently writing empty .msgpack files
- Fail test on missing/empty fixture dir instead of silent skip
- Add all missing action variants (MoveSouth, MoveEast, MoveWest,
  Unpause, ToggleStanceDown, WalkAway) to GDScript fixture generator
- Add GDScript fixture staleness check to make pre-pr
- Validate repo root detection before writing outside client/
- Add file.flush() before close in headless mode
- Document fixture failure recovery in DEVOPS.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 09:45:13 +01:00
jpmschweitzerandClaude Opus 4.6 da63aa580e feat(ci): add make fixtures-client target for GDScript->Rust cross-encoder validation (#475)
Closes the bidirectional protocol compatibility loop (D-030 Layer 1):
- GDScript fixture generator (20 fixtures: inputs, boundary ticks, batch)
- Rust decoder test verifying all GDScript-encoded fixtures deserialize
- Makefile target with generation + verification in one step

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 02:20:24 +01:00
jpmschweitzerandClaude Opus 4.6 4af2197c59 fix(ci): address PR review — filter comments, trim whitespace, warn on missing scripts
Address all review comments from Hoshe and Tyre on PR #27:
- Remove 2>/dev/null from pre-pr-fixtures (critical: swallowed errors)
- Remove dead _file_type function
- Check 4: error on districts with no locations declared
- Check 5: print advisory message when skipping
- Check 8: cross-file line ID uniqueness (not just per-file)
- Check 9: document D-034 asymmetric relationships in docstring
- Document regex fallback rationale in _scan_knowledge
- Add D-035 decision trace to schema descriptions
- Use concrete protocol version in DEVOPS.md example
- Amend D-035 with focused (9th mood) and greeting (14th situation)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 00:23:21 +01:00
jpmschweitzerandClaude Opus 4.6 f06c8be313 feat(ci): add make pre-pr target and fixture staleness check
Implements make pre-pr chain: lint -> build -> test -> content
validation -> fixture staleness. Branch-specific variants:
pre-pr-server, pre-pr-client, pre-pr-content.

Fixture staleness is a blocker (exit 1) — stale fixtures cause
false positive client tests. Spec from hoshe-round3.md Section 5.

Tickets: #460, #465

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 22:43:40 +01:00
jpmschweitzerandClaude Opus 4.6 163de8bf25 fix(ci): address PR review — filter comments, trim whitespace, warn on missing scripts
- Filter YAML comment lines (grep -v '^\s*#') from both extraction
  pipelines to prevent phantom canonical IDs
- Trim trailing whitespace from extracted fact_ids so grep -qxF exact
  match works reliably
- Pre-commit dispatcher now prints explicit warning when a check script
  is missing instead of silently skipping
- Document --no-verify bypass for emergencies in DEVOPS.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 17:42:22 +01:00
jpmschweitzerandClaude Opus 4.6 fe204aa5f4 feat(ci): add pre-commit FactId validation hook (#393)
Grep-based pre-commit check validating fact_id references in content
YAML against canonical knowledge catalogs. Runs in advisory mode when
catalogs are stubs (exit 0), switches to enforcing mode once populated
(exit 1 on unknown fact_ids with file:line output).

- tooling/check-fact-ids: core validation script (<2s runtime)
- .config/hooks/pre-commit: hook dispatcher for modular checks
- Makefile: check-fact-ids + setup-hooks targets, wired into setup
- docs/DEVOPS.md: content validation and pre-commit hooks sections

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 17:17:34 +01:00
jpmschweitzerandClaude Opus 4.6 fb733209e0 chore(config): add automated Rust install to make setup
Adds tooling/install-rust script that installs Rust via rustup if
not present, with clippy and rustfmt components. Idempotent — skips
when already installed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 16:53:02 +01:00
jpmschweitzerandClaude Opus 4.6 5267d405e0 chore(config): add automated Godot download to make setup
Adds tooling/install-godot script that downloads the Godot binary
from GitHub releases to ~/bin/godot4. Skips download when the
correct version is already installed. Supports Linux x86_64/arm64
and macOS. GODOT_VERSION defaults to 4.6 and is overridable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 16:43:07 +01:00
jpmschweitzerandClaude Opus 4.6 1513dbf0f8 docs(meta): add DEVOPS.md, update project docs for decisions directory split
Adds DevOps procedures document and updates CLAUDE.md/README.md to reference
decisions/ domain files and Makefile-based workflow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 15:46:39 +01:00