Files
rune/docs/structural-contract.md
T

60 lines
3.6 KiB
Markdown

# 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
1. Prefer a built-in: `henxels catalogue` lists every statement (built-ins and
this repo's custom ones).
2. Add a henxel to `henxels.yaml`. Custom statements live in
`henxels_checks.py` (auto-loaded; a `.henxels/` folder of check modules also
works). Custom statements REQUIRE an `in:` scope — without one they never
fire.
3. Run `henxels check --all --plain` locally; fix findings cheaply or add an
explicit `except:` and a row to the debt table above.
4. `henxels sync` to refresh `AGENTS.md` and commit both together.
5. 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.