4.9 KiB
The contract (henxels)
Auto-generated from henxels.yaml by henxels sync. Do not edit by hand.
Each bullet is a henxel (a rule). To disobey one, change henxels.yaml —
that is the only sanctioned escape. Run henxels explain <path> before creating
a file to see what governs that spot.
Only use git commit --no-verify in a genuine emergency: it bypasses the hooks that run this contract, the safety mechanism meant to protect the repository. Prefer henxels bless <action> or editing henxels.yaml — both keep the deviation visible.
Rules
- Canaries live at the repo's canary slot and are registered in CI (in ./src/rune/**/canary_*.py)
- No secrets in tracked files
- Plans are markdown, one per build (in ./plans)
- Top-level docs are markdown or yaml references (in ./docs/*)
- The known giant stays visible — file-size debt is tracked, not silent (in ./src/rune/*)
- The umbrella impact map is complete and truthful — every module mapped, no phantom entries (in ./config/umbrellas.json)
- Umbrella canaries exist in the repo — the propagation gate cannot reference ghosts (in ./config/umbrellas.json)
- The umbrella dependency graph stays a DAG — reflexive updates cannot cycle (in ./config/umbrellas.json)
- The bug-fix discharge contract is a governed doc — it exists, is markdown, and changes are deliberate (in ./docs/bugfix-contract.md)
- The discharge checker ships with the repo — red-first enforcement is structural (in ./scripts)
- Discharge checker and its CI wiring change together
- Every workflow is registered in the pipeline registry — new pipeline machinery ships with documentation (in ./.gitea/workflows/*.yml)
- Every TUI surface is registered — no silent UI growth (in ./src/rune/tui.py)
- New rune modules ship with tests or a canary — no ungoverned code surfaces (in ./src/rune/*.py)
- PRDs are living documents — their generated state sections regenerate identically from governed sources
- PRD staleness is a CI failure — docs rot is silent failure (in ./scripts/gen_prd_state.py)
- The adversarial review procedure is binding — dispatch templates must reference it (in ./docs/adversarial-review.md)
- The fixture pipeline ships with the repo — production failure capture is structural (in ./scripts)
- Production failure fixtures are a governed surface — the registry and its README exist (in ./tests/fixtures/production)
- Fixture payloads and the manifest travel together — tamper-evidence needs both
- The fixture manifest and its generated regression tests travel together
Behaviours
- warns when a new file looks like a near-copy of a committed one
Custom henxels & contributing
Before writing a custom check, run henxels catalogue and use the built-in that
matches your intent — don't reinvent one. Never name a custom check after a built-in
or a setting (e.g. warn_about_large_files is a setting, not a check).
Need a check that genuinely doesn't exist? henxels create-new-statement <name> scaffolds a local check
(auto-loaded from henxels_checks.py). If your check is reusable — useful in
other repos, not tied to this one — contribute it upstream with henxels contribute.
We're in the agentic era: send a ready-to-merge PR instead of opening an issue.
Bug fixes (binding)
Every bug found — by gates, CI, canaries, production, or humans — is fixed per
docs/bugfix-contract.md: red test FIRST (reproduces the cause), root-cause
fix, full pipeline re-entry (suite + ratchet + ruff + canaries + henxels;
gates for anything beyond a mechanical minor), regression tests frozen forever,
discharge recorded finding → test → commit. Symptom patches are rejected at
review. To escape: edit henxels.yaml or this file — visibly.
Issue organisation standard (binding, 2026-09-19)
One open note per idea. Issues use the template spine (kind, subsystem, version intent) with a plain-English heading that restates the subject — no meta-labels. Before minting a standalone issue, check whether the work belongs inside an existing parent:
- Parked ideas under a blocked/future epic are filed as checklist items in the parent's "Absorbed children" section, NOT as standalone issues. Absorption is reversible — re-mint standalone when the idea becomes live.
- Live, dependency-tracked, or starting work gets a standalone issue.
- Closures state the reason as data:
closed_becausein the spine (completed / superseded-by-#N / absorbed-into-#N / wont-do / duplicate), plus a standard closing comment (plain summary first). - The reconciliation checker (
scripts/gitea_reconciliation_check.py) must stay green after any issue state change; the artifact is updated in the same change that moves issues.
Async continuation final flow proofs (R13/M9) — 2026-09-19
PR-flow live proof marker: harmless documentation addition exercising the canonical single-continuation path on the PC-local forge. No code change.