3.6 KiB
Structural Contract — henxels Adoption (#138)
The repo's shape is machine-checkable. henxels.yaml (repo root) holds the
contract; henxels sync renders it into the AGENTS.md digest agents read;
henxels check --all --plain enforces it (exit 0 = green, 1 = red) and runs as
a CI lint-job step.
What the contract governs
| Henxel | Scope | Rule |
|---|---|---|
| Canaries registered in CI | ./src/rune/canary_*.py |
Custom statement ci_runs_all_canaries (in henxels_checks.py): every canary on disk must appear in .gitea/workflows/ci.yml |
| No secrets | whole repo | no_secrets — no credentials in tracked files |
| Plans | ./plans |
markdown only, kebab-case filenames |
| Docs | ./docs/* |
.md/.yaml only, except docs/v1/** and docs/wiki/** |
| File-size debt | ./src/rune |
max_lines: 1200, except the documented debt list below |
Warnings (non-blocking): near-duplicate file detection (the shared/ vs
src/rune/ copies and backups/ snapshots are pre-existing debt the warning
correctly keeps visible).
Debt table
Known violations, explicitly excepted in henxels.yaml instead of silently
ignored. Each entry lists the finding that forced the exception.
| File / spot | Rule | Finding | Disposition |
|---|---|---|---|
src/rune/rune_tui.py |
max_lines 1200 | 4231 lines (drafted exception) | except-listed; split is future work |
src/rune/rune_operator.py |
max_lines 1200 | 1265 lines (drafted exception) | except-listed; split is future work |
src/rune/local_tool_runtime.py |
max_lines 1200 | 506 lines — drafted as pre-emptive headroom, not currently over budget | except-listed (as drafted) |
src/rune/memory_store.py |
max_lines 1200 | 2171 lines (live finding) | except-listed; split is future work |
src/rune/curator.py |
max_lines 1200 | 1597 lines (live finding) | except-listed; split is future work |
src/rune/tool_executor.py |
max_lines 1200 | 1523 lines (live finding) | except-listed; split is future work |
src/rune/warm_router.py |
max_lines 1200 | 1292 lines (live finding) | except-listed; split is future work |
tests/test_gitea_provision.py |
no_secrets | flagged as hardcoded credential — actually synthetic fixture tokens (plaintext-transport-test-secret etc.) whose purpose is asserting tokens never leak |
except-listed (false positive) |
src/rune/canary_native.py |
canary gate | tui_validator's import allowlist drifted from rune_tui.py (from __future__ import annotations, bare import rune) — 2 false-failures; validator fix is issue #139, not here |
canary tolerates exactly those 2 messages (prints ⚠ known false-failure, #139, does not count them); any other TUI error still fails the canary |
How to add a rule
- Prefer a built-in:
henxels cataloguelists every statement (built-ins and this repo's custom ones). - Add a henxel to
henxels.yaml. Custom statements live inhenxels_checks.py(auto-loaded; a.henxels/folder of check modules also works). Custom statements REQUIRE anin:scope — without one they never fire. - Run
henxels check --all --plainlocally; fix findings cheaply or add an explicitexcept:and a row to the debt table above. henxels syncto refreshAGENTS.mdand commit both together.- The contract must be green at birth: never merge a rule that fails on master.
Why no git hooks
Subagents work in throwaway worktrees (/mnt/workdrive/rune and siblings) and
commit there; a pre-commit hook would either miss those commits or block the
agent mid-flight. Enforcement is CI-only: the lint job runs
henxels check --all --plain on every push/PR, so the gate is the merge, not
the commit.