2064 — Praetor adoption: measured behaviour and the two exclusions¶
Measurements behind ADR-1249. The first adoption was measured on 2026-09-15 against praetor 5a671ab and origin/master at 78c9d2bfc. It was refreshed on 2026-09-18 against praetor e4b35cb and origin/master at 4becc4072; the refresh section records what changed. Every number here came from running the tool, not from reading its documentation.
How the adoption was proved before it was committed¶
A throwaway clone was built from origin/master plus the candidate governance files, then adopted for real. The first run surfaced three blocking defects in order, none of which are visible from a dry run:
- Discovery bound.
adoptaborted in 8 ms withverification discovery exceeds 4096 entriesand wrote nothing. The repository has 8,751 filesystem entries against a default 4,096-entry cap on the engine's verification walk. The engine now exposes--verification-max-entries(ceiling 200,000),--verification-max-depthand--verification-max-files, and the refresh ran with all three raised to their ceilings. - Lockfile format.
praetorctl auditfails immediately withlockfile digest must be sha256:<64 hex characters>: "". The 54-byte.standards.lockproduced by the earlier scaffolding attempt is a retired placeholder; a live adoption builds a digest-pinned lock plus a repository-local.config/archetypes/policy catalog. - Context budget.
context composition cannot produce valid projections: target file CLAUDE.md exceeds max line budget (565 > 300). The transpiler emits the fullAGENTS.mdbody into six vendor files under a hard 300-line-per-target budget with no configuration knob.
With all three addressed the first adoption completed at exit 0 and praetorctl audit passed every gate with 1,687 baselined infractions.
Refresh onto praetor e4b35cb (2026-09-18)¶
The branch was rebased over 170 master commits and regenerated with a scratch build of praetor e4b35cb, run in a clean clone so the working tree's build directories could not inflate the scan. Five things changed.
The engine now lints AGENTS.md¶
compile-context --verify and audit run the caveman lint over the whole canonical AGENTS.md, including this repository's own part below the praetor harness, with no opt-out (praetor ADR-0010). The prose version failed with 4.7 articles per 100 prose words against a limit of 2.0. adopt --force rewrote the harness in the internal register, and the repository's part was rewritten by hand. The gates for that rewrite, and their results:
| Check | Result |
|---|---|
praetorctl caveman check AGENTS.md | pass: 791 prose words, 0.3 articles per 100 |
praetorctl caveman floor <prose> AGENTS.md | pass: no lost code span, command, id, link, marker or directive |
markdownlint-cli2 AGENTS.md | 0 issues |
| Largest compiled target | .cursor/rules/hiss-invariants.mdc, 263 lines of 300 |
The rewrite also absorbed the content that only the hand-written CLAUDE.md carried (the source layout, the IDE note, make preflight, the banned-function list and the golden-data test files), because CLAUDE.md is now compiled from AGENTS.md and would otherwise have lost it. The hard rules that only CLAUDE.md carried (no benchmark output commits, the per-session ADR index check, the docs/state.md rule and the host-enforcement notes) moved to the hard-rules page for the same reason.
Persona projections are verified in both directions¶
The engine now rejects any file in .claude/agents/, .codex/agents/, .github/agents/ or .gemini/agents/ that has no source in .agents/agents/. The ten reviewer personas this repository keeps in .claude/agents/ had none, so each gained a byte-identical canonical copy in .agents/agents/, and compile-context projected it to the other three vendor directories. The .claude/agents/ files themselves are unchanged; editing one now means editing its .agents/agents/ source and recompiling.
Debt baseline¶
The rescan over the rebased tree records fewer infractions, because master removed debt in the meantime:
| Rule | 2026-09-15 | 2026-09-18 | What it counts |
|---|---|---|---|
| HISS-04 | 901 | 889 | functions above the effective length limit |
| HISS-01 | 583 | 538 | goto (non-DAG control flow) |
| HISS-07 | 74 | 74 | unchecked error assignments |
| HISS-02 | 66 | 65 | unbounded loops |
| HISS-09 | 48 | 48 | unaudited unsafe, eval/exec, banned libc |
| HISS-08 | 15 | 13 | banned sprintf / strcpy, dynamic execution |
| Total | 1,687 | 1,627 | across C, Python, Go, C++, Rust, CUDA, HIP |
The baseline exists so legacy code still builds while new code is gated; the audit's rule is that it may only ratchet down.
The hook gate no longer asks who installed the hooks¶
At e4b35cb, auditGitHooks requires lefthook.yml at the root and, outside CI, any file at .git/hooks/pre-commit. Under CI=true or GITHUB_ACTIONS=true it skips the local check entirely, so the CI job no longer installs lefthook.
Adoption defects the refresh hit¶
- Editor scan bound.
adoptaborts atsynthesize editors: editor language scan exceeds file bound.internal/editor/capabilities.gocaps the walk at 4,096 files and, unlike the verification walk, exposes no flag. The generation run declined theeditorsstep throughadoption.decline; that declaration was not committed, so the editor configurations from the first adoption stand. - Placeholder
ruff.toml. Theworking-dir-and-flavorstep wrote aruff.tomlcontaining only a comment, and the adoption report did not list it. Ruff readsruff.tomlin preference topyproject.toml, so committing it would silently discard this repository's[tool.ruff]configuration. It was not committed.praetorctl flavor audit .passes without it. - Generated markdown. The regenerated personas and
.paperclip/rules.mdfail this repository's markdownlint profile again (MD013, MD022, MD031, MD032). Their content changes (standardsctlrenamed topraetorctl, one new text-register rule) were applied to the lint-clean versions instead. - Harness spacing. The generated harness renders
Check first:`praetorctl caveman check AGENTS.md`without a space after the colon. It is left as generated because the harness is praetor's text.
The HISS-04 limit is reported three different ways¶
| Source | max_func_loc |
|---|---|
.config/archetypes/native-gpu-systems.yaml (the declared archetype) | 75 |
praetorctl audit effective policy | 60, via builtin:audit-compat-v1 |
praetorctl plan | 100, the generic DefaultPolicy |
The audit is the gate, so 60 is the number that binds today. Worth an upstream issue: plan should print what audit will enforce.
Why sync --remote is not used here¶
The engine hardcodes the protected branch name: ReconcileProtection(ctx, "main", …) in cmd/standardsctl/sync.go and refs/heads/main plus refs/heads/lts-* in internal/forge/ruleset.go. This fork's default branch is deliberately master (ADR-0002). There is no default_branch key in .standards.yaml to override it, so the generated .github/rulesets/main.json is kept as a local declaration and branch protection stays under the existing aggregator. The generated ruleset derives its required contexts from this repository's own workflows rather than a fixed list; at the refresh it selects build, Gate — check label / trigger, Required Checks Aggregator, Scorecard PR Gate and Standards & Invariant Verification Gate.
Licensing: measured, and why REUSE stays out of rc1¶
reuse lint over 8,256 files on 2026-09-15:
| Measure | Count |
|---|---|
| Files with copyright information | 2,064 |
| Files missing copyright information | 6,192 |
| Files missing licensing information | 6,876 |
Licence texts referenced but absent from LICENSES/ | 7 |
Invalid licence in LICENSES/ | 1 (Apache-2.0-u2netp) |
The identifier and licence-text findings have since been resolved on master, not here: the EUPL relicensing (ADR-1250) and the residual SPDX correction (ADR-1255) replaced every BSD-3-Clause-Plus-Patent and BSD+Patent tag and added the missing texts to LICENSES/, so this change no longer adds any. The copyright gap is untouched: annotating the remaining files also means classifying vendored third-party trees (core/src/mcp/3rdparty/cJSON, compat/python-vmaf/matlab/matlabPyrTools, model/), where a wrong blanket annotation is a licensing misstatement. That is its own reviewed change, so no REUSE gate is added before 1.0.0.
Generated markdown does not satisfy this repository's markdownlint¶
Praetor's generated artefacts trip the Markdown Lint gate on MD013, MD022, MD024, MD031 and MD032: the agent persona sources, the pre-migration epic document and .paperclip/rules.md. They are fixed in place, the personas at their .agents/agents/ source so every vendor projection inherits the fix. Every engine run reintroduces the violations, and the e4b35cb refresh did, so the real fix belongs upstream: praetor's templates should emit markdown that passes a standard profile.
Pre-existing drift found during the first adoption¶
docs/adr/by-tag/was out of sync with its generator, and the mkdocs ADR-nav block was missing roughly 107 ADRs. Both are now enforced onmasterby ADR-1242, which runs the generators' check modes inmake docs-fragments-check, pre-commit and Docs CI.