ids-unique | Two entities share an id. Almost always from a parallel-branch merge. | aiwf reallocate <path> on the loser. |
ids-unique/trunk-collision | An id allocated on this branch is also allocated on the configured trunk ref (default refs/remotes/origin/main) at a different path — i.e. two different entities now share it across branches. The cross-tree variant of ids-unique. | aiwf reallocate <path> on whichever side hasn't reached trunk yet. The pre-push hook surfaces this before the colliding push lands. |
frontmatter-shape | Required field missing or malformed. | Add the field; check the kind's id format. |
status-valid | Status is not in the kind's allowed set. | Pick a status from the kind's set (see aiwf-promote). |
priority-valid | The priority field's value is outside the closed set (urgent, high, medium, low). | Correct the value by hand and re-run aiwf check. |
refs-resolve/unresolved | A reference points at an id that does not exist. | Either the target was never created, or the id is mistyped. |
refs-resolve/unresolved-milestone | The composite-id reference's milestone half (M-NNN/AC-N) names a milestone that does not exist. | Fix the milestone id or create the milestone. |
refs-resolve/unresolved-ac | The composite-id reference's AC half (M-NNN/AC-N) names an AC that does not exist on the milestone. | Fix the AC number or add the missing AC. |
refs-resolve/wrong-kind | A reference points at an entity of the wrong kind. | A milestone's parent must be an epic; an ADR's supersedes must be ADRs; etc. |
body-prose-id/malformed-shape | An entity's body prose contains an id-shaped token whose suffix isn't a valid id (letter suffix M-a, uppercase placeholder M-NNNN, or narrow-numeric M-1). | Replace with the canonical allocated id (M-0001), or wrap in backticks if the prose is discussing id syntax. Conversational sequential labels like M-1/M-2 belong in chat, not committed prose. |
body-prose-id/unresolved | An entity's body prose references a well-formed id (M-9999) that resolves to no entity. | Fix the spelling, or wrap in backticks if the prose is discussing a hypothetical id rather than a real reference. |
body-prose-id/unresolved-milestone | A composite id in body prose (M-NNNN/AC-N) names a milestone that does not exist. | Fix the milestone id or remove the reference. |
body-prose-id/unresolved-ac | A composite id in body prose names an AC that does not exist on the parent milestone. | Fix the AC number or add the AC. |
no-cycles | A cycle in the milestone depends_on DAG or the ADR supersedes chain. | Remove a back-edge. |
no-cycles/depends_on | The cycle is in milestone depends_on edges. | Break a back-edge in the milestone DAG. |
depends-on-cancelled | A non-terminal milestone's depends_on names a milestone that has since reached the negative-terminal status cancelled — the dependency can never be satisfied. | Retarget the dependency via aiwf milestone depends-on <milestone-id> --on <remaining-ids> (or --clear to empty it), or cancel the dependent milestone too. |
no-cycles/supersedes | The cycle is in ADR supersedes edges. | Break the chain — an ADR cannot transitively supersede itself. |
case-paths | Two entity paths differ only in case. Linux commits both; macOS / Windows case-insensitive filesystems collapse them to one entity. | git mv one of the directories so the names differ in more than case. |
load-error | A file under work/ failed to parse — malformed YAML frontmatter, unreadable file, or a structural issue the loader couldn't recover from. | Open the named file and fix the parse issue; subsequent checks run once load succeeds. |
contract-config | A contract binding in aiwf.yaml references an id with no entity, a missing schema/fixtures path, or a contract entity has no binding. | Run aiwf contract bind / aiwf add contract, fix the path, or aiwf contract unbind. |
contract-config/missing-entity | The binding's id: points at a contract entity that doesn't exist. | Either create the contract entity or remove the stale binding. |
contract-config/missing-schema | The binding's schema: path doesn't exist on disk. | Fix the path or create the schema file. |
contract-config/missing-fixtures | The binding's fixtures: directory doesn't exist on disk. | Fix the path or create the fixtures tree. |
contract-config/no-binding | A contract entity exists but no binding in aiwf.yaml references its id. | aiwf contract bind <id> --validator <name> --schema <path> --fixtures <path>. |
fixture-rejected | A valid/ fixture failed the schema. | Make the schema accept it, or move it to invalid/. |
fixture-accepted | An invalid/ fixture passed the schema. | Tighten the schema, or move to valid/. |
evolution-regression | A historical valid/ fixture fails the HEAD schema. | Revert the schema change, migrate the fixture, or rebind. |
validator-error | Every valid fixture for a contract was rejected — the schema or validator invocation is likely broken. | Inspect the captured stderr and fix the schema or validator command. |
environment | Validator binary not on PATH. | Install it (see the recipe's install instructions) or fix command: in aiwf.yaml. |
acs-shape/id | An AC's id doesn't match AC-N or doesn't follow the per-milestone 1..max ordering. | Fix the id in the milestone's acs[] list. |
acs-shape/title | An AC's title is missing or whitespace-only. | Fill in the title. |
acs-shape/status | An AC's status is not in {open, met, deferred, cancelled}. | Use one of the four statuses (deferred is a live terminal AC state). |
acs-shape/tdd-phase | An AC's tdd_phase is set on a milestone that is not tdd: required, OR it's set to a value not in {red, green, refactor, done}. | Either set the milestone to tdd: required, remove the field from the AC, or fix the phase value. |
acs-shape/tdd-policy | An AC at tdd_phase: done is in a milestone that is not tdd: required. | Either flip the milestone to tdd: required or remove the tdd_phase field. |
acs-body-coherence/missing-heading | The frontmatter acs[] lists an AC, but the body has no ### AC-N — <title> heading for it. | Run aiwf add ac (which scaffolds the heading), or hand-edit the body to add it. |
acs-body-coherence/orphan-heading | The body has an ### AC-N — ... heading but the frontmatter acs[] list does not include AC-N. | Either remove the heading or add the missing AC to acs[]. |
acs-body-coherence/duplicate-heading | The ## Acceptance criteria section repeats a ### AC-N heading for the same id. A duplicate of an id that is also in frontmatter is neither missing nor orphan, so it would otherwise pass clean. Scoped to the AC section, so the ## Work log convention (which repeats ### AC-N — <outcome> headings) is not flagged. | Delete the extra heading; keep exactly one ### AC-N per AC in the section. aiwf add ac now rewrites a placeholder heading in place rather than appending a second one. |
acs-empty-body | A non-archived milestone is in_progress or done and a non-cancelled AC's body under its ### AC-N heading carries no non-heading prose — a title-only stub is not a real contract for that criterion. Unlike the pre-existing entity-body-empty warning (which the terminal-status lifecycle gate silences at done), this rule stays live through done since that is exactly one of its two in-scope statuses. An AC with no ### AC-N heading at all is acs-body-coherence/missing-heading's concern instead. | Write real prose under the AC's ### AC-N heading via aiwf edit-body <milestone-id>. |
archived-entity-not-terminal | A file lives under a per-kind archive/ subdirectory but its frontmatter status is not terminal — i.e., a contributor hand-edited the status off-terminal after the entity was swept (per the archive convention §"Reversal"). The remediation is to revert the hand-edit, not to relocate the file — the kernel does not provide a reverse-archive verb; the canonical pattern when a closed entity needs revisiting is to file a new entity that references the archived one. | Restore the status to a terminal value, or file a new entity that resolves/supersedes the archived one. |
epic-terminal-non-terminal-children | An epic's frontmatter status is terminal (done/cancelled) while it still owns one or more non-terminal child milestones. aiwf promote/aiwf cancel already refuse to move an epic to a terminal status while a child milestone is non-terminal, so a genuine bypass of those guards (a hand-edit, a pre-guard binary) is one way to reach this. The other: aiwf add milestone / aiwf import creating a fresh milestone under an epic that was already terminal — that path has no dedicated guard yet, so this finding stands in for one. Fires regardless of whether the epic's file has been swept into archive/. | Bring each listed child milestone to a terminal status via aiwf promote <milestone-id> done or aiwf cancel <milestone-id> — the epic itself needs no action, since it is already terminal. |
acs-tdd-audit | On a tdd: required milestone, an AC is status: met but its tdd_phase is not done — met without a completed red→green→done cycle. Severity is error under tdd: required and warning under tdd: advisory; the audit does not run under tdd: none. | Drive the AC through its phases (aiwf promote M-NNNN/AC-N --phase …) to done before met, or set the milestone's tdd: policy to match the discipline that actually applies. |
milestone-done-incomplete-acs | A milestone is status: done but one or more of its ACs are still open (not met / deferred / cancelled). | Resolve every AC to a terminal state before wrap; or aiwf promote M-NNNN done --force --reason "…" to override (the standing check keeps surfacing it). |
milestone-done-zero-acs | A non-archived milestone is status: done with an empty acs: list — advisory, not a refusal; a permanently AC-less milestone is a legitimate end state. | Add an AC via aiwf add ac M-NNNN --title "…" if this was unintentional; otherwise no action needed. |
milestone-draft-incomplete-acs | A non-archived draft milestone carries an incomplete AC contract: subcode zero-acs when acs: is empty, or subcode empty-body when it has ACs but one carries no non-heading prose under its ### AC-N heading (the draft-rung, warning-severity mirror of acs-empty-body, which fires error one FSM stage later at in_progress/done). A warning, not a refusal: draft is a legitimate mid-planning state, so this surfaces the missing-contract gap without blocking, keeping a milestone from landing on main with no visible ACs. An AC with no ### AC-N heading at all is acs-body-coherence/missing-heading's concern instead. | Add the ACs at plan time via aiwf add ac M-NNNN --title "…" (zero-acs) or fill the AC body via aiwf edit-body M-NNNN (empty-body); no action needed if the milestone is intentionally still being scoped. |
milestone-cancelled-incomplete-acs | A milestone is status: cancelled but one or more of its ACs are still open. aiwf promote/aiwf cancel both refuse this transition through normal use, with no --force override — so this state means the verb layer was bypassed entirely (a hand-edit, a pre-fix binary). | Resolve every open AC to a terminal state (met / deferred / cancelled). |
id-path-consistent | An entity's frontmatter id disagrees with the id encoded in its on-disk filename/slug. | Renumber via aiwf reallocate <path> (rewrites both sides + references), aiwf rename if only the slug drifted, or hand-correct whichever side is wrong. |
body-prose-id | An entity's body prose contains an id-shaped token whose suffix isn't a valid allocated id (letter suffix, uppercase NNNN placeholder, or narrow-numeric). Surfaces per subcode (e.g. body-prose-id/malformed-shape). | Replace the token with the canonical allocated id, or wrap it in backticks if the prose is discussing id syntax rather than referencing a real entity. |
skill-body-id | A shipped consumer surface cites a real entity id — every *.md under internal/skills/embedded{,-rituals,-guidance}/** (skill bodies AND description: frontmatter, entity templates, role-agent cards, the guidance fragment) plus the statusline's # comments. The mirror image of body-prose-id (there a real id is required; here it is the defect). Inert in a consumer repo, where the source tree is absent. | Replace the real id with a canonical <prefix>-NNNN placeholder, or move the reference into a design/ADR doc-link (the one carve-out). |
git-config-core-worktree-misset | The repo's core.worktree git config points somewhere unexpected, which can misdirect kernel git operations. | Run git config --local --unset core.worktree from the repo root (keep an override only if your workflow — e.g. a bare repo — specifically requires it). |