Prerequisites
Blocked until #9448 (unified check manifest) merges. The annotation scheme below points rules at manifest check ids; without the manifest there is nothing stable to point at. No other blockers.
Problem
AGENTS.md is the single source of truth for agent/contributor rules, and it has grown to ~994 lines across three files (root AGENTS.md ~366, backend/AGENTS.md ~239, desktop/macos/AGENTS.md ~389). The files mix three very different kinds of content with no visible distinction:
- Enforced rules — backed by a CI check or ratchet (e.g. brand UI, import purity, async blockers). Violating them fails CI regardless of whether anyone reads the doc.
- Advisory rules — real guidance with no mechanical backstop (e.g. "prefer
def over async def for sync-only endpoints").
- Descriptions — service maps, setup commands, environment notes.
The failure mode is documented in the file's own maintenance section: "enforced rules don't drift; requested behavior does." Unenforced prose accumulates, readers (human and AI agents) can't tell which lines are load-bearing, and every added line dilutes compliance with the rest. There is currently no forcing function that keeps the doc honest about which rules are actually enforced.
Prescribed change (one PR)
1. Annotation scheme
Every rule bullet (not descriptions, not setup commands) in the three AGENTS.md files gets a trailing annotation:
[enforced: <check-id>] — where <check-id> is an id in .github/checks-manifest.yaml, or for rules enforced outside the manifest (component test suites, release workflow guards), a repo-relative script path.
[advisory] — consciously unenforced; kept because the guidance is valuable and not mechanically checkable.
Keep annotations visually light — end-of-line, short. Do not annotate the service map, directory listings, or command reference sections; the meta-lint (below) scopes to rule sections only, identified by an explicit HTML comment marker pair (<!-- rules --> / <!-- /rules -->) that this PR inserts around rule sections.
2. Meta-lint
Add .github/scripts/check_agents_md_annotations.py (stdlib only, unit-tested like the existing test_check_brand_ui.py pattern, registered in the check manifest, lanes: [local, ci]):
- Inside marked rule sections: every top-level bullet must carry
[enforced: ...] or [advisory]. Missing annotation = failure naming the file/line.
- Every
[enforced: <check-id>] must resolve: manifest id exists, or referenced script path exists on disk. Dangling reference = failure.
- Reverse direction: every check id in the manifest (excluding its documented
exempt: block) must be referenced by at least one [enforced:] annotation somewhere in the three AGENTS.md files — a check nobody documents is undiscoverable; either document it or exempt it with a reason.
3. The triage itself (the real work)
Walk all three files and classify every rule. Decision rubric, applied mechanically:
- Backed by an existing check →
[enforced: id].
- Not backed, but a check would be cheap (< ~1 hour, pattern already exists as a ratchet/scan) → write the check in this PR and mark enforced. Expected candidates: several backend async/import bullets duplicate what
scan_async_blockers.py / scan_import_time_side_effects.py already catch — verify and annotate rather than build new.
- Not backed, check not cheap, guidance still valuable →
[advisory].
- Not backed, and the rule restates what the code/another doc already shows, or nobody can say what violating it looks like → delete the line. Deletions are the point, not a side effect: the PR description must report lines before/after per file.
4. Documentation
Add one paragraph to the root AGENTS.md maintenance section: new rules must carry an annotation; the meta-lint enforces it; prefer [enforced:] — an [advisory] rule that keeps being violated should be either promoted to a check or deleted.
Acceptance criteria
Out of scope
- Rewriting rule wording beyond what annotation requires.
- Component AGENTS.md files other than the three named (extend later once the pattern proves out).
- Building expensive new checks (anything over the ~1 hour rubric gets
[advisory] plus, at the implementer's discretion, a follow-up issue).
Prerequisites
Blocked until #9448 (unified check manifest) merges. The annotation scheme below points rules at manifest check ids; without the manifest there is nothing stable to point at. No other blockers.
Problem
AGENTS.mdis the single source of truth for agent/contributor rules, and it has grown to ~994 lines across three files (rootAGENTS.md~366,backend/AGENTS.md~239,desktop/macos/AGENTS.md~389). The files mix three very different kinds of content with no visible distinction:defoverasync deffor sync-only endpoints").The failure mode is documented in the file's own maintenance section: "enforced rules don't drift; requested behavior does." Unenforced prose accumulates, readers (human and AI agents) can't tell which lines are load-bearing, and every added line dilutes compliance with the rest. There is currently no forcing function that keeps the doc honest about which rules are actually enforced.
Prescribed change (one PR)
1. Annotation scheme
Every rule bullet (not descriptions, not setup commands) in the three AGENTS.md files gets a trailing annotation:
[enforced: <check-id>]— where<check-id>is an id in.github/checks-manifest.yaml, or for rules enforced outside the manifest (component test suites, release workflow guards), a repo-relative script path.[advisory]— consciously unenforced; kept because the guidance is valuable and not mechanically checkable.Keep annotations visually light — end-of-line, short. Do not annotate the service map, directory listings, or command reference sections; the meta-lint (below) scopes to rule sections only, identified by an explicit HTML comment marker pair (
<!-- rules -->/<!-- /rules -->) that this PR inserts around rule sections.2. Meta-lint
Add
.github/scripts/check_agents_md_annotations.py(stdlib only, unit-tested like the existingtest_check_brand_ui.pypattern, registered in the check manifest,lanes: [local, ci]):[enforced: ...]or[advisory]. Missing annotation = failure naming the file/line.[enforced: <check-id>]must resolve: manifest id exists, or referenced script path exists on disk. Dangling reference = failure.exempt:block) must be referenced by at least one[enforced:]annotation somewhere in the three AGENTS.md files — a check nobody documents is undiscoverable; either document it or exempt it with a reason.3. The triage itself (the real work)
Walk all three files and classify every rule. Decision rubric, applied mechanically:
[enforced: id].scan_async_blockers.py/scan_import_time_side_effects.pyalready catch — verify and annotate rather than build new.[advisory].4. Documentation
Add one paragraph to the root
AGENTS.mdmaintenance section: new rules must carry an annotation; the meta-lint enforces it; prefer[enforced:]— an[advisory]rule that keeps being violated should be either promoted to a check or deleted.Acceptance criteria
[enforced:]reference fails the meta-lint (unit test demonstrates).Out of scope
[advisory]plus, at the implementer's discretion, a follow-up issue).