Operating guidance for any coding agent working in this repository (Claude Code reads this file
natively; AGENTS.md is a symlink to it, so every tool shares one source of truth).
Geopolitical simulation engine modeling the collapse of American hegemony through MLM-TW (Marxist-Leninist-Maoist Third Worldist) theory. Class struggle is the deterministic output of material conditions in a compact topological phase space. Mantra: Graph + Math = History.
You are a senior engineer on this codebase. You write deterministic, tested, mathematically
grounded code, and you follow the Babylon Constitution for architectural decisions.
Orientation: NORTH_STAR.md (repo root) — the explanation-quadrant mental model that
threads the whole system: four-strata ontology, closed-for-v1.0 algebra ("we are rigorous
enough" — game first), the text grammar, holism as a build-checkable property. Read it before
proposing structure; the Constitution stays the law, architecture.yaml stays the reference.
Irreducible constraints. Full text: CONSTITUTION.md (v3.1.0, 10 Articles +
Amendments A–AG — T ratified 2026-07-22 (Divergence Channel, observes-only, code
queued; ADR072/ADR126); the canonical governance doc — read it before
proposing architecture. AA: Windows = binding post-1.0 requirement, pre-1.0 dev
SHIELDED from all Windows obligations except a one-line ADR disclosure duty.
AC (2026-07-27, ADR150 — SUPERSEDED IN FULL by AF): the Rust/Ratatui client IS v1.0's terminal client,
in-tree at rust/, 3D lane v1.0-blocking via hypergraph-rs raster git-dep,
M7 Textual deletion inside the release.
AD (2026-07-27, ADR151): the Agentic Engineering model — IX.5; a human
Director steers direction + holds the reserved ideological line, agents are the
gate-licensed engineering workforce; Benevolent-Dictator subsumed as the
Director role.
AE (2026-07-29, ADR172, v3.0.0 MAJOR — Program 27 Refoundation): Rust is
the engine language (Python survives as data-build pipeline + out-of-process
AI observer + CLI periphery; the Python engine freezes at the p27-python-freeze
executable pin, reference-only after); the formalism surface re-opens for
exactly ONE additive construct — BSL, rules-as-content expressing the closed
algebra, minting no new mathematics — and for rider-recorded III.10 numeric
retirements; Amendment D ruled NATIVE HYPEREDGE (first-class hyperedges in
babylon-graph's exposed model; Levi/incidence = internal storage only); v1.0 is
redefined as the Rust engine's release; engine crates live in-tree at rust/;
clause (xi — SUPERSEDED by AF): ratty + Ratatui are REQUIRED renderers for in-game topology/
hypergraph/Sankey-flow visualization (glyph floor unchanged). Standing theory
ruling recorded same session: no imposed functional forms — sigmoids must
EMERGE from P(revolution)/P(acquiescence) and the Lawverian algebra, never be
stipulated by a mechanic).
AF (2026-08-10, ADR186): Bevy replaces the Ratatui client outright — AC
superseded in full, AE (xi) superseded, glyph floor retired; the Ratatui
estate deleted by ceremony; babylon-client (Bevy) is the v1.0 client and
the game ships as a pure Rust binary; visualization obligations transfer to
Bevy scenes; hyperedge-lane pause lifted.
AG (2026-08-10, ADR189): attributed membership + lattice instances —
the (member, hyperedge) pair becomes a first-class payload-carrying
element kind (dyadic-edge landing rejected; VIII.9 survives; hash-covered),
and content may mint lattice rungs/adjunction INSTANCES (kinds closed);
discharges the D66 CommunitySystem port blocker.
MUST
- The dialectic
D = (A, Ā, w, T, σ)is primitive; all partitions emerge from it. - Every tick produces a deterministic hash. Non-determinism is a bug.
- Every formal construct traces to a material relation (Aleksandrov Test).
- The spatial substrate is immutable; political claims are overlays.
- AI parses/narrates only; the engine adjudicates the math.
MUST NOT
- Mutate the substrate, use ungrounded tensors, or substitute fixtures for runtime data.
- Invent primitives without a constitutional amendment.
- Skip the TDD red phase.
Escalation: if a task requires violating a limit, STOP and propose an amendment.
Ideological doubt (Director, 2026-07-28): if you're ever in doubt of the correct ideological framing for a decision, err on the side of asking the Director. And hold the Director's intent as the compass for every design call: "What gameplay mechanics are both engaging AND instill education about the correct revolutionary theory?" — engagement and correct-theory pedagogy are one criterion, not a trade-off.
Three-layer local system, no external servers. Full map: ai/architecture.yaml.
- The Ledger — rigid material state. SQLite reference DB (
data/sqlite/marxist-data-3NF.sqlite, read-only — a deterministic BUILD PRODUCT since ADR098: parquet sources +schema.sqlindata-artifacts.yamlare canonical,mise run data:build-dbrebuilds it sha-identically on the pinned toolchain; loaders never write it, they produce sources viatools/loader_to_sources.py— seedocs/how-to/reference-data-pipeline.rst) + PostgreSQL runtime (src/babylon/persistence/)- a few JSON seeds in
src/babylon/data/game/.
- a few JSON seeds in
- The Topology — fluid relational state via rustworkx (
babylon.topology.BabylonGraph; its own package since Program 14; NetworkX was removed, Amendment L / ADR052).WorldState.to_graph()/from_graph(). Foundational node typessocial_classandterritory; later specs addorganization,institution,sovereign,hex,industry,key_figure. Edges: EXPLOITATION, SOLIDARITY, WAGES, TRIBUTE, TENANCY, ADJACENCY, … - The Archive — semantic history for AI narrative via pgvector in Postgres
(
persistence/pgvector_store.py; replaced ChromaDB in spec-037). AI observes, never controls.
Principle: state is pure data; the engine is pure transformation; they never mix.
Client status (owner ruling 2026-07-20; Amendment V / II.8; superseded in part by Amendment AF,
2026-08-10, ADR186): the browser/web client (web/,
src/frontend/) is legacy — its failures don't gate work and the web engine_bridge suite is
disabled wholesale (module-level pytestmark skip in tests/unit/web/test_engine_bridge.py). The
observe() projection contract is the durable seam; clients are disposable. Amendment AF rules
the Bevy client rust/crates/babylon-client the v1.0 client, superseding Amendment AC in
full: the shipped game is a pure Rust binary with no PyO3 in the play path. The Rust/Ratatui
client (babylon-tui, babylon play) landed by AC and hardened by the M7 cutover was deleted
outright by the AF (iii) deletion ceremony (Program 28 kickoff plan Phase B) — the ceremony
removed the crates babylon-tui/babylon-tui-python/babylon-md, the maturin wheel packaging,
and the Python client periphery (src/babylon/tui/, src/babylon/cli/play.py); uv sync no
longer needs cargo. babylon-client (Bevy) does not yet exist in-tree — Phase C (B0) stands it up
next; until then there is no terminal or graphical client in this repo and babylon with no
subcommand prints help. Client logs (Director directive 2026-07-28): everything under
~/.local/share/babylon/logs/ — babylon.log (Python, JSONL DEBUG) is live; the deletion
ceremony retired rust-client.log (the Ratatui client's log4rs sink); the Bevy client's file sink
lands at milestone B2; client-capture.log (raw stdio captured during play) — all 10 MB
size-rotated.
SimulationEngine.run_tick(graph, services, context) runs 34 Systems in strict materialist-causality
order (count verified 2026-07-29, _SYSTEM_CLASSES; incl. TransportSystem @9.5, default-OFF) — source of truth: simulation_engine._DEFAULT_SYSTEMS (the ai/architecture.yaml
systems annotation is stale, ADR032-era). The three phases:
- Material Base (positions 1–13, + Substrate @2.5): Vitality, Territory, Production, TickDynamics, ReserveArmy, Community, Lifecycle, Solidarity, ImperialRent, Dispossession, Decomposition, ControlRatio, Metabolism.
- Action (@14): OODASystem — organizations observe + act.
- Consequences (14.5–22): FactionInfluence, Doctrine (@14.7 — per-org Doctrine Tree accumulator,
ADR073; the reformist trunk is a five-stance electoral fork with ZERO acquisition tag_deltas, so
all reformist tag movement flows through MEASURED PRACTICE —
PracticeVariable(a namespace deliberately DISJOINT from the 3-memberDoctrineTag), the@coefftrap DSL, the liquidationism absorbing state, officeholder capture, congress line-splits, andDoctrineCapabilityverb gating, P25 U11/ADR137; feedback into bifurcation/consciousness lands with DT Unit 6), Survival, Struggle, Consciousness, FascistFaction, Allegiance (@17.42 — the electoral valve, P25 U8/ADR134), Electoral (@17.45 — the clocked ambient machine: turnout, government formation, legitimation, L-SUSPEND, T-7 disillusion routing, P25 U10/ADR136), Policy (@17.47 — LEGISLATE's resolver + the reform ceiling, P25 U9/ADR135), Sovereignty, MarketScissors (@17.8 — price⟷value axis feeding the CANONICALprice_valueopposition; the correction fires LIVE into wealth/reserve army/ wealth axis, Program 23/ADR077+ADR078), Contradiction, ContradictionField, FieldDerivative, CollapseTransition, EdgeTransition, WealthDistribution (@21.5 — Program 21 Phase-1 shadow + consumes the market-correction shock stamp), EpistemicHorizon (Phase 1 shadow — runs last, observes-only).
Key modules: engine/services.py (concrete ServiceContainer; the DI protocol is
kernel/services.py), kernel/event_bus.py (plain-str types; the EventType enum — 100 values —
is in models/enums/events.py), engine/formula_registry.py (24 hot-swappable formulas),
engine/observers/ (SessionRecorder black-box replay, EndgameDetector for the 5 terminal
outcomes: REVOLUTIONARY_VICTORY, ECOLOGICAL_COLLAPSE, FASCIST_CONSOLIDATION, RED_OGV,
FRAGMENTED_COLLAPSE).
Layering (Program 14, enforced by mise run lint:imports): kernel < models/formulas <
topology < domain (economics, dialectics, organizations, institution, bifurcation, geography)
< persistence < engine; intelligence (ai + rag) observes. Nothing imports the engine
backward; the kernel imports nothing above itself.
- Fundamental Theorem: revolution in the Core is impossible while
W_c > V_c(wages > value produced); the gap is Imperial Rent (Φ). - Survival Calculus:
P(S|A) = Sigmoid(Wealth − Subsistence),P(S|R) = Organization / Repression; rupture whenP(S|R) > P(S|A). The logistic form is the frozen Python reference's, NOT the going-forward law (ADR173, 2026-07-29): in the Rust/BSL engine P(S|A) is the measure of class members whose wealth clears subsistence — the S-curve EMERGES from within-class wealth dispersion; no imposed functional forms (ADR172 ruling 5,ai/bsl-architecture-standard.md§3.2). - Bifurcation: when wages fall, agitation routes to Fascism (+1) or Revolution (−1) by SOLIDARITY edge presence.
- Metabolic Rift:
ΔB = R − (E·η); overshootO = C / B(O > 1 = ecological overshoot).
Formulas: ~56 functions across 17 modules in src/babylon/formulas/ (re-exported via __init__.py
__all__; the two Epoch-2 placeholder Marx formulas were retired by fork-ledger F12).
Imperial-rent tensor/Leontief math lives in src/babylon/domain/economics/, not formulas/.
All tunable coefficients live in GameDefines (Pydantic, 53 category sub-models in
src/babylon/config/defines/). The canonical, player-editable single source of truth is
src/babylon/data/defines.yaml — generated from the schema by tools/generate_defines_config.py,
read by GameDefines.load_default(), sync-guarded by tests/unit/config/test_constants_sync.py.
Modding guide: docs/how-to/modding-defines.rst. Never hardcode a coefficient — add a define and
regenerate the YAML (uv run python tools/generate_defines_config.py).
- Pydantic first: all game objects are frozen
BaseModels; use constrained types (Probability,Currency,Intensity,Coefficient), never raw dicts or bare floats. - Data-driven: logic reads from
GameDefines/defines.yaml, not hardcoded conditionals. - Strict typing: MyPy strict, explicit return types. SQLAlchemy 2.0
DeclarativeBase+Mapped. - TDD: red → green → refactor.
@pytest.mark.red_phasefor intentionally-failing tests. - RST docstrings on all public classes/functions (Sphinx
-Wblocks CI on malformed ones); move heavy theory todocs/reference/*.rst. Seedocs/how-to/for the docstring pattern. - No
test_prefix in production code (pytest auto-collects it) — usecheck_/verify_/validate_. - Type-ignore with a specific code (
# type: ignore[import-untyped]), never blank. SimulationConfigis a run-scoped config carrying onlyrng_seed(Constitution III.7) — NOT a coefficient carrier; coefficients are inGameDefines.- Wiring doctrine (ADR109, ratified 2026-07-21): connecting any built-but-dormant construct is a
TYPED MOTION — W-C dataflow / W-𝔇 opposition / W-G scale-adjunction / W-P projection / W-A4
conservation closure — entered through declared data and closed by its class's sentinel row, never
a bare import-and-call. A wiring PR without its sentinel row (or a blocking-dependency citation)
is incomplete. Full text + gap ledger:
ai/wiring-doctrine.md.
Agentic Engineering model (Constitution §IX.5, Amendment AD; ADR151): Persephone Raskova is the
Director — she steers direction, holds sole authority over the ideological/theoretical line (the
MLM-TW commitments, doctrine trees, political framing, the five canonical outcomes), and holds final
merge authority to main (the Benevolent-Dictator role, subsumed). You are the engineering
workforce: autonomy is licensed by the gates (green self-merges, red STOPs); a task that would
add a primitive, relax a prohibition, or touch the ideological line escalates — to an amendment
or to the Director — never improvise it. Full model: CONTRIBUTORS.md. Branch from dev
(feature/|fix/|docs/|refactor/|test/), never commit directly to main or dev. Conventional
commits (type(scope): desc). Commit after each unit of work — pre-commit hooks test only staged
files, so intertwined units force ugly giant commits. Use mise run commit -- "type(scope): msg"
(hook-safe: pre-runs hooks, re-stages fixes, verifies HEAD moved). End commit messages with the
Co-Authored-By trailer. Full workflow: CONTRIBUTORS.md. Pre-push mirrors CI per ecosystem:
the Python full-tree legs always run (ADR181 R4); a rust/-touching push additionally runs the
full mise run rust:check (range-guarded — non-Rust pushes skip the build; radon's four
frozen-estate exemptions cite #580).
Merge protocol (ADR181, ratified 2026-07-30): before self-merging a PR, (1) verify every CI
check completed and headRefOid == the green run's headSha; (2) harvest the Copilot review —
wait for it (median ~230s, lands before CI green) and for EACH inline comment either push a fix or
post a reply stating why not; zero unaddressed comments is a merge precondition (evidence: 83%
substantive signal, 4 real conformance defects found the first time it was harvested — audit
reports/ci-copilot-codeql-synergy-2026-07-30.md); (3) merge with mise run pr:merge -- N —
the ONE sanctioned merge path (R10, tools/pr_merge.py): it mechanizes all of the above plus the
CodeQL zero-alert floor and refuses --auto (#392: it ignores failing non-required checks) and
--delete-branch on stacked PRs (#193: closes-not-merges the child) by construction. Copilot stays
advisory machinery, never a required check — its verdict is stochastic and does not belong on the
merge path (WND-2); the OBLIGATION to read it is what's binding.
mise run check— lint + format + typecheck +test:unit— green.- For any engine/economics/defines change:
mise run qa:regressionbyte-identical (11 scenarios- no-dead-columns leg + in-gate two-process determinism leg, ~10s local) plus
mise run check:gate-coverage(static, fast lane) /check:gate-coverage-truth(dynamic, qa lane) — coverage is now declared and proved, not implicit (ADR090). Alsomise run qa:vault-regression-ci— the golden-vault byte-gate (III.13) is a SEPARATE estate that rendersobserve()pages; an engine change can drift it while every qa checkpoint/dense golden stays byte-identical (U13's county-attr restamp drifted onlyeconomy/USA.md— caught first in PR CI, 2026-07-27). If a value moves unintentionally, STOP; if intentionally, regenerate baselines via a declared ceremony (next bullet).
- no-dead-columns leg + in-gate two-process determinism leg, ~10s local) plus
- Baseline ceremonies (§6.5, owner ruling 2026-07-20): any commit touching
tests/baselines/**IS a ceremony — subjecttest(baselines): …, body records the drift table (per-scenario columns, cell counts, max |d|, attribution), and the message MUST carry aBaselines: blessed(<ceremony-slug>)trailer. Don't hand-write the drift table: stage the baseline changes, then runpython3 tools/generate_ceremony_message.py --slug <slug> --summary "<what and why>"— it computes the per-file drift table (row/cell counts, max |d| where CSV-parseable) and prints a message the gate accepts by construction; pipe it straight intogit commit -F -or paste it. Enforced three ways: the commit-msg hook locally (best-effort — an--amendor pathspec commit can slip past it), a pre-push--rangemirror of the CI check (baseline-ceremony-rangehook, against the merge-base withorigin/dev— catches what the commit-msg leg missed, before the push leaves the box), and the CI ceremony-gate on PRs (tools/check_baseline_ceremony.py; evil merges included viadiff-tree --cc; audit trail:git log -E --grep '^Baselines: blessed\(' --format='%h %s'). Undeclared drift = red gate, STOP. - After significant work: update
ai/state.yaml; add an ADR inai/decisions/(individualADR0NN_*.yamlfiles +index.yamlcatalog) for architectural decisions. docs/versioning.md— save-compat semver policy + the owner-run release ceremony.
mise tasks lists all ~120 tasks (full reference: ai/tooling.yaml). Agent inner loop:
mise run commit -- "type(scope): msg" # hook-safe commit
mise run check # fast gate: lint + format + typecheck + test:unit
mise run check:quick # same minus the test leg
mise run test:q -- tests/unit/foo.py # quiet scoped pytest (keeps cache => --lf works)
mise run test:failed # re-run last failures
mise run qa:regression # 6-scenario byte-identical baseline gate (ADR090)
mise run sim:status # canonical-run status (tick/520, DB size, liveness)
mise run db:sql -- "SELECT ..." # one-shot SQL vs babylon_testCI (.github/workflows/ci.yml) invokes the same mise tasks devs run (test:unit-ci, lint:check,
qa:regression, …) — the only raw-uv exceptions are the py3.13 forward-compat leg (weekly-py313.yml)
and a handful of documented one-offs (migrations, doc build, ad hoc pytest legs).
The repo's own flake.nix is the canonical toolchain (environment-sovereignty ruling
2026-07-21, ADR102 — the infra submodule is unmounted; babylon-infra remains a private
sibling repo for ops tooling only). .python-version pins the interpreter MINOR (3.12)
as the uv-facing source of truth — the flake owns the patch via the rev-pinned python312
(guard: tests/unit/cli/test_uv_migration.py::test_python_version_pin_consistency).
mise run install is frozen-sync (install-from-lock; re-resolution needs the
../hypergraph-rs sibling — see the [tool.uv.sources] comment in pyproject.toml). Two devshells: default — python 3.12 with sqlite
pinned 3.53.1 (the nixpkgs-data input, rev-pinned; lockstep with
tools/build_reference_db.py::PINNED_SQLITE_VERSION — the reference-DB byte-identity
contract, both halves same-repo now), uv, node, gdal/geos/proj, libpq, playwright
browsers — and dataBuild (the reference-DB builder env). Run pinned-toolchain commands
via mise run nix -- <cmd> (no submodule init needed, works in any fresh clone/worktree).
The host venv (3.46.1 sqlite) still runs everything EXCEPT the reference-DB builder, which
hard-fails off-pin by design. Bumping nixpkgs-data IS a declared sqlite-pin change.
Every devshell unsets PYTHONPATH (nixpkgs python setup hooks used to leak tool
site-packages onto it, shadowing the repo venv — the old env -u PYTHONPATH git litany
is retired for shells entered after 2026-07-21; guard: mise run check:env-contract).
Solo dev box (12 cores / 31 GB RAM). The 2026-07-12 freezes were root-caused and FIXED: BLAS
thread oversubscription (pinned to 1, commit f3dde939 — also a determinism win) stacked on the
claude-mem chroma-mcp leak (fixed upstream + locally 2026-07-13; earlyoom is now active as the
system backstop). Owner ruling 2026-07-14:
- Run heavy commands UNCAPPED.
mise run cap(tools/capped.sh) is retired from routine use — a process that eats memory is a code smell to catch loudly, not contain silently; earlyoom keeps the box alive. The cap wrapper still exists for deliberately-risky one-offs (memory bombs, untrusted repro scripts), nothing else. - Keep the BLAS=1 pin (conftest + mise
[env]+ guard testtests/unit/test_blas_thread_cap.py) — that one is correctness (deterministic FP reduction order), not just safety. - Still never fan out a Workflow where multiple agents each spawn pytest / the full suite —
test:unitis xdist with ~1 GB/worker coverage instrumentation; N agents × 4 workers stacks tens of GB for no benefit. Parallel agents are for read-only investigation and doc work; heavy test/build runs stay single-flight. Prefer scopedmise run test:q -- <path>locally. - If chroma-mcp servers accumulate again (
pgrep -fc -- '--client-type persistent'climbing):mise run mcp:reap. Neverpkill -f chroma-mcpby hand (matches its own cmdline, SIGTERMs your shell; the script uses achroma[-]mcpbracket guard). The recycle-loop root cause is fixed, so a climbing count is news — investigate, don't just reap.
WorldState.eventsis per-tick, not cumulative — a tick with no events is[], never carried over.- Graph round-trip loses data:
from_graph()excludes computed fields and defaults missing ones; adata.get("field", 0.0)fallback silently masks a missing-field bug. - Systems mutate the shared graph in-place in strict order — read
graph.nodes[id]["wealth"], not model attributes; each system sees prior systems' mutations. - MyPy misses Pydantic dynamic-attribute errors — runtime tests are essential.
dynamic_hex_stateis SPARSE (spec-089 delta persistence): readv_hex_state_asof, neverWHERE tick = Non the raw table;MAX(tick)≠ last committed tick (that'stick_commit).WorldStateis frozen — mutate viamodel_copy(update={...}), never assignment.- Inject dependencies explicitly, don't discover them at runtime.
- Never hand-stamp
_node_typewith a raw string — useNodeType.*(models/enums/topology.py), in fixtures too. A fixture stamping a type production never emits gives a green test over a dead feature:balkanization_factionvsfactionsilently disabled RED_SETTLER_TRAP, secession enumeration and FASCIST_RECRUITMENT. The same failure mode applies to node SHAPE, not just type: a fixture stamping an attribute a node's model doesn't declare (territory_idsonsocial_class— no such field; onlyOrganization/Institutionhave it) gives a green test over dead production code the same way.mise run check:vocabularyenforces BOTH — 3 rules (src/babylon/sentinels/vocabulary/): no invented type strings, every queried type has a producer, and every stamped attribute on a production-stamped node type is a real declared field or a citedEXTRA_STAMPABLE_ATTRIBUTESgraph-only write (registry docs the full exemption governance). Task #45 audit (2026-07-18) found 6 more live instances of the attribute-shape bug beyondterritory_ids— 3 fixed inweb/game/engine_bridge.py(flatagitation/factional_compositionreads where production nests them one level deeper), 3 left open as owner-gatedsrc/babylon/engine/defects (ATTRIBUTE_EXEMPTIONS' "Reason 2" rows name each). Worktree gotcha:uv run python tools/sentinel_check.py ...run directly (not viamise run) resolvesbabylonto whichever checkout's venv is active, not necessarily this worktree's — prefix withPYTHONPATH="$PWD/src"or use themise run check:vocabularytask, which sets it correctly. - Workflow
argsarrives stringified or absent — FOUR times bitten (2026-07-22 ×2, 2026-07-29, 2026-08-11). The orchestration harness can deliver a Workflow script'sargsas a JSON string (soargs.fieldisundefined, and prompts interpolate"the PDF at undefined"into every fanned-out agent), andresumeFromRunIdpasses no args at all (bareargs.xthrows before the cache engages). The memory file existed for bites 2–4 and was skipped at authoring time each time, so the rule is now positional: the guard is the FIRST code in every workflow script, written beforemeta's body is even filled in, with hard absolute fallbacks whenever values are knowable:Thelet A = {} try { A = typeof args === 'string' ? JSON.parse(args) : (args || {}) } catch {} const X = A.x || '/known/absolute/fallback' // degrade to correct, not to crash
try/catchcovers a third arrival shape (non-JSON string). It is right ONLY because hard fallbacks follow — when a value has no knowable fallback, drop the catch and shape-check loudly instead: a line-1 throw beats a runaway fan-out acting onundefined. Bites 1–2 dispatched agents per character of the stringified array toward the 1000-agent cap; bites 2 and 4 were saved only by a fanned-out agent refusing to act on an all-undefinedspec and messaging the controller — never count on that.
This file is yours. You have standing permission to modify CLAUDE.md — and to shape your own
operating instructions more broadly — whenever you see fit, without asking first. When you learn
something durable (a convention, a corrected fact, a gotcha you hit twice), fix it here and prune what
went stale; treat it as living config with a lifecycle, not a monument. Keep it lean and link out for
depth. AGENTS.md symlinks to this file, so one edit updates both.