CRITICALLY
- English for all output. Other language only on explicit user request.
- Minimise thinking output shown to user.
Read
ai-docs/context.mdfor project purpose, entities, architecture, and design decisions — on demand.
Read
design-system/SKILL.md(manifest) anddesign-system/README.md(visual rules) on demand; exploredesign-system/preview/,design-system/colors_and_type.css, anddesign-system/ui_kits/widgets/as needed. Pointer-only — not auto-imported. Trigger conditions:
- When working on
quartzite-style(anyStyleimpl, includingDefaultStyle)- When working on
quartzite-widgetspaint paths, widget views, or any user-facing rendering- When changing
Palette/ColorRolesemantics or seeds- When adding or modifying snapshot tests under
quartzite-style/tests/snapshots/- When working on quartzite-paint-api painter primitives, brush, pen, path, font, or color
Machine-enforced rules live in .claude/settings.json (allow/deny entries) and on origin (branch protection). Read those files for the authoritative list — duplicating them here lets the two sources drift.
Honor-system rules (no machine check; still binding):
- DENY:
git push --forceto feature branches — prefer--force-with-lease, and only after explicit user approval. Force-pushing tomasteris server-blocked regardless. - DENY: files outside project root.
- ASK: any tool not allow-listed in
settings.json; if denied — suggest an alternative.
On session start: read .gitignore, treat matched paths as a read blacklist.
cargo build
cargo test # all tests
cargo test test_name # filter by substring
cargo test -- --nocapture # show stdout
cargo clippy --workspace --all-targets -- -D warnings # lint (strict; --all-targets covers benches, tests, examples in addition to lib+bins)
cargo fmt # fix formatting
cargo fmt -- --check # check only
RUSTDOCFLAGS="-D warnings -D missing-docs" cargo doc --no-deps --workspace --all-features # doc gate (matches CI; --all-features so intra-doc links into every feature-gated module — `serde`-gated `snapshot`, `style`, `widgets`, … — resolve regardless of which feature gates them)
cargo build -p quartzite --no-default-features --features libm # verify derive-free / no_std path compiles
actionlint .github/workflows/<file>.yml # required gate for any new/modified workflow fileSee
ai-docs/miri-policy.mdfor the per-test#[cfg_attr(miri, ignore = "…")]default + per-file#![cfg(not(miri))]fallback + workflow-level exclusion-list contract.
AXIOM —
actionlintMUST pass beforegit addon any modified workflow file. Required gate, same status ascargo buildandcargo clippy --workspace --all-targets -- -D warnings. Skipped twice despite the rule existing — escalated to AGENTS.md after the second occurrence.
If you see... Action M .github/workflows/<name>.ymlingit statusRun actionlint <file>(or pass every changed workflow file in one invocation) beforegit addactionlintreports any errorFix it. NEVER bypass. A NEW .github/workflows/*.ymltriggered onpush: branches: [master]only (nopull_request:sibling)The PR safety net does NOT exercise this workflow. Either add a pull_request: branches: [master]trigger so PR CI runs it, OR add a# Why master-only: <reason>comment above theon:block AND verify an existing PR workflow exercises an equivalent code path. Seeai-docs/learnings.md2026-05-13 master-only-trigger entry: 14 consecutive master pushes failedDocsbefore the gap was found.A commit message claims "all X" / "every Y" while touching a finite set Enumerate the set programmatically before commit (e.g. diff <(grep -l 'runs-on:.*ubuntu' .github/workflows/*.yml) <(grep -l '<token>' .github/workflows/*.yml)) and confirm the diff actually covers it.What
actionlintcatches thatcargocannot: runner-version mismatches, deprecated action versions, expression-syntax errors, shell-quoting issues.
AXIOM — Every project instruction file Claude loads per invocation MUST stay below 40,000 chars. Harness-enforced soft cap; crossing it imposes measurable per-invocation cost on every subagent spawn,
/task,/triage, and review pass. Project-side 35,000-char early warning gives one full/taskcycle of headroom before the harness warning starts firing. Applies toAGENTS.md,CLAUDE.md, every.claude/skills/**/*.md, every.claude/agents/**.md, every.claude/rules/*.md, andai-docs/{code-style,doc-convention,context,agent-writing-style,corrections-log}.md.
If wc -c <file>reports...Action ≥ 40,000 chars major— plan extraction / dedup for the next/ai-auditpass; same model PR #324 used for AGENTS.md (extract verbose subsections intoai-docs/<topic>.mdreference pages with anchored links from the source file).35,000–39,999 chars minor— proactive extraction pass; do not let the next/taskpush it over 40k.< 35,000 chars OK. Quick scan:
wc -c AGENTS.md CLAUDE.md .claude/skills/**/*.md .claude/agents/**.md .claude/rules/*.md ai-docs/{code-style,doc-convention,context,agent-writing-style,corrections-log}.md.
Search: ast-index first (see .claude/rules/ast-index.md); fall back to rg <pattern> --type rust [-l | -C 3] when ast-index returns empty.
AXIOM — Pre-publish: clean breaks. No compat shims. The project has not been published to crates.io and has no downstream clients. Public API may be freely renamed, removed, or restructured without backward-compat shims, deprecation layers, or
#[deprecated]wrappers.
If you're tempted to... Do this instead Add pub use OldName as NewName;"for compat"REMOVE the alias — make the clean rename Wrap removed fn with #[deprecated] pub fn old() -> _ { new() }DELETE the wrapper — call sites update directly Keep both old and new APIs side-by-side temporarily Pick one — old is gone
Revisit this rule before the first cargo publish.
See ai-docs/api-naming.md → The _unchecked AXIOM for the _unchecked AXIOM + naming rules.
- Source files: Rust-only (
.rs) undersrc/; max 100 cols (rustfmt default); format viacargo fmt, neverrustfmt <file>directly. Seeai-docs/code-style.md→ Source files. - Linter posture: strict clippy enforced (
-D warnings); no blanket#[allow]without justification; workspace-wide lint policy lives in rootCargo.toml[workspace.lints.rust]+[workspace.lints.rustdoc]+[workspace.lints.clippy](withclippy.tomlfor size-aware thresholds). Seeai-docs/code-style.md→ Linter posture. - Rust idioms: prefer Rust over literal ports; let chains valid (edition 2024); comparison helpers (
.min/.max/.clamp/Option::or/Option::filter) over explicitif/match; never cite GUI/UI frameworks (Qt, GTK, WinForms, SwiftUI, …) as design justification. Seeai-docs/code-style.md→ Rust idioms. - Magic numbers: numeric literals with semantic meaning → module-level
const SCREAMING_SNAKE_CASE, not inline. Self-evident constants (0,1,-1,2) and test fixtures exempt. Seeai-docs/code-style.md→ Magic numbers. - Library safety idioms:
parking_lot::Mutex/parking_lot::RwLockare workspace default (non-poisoning, infallible.lock()/.read()/.write()); safe primitives (OnceLock/Arc/Weak/AtomicBool) over raw pointers +unsafe..unwrap_or_else(|e| e.into_inner())survives only for rare FFI-imposedstd::sync::*Lockretainees (none in-tree). Seeai-docs/code-style.md→ Library safety idioms. - Documentation: workspace declares
missing_docs = "deny"+rustdoc::broken_intra_doc_links = "deny"+clippy::undocumented_unsafe_blocks = "deny"in[workspace.lints.*]; each crate opts in via[lints] workspace = true; every public item has at least one-line///;# Examplesblock on new public items with single-line docs; doc-style conventions live inai-docs/doc-convention.md. Seeai-docs/code-style.md→ Documentation. - Error types:
thiserrorfor new error enum/struct; hand-rolledDisplay/Errorreserved for cases the derive cannot express. Seeai-docs/code-style.md→ Error types. - Enum repr:
#[repr(...)]on enums is REQUIRED only forenumflags2::bitflags(#[repr(uN)]) and external numeric specs (e.g.u16forFontWeight); decorative#[repr]MUST NOT be added. Seeai-docs/code-style.md→ Enum repr. - Tracing:
*_span!guard wrapping the body of any function that meaningfully mutates application state;debug_span!for lifecycle,trace_span!for supplementary; high-frequency paths gated behindverbose-tracingcargo feature. Seeai-docs/code-style.md→ Tracing. #[inline]and the_Simple._doc tag: mark every recursively-simple fn (no branches/loops, ≤ 1 non-simple call) with the marker matching its shape —#[inline](concrete fn or method insideimpl Trait for ConcreteFoo— concrete-impl trait method needs#[inline]for cross-crate inlining without LTO;// _Simple._is not a substitute),/// _Simple._(generic free fn / inherent generic method / trait method declaration whose every conforming impl is required to be simple),// _Simple._(method insideimpl<T> Trait for Foo<T>— avoids overriding trait-inherited rustdoc); strip + cascade re-test of callers when an edit makes a previously-simple fn non-simple. Seeai-docs/code-style.md→#[inline]and the_Simple._doc tag.- Generic-fn split for binary size: public fn with conversion-style generic param (
impl Into<T>/impl AsRef<T>/impl ToString) and > 3 line body extracts the body into a nestedfn inner(...)(NOT a sibling<outer>_innerimpl method); outer carries_Simple._; if the inner ends up simple, unwrap it —#[inline]simple inner is dead weight. Seeai-docs/code-style.md→ Generic-fn split for binary size. - File size: target 200–400 lines per
.rsfile excluding#[cfg(test)]; soft 500/800; hard 1000/1500 (refactor before merge unless exempt — auto-generated, single state machine /match,macro_rules!); per-fnclippy::too_many_lines(>100); counter-rule against over-splitting (one-struct-per-file is not Rust idiom). Seeai-docs/code-style.md→ File size.
See ai-docs/code-style.md for the canonical reference.
AXIOM — Query live state BEFORE asserting any claim about an external dep or the project's own dep graph. Memory is stale. Three dimensions of "I remember X is the case" have each landed wrong claims in this repo:
- Version of an external dep —
criterion = 0.5when live is0.8;actions/deploy-pages@v4when live is@v5.- Behaviour of a third-party Action — "the action sets
RUSTC_WRAPPERby default" whensrc/setup.tsshows it does not (PR #179 sccache).- Presence of a dep in the current project — "would add
parking_lotas a new dep" whencargo tree --invert parking_lotshows it's already there (issue #440).See
ai-docs/dependency-versions.mdfor the per-dimension lookup recipes. Apply the pinning rule (below) to the observed version, never the remembered one.
If you're about to write... Verify first with A specific version of crate Xcurl -sS "https://crates.io/api/v1/crates/X" | jq -r '.crate.max_stable_version'A claim that Action Xsets / exports / defaults toYRead action.yml+src/setup.ts/src/main.tsper the recipeA claim that Xis / isn't / would-be-added-as a dep in this projectgrep -r '<X>' --include='Cargo.toml' .ANDcargo tree --invert <X>(the latter catches transitive presence)If your draft contains substrings like "would add", "introduce X as a dep", "pull in X", "avoid X as a dep", "X is not currently a dependency" — STOP, run the grep + cargo-tree check, and either rewrite with the actual trade-off (perf / feature-gate / test-prod parity / binary-size) or drop the claim.
When adding or editing dependencies in Cargo.toml:
- Use
0.xfor0.x.yversions — never pin the patch. - Use
xforx.y.zversions — never pin minor or patch. - No
~prefix — Cargo's default^semantics are sufficient. - After changing version constraints, run
cargo updateto pull latest compatible versions, thencargo buildto verify.
AXIOM 1 — NEVER edit on local
masterwhen work is intended for a PR. Create a feature branch (git checkout -b feat/...orchore/...) before any file edit — not before commit, before edit. Accumulating uncommitted edits onmasterleaves the tree dirty on the wrong branch and forces a reactive switch later.
If git branch --show-currentreturns...Action masterAND you're about to make a PR-targeted editSTOP. Run git checkout -b <prefix>/<descriptive-name>first. Only then edit.A feature branch Proceed with edits masterAND you've already made commits (recovery)git stash→git checkout -b <feature>→git checkout master && git reset --soft origin/master && git restore --staged .→ push feature branch → open PR. Pop stash on feature branch if needed.The first action of any skill/workflow that produces commits (
/task,/improve,/ai-audit, etc.) isgit branch --show-current; ifmaster, switch before anyEdit/Write. Before anygit push, confirm again — if it ismaster, stop and apply recovery.
- Merge PRs via merge commit (
gh pr merge --merge); never squash/rebase-merge. → § Merge strategy - Run
cargo buildbefore commit soCargo.lockrefreshes. → § Cargo.lock refresh before commit - Stage explicitly; Never
git add -A/.. → § Explicit-file staging - Before every
git commitduring a PR task, stageai-docs/learnings.mdwith related code. After every push, give a post-push learning entry its own commit. → § Staging learnings.md during PR commits - Never
git commit --no-verify(or any hook-skip flag) — fix the hook. → § No --no-verify - NEVER batch a
git commit/ data-dependentAskUserQuestionin the same turn as theEdit/subagent call producing its inputs; verify withgit diff --cached --statfirst. → § Dependent tool calls must not be batched - CI-fix commits get self-review too. Spawn
self-reviewbefore pushing any CI-fix commit. → § CI-fix commit self-review (parent rule) - No "too simple" step-skip in
/task. Steps 6 / 7 / 10 are MANDATORY; user authorisation is the only bypass. → § "Too simple" step-skip rule (parent rule) - NEVER
git reset --hard— discards uncommitted work. → § Recovery from destructive-git-commands - Plan first. Tests before prod code (TDD). Lint changed files. → § TDD + lint-changed-files
- Files with ~50+ lines of substantial logic MUST have a
#[cfg(test)] mod testsblock (exceptions:examples/,benches/withharness = false). → § #[cfg(test)] requirement for substantial logic .gitignore(not.arcignore).- After generating/moving a markdown file with relative links, trace one link via
realpathbefore committing. → § Markdown link tracing after generate/move - PR review comment resolution: Resolve only comments fixed by code; objections stay open for the reviewer. → § PR review comment resolution
- Auto-generated files in merge conflicts.
ROADMAP.mdis derived fromai-docs/plans/INDEX.md+ai-docs/plans/done/*viascripts/gen-roadmap.sh. On merge conflict:git checkout --theirs <file> && bash scripts/gen-roadmap.sh && git add <file>. NEVER hand-resolve the markdown table — the script re-derives from source and hand-edits silently drift. → § Auto-generated files in merge conflicts
AXIOM 2 — Read the PR body via
gh pr view <N>after EVERYgit pushto a feature branch with an open PR. Unconditional. The READ is mandatory even when the push was a routine typo / format / nit. The EDIT is conditional — only when the body contradicts the new commits.
After... Required action git pushto a feature branch with an open PRRun gh pr view <N> --json title,bodyimmediately. Read the body.The body still describes the diff accurately No gh pr editneeded — read completeThe body contradicts the new commits (renames, scope drift, AC flips, cited counts that drifted) Run gh pr editto syncgh pr createimmediately preceded the push (i.e., this is the first push that opened the PR)Skip the read — the body is what you just authored. The rule fires on the next push. See
ai-docs/workflow.md→ PR body vs. tracking-issue body for the issue-vs-PR-body distinction.
AXIOM — Every code-producing commit on a feature branch with an open PR (or about-to-be-opened PR) must pass
self-reviewbeforegit push. The per-skill rules already exist (/taskStep 10,/pr-commentedStep 5,/pr-ci-failedStep 5,/master-ci-failedStep 5,/bugfixStep 6). This AXIOM names them as instances of a single workspace rule, so the next surface that doesn't yet have its own per-skill step still falls under the rule.
If the commit is... Action Initial implementation in /task/taskStep 10 — spawnself-reviewReviewer-comment fix in /pr-commented/pr-commentedStep 5 — spawnself-reviewCI-failure fix in /pr-ci-failed//master-ci-failedper-skill self-reviewstepBugfix in /bugfix(standalone or detoured from/task)/bugfixStep 6 — spawnself-reviewAd-hoc / out-of-skill fix on a feature branch with an open PR Spawn self-reviewmanually overgit diff <merge-base>..HEADbeforegit pushDocs-only / instruction-file-only commit (no .rsdiff)Self-review optional; still required if the diff touches any user-facing artefact APPROVE = push. REJECT = fix on the same branch and re-run; after 3 REJECTs in a row, surface and stop without pushing.
AXIOM —
ai-docs/deferred/_inbox.jsonlis written ONLY by/taskStep 12 and/triage. Hand-edits to_inbox.jsonldefeat the propagation contract that Issue A2 sets up — they hide rows from the parser and conflict with future Step-12 appends; the JSONL line-per-object format is hand-edit-hostile by design (one malformed line breaks the wholejqread).
If you see... Action A row in _inbox.jsonlyou want to move to a thematic fileRun /triage; let it sort the rowA row in _inbox.jsonlyou want to dropRun /triage; mark "drop" during the drain stepA row missing from _inbox.jsonlfor a freshly-merged specRe-run /taskStep 12 manually (or wait for the next merged spec to trigger it)An entry whose source-spec section shape was unrecognised by the parser Step 12 emits a warning; resolve by reformatting the source spec OR by adding the shape to the parser's allow-list (Issue A2 design phase)
AXIOM — Edits to one instruction file MUST propagate to its sync-group siblings in the SAME PR. The Propagation Rule fires whenever you edit an instruction file. Sister files in the same sync group must receive the corresponding change before the PR is opened.
If you edit... You MUST also check / update... .claude/skills/project-review/SKILL.md.claude/agents/review-findings.mdAND.claude/agents/self-review.md(Review group).claude/agents/review-findings.md.claude/skills/project-review/SKILL.mdAND.claude/agents/self-review.md(Review group).claude/agents/self-review.md.claude/skills/project-review/SKILL.mdAND.claude/agents/review-findings.md(Review group).claude/skills/interview/SKILL.md.claude/agents/spec-writer.md(Interview group — Rule-5 substring blacklist mirrors live inspec-writer.md).claude/agents/spec-writer.md.claude/skills/interview/SKILL.md(Interview group — orchestrator-side validation may need to update if the contract shifts).claude/skills/triage/SKILL.md.claude/agents/triage-runner.mdAND.claude/skills/next/SKILL.md(Triage group).claude/agents/triage-runner.md.claude/skills/triage/SKILL.mdAND.claude/skills/next/SKILL.md(Triage group).claude/skills/next/SKILL.md.claude/skills/triage/SKILL.mdAND.claude/agents/triage-runner.md(Triage group)AGENTS.md(rule add / exemption)Run grep -rn "<changed-keyword>" .claude/agents/ .claude/skills/ .claude/rules/ AGENTS.md ai-docs/agent-writing-style.mdand apply the same change to every match (new pre-resolved rules also add a Rule-5 substring-blacklist entry in.claude/agents/spec-writer.md).AGENTS.md"Learning Log" section (Boundary rules 1 / 2, entry format incl.Kind:,Escalated?semantics, 🌱 verdict from/ai-audit).claude/agents/self-improve.mdAND.claude/agents/learnings-escalation-audit.md(Learning-Log group).claude/skills/task/SKILL.md(Steps 6–8 design phase contract).claude/agents/design.mdAND.claude/agents/design-review.mdAND.claude/skills/context-reset/SKILL.md(Task/Design group — artefact format, verdict format incl. GO-with-notes, Step 8's/context-resethandoff contract, and context-reset's trigger /allowed-tools/ write-contract all co-evolve).claude/agents/design.mdOR.claude/agents/design-review.mdOR.claude/skills/context-reset/SKILL.mdSee Task/Design group anchor row above ( .claude/skills/task/SKILL.md)..claude/skills/task/SKILL.mdStep 7 / Step 11 Spec Amendment recipe + Design Amendment recipe.claude/skills/pr-commented/SKILL.mdAND.claude/skills/pr-ci-failed/SKILL.mdAND.claude/skills/master-ci-failed/SKILL.mdAND.claude/agents/self-review.md(Spec-Amendment group — detection trigger + recurrence record inai-docs/workflow.md§ Spec-Amendment group)quartzite-widgets/tests/support/mod.rsquartzite-style/tests/support/mod.rs(Snapshot-helper group)quartzite-style/tests/support/mod.rsquartzite-widgets/tests/support/mod.rs(Snapshot-helper group)ai-docs/agent-writing-style.md(new fail-loud pattern entry under## Patterns)See ai-docs/agent-writing-style.md§ Propagation rule for new patterns.ai-docs/skill-size-exemptions.md.claude/skills/ai-audit/reference.md(Checklist K item 1 anchor + citedwc -lnumbers MUST stay synchronised) (Size-exemption-index group).claude/rules/<file>.md(e.g..claude/rules/ast-index.md)Run the same grep — the Procedure below catches lingering references. Rule files are read on-demand by agents, so a cross-rule-file edit MUST sweep every instruction directory for sister references. Any edit that changes a Tool / Subagent / Skill / Hook contract OR renames a stable anchor in claude-tools-hierarchy.mdUpdate ai-docs/claude-tools-hierarchy.mdin the same PR (contract changes) AND every inbound deep-link to renamed anchors (anchor renames).Any other instruction file Run the same grep — the Procedure (below) catches lingering references
Procedure:
- Before closing the edit,
grep -rn "<changed-keyword>" .claude/agents/ .claude/skills/ .claude/rules/ AGENTS.md ai-docs/agent-writing-style.mdfor any file that references the same rule, exemption, or terminology. - Apply the same change (or the corresponding enforcement adjustment) in every match.
- AGENTS.md rule exemptions especially must propagate to subagent checklists that enforce the rule (
self-review.md,review-findings.md).
AXIOM — Project-defined Tool / Subagent / Skill / Hook names MUST NOT clash with embedded names in
ai-docs/claude-tools-hierarchy.md§§1a/1b/2a/3a/3b. On clash, the project name is renamed; the embedded name is never renamed.
If you... Action Add a new project-defined name Grep the embedded inventory FIRST; pick non-clashing. Detect a clash AFTER adding Rename project side same PR; update every inbound ref. New embedded name now clashes with existing project name Queue project rename as separate task; /ai-auditChecklist O flags it.
Do not refer to a skill as an "agent" or vice versa — the distinction matters for spawning. (project-review is a skill; review-findings and self-review are agents spawned by it.)
Interpret user phrasing literally and conservatively. When uncertain — ask, don't guess.
- "Submit / push to PR" =
git pushthe branch to remote so commits appear in the open PR. NOTgh pr merge. Only merge when the user explicitly says "merge" or "merge the PR". - "wtf?" / "what?" / "huh?" (or similar surprise/frustration) = the previous action was the opposite of what the user wanted. Stop immediately, do not retry, ask what was wrong before doing anything else.
- IDE files (
.idea/,*.iml,.vscode/,*.swp, etc.) — never add, remove, modify, stage, or.gitignorethem unless the user explicitly asks. They are the user's domain. "add ide files" most likely means commit and track them, not gitignore them — confirm before acting.
| Path | Purpose |
|---|---|
ai-docs/context.md |
Project context — read on demand |
ai-docs/code-style.md |
Workspace code-style reference — read on demand |
ai-docs/workflow.md |
Extracted § Workflow narrative (PR-review-comment recipe) |
ai-docs/triage-runner-bridge.md |
Extracted Phase 4.5 bridge Action semantics block from .claude/agents/triage-runner.md (verbatim per-conflict-type action recipe). Read on demand. |
ai-docs/triage-runner-design-links.md |
Extracted Phase 8 Design-link outcomes sub-section shape from .claude/agents/triage-runner.md (per-row outcomes, per-umbrella body-edit summary, two distinct fallback sub-lists, /next propagation-grep result line). Read on demand. |
ai-docs/triage-runner-umbrella-bodyedit.md |
Extracted Phase 6.5 / Phase 7 numbered-pick sub-step 4 (umbrella body auto-edit, sub-steps a–e: gh issue view → anchor-scan → gh issue edit --body-file) from .claude/agents/triage-runner.md. Read on demand. |
ai-docs/corrections-log.md |
Extracted § Learning Log carve-outs + field glossary |
ai-docs/key-decisions.md |
Key Design Decisions detail bodies from context.md |
ai-docs/plans-summary.md |
Maintenance-plans (cross-cutting) detail bodies from context.md |
ai-docs/dependency-versions.md |
Live Cargo / GitHub Action version lookup + behaviour recipes |
ai-docs/agent-writing-style.md |
Binary-rule writing style for dual-model readability |
ai-docs/agent-docs-index.md |
Verbose bodies of § Agent Docs rows. Read on demand. |
ai-docs/api-naming.md |
_unchecked AXIOM + naming rules. Read on demand. |
ai-docs/instruction-file-validation.md |
Dual-model instruction-file-clarity test methodology + bias taxonomy + subagent prompt templates. Read on demand. |
ai-docs/skill-size-exemptions.md |
Audited list of .claude/skills/*/SKILL.md files exempted from the 200-line soft target; consumed by /ai-audit Checklist K item 1. |
ai-docs/templates/ |
Shared templates consumed by multiple skills / agents |
ai-docs/templates/progress-format.md |
Canonical .progress.md format spec (template + lifecycle) |
ai-docs/plans/INDEX.md |
Plan index — statuses and dependency order |
ai-docs/plans/*.spec.md |
Active task spec + acceptance criteria |
ai-docs/plans/*.design.md |
Active task design documents |
ai-docs/plans/*.progress.md |
Active task progress / handoff state — local-only (gitignored) |
ai-docs/pr-comments/pr-<N>.progress.md |
Fallback progress file for /pr-commented on non-/task PRs (gitignored) |
ai-docs/triage/triage-YYYY-MM-DD.progress.md |
/triage resume state for multi-turn runs (gitignored) |
ai-docs/plans/done/ |
Completed plans (spec + design, implemented) |
ai-docs/plans/deferred/ |
Blocked or future plans |
ai-docs/deferred/_inbox.jsonl |
triage queue — rows from completed specs awaiting /triage |
ai-docs/bugfix/trace-*.md |
Bugfix trace + durable-state surface — deleted on resolution |
ai-docs/learnings.md |
Corrections log — feed for /improve |
.claude/agents/spec-writer.md |
Spec-writer subagent — drafts task spec one round per call |
.claude/skills/triage/SKILL.md + .claude/agents/triage-runner.md |
/triage skill — batched promotion of deferred rows to gh issues |
.claude/skills/pr-commented/SKILL.md |
/pr-commented skill — one round of reviewer-comment response |
.claude/skills/pr-ci-failed/SKILL.md |
/pr-ci-failed skill — one round of CI-failure response on PR |
.claude/skills/master-ci-failed/SKILL.md |
/master-ci-failed skill — one round of post-merge red-master fix |
.claude/skills/dependabot-pr/SKILL.md |
/dependabot-pr skill — one round of Dependabot cargo-PR triage (matrix routing → /pr-ci-failed delegation OR @dependabot comment OR bail-with-issue OR confirm-merge pause) |
.claude/skills/ui-design/SKILL.md |
/ui-design skill — pointer to design-system/ (Read manifest + visual rules on demand) |
.claude/rules/ast-index.md |
On-demand code-search rules — ast-index mandatory-search + read-outline rules, plus the verbatim block subagents inherit (see also § Build & Test Search line). |
See ai-docs/agent-docs-index.md → Agent doc rows for the verbose body of each row (writers, lifecycle, special cases).
On ANY instruction violation, of any kind, write a new entry to ai-docs/learnings.md — there is no "obvious", "minor", "trivial", "already-known", or "duplicate" disposition. The history (including recurrences and superseded entries) is the artefact /improve audits to decide escalation fan-out. See ai-docs/corrections-log.md → FORBIDDEN reasoning for skipping a learnings.md write for the enumerated list of skip-reasons that have been used in violation of this rule and are therefore explicitly disallowed. Read the two boundary rules below before you write — both have been violated multiple times.
NEVER edit, rewrite, reorder, summarise, or delete an existing entry in
ai-docs/learnings.md. Only append new entries at the end of the file. This applies even when:
- a newer correction supersedes an older one — write a NEW entry that says so, leave the old one intact
- an entry turns out to be wrong, redundant, or poorly worded — write a NEW entry that corrects it
- you are tempted to "tidy up" or "consolidate" the file
The history of corrections (including superseded and wrong ones) is itself the artefact
/improveaudits. Editing past entries destroys that history.Exception —
Escalated?andSuperseded by:fields, subagent-driven only. Both fields MAY be updated in-place by theself-improveSubagent (/improve) and thelearnings-escalation-auditSubagent (/ai-auditPhase 1). Seeai-docs/corrections-log.md→ Boundary rule 1 Exception for the per-Subagent contract. All other lines of an entry remain immutable.One-off carve-out — 2026-05-19 compaction-recovery-protocol entry. Retro-tagged
**Kind:** validationvia PR #492 Phase 1 withSuperseded by:line as audit trail. Named, narrow, NOT a precedent. Schema migrations require their own named carve-out. Seeai-docs/corrections-log.md→ Boundary rule 1 Exception.
When you write to
ai-docs/learnings.md, you MUST NOT also edit any of these files in the same conversation turn:
AGENTS.mdCLAUDE.md.claude/skills/**(any file).claude/agents/**(any file).claude/settings.jsonai-docs/code-style.mdai-docs/doc-convention.mdWriting a learning entry is NOT authorisation to escalate the rule into instruction files. Set
Escalated? noand stop. Project-level escalation happens only when:
- The user runs
/improve(which spawns theself-improveSubagent), OR- The user explicitly asks ("escalate this", "update AGENTS.md", "add to skill X").
The Propagation Rule fires only when you are already editing an instruction file for an independent reason — it does not authorise pre-emptive escalation triggered by a fresh
learnings.mdentry. The same applies in reverse: if the user corrects a behaviour and asks you to record it, write tolearnings.mdonly — do not also "fix"AGENTS.mdorcode-style.mdin the same turn.Exception —
/improveand/ai-auditworkflows.self-improve(via/improve) +learnings-escalation-audit(via/ai-auditPhase 1) MAY updateEscalated?/Superseded by:on existing entries alongside instruction-file edits. Existing-entry updates ONLY — NEW learning entries STILL cannot be appended in the same turn as instruction-file edits (Rule 2's main protection stays intact). Seeai-docs/corrections-log.md→ Boundary rule 2 Exception.Exception — in-flow learning capture during
/taskSteps 8–12. A NEW learning entry MAY be appended in the same turn as an instruction-file edit when ALL hold: (a) running skill is/taskSteps 8–12 (incl. sub-skills/bugfix,/context-reset); (b) entry documents an in-task insight (not pre-emptive escalation); (c) markedEscalated? no. Seeai-docs/corrections-log.md→ Boundary rule 2 Exception for the full body.
### YYYY-MM-DD — [category] — [short description]
**What happened:** [quote or paraphrase]
**Rule:** [what to do instead, or what to keep doing]
**Kind:** correction | validation (optional; defaults to `correction` when omitted)
**Escalated?** no | AGENTS.md | skill:[name] | hook | settings | agent:[name] | rules:[name] | doc-convention | code-style (comma-separate multiple)
**Superseded by:** [ref] — [one-line reason] (optional; omitted when not applicable)
Kind: defaults to correction when omitted — existing entries need NO rewrite. Write Kind: validation for entries that document a working protocol / pattern the subagent should keep doing (carrot signal); write Kind: correction (or omit) for entries that document a violation to stop doing (stick signal).
See ai-docs/corrections-log.md → Entry format — field glossary for the semantics of each field (Kind: values, Escalated? values, doc-convention vs code-style, Superseded by: reference format).
Categories: code-style | process | architecture | testing | documentation | tooling | search | other
Run /improve when ≥3 unescalated correction entries, ≥2 unescalated validation entries, or a 🌱 Stale-validation flag from /ai-audit accumulates.
- Unit tests in same file under
#[cfg(test)]module. - Integration tests in
tests/directory. - Use
rstestfor parameterized tests when useful. mockallfor mocking traits.- Assert with
assert_eq!/assert_matches!;pretty_assertionscrate encouraged for diffs.assert_matches!formats the scrutinee with{:?}on mismatch, so its type MUST implDebug(Resultneeds bothT+E;Box<dyn Trait>needs aDebugsupertrait) —assert!(matches!(...))imposes no such bound. If the scrutinee is non-Debug, leaveassert!(matches!(...))as-is; do NOT add a production#[derive(Debug)]to satisfy a test-only assertion. Countingassert!(matches!)sites for a migration: the multi-line message form is invisible to single-linerg 'assert!\(matches!'— userg -U. - Test names as
snake_casedescribing behavior:returns_empty_when_not_found. - No
unwrap()in production code without justifying comment;expect("reason")preferred. - No
#[allow(clippy::...)]/#[allow(dead_code)]unless unavoidable. - Test behavior, transitions, errors, edge cases.