aria-commands · git:20260819.3d2b633 · 2026-08-19 · sha256 d33bdeabbdadfbf4
aria-commands git:20260819.3d2b633B
Immutable. This exact content is served forever at /api/v1/blob/d33bdeabbdadfbf4.
---
description: "ARIA workflow commands — /extract, /index, /backlog, /stats, /ask, /intake, /meeting-notes, /digest, /sync-decisions, /wrapup, /codemap, /distill, /stitch, /handoff, /prospect, /retrospect, /foundational-review, /readiness-audit, /interview, /recap, /auto, /roadmap, /preflight, /snapshot, /setup, /help, /audit-share. Use when the user invokes any of these slash commands or their natural-language equivalents."
globs: ["knowledge/**/*", "CODEMAP.md", "STITCH-*.md"]
alwaysApply: false
---
# ARIA — Commands
This file ports ARIA skill instructions for the **Cursor** port. Triggers are natural-language (e.g., "extract session knowledge", "map the codebase", "wrap up session") in addition to slash-command names. Skill aliases: `/share-audit` → `/audit-share`, `/knowledge-audit` → `/audit-knowledge`, `/config-audit` → `/audit-config`.
**Cursor port notes:** Config lives at `.cursor/aria-knowledge.local.md` (per-repo). Rule 22 uses the edit-intent marker (`scripts/aria/record-edit-intent.sh`) — see `AGENTS.md`. Connect MCP servers in **Cursor Settings → MCP** for `/intake thread`, `/intake extract` (~~docs MCP), `/meeting-notes`, `/digest`, and `/sync-decisions`. Retired `/clip`, `/clip-thread`, `/extract-doc` are folded into `/intake`. ADR-094 dual-port Runtime Gates are **not** used in Cursor (no aria-cowork collision in typical Cursor sessions). `/statusline` and `/aria-assist` are Claude Code-only and are not compiled here. `/auto` has Cursor-runtime limits (no CronCreate, no statusline, no `auto-runloop.sh` — see the `/auto` section).
---
---
## /extract
# /extract — Pre-Compaction Knowledge Extraction
Scan the current conversation since the last extraction for uncaptured insights, decisions, feedback, project context, and references. Dump everything to backlogs for review at the next knowledge audit. No confirmation dialog — just scan, deduplicate, and append.
## Step 0: Resolve Config and Detect Project Context
Read `.cursor/aria-knowledge.local.md` and extract:
- `knowledge_folder` — required
- `projects_enabled` — default `false`
- `projects_list` — default empty (only relevant if `projects_enabled: true`)
If the config file doesn't exist, stop: "aria-knowledge is not configured. Run /setup to get started."
Use `{knowledge_folder}` as the base path for all file operations in subsequent steps.
### Detect current project (only if `projects_enabled: true`)
Determine the current working directory and check if it matches a configured project path:
1. Get the current working directory (typically the user's primary working directory, e.g., `~/Projects/path/to/proj-a`).
2. Parse `projects_list` into `tag:path` pairs.
3. For each pair, check if the CWD contains the configured path as a substring. If so, set `current_project` to that tag and stop iterating (first match wins).
4. If no path-based match is found AND `projects_remotes` is configured AND git is available, fall back to git-remote matching: run `git config --get remote.origin.url` from the CWD; for each `tag:url-pattern` pair in `projects_remotes`, check if the remote URL contains the pattern; if so, set `current_project` to that tag.
5. If still no match, leave `current_project` unset — subsequent steps will skip auto-tagging.
This logic mirrors the `kt_project_for_path` shell helper in `scripts/aria/config.sh`. Skills can either invoke that helper via Bash or replicate the matching logic in markdown-driven flow as above.
Examples:
- CWD = `~/Projects/myproject/sub-module/file.md`, `projects_list: myproject:myproject,other:other` → `current_project = myproject` (substring match on `myproject`)
- CWD = `~/Projects/other`, `projects_list: myproject:myproject,other:other` → `current_project = other`
- CWD = `~/Downloads/scratch-folder`, `projects_list: myproject:myproject,other:other` → `current_project` unset (no configured path matches)
## Step 1: Determine Extraction Scope
Check if a previous extraction happened this session by looking for a timestamp marker. If this is the first extraction of the session, scan the entire conversation. If a previous extraction occurred, scan only from that point forward.
The timestamp is tracked as the last entry date in the backlogs from this session — check the most recent entry dates in:
- `{knowledge_folder}/intake/insights-backlog.md`
- `{knowledge_folder}/intake/decisions-backlog.md`
- `{knowledge_folder}/intake/extraction-backlog.md`
- `{knowledge_folder}/intake/ideas/` — use the `YYYY-MM-DD` prefix of the most recent `*.md` file (via `ls -1 intake/ideas/*.md | sort -r | head -1`)
If no entries exist from today's date, treat the entire conversation as unscanned.
## Step 2: Scan Conversation for Uncaptured Knowledge
Review the conversation and categorize findings into six buckets. The first five (insights, decisions, feedback, project context, references) capture **observations about what IS** — they promote to knowledge during audit. The sixth bucket (ideas) captures **proposals about what SHOULD BE different** — these route via the audit's Accept submenu (tracker / roadmap / todo / adr / backlog / bundle / rule) rather than promoting directly into knowledge files.
### Insights
- Insight blocks that were output but NOT yet appended to `insights-backlog.md` (per-task capture may have already appended some — Step 3 dedup handles this)
- Non-obvious technical observations discussed in conversation
- Patterns discovered during debugging or exploration
- Codebase behaviors that surprised either party
### Decisions
- Architectural or design choices made during the session
- Technology or approach selections with rationale
- Cross-project decisions that set precedents
- Scope decisions (what was included/excluded and why)
### Feedback
- Corrections from the user ("don't do X", "that's wrong", "not like that")
- Confirmed approaches ("yes exactly", "perfect", accepting an unusual choice)
- Workflow preferences expressed during the session
- Communication style preferences
### Project Context
- Status updates about what's in-flight or blocked
- Who is working on what and by when
- Sprint or milestone context
- Dependency or integration information
### References
- External URLs, tools, dashboards, or services mentioned
- Linear projects, Slack channels, or other system pointers
- Documentation locations discovered during the session
### Ideas (proposals, not observations)
- Feature proposals for any project ("this should support X", "X could be better if Y")
- Bug reports noticed in passing ("X silently fails when Y", "this UX is broken in case Z")
- Design ideas or refactoring proposals not yet scoped for implementation
- Workflow improvements ("it would help if the tool did X")
- **Classification signal:** phrases like "should", "could be", "missing handling for", "UX gap", "would help if", "this is broken" typically indicate an idea rather than an observation
- **Soft routing:** classification is a suggestion, not a hard rule. An item can legitimately be both observation and proposal — if so, put the observation in its appropriate bucket (insights/decisions/etc.) AND a separate file in `intake/ideas/` covering just the proposal. The audit step can refine routing if needed.
## Step 2.5: Sweep Subagent Captures
Scan `{knowledge_folder}/intake/subagent-captures/` for **all** pending `.md` captures (transcripts archived by the `SubagentStop` hook from heavyweight subagents). If the directory is absent or empty, skip silently to Step 3.
> **Why sweep all, not just this session's:** a skill does not receive the runtime `session_id`, so `/extract` cannot reliably match captures to "the current session" by their `{parent-session-8}` filename prefix (`save-transcript.sh` documents this same limitation). The prefix stays useful for provenance/audit, but it is not a skill-side filter. Sweeping all pending captures is safe — they are sticky and governed regardless of origin, and each is ledger-cleared once folded in, so nothing is double-processed.
For each capture, digest it for cheap review:
```
bash scripts/aria/digest-transcript.sh "{capture_path}" "/tmp/aria-digest-{filename}"
```
Fold any findings from the digest into the SAME six buckets from Step 2 (insights, decisions, feedback, project context, references, ideas). They then flow through Step 3 (dedup) and Step 4 (append) with the conversation's own findings.
**Ledger-clear after Step 4:** once a capture's findings have been appended to a backlog, create `{knowledge_folder}/archive/extract-{date}/subagent-captures/` if needed, append an entry to its `REMOVED.md` (filename + parent-session-id + agent_type + agent_id), then `rm` the capture `.md`. Leave any captures you did not process (no extractable content, or skipped) for `/audit-knowledge`.
## Step 3: Deduplicate
For each finding, check against:
1. Existing entries in `{knowledge_folder}/intake/insights-backlog.md`
2. Existing entries in `{knowledge_folder}/intake/decisions-backlog.md`
3. Existing entries in `{knowledge_folder}/intake/extraction-backlog.md`
4. Existing files in `{knowledge_folder}/intake/ideas/*.md` (glob the directory; read frontmatter + body of each to compare)
5. AGENTS.md files in the current working directory (root and project-level)
6. Memory files in `~/.claude/projects/` for the current project
7. Knowledge files in `{knowledge_folder}/`
**Skip anything already captured.** Be conservative — if the content is substantively the same even with different wording, skip it.
**If any deduplication source cannot be read** (missing file, permissions error), note which source was skipped and include it in the Step 5 report: "Deduplication incomplete — could not read [file]. Some entries may be duplicates."
## Step 4: Append to Backlogs
Route each finding to the appropriate backlog file. Do NOT ask for confirmation — just append.
### Project tag auto-prepending
If `current_project` was set in Step 0:
- For findings that don't already have a project attribution, use `current_project` as the `[project]` value in the entry header.
- For findings that already have an explicit project attribution that conflicts (e.g., user said "this is a cross-project pattern" while CWD is `path/to/proj-a`), preserve the explicit attribution — don't override it.
- The auto-tag is a default, not a forced override. The audit process will refine it during promotion.
If `current_project` is unset, use the existing rules: tag with the project (or "cross") when identifiable from conversation context; otherwise omit `[project]` from the header (use `[no-project]` or just the context label).
Examples:
- CWD inside proj-a, finding doesn't mention a project → `### 2026-04-15 — proj-a — feedback — [context]`
- CWD inside proj-a, finding explicitly says "this is cross-project" → `### 2026-04-15 — cross — decision — [context]`
- CWD outside any configured project, finding mentions df → `### 2026-04-15 — df — insight — [context]`
- CWD outside any configured project, finding has no clear project → `### 2026-04-15 — [no-project] — reference — [context]`
### Insights → `{knowledge_folder}/intake/insights-backlog.md`
Use existing format:
```markdown
### YYYY-MM-DD — [project] — [task context]
- Insight bullet 1
- Insight bullet 2
```
### Decisions → `{knowledge_folder}/intake/decisions-backlog.md`
Use existing format:
```markdown
### YYYY-MM-DD — [project(s)] — [decision context]
**Decision:** What was decided
**Why:** Rationale
**Alternatives considered:** What else was evaluated
```
### Feedback, Project Context, References → `{knowledge_folder}/intake/extraction-backlog.md`
Use this format:
```markdown
### YYYY-MM-DD — [type: feedback|project|reference] — [context]
**Content:** What was captured
**Source:** Where in the conversation this came from (brief description)
```
### Ideas → `{knowledge_folder}/intake/ideas/{YYYY-MM-DD}-{project}-{slug}.md` (one file per idea)
Ideas use **per-file storage**, not a single append-only backlog. Write one new markdown file per idea under `intake/ideas/`.
**Filename pattern:**
```
{YYYY-MM-DD}-{project}-{slug}.md
```
- `YYYY-MM-DD` — today's date (from the conversation's current date, not the OS clock; convert relative dates per Rules)
- `{project}` — the project tag from Step 0's `current_project`, or an explicit project attribution from the finding, or `cross` for cross-project, or `no-project` if unattributed
- `{slug}` — kebab-cased short title derived from the idea: lowercase, alphanumerics + hyphens only, truncated to ~60 chars, strip trailing hyphens
- **On collision** (same date + project + slug already exists): append `-2`, `-3`, etc. to the slug until unique. Check via `ls intake/ideas/` before writing.
Examples:
- `2026-04-21-aria-force-interactive-index-steps.md`
- `2026-04-21-proj-a-extract-shared-postcard.md`
- `2026-04-21-cross-generalize-build-playbook.md`
**File format (YAML frontmatter + body):**
```markdown
---
date: YYYY-MM-DD
project: project-tag-or-cross
type: feature | bug | design | refactor | workflow
title: Short title matching the filename slug
---
**Proposal:** What change is being proposed.
**Motivation:** Why it would help (what gap or friction it addresses).
**Source:** Where in the conversation it came up (brief description).
```
Ideas do NOT promote to knowledge files directly — during audit review the user picks a destination from the Accept submenu: external tracker (Linear, GitHub Issues, Jira, etc.), project `ROADMAP.md` or `TODO.md` (when present), the decisions backlog (for ADR review), a dated entry in `IDEAS-BACKLOG.md`, a bundled merge of related ideas, or the rules backlog (for working-rule review).
### Before writing:
- For the four single-file backlogs (insights, decisions, extraction, rules): remove any "(No pending ...)" placeholder, then append new entries below existing ones with a blank line separator.
- For ideas (per-file): write a new file per the filename pattern above; there is no placeholder to remove.
- **If a single-file backlog is missing:** do not create it from scratch. Stop and tell the user: "Backlog file [name] is missing. Run /setup to repair the knowledge folder structure."
- **If the `intake/ideas/` directory is missing:** do not create it. Stop and tell the user: "Ideas directory `intake/ideas/` is missing. Run /setup to repair the knowledge folder structure."
- **Legacy-file detection (one-time):** if `{knowledge_folder}/intake/ideas-backlog.md` exists alongside `intake/ideas/`, surface a one-line note in Step 5's report: "Legacy `ideas-backlog.md` detected — run `/setup` or `bash scripts/aria/migrate-ideas-backlog.sh` to migrate pre-2.11 entries." Do not attempt the migration from within `/extract`.
## Step 5: Report
After appending, output a brief summary:
```
## Extraction Complete
- **Insights:** N new (appended to insights-backlog.md)
- **Decisions:** N new (appended to decisions-backlog.md)
- **Feedback:** N new (appended to extraction-backlog.md)
- **Project context:** N new (appended to extraction-backlog.md)
- **References:** N new (appended to extraction-backlog.md)
- **Ideas:** N new (written to intake/ideas/ — one file per idea; routed at audit time to tracker / roadmap / todo / adr / backlog / bundle / rule)
- **Skipped:** N duplicates
Knowledge staged in backlogs for next audit to review and promote. Ideas staged for the Accept submenu — pick destination per idea at next `/audit-knowledge`.
```
If nothing was found:
```
## Extraction Complete
No uncaptured knowledge found — everything from this session is already persisted.
```
## Rules
- **Never ask for confirmation** — scan and dump. The audit process handles review and promotion.
- **Be thorough but not noisy** — capture genuinely useful knowledge, not every minor exchange. "User asked to read a file" is not knowledge. "User explained that the auth middleware rewrite is driven by legal compliance" IS knowledge.
- **Convert relative dates** — "last Thursday" becomes the actual date (YYYY-MM-DD)
- **Project attribution** — always tag with the project (or "cross") when identifiable
- **Don't duplicate CLAUDE.md content** — if it's already a rule or convention in any CLAUDE.md, skip it
- **Feedback is high-value** — corrections and confirmed approaches are the most actionable extraction type. Capture the correction AND the reason if one was given.
- **Keep entries concise** — each backlog entry should be self-contained but brief. The audit process adds depth when promoting.
- **One extraction per natural breakpoint** — don't run multiple times for the same conversation segment
---
## /index
# /index — Knowledge Index Builder
Scan all promoted knowledge files, normalize tags, detect issues, and regenerate `{knowledge_folder}/index.md`.
## Step 0: Resolve Config
Read `.cursor/aria-knowledge.local.md` and extract:
- `knowledge_folder` — base path for all operations
- `freeform_promotion_threshold` — minimum file count before suggesting promotion (default: 3)
- `staleness_threshold_months` — months before a file is flagged stale (default: 6)
- `projects_enabled` — default `false`; controls whether project tier is scanned and indexed
- `projects_list` — default empty; comma-separated `tag:path` pairs; only relevant if `projects_enabled: true`
- `projects_promotion_threshold` — default `2`; minimum projects sharing a similar pattern to surface as a cross-project promotion candidate
- `projects_shared_knowledge` — default empty; comma-separated list of project tags enabled for shared knowledge. When non-empty, scan each listed project's `_project-knowledge/` folder for team-shared knowledge. Tags not in this list are skipped (their `_project-knowledge/` folders, if any exist on disk, are NOT indexed).
If the config file doesn't exist, stop: "aria-knowledge is not configured. Run /setup to get started."
## Step 1: Scan Promoted Folders
Scan these directories for `.md` files (excluding directory README stubs that only contain a few lines of boilerplate):
- `{knowledge_folder}/approaches/`
- `{knowledge_folder}/decisions/`
- `{knowledge_folder}/guides/` (recursive — includes subdirectories)
- `{knowledge_folder}/references/` (recursive — includes `sources/` and other subdirectories)
**Do NOT scan:** `archive/`, `intake/`, `rules/`, top-level `logs/*.md` (audit-log files like `config-audit-log.md`, `knowledge-audit-log.md`, `hook-debug.log`), or root-level files (`README.md`, `LOCAL.md`, `OVERVIEW.md`, `index.md`).
**Carve-out for review reports:** the two subfolders `logs/prospect/` and `logs/retrospect/` ARE scanned as a separate "reviews tier" — see the dedicated sub-step below. Review reports use the same frontmatter scanning convention as cross-project files (tags, Last updated, first heading).
For each `.md` file found:
1. Read the file
2. Extract YAML frontmatter (content between `---` markers at the top of the file)
3. From frontmatter, extract:
- `tags:` — array of tags (e.g., `tags: [api, pagination, django]`). If missing, record as untagged.
- `Last updated:` — date string (YYYY-MM-DD). If missing, record as unknown.
- `semantic-hints:` — array of free-form phrases (e.g., `semantic-hints: [cursor pagination, keyset pagination]`). Optional; if missing, treat as empty list. Added 2.16.0.
4. Extract the first `#` heading as the file's description
5. Store: `{path, tags[], hints[], description, last_updated, source: "cross-project"}`
Report: "Scanned N files in approaches/, decisions/, guides/, references/."
### Reviews tier scan (always run if either review subfolder exists)
After the cross-project scan, scan `logs/prospect/` and `logs/retrospect/` for review reports written by the `/prospect` and `/retrospect` skills.
For each `.md` file found in either subfolder:
1. Read the file
2. Extract YAML frontmatter — review reports have a richer schema than cross-project files. Pull:
- `tags:` — array of tags. Always includes `prospect` or `retrospect` plus the scope keyword. If missing (legacy reports written before the structured-frontmatter format), record as untagged and emit a soft warning (one-line) suggesting the file be re-run or hand-tagged.
- `Last updated:` — fall back to `date:` if `Last updated:` is absent (review reports use `date:` for the report-creation date).
- `type:` — `prospect` or `retrospect`. Used to bucket the file under "Retrospects" or "Prospects" in the `## Review Index` section of `index.md` (see Step 9).
- `scope:` — the scope keyword (e.g., `release`, `deployment`, `commit`, `plan`).
- `tickets:` — for cross-reference enrichment (consumed by `/context`).
3. Extract the first `#` heading as the file's description (typically "/prospect — <goal>" or "/retrospect — <goal>").
4. Store: `{path, tags[], description, last_updated, source: "review", review_type: <prospect|retrospect>, scope: <keyword>, tickets: [...]}`
Report: "Scanned R review reports across logs/prospect/ and logs/retrospect/ (P prospect, Q retrospect)."
If neither subfolder exists yet on disk, skip this sub-step silently (first run before any /prospect or /retrospect has been invoked).
### Project tier scan (only if `projects_enabled: true` and `projects_list` is non-empty)
After the cross-project scan, scan the project tier:
For each `tag:path` pair in `projects_list`:
1. Glob `{knowledge_folder}/projects/{tag}/**/*.md` recursively
2. **Exclude** `projects/{tag}/README.md` (per-project navigation, not knowledge content)
3. **Exclude** `projects/README.md` (the projects/ tier README, plugin-managed)
4. For each file found, perform the same frontmatter extraction as above.
5. **Path-derived tag union (Decision #9):** automatically add `{tag}` to the file's tag set even if not in YAML frontmatter. The union of YAML tags + path tag is what gets indexed. This means project files don't have to manually include the project tag in their frontmatter.
6. Store: `{path, tags[], description, last_updated, source: "project-specific", project: tag}`
Report: "Scanned M files across N project subdirectories: [project tags]."
If `projects_enabled: false` or `projects_list` is empty, skip this sub-step entirely. Project tier files (if any exist on disk) won't be indexed.
### Team-shared scan (only if `projects_shared_knowledge` list is non-empty)
After the project tier scan, scan each enabled project's `_project-knowledge/` folder. Only projects whose tags appear in `projects_shared_knowledge` are scanned — non-listed projects are skipped, even if their `_project-knowledge/` folders exist on disk.
For each tag in `projects_shared_knowledge` (parsed as comma-separated list):
1. Resolve the project root: look up the tag in `projects_list` to get its path, then resolve to `~/Projects/<path>`. If the tag is not present in `projects_list`, log a warning and skip (config inconsistency — `/setup` validation should catch this, but defensive).
2. **Determine scan locations** based on whether the tag has a `projects_groups` entry (multi-repo project):
- **Single-repo project** (no `projects_groups[tag]`): scan `<project-root>/_project-knowledge/` directly.
- **Multi-repo project** (`projects_groups[tag]` is set): the project-root is a container, not a repo. Iterate the role:sub-repo pairs in `projects_groups[tag]` (preserving declaration order), and for EACH sub-repo, scan `<project-root>/<sub-repo>/_project-knowledge/`. Skip sub-repos whose path doesn't exist on disk (sub-repo not yet cloned).
3. For each scan location determined in step 2, probe for `_project-knowledge/`. If the folder doesn't exist, skip that location (no team-shared knowledge yet there). Continue to next location (don't bail on the whole tag — sibling sub-repos may have content).
4. Glob `<scan-location>/_project-knowledge/**/*.md` recursively.
5. **Exclude** `<scan-location>/_project-knowledge/README.md` (auto-generated convention explainer, not knowledge content).
6. For each file found, perform frontmatter extraction as in the cross-project scan above.
7. **Path-derived metadata:**
- If the file path is at `<scan-location>/_project-knowledge/*.md` (top level), categorize as `team-shared` with `project: <tag>` (always the parent project tag from `projects_shared_knowledge`, NOT the sub-repo name — sub-repo identity is captured in the absolute path stored in step 9).
- If the file path is under `<scan-location>/_project-knowledge/cross/*.md`, categorize as `team-shared-cross` with `project: cross`.
- The path-derived project tag is added to the file's tag set even if not in YAML frontmatter (same Decision #9 pattern as project tier).
8. **IDEAS-BACKLOG.md handling:** treat `_project-knowledge/IDEAS-BACKLOG.md` and `_project-knowledge/cross/IDEAS-BACKLOG.md` as single files (don't try to split them into entries for indexing). Index them as one file each, tagged with the project tag (or `cross`).
9. Store: `{path: <absolute-path-from-home>, tags[], description, last_updated, source: "team-shared", project: <tag>, scope: "repo" | "cross"}`.
Report: "Scanned T team-shared files across P enabled projects: [project tags from `projects_shared_knowledge` with non-empty `_project-knowledge/` folders]."
If `projects_shared_knowledge` is empty/missing, skip this sub-step entirely. Team-shared files (if any exist on disk) won't be indexed.
## Step 2: Read Existing Index
Read `{knowledge_folder}/index.md` if it exists.
Extract:
- `## Known Tags` section — the current canonical tag vocabulary (comma-separated list)
- `## Projects` section — current project-to-tag mappings
If `index.md` doesn't exist (first run), use the seeded known tags:
```
api, architecture, css, database, deployment, django, react, nextjs, react-native, tailwind, testing, infrastructure, performance, security, accessibility, stripe, linear, supabase, figma, claude-code, process, decision-framework, enforcement, aria
```
And leave the Projects section empty (will be populated in Step 6).
## Step 2b: Read and Validate Aliases (added 2.16.0)
Read `{knowledge_folder}/aliases.md` if it exists.
Parse the alias map: each line matching the pattern `` - `<alias>` → `<canonical>` `` contributes one entry. The alias and canonical are the backtick-quoted strings; whitespace around the arrow is tolerated; non-matching lines (headers, comments, blank lines) are ignored.
If the file doesn't exist OR contains no parseable entries, treat the alias map as empty and continue to Step 3.
**Chain check (internal to the alias map):** if any canonical name in the parsed map ALSO appears as an alias key in another entry of the same map, abort `/index` with:
> `"Alias chain detected: \`x\` → \`y\` → \`z\` in aliases.md. Aliases must point directly to a canonical tag, not to another alias. Fix the chain (typically: rewrite the intermediate alias to point at the final canonical) and re-run /index."`
**Collision check (against Step 1's per-file tag data):** for each alias `a` in the parsed map, scan the per-file `tags[]` arrays collected in Step 1. If any file declares `a` in its `tags:` frontmatter, abort `/index` with:
> `"Alias \`a\` in aliases.md collides with existing tag \`a\` used in N file(s): <comma-separated paths>. Either remove the alias from aliases.md or rename the tag in those files."`
On successful validation, retain the alias map for Step 9 (Known Tags annotation). The map is also consumed by `/context` Step 2.5 (which reads it from the `## Known Tags` section's `[aliases: ...]` annotations, not from `aliases.md` directly).
## Step 3: Tag Normalization
Compare all tags found across scanned files. Detect similar tags using these heuristics:
- **Plural/singular:** `api` vs `apis`, `test` vs `tests`
- **Hyphen variants:** `react-native` vs `reactnative` vs `react native`
- **Common abbreviations:** `db` vs `database`, `infra` vs `infrastructure`
For each pair of similar tags, check which one is in the Known Tags set. If one is known and the other isn't, the known one is the normalization target.
If both are unknown, prefer the more common one (appears on more files).
**Present conflicts to user:**
```
## Tag Normalization
Found similar tags:
1. `apis` (1 file) → normalize to `api` (4 files)? [y/n]
2. `reactnative` (1 file) → normalize to `react-native` (2 files)? [y/n]
```
For each approved normalization:
- Edit the source file's YAML frontmatter to replace the old tag with the normalized tag
- Record the change for the summary
If no similar tags found, skip this step silently.
## Step 4: Freeform-to-Known Tag Promotion
Identify tags that are NOT in the Known Tags set but appear on `{freeform_promotion_threshold}` or more files (default: 3).
**Exclude ephemeral tags before applying the threshold.** Session/phase/plan stamps recur across many files (so they hit the threshold) but are NOT durable concepts and should never become canonical Known Tags. Drop any candidate whose **whole tag** matches one of these patterns (case-insensitive) before counting:
- `^s\d+$` — session stamps (`s4`, `s60`, `s82`, `s111`)
- `^p-?\d+$` — plan / work-item ids (`p-23`, `p23`)
- `^phase-?\d+$` — phase stamps (`phase-3`, `phase3`)
- `^plan-\d+[a-z]?$` — plan ids (`plan-01a`)
- literal denylist: `future-session-plan`, `soft-launch`
This **suppresses AUTO-promotion suggestions only — it is not a hard ban.** A user can still hand-add any of these to Known Tags (Step 9 writes whatever is in the set, and a tag in Known Tags never re-enters the freeform pool). Because a pattern could occasionally match a genuine concept (e.g. a future `s3` meaning AWS S3 — note `s3` is not a session stamp in the current corpus, but `^s\d+$` would match it), the skipped set is **surfaced, not silent**. Emit a one-line note so a false-positive can be rescued:
```
Skipped N ephemeral tag(s) from promotion (session/phase/plan stamps): s82, s75, phase-3, … — hand-add to Known Tags if any is actually a durable concept.
```
To make an exclusion permanent for a real concept, hand-add that tag to Known Tags (it then never re-enters the freeform pool). To tune the patterns, edit this list — there is intentionally no config field for it (Rule 13: the hand-add override + this documented list cover the need without a new setting).
**Present suggestions:** (candidates remaining after the ephemeral-exclusion filter)
```
## Freeform Tag Promotion
These freeform tags appear frequently:
1. `webhooks` — 4 files. Promote to known tags? [y/n]
2. `authentication` — 3 files. Promote to known tags? [y/n]
```
For each approved promotion:
- Add the tag to the Known Tags set (will be written to index.md in Step 9)
- Record for summary
If no tags qualify, skip this step silently.
## Step 5: Untagged File Resolution
For each file with no `tags:` in its frontmatter:
**Present list and offer to fix:**
```
## Untagged Files
Found N files without tags:
1. guides/claude/environment-architecture.md — "Environment Architecture"
Suggested tags: [claude-code, architecture, infrastructure]
2. approaches/combo-class-pattern.md — "Combo Class Pattern"
Suggested tags: [css, tailwind, df]
Add suggested tags? (all / numbers / skip)
```
For each file the user approves:
- Read the file
- If the file has existing YAML frontmatter (between `---` markers), add `tags: [tag1, tag2]` as a new line inside it
- If the file has no frontmatter, add a frontmatter block at the top:
```
---
Last updated: YYYY-MM-DD
tags: [tag1, tag2]
---
```
(Use the file's existing `Last updated` date if found in the body, or today's date if none exists)
- Record the change for the summary
Tag suggestions are based on:
- Filename keywords (e.g., `api-pagination` → `api`, `pagination`)
- First heading keywords
- Content scan for known tag keywords
- Parent directory (e.g., file in `guides/claude/` → suggest `claude-code`)
## Step 6: Project-to-Tag Mapping Update
Determine the authoritative project list using this priority:
1. **If `projects_enabled: true` and `projects_list` is non-empty:** use `projects_list` as the project enumeration. Each `tag:path` pair contributes a project entry where the tag is the project key and the path is the project location. This is the configured set of projects ARIA recognizes.
2. **Otherwise** (or as a supplement when `projects_enabled: false`): read the root project CLAUDE.md to find a project table. Look for the closest ancestor directory containing a `CLAUDE.md` with a project table.
For each project (from either source):
1. Read the project's CLAUDE.md (e.g., `cs/CLAUDE.md`, `ss/AGENTS.md`) if it exists at the configured path
2. Extract tech stack, tools, frameworks, and services mentioned
3. Match extracted keywords against the Known Tags set (including any newly promoted tags from Step 4)
4. Also check which tags appear on files that mention the project name in their path or content
5. **If projects tier is enabled:** add tags inferred from project-tier files (i.e., tags appearing on files under `projects/{tag}/**` from Step 1's project tier scan) to the project's relevant tag set
Build a mapping:
```
proj-a — Project A: api, django, react, react-native, css, tailwind, stripe, supabase, database, deployment
proj-b — Project B: api, django, nextjs, stripe, supabase, database, deployment
proj-c — Project C: css, tailwind, accessibility
aria — ARIA: claude-code, process, decision-framework, enforcement
```
Compare against existing mappings (from Step 2). If any changed:
```
## Project Mapping Updates
- proj-a: added `supabase` (found in proj-a/CLAUDE.md tech stack)
- proj-b: no changes
```
If this is the first run (no existing mappings), present the full initial mapping for confirmation.
## Step 7: Staleness Detection
**Scope first — three exclusions, all of them measured.** Applying a date threshold to every scanned
file manufactures false positives that bury the real queue. On the 2026-08-05 corpus (942 files) the
naive scan returned **240** entries of which only **78** had a review question to answer.
1. ⛔ **EXCLUDE `decisions/` in both tiers** (`decisions/` and `projects/*/decisions/`). A decision
record is immutable history — its obsolescence is expressed by **supersession**, not by a date, so
there is no review question a threshold can raise. Measured: **162 of 240** flagged entries were
ADRs. Ratified as **ADR 117** (`projects/aria/decisions/117-staleness-detection-exempts-decision-records.md`).
2. ⛔ **EXCLUDE files with no frontmatter at all** — directory `README.md`s, probe-test fixtures, and
verbatim source dumps under `references/sources/` or similar. These are structural or archival, not
guidance, so "is it current?" does not apply. Measured: 25 of 44 undated files. Do **not** report
them as stale and do **not** report them as a gap; count them and move on.
3. **Report separately, do not flag:** a file that HAS frontmatter but carries neither date field.
Measured: 12, of which only 2 were genuine guidance files. Surface those as *"needs a date field"*,
which is a different action from *"needs review"*.
**Then read the date — BOTH idioms are in live use.** Accept `Last updated:` **or** `date:` from the
first ~10 frontmatter lines, preferring `Last updated:` when both are present. Measured: **7** files
carry only `date:`, and a `Last updated`-only reader reports them as undated, which then reads as a
data-quality gap rather than a detector gap.
For each remaining file, compare its resolved date against today. If the age exceeds
`{staleness_threshold_months}` months (config default: 6; note the user's config may set it lower —
3 is in use, and the config value wins):
- Add to the stale files list with age and threshold info.
Carry three counts forward to Step 9, not one: **review-able**, **exempt (decisions)**, and
**no-date-field**. A single total is the shape that produced the buried queue.
This data is used when generating the `## Stale Files` section in Step 9. No user interaction here —
just collection.
> ⚠ **When you report the stale count, name the population it was drawn from.** "216 files are stale"
> and "78 files need review out of 942 scanned, with 162 exempt" are different claims, and only the
> second one is actionable.
## Step 7b: Heavy-Pass Gate (REQUIRED before Steps 8.x)
Steps 8 (Cross-Reference Pass), 8b (Entity Detection), 8c (Skill Connection Discovery), and 8d (Cross-Project Promotion Candidates) require **body content scans** of every promoted knowledge file — substantially more expensive than the frontmatter-only scan that powers Steps 1-7. For a 500+ file knowledge base, expect ~3 minutes wall-clock and meaningful token cost.
These steps were silently skipped in the first 35+ `/index` passes (added to the spec but never invoked at routine pass cost). v2.20.0+ makes the cost explicit and gates the heavy work behind user confirmation.
**Prompt:**
> Routine `/index` indexes via frontmatter (tags, dates, descriptions). Heavy-pass Steps 8.x run additional body-content scans for:
> - **Step 8** — Cross-reference suggestions (pairs of files sharing ≥2 tags with no mutual `## Related` link, plus reverse-link gaps)
> - **Step 8b** — Entity detection (tools/services/frameworks appearing in ≥2 files)
> - **Step 8c** — Skill-knowledge connection discovery (skill names referenced in knowledge files; name-overlap matching)
> - **Step 8d** — Cross-project promotion candidates (similar patterns across ≥2 projects in `projects_list`)
>
> Cost: ~3 min wall-clock for N files (N = scanned count from Step 1) + meaningful token spend. Output: enriched `index.md` sections.
>
> Run heavy-pass Steps 8.x? **(y / n / partial)**
- **`y`** — proceed with Steps 8, 8b, 8c, 8d as defined below
- **`n`** — skip Steps 8.x entirely; proceed to Step 9 with frontmatter-only data; resulting `index.md` will omit the `## Cross-Reference Suggestions`, `## Entities`, `## Skill Connections`, `## Cross-Project Promotion Candidates` sections
- **`partial: <substep-list>`** — run a subset (e.g., `partial: 8d` runs only cross-project; `partial: 8,8d` runs cross-reference + cross-project, skips entity detection + skill connections). Useful when one substep is the user's actual interest and the other three are noise for this pass.
### When `/index` is called from `/audit-knowledge` Step 7b
If `/index` is being invoked as part of `/audit-knowledge`'s Step 7b rebuild (not stand-alone), the heavy-pass gate **still fires** — the audit user is the same human; ask them once. If the user declines or chose `partial`, the audit's Step 5b drift-detection capabilities are degraded (skill-knowledge drift relies on Step 8c output, cross-project candidate detection relies on Step 8d). Surface this degradation explicitly in `/audit-knowledge` Step 6's "Integrity Issues" section with a "Limited by Steps 8.x skip" note.
### When the user pre-authorizes via argument
`/index` accepts an optional argument:
- `/index` — default; prompt at Step 7b
- `/index --deep` — pre-authorize all of Steps 8.x (treat the gate as auto-`y`)
- `/index --shallow` — pre-authorize skip of all Steps 8.x (treat the gate as auto-`n`)
- `/index --partial=8d` — pre-authorize partial run (same syntax as the prompt's `partial:` response)
When pre-authorized, skip the prompt and proceed accordingly. Audit-time invocations should typically run shallow or partial (audit is the long-flow already); explicit `/index --deep` is the right shape for periodic baseline refreshes (every 1-2 weeks, or when a major batch of new files lands).
### Skill-spec history (informational)
Steps 8.x were defined in the spec from v1.0 but never invoked because routine `/index` calls treated them as frontmatter-tier work. 35+ passes silently skipped. v2.20.0 (2026-05-20) introduces this gate after the 37th-pass `/audit-knowledge` first invoked Steps 8.x via parallel agent (one-time baseline) and the resulting agent output ran ~3 min producing 599 lines of findings — Mike confirmed the cost-value gate-explicit pattern over routine-silent-skip.
## Step 8: Cross-Reference Pass
For each pair of promoted files, compute tag overlap:
1. Count shared tags between the two files
2. If overlap >= 2 tags, check each file's `## Related` section for existing cross-references
3. If one or both files don't reference the other, record as a suggestion
Also check for **reverse link gaps**: if file A's `## Related` links to file B, but file B's `## Related` doesn't link to file A.
**Present suggestions:**
```
## Cross-Reference Suggestions
1. approaches/api-pagination.md <-> decisions/003-cursor-vs-offset.md
Shared tags: api, pagination
Neither references the other — add cross-links? [y/n]
2. references/stripe-webhook-patterns.md <-> guides/payments/checkout-flow.md
Shared tags: stripe, cs
checkout-flow.md links to stripe-webhook-patterns.md but not the reverse — add reverse link? [y/n]
```
For each approved cross-reference:
- If the file has a `## Related` section, append the new link:
```markdown
- [Target File Title](../relative/path/to/target.md)
```
- If the file has no `## Related` section, add one at the end of the file:
```markdown
## Related
- [Target File Title](../relative/path/to/target.md)
```
- Use relative paths from the source file to the target file
If no suggestions, skip this step silently.
## Step 8b: Entity Detection
Scan all promoted files for recurring proper nouns — tool names, service names, API names, framework names, and other named entities that appear across multiple knowledge files.
**How to detect entities:**
1. Scan headings, bold text, and inline code spans in promoted files for proper nouns and technical names
2. Filter to entities that appear in **2+ files** (single-file mentions aren't useful for cross-referencing)
3. Exclude entities that are already covered by tags (e.g., if "Stripe" is both a tag and an entity, the tag index already covers it)
4. Exclude common words that happen to be capitalized (sentence starters, section headings like "Overview", "Summary")
**Build an entity map:**
```
Stripe → approaches/payment-flow.md, references/stripe-webhook-patterns.md, decisions/003-payment-provider.md
Supabase → guides/infrastructure/supabase-setup.md, decisions/005-builder-architecture.md
Django → approaches/api-pagination.md, guides/api-auth.md
```
This data is used when generating the `## Entities` section in Step 9. No user interaction here — just collection.
## Step 8c: Skill Connection Discovery
Scan for connections between the plugin's skills and knowledge files. This enables `/audit-knowledge` to detect when a skill evolves but its related knowledge docs haven't been updated.
**Scan skill files:**
1. Glob for `${CLAUDE_PLUGIN_ROOT}/skills/*/SKILL.md` (or use the plugin's own skill directory)
2. Also scan any other installed plugins: `~/.claude/plugins/**/skills/*/SKILL.md`
3. For each skill: extract `name` from frontmatter or directory name, extract `description`, scan for `## Related` sections
**Auto-discover connections** using these heuristics (in priority order):
1. **Explicit references:** Knowledge file content mentions the skill name (e.g., `/codemap` appears in an approach doc). Grep promoted files for `/skillname`.
2. **Skill `## Related` section:** Skill file explicitly references knowledge files. Parse relative paths.
3. **Name overlap:** Skill name is a substring of a knowledge filename or vice versa (e.g., "codemap" matches `codebase-documentation`). Use fuzzy matching — strip hyphens and compare.
4. **Tag/keyword overlap:** Skill description keywords match knowledge file tags. Extract significant nouns from the skill description and match against the Known Tags set + file tags.
**Present discoveries for user confirmation:**
```
## Skill Connections
Discovered N connections between skills and knowledge files:
1. /codemap → approaches/codebase-documentation.md
Match: skill name referenced in file content
Relationship: [auto-suggest or ask user]
2. /codemap → decisions/007-codebase-documentation-structure.md
Match: "/codemap" mentioned in decisions backlog clear comment
Relationship: [auto-suggest or ask user]
3. /extract → decisions/002-knowledge-extraction-architecture.md
Match: name overlap ("extract" ↔ "extraction")
Relationship: [auto-suggest or ask user]
Add connections? (all / numbers / skip / add manual)
```
For "add manual": let the user specify `skill → file → relationship` for connections the heuristics missed.
**Relationship types** (suggest the most likely, let user override):
- `documents the approach this skill implements`
- `records decisions that shaped this skill`
- `provides reference data used by this skill`
- `defines rules enforced by this skill`
- `guides usage patterns for this skill`
Store approved connections for Step 9 output.
## Step 8d: Cross-Project Promotion Candidate Detection
Skip this step entirely if `projects_enabled: false` or `projects_list` has fewer than 2 entries.
Scan files indexed under the project tier (from Step 1's project tier scan) for patterns that may represent the same concept across multiple projects.
**Detection heuristics** (compute pairwise across all project-tier files):
1. **Filename similarity:** Files with similar kebab-case names (e.g., `state-management-patterns.md` in `projects/proj-a/patterns/` AND `projects/proj-b/patterns/`). Use case-insensitive equality of stem (filename without `.md`) as the primary match; allow minor variants (`-patterns` vs `-pattern`, plural vs singular).
2. **Tag overlap:** Files sharing 3+ tags (excluding the project tags themselves, which are auto-derived from path).
3. **Title/H1 similarity:** Files whose H1 (first `#` heading) shares 3+ significant terms (excluding stop words and project names).
**Threshold:** if a pattern (i.e., a similar file) appears in ≥`projects_promotion_threshold` projects (default 2), surface as a candidate.
For each candidate group, collect:
- The set of project-tier files that triggered the match
- The shared tags (excluding project tags)
- A suggested cross-project location (typically `approaches/{descriptive-name-derived-from-shared-tags-or-title}.md`)
This data is used when generating the `## Cross-Project Promotion Candidates` section in Step 9. **No user interaction at this step** — just collection. Promotion itself happens in `/audit-knowledge` Step 5e (Phase 3 of the project knowledge feature).
**Rationale for surfacing in the index:** the index is a regularly-rebuilt artifact. Detecting candidates here means users see them whenever they look at the index, not only when they explicitly run `/audit-knowledge`. Lower-friction discovery, same downstream promotion workflow.
## Step 9: Rebuild and Write `index.md`
Generate `{knowledge_folder}/index.md` with this structure:
```markdown
# Knowledge Index
Last rebuilt: YYYY-MM-DD
## Projects
### [project_key] — [project_name]
Relevant tags: tag1, tag2, tag3
Project-tier files: N (decisions: D, patterns: P, other: O)
Last project-tier update: YYYY-MM-DD
Promotion candidates: M (see below — same pattern appears in ≥`projects_promotion_threshold` projects)
(repeat for each project. If `projects_enabled: false`, the per-project metrics lines after "Relevant tags:" are omitted — just the project key and tag mapping appear, mirroring v2.7.x format.)
(For projects with zero project-tier files, show `Project-tier files: 0` and omit "Last project-tier update" — Decision #8: list configured projects even if empty so the user sees what's available.)
## Known Tags
tag1, tag2 [aliases: alt1, alt2], tag3, tag4, ...
(Canonical tags with aliases declared in `aliases.md` are annotated inline: `tag [aliases: alias1, alias2]` enumerates all aliases pointing to that canonical. Tags with no aliases appear without annotation. The flat tag list is comma-separated. `/context` reads these annotations to build its alias→canonical map at query time. Added 2.16.0.)
## Tag Index
### [known_tag]
- relative/path/to/file.md — File description
(repeat for each known tag that has matching files, sorted alphabetically)
## Other Tags
### [freeform_tag]
- relative/path/to/file.md — File description
(repeat for each freeform tag, sorted alphabetically)
## Semantic Hints Index
### [hint phrase]
- relative/path/to/file.md
(Repeat for each unique hint phrase declared across promoted files, sorted alphabetically by phrase. Each file appears under every hint it declares. Hint phrases are stored verbatim from frontmatter — `/context` does the case-insensitive + hyphen-normalized substring match at query time. Omit this section entirely if no files declare `semantic-hints:`. Added 2.16.0.)
## Team-Shared Tag Index
### [tag]
- ~/Projects/<path>/_project-knowledge/2026-04-28-init-foo.md — File description [project: proj-a, scope: repo]
- ~/Projects/<path>/_project-knowledge/cross/2026-04-28-init-bar.md — File description [project: cross, scope: cross]
(repeat for each tag matching team-shared files, sorted alphabetically. File paths are absolute-from-home so /context can distinguish them from knowledge-folder-relative paths in the regular Tag Index. The trailing `[project: ..., scope: ...]` annotation lets /context render team-shared results with their origin info. Omit this section entirely if `projects_shared_knowledge` is empty/missing or no team-shared files exist.)
## Review Index
### Retrospects
- YYYY-MM-DD [scope] — Goal text (truncated to ~60 chars) [LINEAR-123, LINEAR-456] → outcome
- YYYY-MM-DD [scope] — Goal text [tickets] → outcome
### Prospects
- YYYY-MM-DD [scope] — Goal text [tickets] → verdict
- YYYY-MM-DD [scope] — Goal text [tickets] → verdict
(Sourced from files indexed under the reviews tier — `logs/retrospect/*.md` and `logs/prospect/*.md`. Within each subsection, sort descending by `date:` from frontmatter (newest first — most actionable for catch-up). Each entry shows: ISO date, scope keyword in brackets, truncated goal, ticket list (if any), and the report's overall_outcome (retrospects: closed / partial / unresolved / mixed) or overall_verdict (prospects: PROCEED / PROCEED-WITH-CHANGES / HOLD / KILL). The ticket list links to the ticket reference if Linear MCP is available; otherwise shows the bare IDs.
Path is relative to the knowledge folder root: `logs/retrospect/YYYY-MM-DD-scope-slug.md`. Use the file path as the link target so the user can click through to the full report.
If no review reports exist (first run before any /prospect or /retrospect was invoked), omit this section entirely.
If reviews lack frontmatter `overall_outcome` / `overall_verdict` (legacy reports written before the structured-frontmatter format), substitute "[no verdict recorded]" rather than omitting the entry.)
## Stale Files
_Recomputed YYYY-MM-DD. **R** promoted files need review (of **N** scanned) — threshold: **M** months._
⚠ **E of these are decision records and are EXEMPT** — an ADR does not go stale, it gets **superseded**.
Ratified as ADR 117. They are listed separately and are **NOT** a review queue.
### Review-able tiers (R files)
- relative/path/to/file.md — Last updated: YYYY-MM-DD
- relative/path/to/other.md — date: YYYY-MM-DD (`date:` idiom, resolved)
### Decision records — NOT a review queue (E files, exempt per ADR 117)
_Listed for completeness only. Obsolescence here is expressed by supersession; do not review by date._
- relative/path/to/decisions/NNN-something.md — Last updated: YYYY-MM-DD
### Needs a date field — not stale (D files)
_Has frontmatter but carries neither `Last updated:` nor `date:`. A different action from "needs review"._
- relative/path/to/file.md
(Sort each list by date ascending, then path. **X files with no frontmatter at all were excluded as
structural** — READMEs, probe fixtures, verbatim source dumps; state the count, do not list them and do
not report them as a gap. Omit any subsection with zero entries; omit the whole section only if all
four are zero.)
> ⚠ **The header line must name the population.** "216 files are stale" and "78 need review of 942
> scanned, 162 exempt" are different claims and only the second is actionable. Never emit a single
> undifferentiated total.
## Untagged Files
- relative/path/to/file.md — File description (no tags in frontmatter)
(Omit this section entirely if no untagged files remain after Step 5.)
## Entities
### [Entity Name]
- relative/path/to/file1.md
- relative/path/to/file2.md
(Repeat for each entity appearing in 2+ files, sorted alphabetically. Omit this section entirely if no entities detected or all are already covered by tags.)
## Skill Connections
| Skill | Related knowledge | Relationship |
|-------|------------------|-------------|
| /skillname | relative/path/to/file.md | documents the approach this skill implements |
(Repeat for each approved connection from Step 8c, sorted by skill name. Omit this section entirely if no connections discovered or approved.)
## Cross-Project Promotion Candidates
### [shared-tag-or-title-derived-name]
- Appears in: projects/{tag1}/patterns/file.md, projects/{tag2}/patterns/file.md
- Shared tags: tag-a, tag-b, tag-c
- Suggested location: approaches/{descriptive-name}.md
- Run `/audit-knowledge` Step 5e to promote (synthesizes content + adds `originally_at:` provenance)
(Repeat for each candidate group from Step 8d, sorted by number of projects involved descending then alphabetically. Omit this section entirely if `projects_enabled: false` or no candidates detected.)
```
**File paths** in the index are relative to the knowledge folder root (e.g., `approaches/api-pagination.md`, not the absolute path).
**Tag Index entries** are sorted: known tags alphabetically, then other tags alphabetically. Within each tag, files are sorted alphabetically by path.
**A file appears under every tag it carries.** If `api-pagination.md` has `tags: [api, pagination, django]`, it appears under all three tag headings.
## Step 10: Report Summary
```
Index rebuilt successfully.
Files: N scanned, M tagged, K untagged
Tags: L unique (J known, F freeform)
Normalizations: P applied
Promotions: Q tags promoted to known
Stale files: R need review of N scanned (threshold: T months) — plus E exempt decision records, D needing a date field, X excluded as structural
Cross-references: R suggested, X added
Entities: E detected (across 2+ files)
Skill connections: C discovered, D approved
Project mappings: updated/unchanged
```
## Rules
- **Never modify files outside the knowledge folder** except for the root CLAUDE.md read (read-only) in Step 6
- **Always present changes before making them** — normalizations, promotions, untagged fixes, and cross-references all require user approval
- **Preserve existing frontmatter** — when adding tags to a file, don't remove or modify other frontmatter fields
- **Relative paths in index** — all paths in index.md are relative to the knowledge folder root
- **Skip empty directories** — if approaches/ has no .md files, don't create an empty tag section
- **Directory README stubs are not knowledge files** — skip files that are only 1-5 lines of boilerplate (the README.md stubs in approaches/, decisions/, etc.)
---
## /backlog
# /backlog — Backlog Viewer & Manager
View pending items across all four backlogs, or manage entries.
## Step 0: Resolve Config
Read `.cursor/aria-knowledge.local.md` and extract `knowledge_folder`. If the file doesn't exist, stop: "aria-knowledge is not configured. Run /setup to get started."
Set backlog paths:
- `{knowledge_folder}/intake/insights-backlog.md`
- `{knowledge_folder}/intake/decisions-backlog.md`
- `{knowledge_folder}/intake/extraction-backlog.md`
- `{knowledge_folder}/intake/rules-backlog.md`
## Step 1: Parse Argument
- **No argument:** go to Step 2 (overview)
- **`insights`**, **`decisions`**, **`extraction`**, or **`rules`:** go to Step 3 (detail view)
- **`clear [type] [date]`:** go to Step 4 (clear entries)
## Step 2: Overview Mode
Read all four backlog files. For each, count the number of `### YYYY-MM-DD` entries after the `---` separator and find the most recent date. **If any backlog file is missing**, show "missing — run /setup to repair" instead of a count for that file.
Output:
```
## Pending Backlogs
- Insights: N entries (latest: YYYY-MM-DD)
- Decisions: N entries (latest: YYYY-MM-DD)
- Extraction: N entries (latest: YYYY-MM-DD)
- Rules: N entries (latest: YYYY-MM-DD)
```
If a backlog has no entries (or contains only placeholder text like "(No pending insights)" or "(No pending rules)"), show 0 entries.
## Step 3: Detail View
Read the requested backlog file. Output all entries after the `---` separator.
If no entries: "No pending [type] items."
## Step 4: Clear Entries
**Arguments:** `clear [type] [date]`
- `type`: `insights`, `decisions`, `extraction`, or `rules`
- `date`: YYYY-MM-DD — remove entries on or before this date
**Validate the date argument before proceeding:**
- Must match `YYYY-MM-DD` format. If not: "Invalid date format. Use YYYY-MM-DD (e.g., 2025-03-15)."
- Must not be in the future. If it is: "Cannot clear future-dated entries. Today is [today's date]. Did you mean [suggestion]?"
- If more than 30 entries would be cleared, add a warning: "This will clear N entries — that's a large batch. Are you sure?"
Before clearing, show what will be archived:
> "This will archive N entries from [type]-backlog.md dated on or before [date]:
> - [date] — [brief context from each entry]
>
> Entries move to `{knowledge_folder}/archive/backlog-cleared-{type}-{YYYY-MM-DD-HHmmss}.md`. Proceed? (y/n)"
If user confirms, apply the **archive-then-remove pattern** (v2.15.2+):
1. Create `{knowledge_folder}/archive/` if it doesn't exist.
2. Write archive file at `{knowledge_folder}/archive/backlog-cleared-{type}-{YYYY-MM-DD-HHmmss}.md` with this shape:
```markdown
---
archived_at: YYYY-MM-DDTHH:MM:SS
source_backlog: intake/{type}-backlog.md
cleared_through_date: YYYY-MM-DD
entry_count: N
reason: /backlog clear user-invoked
---
# Archived {type} backlog entries — cleared {YYYY-MM-DD}
The following N entries were cleared from `intake/{type}-backlog.md` on {YYYY-MM-DDTHH:MM:SS} via `/backlog clear {type} {date}`. They are preserved here for recovery if needed.
---
### YYYY-MM-DD — [entry 1 title]
[full body of entry 1]
### YYYY-MM-DD — [entry 2 title]
[full body of entry 2]
...
```
Copy the full body of each matching `### YYYY-MM-DD` entry (from the entry header down to the next `###` heading or end of file) into the archive file.
3. After the archive is written, remove the matching entries from `intake/{type}-backlog.md`. If all entries are removed, replace with the placeholder text (e.g., "(No pending insights)").
4. Report: "Archived N entries to `archive/backlog-cleared-{type}-{YYYY-MM-DD-HHmmss}.md`. Source backlog updated."
**Never delete (v2.15.2+):** Backlog entries are NEVER `rm`'d during clear. The archive-then-remove pattern moves user-authored content to the archive surface (full body preserved, not just a ledger) before removing from the live backlog. Rule 6 ("Don't delete — archive") is preserved on-disk, no git history dependency.
**User override (explicit, v2.15.2+):** If the user explicitly approves or asks for a bare deletion that skips the archive (phrases like *"delete without archiving"*, *"really delete these entries"*, *"don't archive this clear"*), the destructive operation is permitted. The default safety floor remains archive-then-remove; this override exists for cases where the user has explicit reason to skip preservation (e.g., backlog entries contain sensitive content they want untraceable, or they're clearing test/spam entries that don't deserve archive space). **Before honoring an override, surface the entry count + the date range that would have been archived** and confirm. User-approved bare deletes are one-off — a subsequent `/backlog clear` invocation defaults back to archive-then-remove.
If user declines: "No entries cleared."
---
## /stats
# /stats — Knowledge Base Health
Read-only dashboard showing the current state of the knowledge repository.
## Step 0: Resolve Config
Read `.cursor/aria-knowledge.local.md` and extract `knowledge_folder`. If the file doesn't exist, stop: "aria-knowledge is not configured. Run /setup to get started."
Use `{knowledge_folder}` as the base path for all operations.
## Step 1: Count Promoted Files
Count `.md` files (excluding README.md) in each promoted folder:
- `{knowledge_folder}/rules/*.md`
- `{knowledge_folder}/approaches/*.md`
- `{knowledge_folder}/decisions/*.md`
- `{knowledge_folder}/guides/**/*.md` (recursive — guides may have subdirectories)
- `{knowledge_folder}/references/*.md`
- `{knowledge_folder}/archive/*.md`
Record counts per category and total.
## Step 2: Count Backlog Items
For each backlog file, count the number of `### ` (h3) entries below the `---` separator:
- `{knowledge_folder}/intake/insights-backlog.md`
- `{knowledge_folder}/intake/decisions-backlog.md`
- `{knowledge_folder}/intake/extraction-backlog.md`
- `{knowledge_folder}/intake/rules-backlog.md`
Also count `.md` files in `{knowledge_folder}/intake/task-boundary-captures/`.
Also count `.md` files in `{knowledge_folder}/references/sources/` (unreviewed clippings).
## Step 3: Read Audit Dates
Extract the `**Date:**` from:
- `{knowledge_folder}/logs/knowledge-audit-log.md`
- `{knowledge_folder}/logs/config-audit-log.md`
- The `/setup on` date from `.cursor/aria-knowledge.local.md`
Calculate days since each. If a date is "(no audits yet)" or missing, note "never."
## Step 3a: Check Codemap Dates
Use Glob to find CODEMAP.md files under cwd (up to 2 levels deep). Try these patterns:
- `CODEMAP.md` (depth 0)
- `*/CODEMAP.md` (depth 1)
- `*/*/CODEMAP.md` (depth 2)
For each file found:
1. Read the first 10 lines
2. Parse the `Last updated` date from the header. Expected pattern: `> Last updated: YYYY-MM-DD | Sections: N | Features: M`
3. Calculate days-since from today's date
If the header is missing or unparseable, show `(no date)` for that entry.
If no CODEMAP.md files are found under cwd, the section still renders with a single line noting absence.
**Presentation-only.** This step does not classify stale/current or run git-activity checks. Staleness classification with file-change detection belongs to `/audit-knowledge` Step 5d — `/stats` just surfaces the raw date so the user can decide whether to run the audit.
## Step 3b: Cross-Project Tracked Artifacts (added v2.16.1)
In addition to the cwd-scoped Glob in Step 3a, iterate `KT_PROJECTS_LIST` (from config) to surface CODEMAP + STITCH dates across ALL configured projects — a dashboard view, not just the current working directory.
Skip this step entirely if `KT_PROJECTS_ENABLED != true` or `KT_PROJECTS_LIST` is empty.
For each `tag:path` entry in `projects_list`:
1. Resolve `project_root = $HOME/Projects/<path>`. If directory doesn't exist, note "(configured but missing)" and continue.
2. Stat `{project_root}/CODEMAP.md`:
- If exists, parse `> Last updated: YYYY-MM-DD` from the header (or fall back to mtime). Compute days-since.
- If missing, note "(no CODEMAP)".
3. Stat `{project_root}/STITCH.md`:
- If exists, days-since via mtime (STITCH files don't carry a header date in v2.16.x).
- If missing, note as single-repo (suppress this row entirely if user prefers terseness — or render "(single-repo, no STITCH)").
4. Classify against thresholds: `codemap_staleness_threshold_days` (default 14) and `stitch_staleness_threshold_days` (default 30). Status = fresh / STALE (>threshold) / REFUSAL-ZONE (>2× threshold).
**Presentation-only.** Same discipline as Step 3a — surfaces dates + status without auto-acting. Pairs with `/audit-config` Step 5a, which produces actionable findings.
## Step 4: Index Health (if index.md exists)
If `{knowledge_folder}/index.md` exists, read it and extract:
- **Known tags count:** count lines in `## Known Tags` section
- **Top tags:** from `## Tag Index`, count files listed under each `### tag` header, sort by count, show top 5
- **Stale files:** read `## Stale Files` section, count entries
- **Untagged files:** read `## Untagged Files` section, count entries
- **Semantic-hints coverage (added 2.16.0):** count files declaring `semantic-hints:` frontmatter / total promoted files; report as `N of M (P%)`. Always emit (zero coverage = "0 of M (0%)") to track adoption over time. Source: scan promoted-folder files (same set as Step 1) for the `semantic-hints:` field; matches `/index`'s Semantic Hints Index input.
If `index.md` doesn't exist, note: "No index — run /index to build."
## Step 5: Coverage Gaps
Check which promoted folders have zero `.md` files (excluding README.md):
- If `approaches/` is empty: note it
- If `decisions/` is empty: note it
- If `guides/` is empty: note it
- If `references/` is empty: note it
These suggest areas where knowledge capture hasn't started yet.
## Step 6: Present
Output in this format:
**Output policy:** emit every section defined in the format below with all fields, even when counts are zero. Zero counts are meaningful data points — "Pending insights: 0" confirms the backlog is clear, "Stale files: 0" confirms the index is current. Do not collapse the dashboard into prose or shorten sections for brevity — the structured format is the skill's value, enabling trend comparison across runs. The Index Health and Coverage Gaps sections have explicit conditional branches embedded in the template; all other sections are always-emit.
```
## Knowledge Stats
### Repository
- Promoted files: N total
- Rules: N
- Approaches: N
- Decisions: N
- Guides: N
- References: N
- Archived: N
### Intake
- Pending insights: N
- Pending decisions: N
- Pending extractions: N
- Pending rules: N
- Unreviewed clippings: N
- Pre-compact captures: N
### Audit Status
- Knowledge audit: [YYYY-MM-DD (N days ago) | never]
- Config audit: [YYYY-MM-DD (N days ago) | never]
- Last /setup: [YYYY-MM-DD (N days ago)]
### Codemap Status
[If codemaps exist, one line per file:]
- <relative-path>: updated YYYY-MM-DD (N days ago)
[If no codemaps found:]
- No CODEMAP.md found under cwd
### Cross-Project Tracked Artifacts (added 2.16.1)
[If projects_enabled=true AND projects_list non-empty, one block per project:]
- <tag>:
- CODEMAP: updated YYYY-MM-DD (N days ago) [fresh | STALE | REFUSAL ZONE]
- STITCH: updated YYYY-MM-DD (N days ago) [fresh | STALE | REFUSAL ZONE]
(or: "single-repo — no STITCH")
[If projects_root directory missing for a tag:]
- <tag>: (configured but missing — verify projects_list path)
[If projects feature disabled:]
- Projects feature disabled in config — set projects_enabled: true to enable cross-project tracking
### Index Health
[If index exists:]
- Known tags: N
- Top tags: tag1 (N files), tag2 (N files), tag3 (N files), tag4 (N files), tag5 (N files)
- Untagged files: N
- Stale files: N
- Semantic-hints coverage: N of M files (P%)
[If no index:]
- No index built yet — run /index
### Coverage Gaps
[List empty categories, or "All categories have content."]
```
## Rules
- **Read-only** — this skill never modifies files
- **Fast** — just counting and date parsing, no heavy analysis
- **No recommendations** — just present the data. The user decides what to act on.
---
## /ask
# /ask — Query-Driven Knowledge Creation
Research a question, check if the answer already exists in the knowledge base, and if not, draft a knowledge doc that saves directly to promoted files after user review. Fast path from question to knowledge — no backlog intermediary.
## Step 0: Resolve Config
Read `.cursor/aria-knowledge.local.md` and extract `knowledge_folder`. If the file doesn't exist, stop: "aria-knowledge is not configured. Run /setup to get started."
Use `{knowledge_folder}` as the base path for all file operations in subsequent steps.
## Step 1: Parse Question
The user provides a question as the argument. If no argument is provided, ask: "What would you like to know?"
Extract the core topic and likely tags from the question for use in Step 2.
## Step 2: Check Existing Knowledge
Before researching, check if the answer already exists:
1. **Resolve aliases first (added 2.16.0):** if `{knowledge_folder}/aliases.md` exists, parse the alias→canonical map and replace any tag token in the question that matches an alias with its canonical form before the index lookup. No notification line needed — this is internal to `/ask`'s coarse check (`/context` is the surface that surfaces resolution notifications).
If `{knowledge_folder}/index.md` exists, extract tags from the (post-alias-resolution) question and check for matching files in both the `## Tag Index` section AND the `## Semantic Hints Index` section. Tag matching is exact equality (existing behavior); hint matching is substring (case-insensitive, hyphen-normalized) — same rule as `/context` Step 4. A hint match counts the same as a tag match for partial-match detection. (Added 2.16.0.)
2. Scan headings of files in `approaches/`, `guides/`, `references/`, `decisions/` for topic overlap
3. Check `intake/` backlogs for pending items on the same topic
**If a strong match is found:** Present the existing file(s) to the user:
> "This may already be covered in [filename]. Want me to load it? Or research fresh?"
- If user says load: read and present the file, done
- If user says research: proceed to Step 3
- If partial match: note it for Step 5 ("related existing doc found — consider updating instead of creating new")
**If no match:** Proceed to Step 3.
## Step 3: Research
Answer the question using available sources:
1. **Knowledge base** — scan relevant files for partial answers or related context
2. **Codebase** — if the question relates to the current project, check code, configs, and project docs
3. **Web** — use WebSearch and WebFetch for external information (APIs, frameworks, best practices)
Synthesize a clear, complete answer. Focus on practical, actionable knowledge — not textbook definitions.
## Step 4: Determine Category
Based on the answer content, suggest where it belongs:
| Content type | Category | Example |
|---|---|---|
| How to do X (proven method) | `approaches/` | API pagination patterns |
| How X works (operational) | `guides/` | Supabase auth setup |
| What others say about X | `references/` | Stripe webhook best practices |
| We chose X because Y | `decisions/` | Why cursor over offset pagination |
| X must/must not (principle) | `rules/` | Rare — usually via `/audit-knowledge` |
## Step 5: Draft Knowledge Doc
Write a draft in the standard format for the suggested category:
```markdown
---
tags: [detected tags from question and answer]
---
# [Title]
**Last updated:** YYYY-MM-DD
[Answer content — structured with sections as appropriate]
## Related
[Links to any existing knowledge files that connect to this topic]
```
If Step 2 found a partial match, note: "Related: [existing file] — consider whether this should update that file instead of creating a new one."
## Step 6: Present for Review
Show the draft with metadata:
```
## /ask Result
**Question:** [original question]
**Category:** [suggested category]
**File:** [suggested filename in kebab-case]
**Tags:** [detected tags]
[Draft content]
Save to {knowledge_folder}/[category]/[filename]? (yes / edit / change category / reject)
```
## Step 7: Save or Discard
Based on user response:
- **"yes"** — write the file to the suggested location
- **"edit"** — user provides edits, then save
- **"change category"** — user specifies different category/filename, then save
- **"update [existing file]"** — merge content into the specified existing file instead of creating new
- **"reject"** — discard, nothing saved
After saving, confirm: "Saved to [path]. Run /index to update the tag index."
## Rules
- **Check existing first** — never create a duplicate when an update would serve better
- **Skip backlogs** — the user is reviewing in real-time, no need for staging
- **Respect copyright** — for web-sourced answers, synthesize in your own words. Include source URLs in a References section but don't copy content.
- **Practical over theoretical** — answers should help future sessions, not read like documentation. "Here's how to do X" over "X is defined as..."
- **Tag detection** — match question keywords against known tags from index.md. Add new freeform tags if no known tag fits.
- **One question, one doc** — if the question spans multiple topics, suggest splitting into separate `/ask` invocations.
---
## /intake
# /intake — Bulk Knowledge Import + Doc-Anchored Capture
Two modes:
- **Bulk mode (default)** — Scan files, directories, or URLs for knowledge-worthy content and stage findings to the existing backlogs (insights / decisions / extraction). Multi-source, category-based, dedup-aware. Same surface as prior /intake versions.
- **Doc mode (`/intake doc`)** — Capture a single doc as a structured intake entry at `intake/docs/{YYYY-MM-DD}-{slug}.md` with a 5-section body: what the doc claims / worth keeping / contested or unclear / action implied / my reaction. For when you're reading something and want a thoughtful capture rather than a bulk scan.
## Step 0: Resolve Config + Mode Detection
Read `.cursor/aria-knowledge.local.md` and extract `knowledge_folder`. If the file doesn't exist, stop: "aria-knowledge is not configured. Run /setup to get started."
Use `{knowledge_folder}` as the base path for all file operations in subsequent steps.
**Mode detection (first match wins):**
1. First arg `== extract` (case-insensitive) → `mode = extract`. The remaining arg is ONE source (URL / file / dir / doc-URL); decompose it into backlog entries by running the bulk-scan logic (Step 1 onward) on that single source. `extract` is **standalone** — it does NOT combine with `doc`/`thread` (no `/intake extract doc …`); a doc to decompose is just `/intake extract <doc-url>` (extract fetches it). If the arg after `extract` is literally `doc` or `thread`, treat as malformed and prompt for clarification.
2. First arg `== doc` → `mode = doc`. Jump to "Doc Mode Steps" (D1–D6), unchanged.
3. First arg `== thread` → `mode = thread`. Jump to "Thread Mode Steps" (T1–T3) below.
4. *(auto)* Single arg whose host is a chat/email service (`slack.com`, `teams.microsoft.com`, `mail.google.com`, outlook/office) → `mode = thread` (no keyword needed).
5. *(auto)* Single arg matching `^https?://` OR free text (not an existing path) → `mode = clip-whole`. Jump to "Clip-Whole Steps" (C1–C3) below.
6. *(auto)* Args are existing file paths / directories / globs, OR multiple sources → `mode = bulk`. Proceed to Step 1 (bulk scan, unchanged).
7. No args → ask: "What would you like to intake? (a URL, text, file/dir/glob; or `extract <src>` to decompose, `doc <src>` for a reflection capture, `thread <id>` for a chat/email thread)".
The mental model: default = *capture this whole*; `extract` = *decompose it*; `doc` = *reflect on it (5-section)*; `thread` = the one source that needs naming (or auto-detected from a chat URL). **Note (behavior change from prior versions):** a bare URL now CLIPS WHOLE — it no longer auto-mines into backlogs. To mine a single URL, use `/intake extract <url>` (or let `/audit-knowledge` Step 2f decompose the clipping later).
---
## Doc Mode Steps (mode = doc only)
Doc mode runs steps D1 → D6 to completion and exits. **Do not** run any bulk-mode step (Step 1 onward) in doc mode.
### Step D1: Acquire Doc Source
The source can be a URL, file path, or just a title (when capturing notes on a doc you read elsewhere).
- **If args after `doc` contain a URL:** use as `source_url`; attempt WebFetch in D2 to extract title/author/content
- **If args after `doc` contain a file path:** use as `source_path`; Read in D2
- **If args after `doc` are plain text (no URL/path detected):** treat as `source_title`; no content fetch — user fills body manually in D3
- **If no args after `doc`:** prompt: "What doc are you capturing? Paste a URL, file path, or title."
### Step D2: Read or Note Doc Content
- **URL source:** WebFetch the URL. Extract title, author (if discoverable from byline/meta), and key content. Respect copyright — capture summary and key claims for downstream synthesis, not full page text.
- **File path source:** Read the file. If very large (>500 lines), use the same chunked-scan strategy as bulk mode (first 100, last 50, section headers, then targeted areas).
- **Title-only source:** No content fetch. User will fill body sections manually in D3.
Capture the following for D3:
- `source_title` (from page title, file frontmatter, or user-provided string)
- `source_url` (if URL; else omit)
- `source_author` (if discoverable; else omit per #28a-5)
- `captured_at` (current ISO 8601 timestamp)
- `read_at` (defaults to `captured_at` — D4 preview lets user adjust if they read the doc earlier)
- Summarized claims, candidate "worth keeping" items, and any contested or action-implied content noticed during the scan
### Step D3: Populate Template
1. Read `knowledge/intake/docs/_TEMPLATE.md (or inline structure from Step D3 below if missing)` to load the body template.
2. Generate slug from `source_title`: lowercase, hyphenated, alphanumeric only, max ~60 chars. Example: `"The Bitter Lesson"` → `the-bitter-lesson`. If `source_title` is empty, use `doc-{HHMMSS}` as fallback.
3. Resolve target path: `{knowledge_folder}/intake/docs/{YYYY-MM-DD}-{slug}.md`. If file already exists at that path, append `-2`, `-3`, etc. to slug until unique.
4. Fill the frontmatter using captured fields from D2. Omit `source_url` if absent; omit `source_author` if absent. Always populate `captured_at`, `read_at`, `type: intake-doc`.
5. Suggest 2-5 tags based on doc topic (cross-check existing `index.md` tags to prefer canonical names; new tags are fine but flag them).
6. Suggest 2-4 `semantic-hints:` free-form phrases that match how a future query might reach this doc (per the convention in `template/README.md`).
7. Pre-fill body sections from the D2 scan:
- **What the doc claims** — 2-4 sentence summary in your own words
- **Worth keeping** — bullet list of insights/quotes/data points worth durable storage; aim for 2-6 bullets
- **Contested or unclear** — populate if the scan surfaced anything debatable; leave empty (or omit the section) if nothing flagged
- **Action implied** — populate if the doc suggests a decision or next step relevant to ongoing work; omit if N/A
- **My reaction** — leave as a single-line placeholder (`{Your reaction — 1-3 sentences. This section is yours, not the doc's.}`) for the user to fill, since "reaction" is the user's voice not Claude's
### Step D4: Preview
Show the populated entry before writing. Format:
```
## Doc Intake Preview
**Target:** {knowledge_folder}/intake/docs/{YYYY-MM-DD}-{slug}.md
[full populated entry: frontmatter + body]
---
Save to intake/docs/?
- `yes` — write the file as shown
- `edit {section}` — revise a specific section (claims / keeping / contested / action / reaction / tags / hints / title / slug)
- `skip` — abort, write nothing
```
Wait for explicit response. Allow multiple `edit` directives in sequence (re-show preview after each revision).
### Step D5: Write
On `yes`, write the entry to `{knowledge_folder}/intake/docs/{YYYY-MM-DD}-{slug}.md`. Create the `intake/docs/` subfolder if it doesn't exist (this is the first doc-mode capture).
### Step D6: Report
```
## Doc Intake Complete
- **Source:** {source_title or source_url or "untitled"}
- **Path:** {knowledge_folder}/intake/docs/{YYYY-MM-DD}-{slug}.md
- **Tags:** {tag list}
Entry staged in intake/docs/ for next /audit-knowledge to review and promote.
```
**Exit after report.** Doc mode runs D1 → D6 only; bulk-mode steps (Step 1 onward) are not executed.
---
## Clip-Whole Steps (mode = clip-whole)
Capture the source **whole** as one clipping for later review at `/audit-knowledge` Step 2f. Runs C1–C3 and exits. (Absorbs the retired `/intake`.)
### C1: Acquire content
- **URL:** WebFetch; extract the page title + a summary (do NOT copy full page content — respect copyright). Capture the URL as `source`.
- **Text snippet:** use the provided text verbatim as the body; title = first line.
### C2: Write the clipping
Resolve target `{knowledge_folder}/references/sources/{slug}.md` (slug from title; append `-2`/`-3` until unique). Write:
```
---
source: [URL or "manual"]
date: YYYY-MM-DD
tags: [user-provided tags, or auto-detected from index.md, or empty array]
---
# [Title or first line of text]
[Summary for URLs, full text for snippets]
```
**Tag detection:** if the user didn't provide tags, match title/content words against known tags in `{knowledge_folder}/index.md` (if it exists). Only high-confidence matches — don't guess.
### C3: Confirm
```
Clipped to references/sources/{slug}.md
Tags: [tags or "none"]
Reviewed at the next /audit-knowledge run (Step 2f).
```
**Exit after C3.**
---
## Thread Mode Steps (mode = thread)
Pull a chat/email thread via a `~~chat` (Slack/Teams) or `~~email` (Gmail/MS365) MCP into one clipping. Runs T1–T3 and exits. (This mode absorbs the former standalone thread-capture skill, retired v2.33.0.)
### T1: MCP availability check
If no `~~chat`/`~~email` MCP is connected/authenticated, surface:
> "`thread` mode needs a chat or email MCP connected. These are bundled with the plugin — run the MCP's authenticate flow (e.g. Slack auth), or connect it via your MCP config, then retry. (This is NOT Cowork-only — thread mode runs in Claude Code once the MCP is authed.)"
Then exit. **Do NOT redirect to Cowork** — the capability is Code-native once the MCP is authenticated. (The ADR-094 Bash-availability runtime gate is separate and only fires on a genuine runtime mismatch, not on an unauthenticated MCP.)
### T2: Pull the thread
Fetch thread content + metadata (participants, timestamps, channel/subject) via the connected MCP.
### T3: Write the clipping
Write to `{knowledge_folder}/references/sources/{slug}.md` with the same frontmatter shape as C2 (`source` = thread URL/id; body = thread metadata + messages). Confirm as in C3.
**Exit after T3.**
---
## Step 1: Parse Sources
The user provides one or more sources as arguments. Each source can be:
- **File path** — a single file (e.g., `./docs/architecture.md`, `ss/AGENTS.md`)
- **Directory** — scan all `.md` files recursively (e.g., `./docs/`, `ss/`)
- **Glob pattern** — match specific files (e.g., `ss/**/*.md`, `./notes/*.txt`)
- **URL** — fetch and scan web content (e.g., `https://docs.example.com/api`)
For each source:
1. Verify it exists (for paths) or is reachable (for URLs)
2. Report what was found: "Found N files to scan" or "Fetched URL: [title]"
3. If a directory, list the files that will be scanned and ask for confirmation before proceeding (directories could contain hundreds of files)
**Limits:**
- Max 20 files per invocation (suggest splitting into multiple runs for larger sets)
- For URLs, fetch via WebFetch and extract content — do NOT copy full page content (respect copyright). Extract a summary and key points only.
If no argument is provided, ask: "What would you like to intake? Provide a file path, directory, glob pattern, or URL."
## Step 2: Read Content
For each source file or URL:
1. Read the content
2. Note the source path/URL for attribution
3. If the file is very large (>500 lines), scan in chunks — read the first 100 lines, last 50 lines, and any section headers to identify knowledge-dense areas, then read those areas selectively
For directories, process files in alphabetical order.
For multi-file sources (directory or glob), issue Read calls for all files in a single parallel tool-use block. Content scanning in Step 3 runs in the main thread after reads complete. Exception: URL sources are fetched individually via WebFetch since each request is a network operation.
## Step 3: Scan for Knowledge
Review each source for the same five categories as `/extract`:
### Insights
- Technical observations, patterns, architectural descriptions
- Non-obvious behaviors or gotchas documented in the source
- Lessons learned or retrospective notes
### Decisions
- Architectural or design choices with rationale
- Technology selections, approach decisions
- Constraints or trade-offs documented in the source
### Feedback / Conventions
- Coding conventions, style rules, workflow preferences
- Team agreements or process documentation
- "Do this, not that" patterns
### Project Context
- Status information, roadmaps, milestone descriptions
- Team structure, ownership, dependency maps
- Integration points or external system documentation
### References
- URLs, tools, services, API endpoints mentioned
- External documentation pointers
- Vendor or third-party integration details
**Be selective** — not every paragraph is knowledge. Focus on content that would help future sessions: patterns, decisions, constraints, and non-obvious information. Skip boilerplate, auto-generated content, and implementation details that are better found by reading the code directly.
## Step 4: Deduplicate
Issue Read calls for the three backlog files AND `{knowledge_folder}` scan targets in a single parallel tool-use block. Dedup comparison runs in the main thread after reads complete.
For each finding, check against:
1. Existing entries in `{knowledge_folder}/intake/insights-backlog.md`
2. Existing entries in `{knowledge_folder}/intake/decisions-backlog.md`
3. Existing entries in `{knowledge_folder}/intake/extraction-backlog.md`
4. AGENTS.md files in the current working directory
5. Existing knowledge files in `{knowledge_folder}/`
**Skip anything already captured.** Note skipped items in the preview.
## Step 5: Preview Findings
Present all findings grouped by category **before staging anything**:
```
## Intake Preview
**Sources scanned:** N files from [path/URL summary]
**Findings:** N items (N insights, N decisions, N feedback, N project, N references)
**Skipped:** N duplicates
### Insights (N)
1. [brief description] — from [source file]
2. [brief description] — from [source file]
### Decisions (N)
1. [brief description] — from [source file]
### Feedback / Conventions (N)
1. [brief description] — from [source file]
### Project Context (N)
1. [brief description] — from [source file]
### References (N)
1. [brief description] — from [source file]
Stage all to backlogs? (all / numbers to exclude / none)
```
## Step 6: Stage Approved Items
Based on user response:
- **"all"** — append everything to the appropriate backlogs
- **Numbers to exclude** (e.g., "exclude 3, 7") — stage everything except the specified items
- **"none"** — abort, stage nothing
Route each approved item to the appropriate backlog file using the same format as `/extract`:
### Insights → `{knowledge_folder}/intake/insights-backlog.md`
```markdown
### YYYY-MM-DD — [project or "intake"] — Imported from [source filename]
- Insight bullet 1
- Insight bullet 2
```
### Decisions → `{knowledge_folder}/intake/decisions-backlog.md`
```markdown
### YYYY-MM-DD — [project or "intake"] — Imported from [source filename]
**Decision:** What was decided
**Why:** Rationale (if documented in source)
**Alternatives considered:** (if documented in source, otherwise omit)
```
### Feedback, Project Context, References → `{knowledge_folder}/intake/extraction-backlog.md`
```markdown
### YYYY-MM-DD — [type: feedback|project|reference] — Imported from [source filename]
**Content:** What was captured
**Source:** [file path or URL]
```
## Step 7: Report
```
## Intake Complete
- **Sources:** N files scanned
- **Insights:** N staged
- **Decisions:** N staged
- **Feedback:** N staged
- **Project context:** N staged
- **References:** N staged
- **Skipped:** N duplicates, N excluded by user
Knowledge staged in backlogs for next /audit-knowledge to review and promote.
```
## Rules
- **Always preview before staging** — unlike `/extract`, intake operates on content the user may not have reviewed. Show findings first. Applies to both bulk mode (Step 5 preview) and doc mode (Step D4 preview).
- **Attribute sources** — every staged item includes the source file path or URL so the audit process knows where it came from. Bulk mode adds source attribution per finding; doc mode captures `source_url`, `source_title`, `source_author` (when known) in frontmatter.
- **Respect copyright** — for URLs, capture summaries and key points, never full page content. The URL itself is the reference. Applies to both modes — doc mode's "What the doc claims" should be in your own words, not a verbatim excerpt.
- **Don't over-extract** — a 500-line architecture doc might yield 3-5 knowledge items, not 50. Extract the patterns and decisions, not every detail. In doc mode, "worth keeping" usually has 2-6 bullets, not 20.
- **Project attribution** — if the source path indicates a project (e.g., `ss/`, `cs/`, `df/`), tag the entries with that project. Otherwise use "intake" or "cross". Same rule for both modes.
- **Large directories need confirmation** — if a directory scan finds >10 files, list them and ask before proceeding. The user may want to narrow the scope. Bulk-mode only — doc mode is always single-source.
- **One intake, one scope** — don't mix sources from different projects in a single intake. If the user provides paths from multiple projects, process each project's sources as a separate group with its own attribution. Doc mode is inherently single-source so this rule applies only to bulk mode.
- **Doc mode: reaction is the user's voice** — pre-fill "What the doc claims" / "Worth keeping" / "Contested" / "Action" from your D2 scan. Leave "My reaction" as a single-line placeholder for the user to fill. Don't fabricate an opinion you don't actually hold.
- **Doc mode: lazy subfolder creation** — `intake/docs/` is created on first doc-mode capture, not bootstrapped on /setup. Doesn't exist until needed.
- **Doc mode: slug collisions** — if `{date}-{slug}.md` already exists, append `-2`, `-3`, etc. Don't overwrite. The audit process will dedup if the captures are about the same doc.
- **Doc mode: title-only captures are valid** — if the user doesn't have a URL or file, just a title, accept that. The 5-section body still has value as structured notes-while-reading.
---
## /meeting-notes
# /meeting-notes — Capture Meeting Transcript to Intake
Save a meeting transcript or notes to `intake/meetings/{YYYY-MM-DD}-{slug}.md` with structured participants / topics / action items / decisions sections. Source can be a `~~docs` MCP (Notion meeting page, Confluence meeting doc) OR pasted transcript text (Granola export, raw transcript, hand-written notes).
## Step 0: Resolve Config
Read `.cursor/aria-knowledge.local.md` and extract `knowledge_folder`. If the file doesn't exist, stop: "aria-knowledge is not configured. Run /setup to get started."
Verify `{knowledge_folder}/intake/meetings/` exists. If not, create it (lazy creation for first-time use of this skill).
## Step 1: Probe Connected MCPs (with paste fallback)
Check Cursor's available MCP tool list for `~~docs` MCPs:
- **`~~docs`** (notion, atlassian, box, egnyte, google docs): if connected, available for MCP-sourced meeting docs.
**Branching logic** (this skill diverges from other MCP-consuming skills here — see [ADR-015](https://github.com/mikeprasad/knowledge/blob/main/projects/aria-cowork/decisions/015-capability-probe-pattern.md) §"Application across the 5 MCP-consuming skills"):
- **If `~~docs` IS connected AND input looks like a URL/ID:** route through MCP fetch (Step 2 MCP branch).
- **If `~~docs` IS NOT connected OR input is `paste` (or empty):** offer paste fallback (Step 2 paste branch).
- **If input is `paste` even with `~~docs` connected:** respect the explicit paste choice; skip MCP fetch.
Unlike `/intake extract`, `/intake thread`, `/digest`, and `/sync-decisions`, this skill does NOT hard-stop when no `~~docs` MCP is present. Meeting transcripts often arrive via paste (from Granola, from a meeting tool's export button, from hand-typed notes) — closing that path would defeat the skill's primary use case.
## Step 2 (MCP branch): Parse + Fetch from ~~docs
Same as `/intake extract` (MCP-doc path) Step 2-3. Routing table:
| Input shape | Routes to |
|---|---|
| Contains `notion.so` | notion (`~~docs`) |
| Contains `atlassian.net/wiki` | atlassian (`~~docs`) |
| Contains `docs.google.com/document` | google docs (`~~docs`) |
| Bare ID + known MCP | use the connected one |
Fetch the doc body. Proceed to Step 3.
## Step 2 (paste branch): Prompt for transcript
If no MCP fetch is possible (or the user passed `paste`), prompt:
```
Paste the meeting transcript or notes below. End with a blank line + `---END---` on its own line. Common sources:
- Granola export (Markdown)
- Slack thread copy-paste
- Hand-typed notes
- Zoom / Teams / Meet auto-transcript export
- Any plaintext or Markdown
I'll structure it into participants / topics / action items / decisions.
```
Wait for the paste. Read the content until `---END---` marker. Proceed to Step 3.
## Step 3: Structure the Transcript
Parse the transcript body to identify these sections (Claude infers from content; this is NOT a strict parser — handle informal transcripts):
1. **Participants** — names + roles if present. Look for lists at the top, "@" mentions, speaker labels.
2. **Date + duration** — if not explicit, ask user or default to today.
3. **Topics discussed** — section headings, bullet points, "we talked about" markers.
4. **Action items** — "TODO", "[ ]", "@person will", "action:" markers. Extract assignee + description + due date if present.
5. **Decisions** — "we decided", "agreed to", "going with X over Y" patterns. Extract the decision + rationale if stated.
6. **Open questions** — "?", "unresolved", "follow-up", "need to figure out" markers.
7. **Topic-level summary** — 1-2 sentences per major topic.
Be conservative: if a section can't be reliably extracted, mark it `(none identified)` rather than fabricating.
## Step 4: Compose Meeting Note
Slug-ify the meeting title for the filename (from input arg, or extracted from first heading, or "meeting" as fallback). Lowercase, hyphen-separated, ASCII-only, max ~50 chars.
Filename: `intake/meetings/{YYYY-MM-DD}-{slug}.md`. If a file with that name exists, append `-2`, `-3` to deduplicate.
Body template:
```markdown
---
date: <YYYY-MM-DD>
title: <meeting title>
source: <doc-url OR "pasted transcript">
source_type: <notion|atlassian|box|egnyte|google docs|paste>
participants: [<name list>]
duration: <if known>
tags: [meeting, <project-tag-if-inferable>]
---
# <Meeting title>
## Context
- **Date:** <YYYY-MM-DD>
- **Source:** <doc-url OR "pasted transcript">
- **Participants:** <comma-separated list>
- **Duration:** <if known, else omit>
## Topics
<for each major topic, with 1-2 sentence summary:>
### <Topic name>
<summary>
## Action Items
<for each action item:>
- [ ] **<assignee>** — <action description> <(due: <date> if present)>
<or: "(none identified)" if section is empty>
## Decisions
<for each decision:>
- **<decision>** — <rationale if stated, else "no rationale captured">
<or: "(none identified)">
## Open Questions
- <question>
<or: "(none identified)">
## Raw Transcript
<the original transcript body, preserved verbatim for reference. If from MCP fetch, include source-doc URL at top of this section.>
---
## Reaction
<intentionally left empty — the user's reaction / why this meeting is worth keeping in knowledge. /audit-knowledge surfaces this for review.>
```
## Step 5: Write + Report
Write the composed meeting note to `{knowledge_folder}/intake/meetings/{date}-{slug}.md`.
Report to user:
```
Captured meeting "<title>" to intake/meetings/<date>-<slug>.md.
- Source: <vendor or "pasted">
- Participants: <N>: <list, max 5 + "and N more">
- Topics: <N>
- Action items: <N>
- Decisions: <N>
- Open questions: <N>
Next: add a reaction in the "## Reaction" section (or wait for /audit-knowledge to surface it). Action items + decisions may be worth extracting separately via `/intake extract` on this file if you want them in the standard intake backlogs.
```
## Rules
- **Never delete the source doc.** Read-only on the MCP side.
- **Preserve the raw transcript verbatim.** The structured sections are derived; the raw transcript is the source-of-truth. Both ship in the same file so audit / future re-extraction has the source.
- **Strip secrets if obvious** — same redaction rules as `/intake thread`. Note redactions in frontmatter.
- **Default `(none identified)` over fabrication.** If a section can't be reliably parsed, mark it empty rather than guess.
- **One meeting per invocation.** Multiple meetings = multiple `/meeting-notes` calls.
## Notes
- The Reaction section pattern matches `/intake doc` (v2.17.0) and `/intake thread` (v2.18.0 origin) — capture artifacts ship with a user-fillable "why this matters" slot that Claude never autocompletes.
- Bidirectional per [ADR-014](https://github.com/mikeprasad/knowledge/blob/main/projects/aria-cowork/decisions/014-bidirectional-feature-flow.md) — aria-cowork v0.4.0 imports byte-faithfully.
- Output schema is byte-identical per [ADR-013](https://github.com/mikeprasad/knowledge/blob/main/projects/aria-cowork/decisions/013-cowork-modified-skills-schema-identical-outputs.md). Both plugins write to `intake/meetings/` in the shared knowledge folder.
- **Paste-fallback divergence** documented in [ADR-015](https://github.com/mikeprasad/knowledge/blob/main/projects/aria-cowork/decisions/015-capability-probe-pattern.md) §"Application across the 5 MCP-consuming skills" — this is the one skill that doesn't hard-stop on missing MCPs.
- The skill is **intake-only** — it doesn't promote meeting notes to `references/` or `decisions/`. That's `/audit-knowledge`'s job at next audit, or the user can manually promote via `/intake extract` on this file to split out decisions/action items.
- Composes naturally with Granola exports — Granola's Markdown format already includes participants + transcript + (optionally) extracted action items. The paste branch picks this up cleanly.
---
## /digest
# /digest — Cross-Tool Weekly Rollup
Synthesize a digest of activity across connected MCPs into `intake/digests/{YYYY-MM-DD}.md`. Pulls from `~~chat` + `~~email` + `~~project tracker` + `~~docs` to produce a "what's pending / what shipped / what's blocked" rollup. Composite of all 4 categories — the most cross-tool of the v2.18.0 skills.
## Step 0: Resolve Config
Read `.cursor/aria-knowledge.local.md` and extract `knowledge_folder`. If the file doesn't exist, stop: "aria-knowledge is not configured. Run /setup to get started."
Lazily create `{knowledge_folder}/intake/digests/` if it doesn't exist.
## Step 1: Probe Connected MCPs (all 4 categories)
Check Cursor's available MCP tool list for each `~~category`. The digest runs with ANY non-zero set of connected MCPs — gather from whichever are connected, surface gaps for the rest.
| Category | MCP options | Status |
|---|---|---|
| `~~chat` | slack, ms365 | <connected: list / not connected> |
| `~~email` | gmail, ms365 | <connected: list / not connected> |
| `~~project tracker` | linear, asana, atlassian, monday, clickup, notion | <connected: list / not connected> |
| `~~docs` | notion, atlassian, box, egnyte, google docs | <connected: list / not connected> |
If NO MCPs in ANY category are connected, output the standard fallback notice and stop:
> No required MCPs connected for `/digest`. Connect at least one of: Slack/MS365 (~~chat), Gmail/MS365 (~~email), Linear/Asana/etc. (~~project tracker), or Notion/Confluence/etc. (~~docs) via Cursor Settings → MCP. See `plugin-claude-cowork/CONNECTORS.md` in the aria-knowledge repo. Skipping this run.
Per [ADR-015](https://github.com/mikeprasad/knowledge/blob/main/projects/aria-cowork/decisions/015-capability-probe-pattern.md) — degrade gracefully for missing categories; don't fabricate.
## Step 2: Parse Time Window
Determine the digest window from args:
| Arg | Window |
|---|---|
| (none) or `--week` | Last 7 days (today minus 7) |
| `--since YYYY-MM-DD` | From that date to today |
| `--since YYYY-MM-DD --until YYYY-MM-DD` | Closed range |
| `--month` | Last 30 days |
| `--quarter` | Last 90 days |
Default: 7-day window. State the window explicitly in the digest header.
## Step 3: Gather per Connected Category
For each CONNECTED category, run the appropriate fetch. Skip disconnected categories (note as gaps in the digest output).
### 3a. `~~chat` (if connected)
For Slack: search for messages where the user is mentioned (`@user`) OR is the author, in the time window. Also fetch unresolved threads (the user posted and got no response after 24+ hours).
For MS365 Teams: similar — channel mentions + direct messages.
Gather:
- Mention count per channel
- Top 5-10 threads ranked by reply count or recency
- Threads where the user owes a response (last message is not theirs + question mark or @-mention)
### 3b. `~~email` (if connected)
For Gmail or MS365: search inbox for unread messages in the window, sent items in the window, and emails flagged/starred.
Gather:
- Top 10 sent emails by reply-count (active threads the user drove)
- Unread count by sender
- Flagged / starred items
### 3c. `~~project tracker` (if connected)
For Linear / Asana / Atlassian / Monday / ClickUp / Notion-as-tracker: query for issues assigned to the user with status changes in the window.
Gather (per tool, format-normalized):
- **Pending:** open issues assigned, sorted by priority + last-update
- **Shipped:** issues moved to Done/Closed/Shipped in the window
- **Blocked:** issues with Blocked status, or stale (no update in 7+ days while still Open)
- **Mentions:** issues where the user was mentioned in comments
### 3d. `~~docs` (if connected)
For Notion / Confluence / etc.: list docs touched by the user (created OR edited) in the window. List docs the user was @-mentioned in.
Gather:
- Recently touched (top 10, ordered by edit time)
- Mentions / pages the user was tagged in
## Step 4: Synthesize the Digest
Compose a unified narrative from the gathered data. Sections:
1. **Window & sources** — explicit time window + which MCPs were available + which were not
2. **What's pending** — open items needing the user's action (from project tracker + threads owed responses + unread flagged emails)
3. **What shipped** — closed/completed/moved items in the window (from project tracker + sent emails noting completion + docs marked done)
4. **What's blocked** — items in Blocked status, stale items, threads waiting on others
5. **Cross-tool patterns** — observations that span sources (e.g., "3 mentions of Project Phoenix across chat + tracker but no doc in `~~docs` for it; consider creating one")
6. **Gaps surfaced** — categories that weren't connected + what would be added by connecting them
Be conservative: rank by signal, not volume. A digest with 12 strong items beats 47 noisy ones.
## Step 5: Write + Report
Filename: `intake/digests/{YYYY-MM-DD}.md`. Use today's date (the digest's *creation* date, not the window's end date). If a file with that name exists, append `-2`, `-3` for multiple digests in one day.
Body template:
```markdown
---
date: <YYYY-MM-DD>
window_start: <YYYY-MM-DD>
window_end: <YYYY-MM-DD>
sources_connected: [<list of connected MCP names>]
sources_unavailable: [<list of disconnected categories>]
tags: [digest, weekly|monthly|quarterly]
---
# Digest — <window_start> → <window_end>
## Sources
**Connected:** <list of MCPs that contributed data>
**Not connected:** <list of categories that would have added value> — to fill these gaps, connect the relevant MCP via Claude Code's MCP config or Cowork Settings → Connectors.
## What's pending
<bulleted list, grouped by source-of-priority. Each item includes a link back to the source if a URL is exposed by the MCP.>
## What shipped
<bulleted list of completed items in the window.>
## What's blocked
<bulleted list of stuck items + brief why.>
## Cross-tool patterns
<observations that span 2+ sources. Skip section if nothing notable.>
## Gaps surfaced
<categories that weren't connected + what they would have added. Skip if all 4 connected.>
---
## Reaction
<intentionally left empty — the user's reaction. /audit-knowledge surfaces this for review.>
```
Report to user:
```
Digest written to intake/digests/<date>.md.
- Window: <start> → <end>
- Sources connected: <N>/4
- Pending items: <n>
- Shipped items: <n>
- Blocked items: <n>
Disconnected categories surfaced N gap callouts in the digest. Connect more MCPs to enrich next week's digest.
```
## Rules
- **Never auto-act on digest content.** This skill READS from MCPs; it does not write back. (For external writes, see `/sync-decisions` — that's the only v2.18.0 skill that writes externally.)
- **Always surface gaps.** If `~~docs` wasn't connected, the digest must say so — don't silently omit categories.
- **Default conservative ranking.** Better 5 strong items per section than 30 weak ones. The digest is for *Mike-reading-on-Sunday-night*, not exhaustive audit.
- **Strip secrets if obvious** — same redaction rules as other skills.
- **Window defaults to 7 days.** Re-invoke with `--month` or `--quarter` for longer rollups (rare; weekly is the standard cadence).
## Notes
- Most expensive of the v2.18.0 skills in terms of MCP calls — calls 4 categories' worth of tools in one invocation. Run cadence: weekly (Sunday or Monday morning), not on every session.
- Bidirectional per [ADR-014](https://github.com/mikeprasad/knowledge/blob/main/projects/aria-cowork/decisions/014-bidirectional-feature-flow.md) — aria-cowork v0.4.0 imports byte-faithfully. The Cowork-side context (conversational sessions, more cross-tool synthesis built into the workflow) makes this skill particularly load-bearing on the Cowork side.
- Output schema is byte-identical per [ADR-013](https://github.com/mikeprasad/knowledge/blob/main/projects/aria-cowork/decisions/013-cowork-modified-skills-schema-identical-outputs.md).
- Probe semantics per [ADR-015](https://github.com/mikeprasad/knowledge/blob/main/projects/aria-cowork/decisions/015-capability-probe-pattern.md) — graceful degradation built-in for partial-connection scenarios.
- Composes with `/audit-knowledge` — digests are intake artifacts and route through standard audit disposition. Most digests will be `Defer` (interesting but not promotion-worthy) or `Bundle` (cluster patterns across multiple digests for a cross-week insight).
- Inspired by Anthropic's productivity plugin `update --comprehensive` mode, adapted for ARIA's intake-then-audit model rather than productivity's TASKS.md sync model.
---
## /sync-decisions
# /sync-decisions — Mirror Decisions to External Docs
Read approved decisions from `{knowledge_folder}/decisions/` and write them out to a connected `~~docs` MCP destination (a Notion page, Confluence space, Google Doc, etc.). The only v2.18.0 skill that writes externally; embeds Rule 22 advisory preamble per [ADR-016](https://github.com/mikeprasad/knowledge/blob/main/projects/aria-cowork/decisions/016-rule-22-advisory-preamble-for-external-writes.md).
## Step 0: Resolve Config
Read `.cursor/aria-knowledge.local.md` and extract `knowledge_folder`. If the file doesn't exist, stop: "aria-knowledge is not configured. Run /setup to get started."
Verify `{knowledge_folder}/decisions/` exists. If not, stop: "No decisions/ folder found. Nothing to sync."
Lazily create `{knowledge_folder}/logs/sync-decisions.md` if it doesn't exist (used by Step 7 for sync history).
## Step 1: Probe Connected MCPs
Check Cursor's available MCP tool list for `~~docs` MCPs that support WRITE operations:
- **`~~docs`** (notion, atlassian, box, egnyte, google docs): if connected, check the MCP's exposed tools — `~~docs` MCPs that only expose `read_page` / `search_pages` are READ-ONLY for this skill's purpose. Need a write surface (`create_page`, `update_page`, `append_block_children`, or equivalent).
If NO `~~docs` MCP with write capability is connected, output the standard fallback notice and stop:
> No required MCPs connected for `/sync-decisions`. This skill writes externally — needs a `~~docs` MCP with write capability (page creation or block append). Connect Notion, Atlassian (Confluence), Box, Egnyte, or Google Docs via Cursor Settings → MCP. See `plugin-claude-cowork/CONNECTORS.md` in the aria-knowledge repo. Skipping this run.
Per [ADR-015](https://github.com/mikeprasad/knowledge/blob/main/projects/aria-cowork/decisions/015-capability-probe-pattern.md).
## Step 2: Enumerate Decisions to Sync
Determine which decisions are candidates based on args:
| Arg | Candidates |
|---|---|
| (none) | All decisions whose `synced_to_~~docs:` frontmatter field is absent OR older than file's last-modified time |
| `<decision-slug>` | Just the named decision (e.g., `/sync-decisions 069-karpathy-4-line-foundation`) |
| `--all` | Every decision in `decisions/`, regardless of prior sync state |
| `--since YYYY-MM-DD` | Decisions modified or created on/after that date |
Read each candidate decision file from `{knowledge_folder}/decisions/`. Cache them in working memory.
If zero candidates: report "No decisions need syncing" and stop.
## Step 3: Resolve Sync Target
Determine the destination on the `~~docs` MCP side. Args + heuristics:
| Source of target | Used when |
|---|---|
| `--target <space-or-page-url>` from args | User specified |
| `sync_target:` frontmatter field on the decision | Decision specifies own target |
| `default_sync_target` from aria-knowledge.local.md (if set) | User has a global default |
| Ask user interactively | None of above resolved |
If target resolution requires asking the user, present:
```
Which destination?
- (a) Existing page URL: paste the ~~docs page URL where decisions should be appended
- (b) New top-level page: I'll create `aria-knowledge-decisions` (or a name you provide) in the workspace root
- (c) Cancel this sync run
Decision-specific syncs (where decisions/<slug>.md has its own `sync_target:` field) override this; this prompt only fires for decisions without a per-file target.
```
If no target resolvable + no interactive answer, abort with clear message: "No sync target resolvable. Set `default_sync_target` in aria-knowledge.local.md or invoke with `--target <url>`."
## Step 4: Rule 22 Advisory Preamble (per ADR-016)
For EACH candidate decision, walk through this checklist:
```
Before each external write — Rule 22 advisory checklist
This skill writes to an external system. aria-knowledge's PreToolUse hook gates Edit/Write but does NOT catch MCP write tools — Rule 22 here is text-only discipline per ADR-016.
1. **State the change in one sentence.** "Writing decision <slug> to <target-url-or-page-title>."
2. **Why this destination?** Is this the right audience for this decision content? Could it leak into a view the user didn't intend (public workspace, wrong project, wrong channel)?
3. **Reversibility check.** Can the user edit/delete the write from <vendor> after it lands? (For Notion / Confluence / Google Docs: YES, user can edit at destination. For Box / Egnyte: depends on workspace permissions. Note any constraint explicitly.)
4. **Surface for explicit go.** Present the full proposed write content + destination. Wait for explicit user `yes` / `go` before calling the write tool.
```
Concretely: for each decision, surface this block to the user:
```
Decision: <slug>
Source file: decisions/<slug>.md
Destination: <target-url-or-page-title> on <vendor>
Operation: <create new page | append to existing page | update existing page>
Reversibility: <user can edit at destination: yes/no/constrained>
--- Proposed write content (preview) ---
<the full decision content as it will appear externally — markdown if Notion/Confluence; plaintext if Google Doc; etc.>
--- End preview ---
Ready to write? (yes / no / edit)
```
## Step 5: Per-Decision Go-Gate
Wait for the user's explicit response:
- **`yes` / `go`:** proceed to Step 6 for THIS decision.
- **`no` / `skip`:** mark this decision as skipped in the sync log; move to the next candidate.
- **`edit`:** ask the user what to change in the preview content, regenerate, re-present, ask again.
- **`yes to all writes` (literal):** authorize this AND all remaining decisions in the candidate list without per-decision confirmation. This is the ONLY way to batch — explicit user opt-in per ADR-016. The exact phrase "yes to all writes" must appear in the user's reply; partial forms like "yes to all" or "yes all" do NOT trigger the batch carve-out (per the retro 2026-05-19 finding aligning SKILL.md to ADR-016's literal-phrase requirement).
- **`cancel`:** abort the entire sync run; nothing else gets written.
- **(silence or non-matching reply):** treat as `no`; re-prompt.
Per ADR-016: "Do NOT proceed on implicit consent. Do NOT batch multiple writes behind a single `yes` unless the user explicitly says 'yes to all writes' in this invocation."
## Step 6: Execute Write
Call the connected `~~docs` MCP's write tool with the resolved target + proposed content.
For each MCP:
- **Notion:** `pages.create` (new page) or `blocks.children.append` (append to existing page). Set `parent` to the target page; populate `properties.title` from decision title.
- **Atlassian (Confluence):** `confluence.pages.create` or `confluence.pages.update`. Set `space` + `parent` per target.
- **Google Docs:** `documents.batchUpdate` with InsertText requests appending to the target doc.
- **Box / Egnyte:** create a new doc file (typically `.md` or `.txt`) at the resolved path; vendor-specific tool name.
On success, capture:
- The destination URL / ID returned by the MCP
- The timestamp of the write
- The size of the write
On failure, surface the error and ask the user: continue with remaining decisions? Abort?
## Step 7: Update Frontmatter + Log
For each successfully synced decision:
**Update the decision file's frontmatter** to record the sync — add or update:
```yaml
synced_to_~~docs:
- target: <destination URL>
vendor: <notion|atlassian|box|egnyte|google docs>
synced_at: <ISO timestamp>
operation: <create|append|update>
```
This is the only modification to the source file — content is unchanged; only frontmatter records the sync state.
**Append to `logs/sync-decisions.md`:**
```markdown
## <YYYY-MM-DD HH:MM> — <vendor>
- **<decision-slug>** → [<destination URL>](<destination URL>) (<operation>)
Source: `decisions/<slug>.md`
Size: <N> chars
<repeat per synced decision>
```
If a decision was skipped (`no` response), log it too with `status: skipped` for audit traceability.
## Step 8: Report
Summary to user:
```
Sync complete: <N synced> / <N skipped> / <N failed>
Synced to <vendor> (<target>):
- <slug>: [<destination URL>](<destination URL>) <create|append>
- <slug>: ...
Skipped (user declined):
- <slug>
- <slug>
Failed (MCP errors):
- <slug>: <error message>
Sync log: logs/sync-decisions.md
```
## Rules
- **Never batch without explicit user opt-in.** The "yes to all writes" path requires the literal 4-word phrase per ADR-016. Partial forms (e.g., "yes to all", "yes all", "go all") do NOT trigger the batch carve-out.
- **Never modify the decision body.** Only the `synced_to_~~docs:` frontmatter field changes locally; the markdown body is untouched.
- **Never delete a destination page.** This skill creates / appends / updates only — destination cleanup is the user's responsibility at the vendor side.
- **Strip secrets if obvious** — same redaction rules as other skills. If a decision body contains what look like API keys / tokens, redact before write + surface in preview.
- **One sync target per invocation.** If the user wants different decisions to go to different targets, run `/sync-decisions <slug> --target <url1>` then `/sync-decisions <other-slug> --target <url2>`.
- **Log every attempt.** Success, skip, or failure — all go in `logs/sync-decisions.md` for audit traceability.
## Notes
- **First WRITE-side skill in either ARIA plugin.** Embedding the Rule 22 advisory preamble verbatim per [ADR-016](https://github.com/mikeprasad/knowledge/blob/main/projects/aria-cowork/decisions/016-rule-22-advisory-preamble-for-external-writes.md). Future write-side skills MUST embed the same preamble.
- Bidirectional per [ADR-014](https://github.com/mikeprasad/knowledge/blob/main/projects/aria-cowork/decisions/014-bidirectional-feature-flow.md) — aria-cowork v0.4.0 imports byte-faithfully. The advisory preamble template is identical across plugins; the only divergence is the Step 0 config path resolution.
- Output schema is byte-identical per [ADR-013](https://github.com/mikeprasad/knowledge/blob/main/projects/aria-cowork/decisions/013-cowork-modified-skills-schema-identical-outputs.md). Both plugins write to the same shared `logs/sync-decisions.md` + update the same `synced_to_~~docs:` frontmatter shape.
- The `synced_to_~~docs:` frontmatter convention is **new in v2.18.0**. Documented in `CONFIG.md` schema section (added in Phase 3a).
- Composes with `/audit-knowledge` — synced decisions are no different from unsynced for audit purposes. The `synced_to_~~docs:` field is informational, not consumed by audit routing.
- **Does NOT replace `_project-knowledge/` git-based team sharing.** That mechanism (v2.13.0) is for per-repo team-mate sharing; `/sync-decisions` is for org-wide wiki / docs publishing. Both can run in parallel.
- Future direction (post-v2.18.0): a `/sync-rules` or `/sync-references` skill could follow the same pattern if external mirroring of other knowledge artifacts is wanted. ADR-016's preamble template ports forward.
---
## /codemap
# /codemap — Codebase Mapping
Systematically scan a codebase and produce a feature-organized CODEMAP.md. Works for any multi-repo or single-repo project, any framework. The output is optimized for AI-assisted development — a new session can load the directory (~50 lines) and selectively read only the sections relevant to its task.
## Step 0: Resolve Config & Parse Mode
Read `.cursor/aria-knowledge.local.md` and extract `knowledge_folder`. If the file doesn't exist, the skill still works — `knowledge_folder` is only needed for the knowledge extraction offer in Step 5.
**Parse the mode from arguments:**
| Argument | Mode | What it does |
|----------|------|-------------|
| (none) | Auto | If CODEMAP.md exists → prompt: "update or recreate?" Otherwise → `create` |
| `create` | Create | Full generation from scratch (Steps 1-8) |
| `inventory` | Inventory | Steps 1-2 only — detect + index, report to user, no file written |
| `update` | Update | Read existing CODEMAP.md, detect changes, refresh affected sections (Step 6) |
| `section <name>` | Section | Rebuild one specific section in an existing CODEMAP.md (Step 7) |
**Locate the project root:**
- If the current working directory contains a project-level CLAUDE.md, use that as the project root
- If inside a subdirectory of a project, walk up to find the nearest CLAUDE.md
- If unclear, ask the user: "Which directory is the project root?"
**Locate or create CODEMAP.md:**
- Default location: `{project_root}/CODEMAP.md`
- If the user specifies a different path, use that
For `inventory` mode, skip to Step 1 and stop after Step 2.
For `update` mode, verify CODEMAP.md exists. If it does, skip to Step 6. If not, tell the user: "No CODEMAP.md found — switching to create mode." and proceed with `create`.
For `section` mode, verify CODEMAP.md exists. If it does, skip to Step 7. If not, same fallback as update.
For `create` mode, proceed through Steps 1-5.
---
## Step 1: Detect Project Structure
Scan the project root for framework indicators:
**Backend detection:**
- `manage.py` + `settings.py` → Django (extract version from requirements.txt or pip)
- `package.json` with `express`/`fastify`/`hono`/`nestjs` → Node backend
- `Cargo.toml` → Rust
- `go.mod` → Go
- `Gemfile` with `rails` → Rails
**Frontend detection:**
- `next.config.*` or `package.json` with `next` → Next.js (extract version)
- `package.json` with `react` but no `next` → React SPA
- `package.json` with `vue`/`nuxt`/`svelte`/`sveltekit` → respective framework
- `app.json` or `expo` in package.json → React Native
**Repo structure detection:**
- Multiple directories each with their own `package.json` or `manage.py` → multi-repo
- Single root `package.json` with `workspaces` → monorepo
- Single app directory → single-repo
**Present detection results to user:**
```
## Project Detection
Root: /path/to/your-project
Structure: Multi-repo (2 repos)
| Repo | Framework | Version | Language |
|------|-----------|---------|----------|
| frontend/ | Next.js (App Router) | 13.x | TypeScript |
| backend/ | Django + DRF | 4.2.x | Python |
Confirm or correct?
```
User confirms or corrects. Store the confirmed detection for subsequent steps.
---
## Step 2: Index Key Files
For each confirmed repo, scan for key file categories using framework-aware patterns.
### Django Backend Scanning
| Category | How to find | What to record |
|----------|-------------|----------------|
| **Apps** | Directories with `models/` or `models.py` + `views/` or `views.py` | App name, whether it has urls.py, admin.py |
| **Models** | `*/models/*.py` and `*/models.py` (exclude `__init__.py`) | Model class names via `^class \w+\(` grep |
| **Views** | `*/views/*.py` and `*/views.py` | View function/class names |
| **URLs** | `*/urls.py` + root `urls.py` | URL patterns, include() targets |
| **Config** | `settings.py`, `.env*`, `requirements.txt` | Framework version, installed apps, integrations |
| **Middleware** | `MIDDLEWARE` in settings.py, `*/middlewares/` | Custom middleware files |
| **Cron** | `CRONJOBS` in settings.py, `*/cron/*.py` | Cron job entries |
| **Utils** | `*/utils/*.py`, `*/utils.py` | Utility module names |
### Next.js / React Frontend Scanning
| Category | How to find | What to record |
|----------|-------------|----------------|
| **Pages/Routes** | `app/**/page.tsx` or `pages/**/*.tsx` | Route paths (derive from directory structure) |
| **Components** | `components/**/*.tsx` | Component directories and key files |
| **Hooks** | `hooks/*.ts` or `hooks/**/*.ts` | Hook names (extract from filenames) |
| **State** | `redux/`, `store/`, `context/` | Slice names, store shape |
| **API layer** | Files containing `createApi`, `apiSlice`, `fetch(` | API definition files |
| **Config** | `next.config.*`, `tsconfig.json`, `tailwind.config.*` | Key config values |
| **Middleware** | `middleware.ts` | What it does (read first 20 lines) |
| **Models/Types** | `models/`, `types/`, `interfaces/` | Type definition files |
### For Other Frameworks
Apply analogous patterns:
- **Express/Fastify:** scan `routes/`, `controllers/`, `models/`, `middleware/`
- **Rails:** scan `app/controllers/`, `app/models/`, `config/routes.rb`
- **React Native:** scan `src/screens/`, `src/navigation/`, `src/api/`
### Categorize Apps/Modules
Group discovered apps into tiers:
- **Core Domain** — apps with substantial models + views + URLs (the main business logic)
- **Reference/Taxonomy** — apps that primarily serve lookup data (small models, simple CRUD)
- **Support** — utility apps, content management, tracking
**Report inventory to user:**
```
## Inventory Summary
### backend/ (Django 4.2)
Core Domain: user (30+ models), booking (12+ models), interview, project, company, payment_gateway, meeting
Reference: expertise, industry, market, school, degree, job_title
Support: event_code, fee, blocks, issue, link_redirect, notification, rating
### frontend/ (Next.js 13)
Pages: ~137 page.tsx files across /admin, /client, /dashboard, /auth, /profile
Hooks: ~58 custom hooks
Components: ~243 .tsx files in 18 areas
Redux: 14 slices + RTK Query
Total: 7 core apps, 6 taxonomy apps, 7 support apps, 137 pages, 58 hooks
```
In `inventory` mode, **stop here** and output the report. Do not write any files.
---
## Step 3: Feature Detection
Using the indexed files from Step 2, identify features by clustering related files across repos.
**Detection heuristics:**
1. **Backend app → feature mapping:** Each Core Domain app is a candidate feature. Some apps combine into one feature (e.g., `booking` + `meeting` → "Expert Sessions & Bookings"). For non-Django backends, use controller/route directories as the grouping unit.
2. **Route prefix grouping:** Frontend routes that share a prefix map to the same feature:
- `/dashboard/ai-interviews/*` → same feature as `interview/` backend app
- `/dashboard/bookings/*` + `/client/dashboard/bookings/*` → same feature as `booking/` backend app
3. **Cross-cutting detection:** Some concerns span all features and get their own sections:
- Auth (login, registration, JWT) → standalone section
- Data flow (request lifecycle) → standalone section
- Shared patterns (conventions that repeat everywhere) → standalone section
**Stack-aware cross-cutting candidates:** Before presenting the feature list, include these stack-level concerns as candidate cross-cutting sections. They tend to be under-documented in feature organization because they span all features. Offer each as proposed; user accepts/declines per item.
- **Django detected:** URLConf tree overview, Signal registry (`post_save`/`pre_save` handlers), Migration state (latest migration per app), Env matrix (grouped env var names, no values)
- **Next.js / React detected:** Route tree overview, API client & interceptors configuration, Env matrix
- **Laravel detected:** Route file overview, Job/queue registry, Service providers, Env matrix
- **Expo / React Native detected:** Screen tree overview, Navigation config, API client, Env matrix
Add these to the `Cn.` cross-cutting block. E.g., `C6. URLConf Tree`, `C7. Signal Registry`, `C8. Env Matrix`.
**Present proposed feature list to user:**
```
## Proposed Features
0. Project Identity & Stack
1. Data Flow Overview (request lifecycle, auth hydration, token refresh)
2. Entity Model (all models, ER diagram, app registry)
3. Auth & User Management (JWT, social login, registration, profiles)
4. AI Interviews (expert/client interviews, Ribbon, OpenAI)
5. Expert Sessions & Bookings (session lifecycle, calendar, Zoom)
6. Kodex Panels (async Q&A, expert answers, client management)
7. Expert Storefronts & Profiles (public profiles, storefront, marketplace)
8. Knowledge Library & Notation (search, transcripts, notes)
9. Insights / Feed (posts, social interactions)
10. Works & Research (works CRUD, purchases, downloads)
11. Payments (Stripe Connect, checkout, payouts)
12. Search (Algolia indexing and search UI)
13. Notifications (email, SMS, WebSocket)
14. Admin Tools (admin dashboard, approvals, content management)
15. Client Projects (project briefs, expert selection)
Cross-cutting:
C1. Shared Patterns & Conventions
C2. Common Change Patterns
C3. Integrations
C4. File Index
C5. Dependency Graph
Confirm, rename, merge, split, reorder, or add?
```
User validates the feature list. This becomes the section structure for the CODEMAP.
---
## Step 4: Write Sections (Output Order)
Write sections in the order they appear in the final file. **Write each section to CODEMAP.md immediately after mapping it** — don't buffer everything for the end. This prevents context loss on long mapping sessions.
Start by writing the file header (populated fully in Step 5):
```markdown
# {Project Name} Codemap
> Feature-organized codebase reference for AI-assisted development.
> Last updated: YYYY-MM-DD | Sections: N | Features: M
>
> **How to use:** Read the directory below (~N lines), then load specific
> sections with `Read CODEMAP.md offset=X limit=Y`.
> To find a section's line: `Grep "^## {number}\." CODEMAP.md`
## Directory
(placeholder — rebuilt in Step 5 after all sections are written)
---
```
Then write sections in this order:
### Section 0: Project Identity & Stack
Write from Step 1 detection results: framework versions, languages, key config files, env vars. If CLAUDE.md already covers this, keep brief and reference CLAUDE.md.
### Section 1: Data Flow Overview
Read these to build the section:
1. **API base configuration** — how the frontend makes API calls (base URL, auth headers, credentials)
2. **Auth middleware chain** — backend middleware order, JWT validation, permission checking
3. **Token refresh mechanism** — how 401s are handled, refresh flow, mutex/lock patterns
4. **Auth state hydration** — what happens on page load (localStorage, cookie checks, redirect logic)
5. **Frontend middleware** — URL rewrites, redirects
6. **Backend permission system** — how public vs. protected endpoints are distinguished
7. **Response formatting** — standard response shapes, error handling
8. **Storage** — where tokens, user data, and files are stored (cookies, localStorage, S3, etc.)
Use **Mermaid** for all diagrams — renderable in GitHub/Obsidian for team members, formally structured for Claude:
```mermaid
flowchart LR
Browser["Browser Component"] --> APILayer["API Layer (RTK Query / fetch)"]
APILayer --> Middleware["Backend Middleware Chain"]
Middleware --> View["View → Response"]
```
```mermaid
flowchart LR
Request --> CheckPermission
CheckPermission -->|"no auth"| PublicCheck["PublicUrlAccess check"]
PublicCheck -->|"public path"| View
PublicCheck -->|"not public"| Reject["Reject (401)"]
CheckPermission -->|"Bearer token"| JWT["JWT validation → User load"] --> View
```
### Section 2: Entity Model
Build from the model files indexed in Step 2.
**App Registry table** — categorized by tier (Core Domain / Reference-Taxonomy / Support):
| App | Domain | Route prefix | Purpose |
|-----|--------|-------------|---------|
| **user** | Accounts, auth, profiles | `api/user/` | Central user entity |
**Mermaid ER diagram** — show core entity relationships:
```mermaid
erDiagram
User ||--|| Profile : has
User ||--o{ Post : authors
User ||--o{ ExpertProfile : configures
User ||--o{ Follow : initiates
User ||--o{ Experience : has
Company ||--o{ CompanyUserAccess : grants
Company ||--o{ Project : owns
Project ||--o{ Booking : generates
Booking ||--|| BookingBrief : describes
Booking ||--o{ BookingSchedule : proposes
Booking ||--o{ BookingKodexAnswer : contains
Booking ||--o{ PaymentGatewayBooking : "paid via"
Interview ||--o{ Invite : sends
```
Focus on **core domain models only** (10-15 entities max). Taxonomy and support models are listed in the App Registry table but don't need ER representation.
### Sections 3-N: Feature Sections
For each confirmed feature from Step 3, read the key files and trace the full stack. Process **one feature at a time** to manage context.
**Reading strategy per feature:**
1. URL/route files → identify all endpoints for this feature
2. Main view file(s) → function signatures, decorators, model queries, response patterns. For large files (1000+ lines), grep for `^def ` or `^class ` first, then read key functions selectively.
3. Model files → class definitions, field types, relationships
4. Frontend hooks → what they call, what state they manage
5. Frontend page components → only if the hook/route picture is unclear
**Each feature section contains:**
**Frontend spine:**
| Route | Purpose |
|-------|---------|
| `/path/to/page` | What the page does |
| Hook | What it does |
|------|-------------|
| `use-feature.ts` | Key behavior, which API endpoints it calls |
| Redux / API Endpoints | Method | Backend URL |
|----------------------|--------|-------------|
| `useXxxMutation` | POST | `/api/xxx` |
**Backend spine:**
| URL | View | Notes |
|-----|------|-------|
| `POST /api/xxx` | `view_function()` | Key behavior, permissions, issues |
**Models:** Key fields and relationships. Tables for simple listings, prose for complex relationships.
**Integrations:** If the feature touches external services — which service, what operations, key files, env vars.
**Security issues (inline):** Flag at the point they occur — IDOR, exposed secrets, missing validation, permission bypasses, anti-patterns. Bold the issue type:
> **IDOR:** `user_settings.py` accepts `user_id` from POST body without checking `request.user.id`
### Section C1: Shared Patterns & Conventions
After writing all feature sections, scan them for repeating patterns:
- Hook naming/structure conventions
- View/controller patterns
- Error handling patterns (including anti-patterns)
- Permission, state management, file storage, GUID/slug patterns
Document each pattern once with a brief description and an example file reference.
### Section C2: Common Change Patterns
For each detected framework, produce "how to" recipes:
**Django:** add endpoint, add model, add cron job, add email template, modify auth — which files to touch, which patterns to follow.
**Next.js / React:** add page, add API endpoint (frontend), add component, modify auth — directory structure, route constants, conventions.
Adapt to whatever frameworks were detected in Step 1.
### Section C3: Integrations
Summary table of all external services:
| Integration | Env Key(s) | Used by | Key files |
|-------------|-----------|---------|-----------|
| Stripe | `STRIPE_KEY`, `STRIPE_SECRET` | Payments (Section N) | `payment_gateway/views/` |
### Section C4: File Index
Per-repo "looking for X? it's in Y" tables:
| Looking for... | Location |
|----------------|----------|
| Django settings | `project/settings.py` |
| API base config | `redux/services/apiSlice.ts` |
### Section C5: Dependency Graph
Mermaid flowchart of app-to-app dependencies:
```mermaid
flowchart TD
user --> booking
user --> project
user --> company
user --> interview
booking --> payment_gateway
booking --> industry
interview --> algolia
project --> company
meeting --> booking
notification --> user
```
Build from import statements observed during deep mapping. Focus on app-to-app, not file-to-file.
### Build Log
Write at the end of the file:
| # | Section | Status | Updated |
|---|---------|--------|---------|
| 0 | Project Identity & Stack | Complete | YYYY-MM-DD |
| 1 | Data Flow Overview | Complete | YYYY-MM-DD |
| ... | ... | ... | ... |
---
## Step 5: Generate Directory & Finalize
After all sections are written, go back to the top of the file and:
1. **Build the directory table** from the actual section headings:
```markdown
## Directory
| # | Section | Covers | Key paths |
|---|---------|--------|-----------|
| 0 | Project Identity & Stack | frameworks, env vars, config | settings.py, next.config.js |
| 1 | Data Flow Overview | request lifecycle, auth, token refresh | apiSlice.ts, check_permission.py |
| ... | ... | ... | ... |
| C1 | Shared Patterns | conventions, anti-patterns | cross-cutting |
| C2 | Common Change Patterns | how to add endpoints, models, pages | procedural recipes |
| C3 | Integrations | external services, env keys | service summary |
| C4 | File Index | quick lookup tables | per-repo reference |
| C5 | Dependency Graph | app-to-app imports | adjacency list |
```
The **Covers** column should have enough keywords for Claude to match a task to a section without reading section content. The **Key paths** column lists file paths so Claude can match files it's already working on.
2. **Count the directory lines** (from `## Directory` to the first `---` separator) and update the "How to use" instruction with the actual count.
3. **Update the header** with final section/feature counts and today's date.
4. **Report stats:**
```
CODEMAP.md written: {total_lines} lines, {sections} sections, {features} features
Directory: lines 1-{N} (~{tokens} tokens to load)
Full file: ~{total_tokens} tokens
```
5. **Offer knowledge extraction:**
> "Mapping complete. I found {N} security issues and {N} patterns during scanning. Want me to run /extract to stage these to ARIA backlogs?"
---
## Step 6: Update Mode
When the user runs `/codemap update`:
### 6a. Read existing CODEMAP.md
Read the directory section (first ~80 lines). Extract:
- Section names and numbers
- Last-updated dates from the Build Log (read the last ~30 lines)
### 6b. Detect changes
Run: `git log --name-only --since="{last_update_date}" --pretty=format:"" -- {repo_paths}`
This gives all files changed since the last CODEMAP update. Also check for:
- New files in route/model/view/hook directories (Glob for files not referenced in the existing map)
- Deleted files that are still referenced in the map (Grep the map for paths, verify they exist)
### 6c. Map changes to sections
For each changed file, determine which section(s) it affects:
- Match file path against the "Key paths" column in the directory
- Match file path against file references within each section (Grep the CODEMAP)
- New files in a feature's directory → that feature's section needs refresh
- Changed models → Entity Model section + the feature section that uses them
- Changed config/middleware → Data Flow section
- Changed cross-cutting files → relevant C-section
### 6d. Present update plan
```
## Update Plan
Changes detected since YYYY-MM-DD:
- 12 files modified, 3 new files, 1 deleted file
Sections to refresh:
- ## 4. AI Interviews — interview/views/interview.py modified, 1 new model
- ## 11. Payments — payment_gateway/views/checkout.py modified
- ## C4. File Index — 3 new files to add
- ## Build Log — update dates
Sections unchanged: 0, 1, 2, 3, 5-10, 12-15, C1-C3, C5
Proceed? (all / numbers to skip / cancel)
```
### 6e. Refresh affected sections
For each section to refresh:
1. Re-run the deep mapping process (Step 4) for that feature only
2. Replace the section content in CODEMAP.md using Edit (match from `## N.` to next `## `)
3. Update the Build Log date for that section
4. If new features were detected, add them as new sections and update the directory
### 6f. Rebuild directory
After all sections are updated:
1. Regenerate the directory table from the current section headings
2. Update the directory line count in the "How to use" instruction
3. Update the `Last updated` date in the header
---
## Step 7: Section Mode
When the user runs `/codemap section <name>`:
1. Read the directory from the existing CODEMAP.md
2. Match `<name>` against section names (fuzzy match — "interviews" matches "AI Interviews")
3. If no match, present the section list and ask the user to pick
4. If matched, re-run the deep mapping process (Step 4) for that section
5. Replace the section content using Edit
6. Update the Build Log date
7. Rebuild directory (Step 5, directory rebuild only)
---
## Rules
### Process
- **Always present detection results and feature lists for user confirmation** before writing anything. The user's mental model of their codebase is authoritative.
- **One feature at a time** during deep mapping. Write each section before moving to the next. This prevents context loss on large codebases.
- **Inventory mode produces no files** — it's a read-only scan that reports to the user. Use it for quick orientation.
- **Update mode only touches affected sections** — never rewrite sections that haven't changed. Preserve user edits to sections (comments, annotations, corrections) unless the underlying code has changed.
### Content
- **Feature-organized, not repo-organized.** Every feature section traces the full stack: frontend routes → hooks → state → backend views → models → integrations. A developer working on "AI Interviews" should find everything in one section.
- **Security issues inline.** Flag IDOR, exposed secrets, missing validation, permission bypasses at the point where they occur in the feature section. Bold the issue type.
- **Mermaid for all diagrams.** Use Mermaid flowcharts for auth flows and dependency graphs, Mermaid erDiagram for entity relationships. Mermaid renders in GitHub, Obsidian, and VS Code for team members, and is formally structured for Claude. The small token premium (~500 tokens across all diagrams) is negligible with directory-based selective loading.
- **Be thorough but not exhaustive.** Document routes, views, models, hooks, and integrations. Don't document every line of code — the map is a navigation aid, not a code review.
- **Common Change Patterns are procedural.** Write them as step-by-step recipes: "to add X, touch files A, B, C in this order."
- **File Index answers "where is X?"** not "what does X do?" Keep entries to file path + brief purpose.
### Context Management
- **Large view/controller files (1000+ lines):** Don't read the whole file. Grep for function/class definitions first (`^def `, `^class `, `export function `), then read key functions selectively.
- **Large model files:** Read class definitions and field declarations. Skip method implementations unless they contain business logic that affects the feature map.
- **Endpoint files (like RTK Query slices):** Grep for endpoint names and URL patterns rather than reading the full file.
### Maintenance
- **Build Log tracks per-section status.** Every section gets a status (Complete/Partial/Scaffolded) and a date. Update mode uses these dates to detect staleness.
- **Directory is self-maintaining.** The skill rebuilds the directory table and line count instruction whenever it writes or updates the file.
- **`---` separators between sections** are mandatory — they serve as visual breaks and as Grep anchors for section boundaries.
### Integration
- **After create or update, offer /extract.** Security issues and patterns discovered during mapping are valuable knowledge that should flow into ARIA backlogs.
- **Don't duplicate CLAUDE.md content.** If project identity info already exists in CLAUDE.md, keep the CODEMAP's Section 0 brief and reference CLAUDE.md for details. The CODEMAP's value is the feature mapping, not restating what CLAUDE.md already covers.
---
## /distill
# /distill — Task transformation
Turn raw task text into a tiered executable spec following `TASK.schema.md`. Auto-tiers by complexity or accepts explicit `--tier`. Optional `--group` loads CODEMAPs for cited-path context.
## Step 0: Inputs
- **Raw task input** — inline string argument, file path, or prompt user to paste if no argument provided.
- **Optional `--group=<tag>`** — load CODEMAPs + STITCH for cited-path context (see shared-block below).
<!-- shared-block: group-loader -->
Read `.cursor/aria-knowledge.local.md`. Parse YAML frontmatter `projects_groups` (multi-line YAML block — see `CONFIG.md` "Skill-only fields" for canonical schema, including the optional `stitch_path` sub-field and custom-role conventions).
Look up `<tag>` in `projects_list` (get `project_root`) and `projects_groups` (get role → folder dict).
- If `<tag>` missing from `projects_list`: stop with *"unknown project tag: <tag>"*.
- If `<tag>` in `projects_list` but missing from `projects_groups` and `<project_root>` has multiple distinct codebases that must stay in sync (separate repo-marker sub-dirs, OR one repo with a shared-contract source + multiple generated/typed clients — see scan below): trigger **auto-propose bootstrap**. The git-repo boundary is NOT the signal — a monorepo with a `contract/` → `ios/`+`android/`+`backend/` seam qualifies just as much as separate repos.
- If `<tag>` is a single undifferentiated codebase (no separate sub-dirs and no contract→multi-client seam): load `<project_root>/CODEMAP.md` only.
**Auto-propose bootstrap** (when `projects_groups[<tag>]` is missing but `<project_root>` contains multiple sync-bound codebases — separate repo dirs or a contract→clients seam):
1. Scan `<project_root>` one level deep for sub-directories with repo or contract markers:
- `openapi.{yaml,yml,json}` / `*.proto` / `schema.graphql` (or a dir named `contract`/`contracts`/`api-spec`/`proto`) → `contract` (the shared source clients are generated from — its drift is what STITCH tracks)
- `manage.py` + `settings.py` → `backend` (Django)
- `composer.json` + `artisan` → `backend` (Laravel)
- `Gemfile` with `rails` → `backend` (Rails)
- `package.json` with `express`/`fastify`/`nestjs` → `backend` (Node)
- `pyproject.toml`/`requirements.txt` with `fastapi`/`pydantic` → `backend` (FastAPI)
- `Package.swift` / `*.xcodeproj` / an `ios` dir → `ios` (Swift/SwiftUI)
- `build.gradle{,.kts}` with an `android` dir → `android` (Kotlin/Android)
- `next.config.*` → `web` (Next.js)
- `app.json` + `expo` in package.json → `mobile` (Expo)
- `package.json` with `react` (no `next`/`expo`) → `web` (React SPA)
- other `package.json` → prompt user for role name
2. Handle role conflicts: if two dirs inferred as same role, prompt user to assign distinct keys (`web`, `web-admin`, etc.).
3. Propose the group structure to user: sub-repo names, inferred roles, YAML block to insert. Show a preview diff of the change to `.cursor/aria-knowledge.local.md`.
4. On approval, edit the config file to add the `projects_groups[<tag>]` entry, preserving existing fields and YAML structure.
5. On decline, stop with *"proceed after registering group manually"*.
Resolve each `(role, folder)` pair to absolute path: `<project_root>/<folder>`. For each absolute path, read `CODEMAP.md` if it exists. Read `<project_root>/STITCH.md` if it exists. Return resolved path map + warnings for any missing CODEMAPs.
<!-- /shared-block: group-loader -->
- **Optional `--tier=micro|standard|full`** — explicit tier overrides auto-scoring. Else compute score:
| Signal | Points |
|--------|--------|
| >1 layer (FE+BE, BE+DB, …) | +2 |
| New endpoint / route / model / migration | +2 |
| External service (Stripe, Twilio, S3, SendGrid, Algolia, OpenAI, Vercel, …) | +2 |
| Auth / permissions / security | +2 |
| Input >150 words or multi-paragraph | +1 |
| Names >3 files | +1 |
| Single-sentence trivial edit | −3 |
Score ≤ 0 → `micro`; 1–3 → `standard`; ≥ 4 → `full`.
## Step 1: Schema
Follow `knowledge/distill/TASK.schema.md` section tags `[R]` `[L]` `[O]` `[F]`.
- **Always emit (`[R]`):** 1 Objective, 2 Scope, 5 Dependencies & API Requirements, 10 QA, 11 DoD.
- **Layers (`[L]`):** include Frontend / Backend / Database only if the task actually touches that layer. Never emit empty headings.
- **Tier gates:**
- `full` adds **3 Non-Goals** (`[F]`).
- `standard` and `full` add **4 Assumptions** (`[O]`, include when non-empty) and **9 Edge Cases** (`[O]`, include when non-empty).
- `micro` skips Non-Goals; Assumptions only if a blocking ambiguity exists.
## Step 2: Single chosen approach
One implementation path per layer section. No option menus inside a layer. Matches the discipline of Rule 22's Execute step: commit to one plan.
## Step 3: Validation
- All `[R]` sections present for the chosen tier.
- No empty `[L]` sections (omit entirely if layer not touched).
- With `--group`, every cited file path must appear in the loaded CODEMAP or STITCH content. If Claude invents a path, either remove the citation or promote the uncertainty to **Assumptions** as a blocking item.
- **Advisory vocabulary check:** scan output for the list in `TASK.schema.md` (`flexible`, `extensible`, `scalable framework`, `we could also`, `alternatively`, `one option`, `potentially`, `might want to`). Prefer concrete alternatives. Not a hard rejection — surface as a soft warning in skill output, continue otherwise.
On validation failure: self-correct once, then move remaining gaps to **Assumptions** as blocking items.
## Step 4: Output
Default output path: `TASK.md` in CWD.
**Overwrite safety:**
- If `TASK.md` exists and is non-empty, **first-run behavior**: emit a one-time notice explaining the auto-archive default. Subsequent runs are silent.
- **Default:** move existing `TASK.md` to `.aria-distill/archive/TASK-YYYY-MM-DD-HHMMSS.md`, then write fresh output to `TASK.md`.
- **Archive directory** (`.aria-distill/archive/`) created lazily on first archive. First-run notice suggests adding `.aria-distill/` to `.gitignore`.
**Flags override defaults:**
- `--append` — add new entry below existing `TASK.md` content, separated by `---` and a `## Distilled YYYY-MM-DD HH:MM` header. No archive.
- `--out=<path>` — write to the specified path. Existing `TASK.md` untouched; no archive.
- `--no-archive` — overwrite existing `TASK.md` without archiving. Destructive opt-in; display a warning before proceeding.
**Writing steps:**
1. Determine final target path from flags / default.
2. If archive applies: verify `.aria-distill/archive/` exists (create if not), move existing file in with timestamped name.
3. Write spec to target path.
4. Print summary: tier chosen, score (if auto), target path, archive path (if any), advisory-vocab warnings (if any).
No backlog or side files beyond the archive.
---
## /stitch
# /stitch — Cross-repo stitch layer
Generate a cross-repo binding artifact (`STITCH.md`) for a product group (backend + one or more frontends). Tables only, not narrative. Drift detection uses CODEMAP endpoint sections by default with explicit opt-in fallback to grep.
## Step 0: Load config
<!-- shared-block: group-loader -->
Read `.cursor/aria-knowledge.local.md`. Parse YAML frontmatter `projects_groups` (multi-line YAML block — see `CONFIG.md` "Skill-only fields" for canonical schema, including the optional `stitch_path` sub-field and custom-role conventions).
Look up `<tag>` in `projects_list` (get `project_root`) and `projects_groups` (get role → folder dict).
- If `<tag>` missing from `projects_list`: stop with *"unknown project tag: <tag>"*.
- If `<tag>` in `projects_list` but missing from `projects_groups` and `<project_root>` has multiple distinct codebases that must stay in sync (separate repo-marker sub-dirs, OR one repo with a shared-contract source + multiple generated/typed clients — see scan below): trigger **auto-propose bootstrap**. The git-repo boundary is NOT the signal — a monorepo with a `contract/` → `ios/`+`android/`+`backend/` seam qualifies just as much as separate repos.
- If `<tag>` is a single undifferentiated codebase (no separate sub-dirs and no contract→multi-client seam): load `<project_root>/CODEMAP.md` only.
**Auto-propose bootstrap** (when `projects_groups[<tag>]` is missing but `<project_root>` contains multiple sync-bound codebases — separate repo dirs or a contract→clients seam):
1. Scan `<project_root>` one level deep for sub-directories with repo or contract markers:
- `openapi.{yaml,yml,json}` / `*.proto` / `schema.graphql` (or a dir named `contract`/`contracts`/`api-spec`/`proto`) → `contract` (the shared source clients are generated from — its drift is what STITCH tracks)
- `manage.py` + `settings.py` → `backend` (Django)
- `composer.json` + `artisan` → `backend` (Laravel)
- `Gemfile` with `rails` → `backend` (Rails)
- `package.json` with `express`/`fastify`/`nestjs` → `backend` (Node)
- `pyproject.toml`/`requirements.txt` with `fastapi`/`pydantic` → `backend` (FastAPI)
- `Package.swift` / `*.xcodeproj` / an `ios` dir → `ios` (Swift/SwiftUI)
- `build.gradle{,.kts}` with an `android` dir → `android` (Kotlin/Android)
- `next.config.*` → `web` (Next.js)
- `app.json` + `expo` in package.json → `mobile` (Expo)
- `package.json` with `react` (no `next`/`expo`) → `web` (React SPA)
- other `package.json` → prompt user for role name
2. Handle role conflicts: if two dirs inferred as same role, prompt user to assign distinct keys (`web`, `web-admin`, etc.).
3. Propose the group structure to user: sub-repo names, inferred roles, YAML block to insert. Show a preview diff of the change to `.cursor/aria-knowledge.local.md`.
4. On approval, edit the config file to add the `projects_groups[<tag>]` entry, preserving existing fields and YAML structure.
5. On decline, stop with *"proceed after registering group manually"*.
Resolve each `(role, folder)` pair to absolute path: `<project_root>/<folder>`. For each absolute path, read `CODEMAP.md` if it exists. Read `<project_root>/STITCH.md` if it exists. Return resolved path map + warnings for any missing CODEMAPs.
<!-- /shared-block: group-loader -->
**For `/stitch` specifically:** the group MUST have **≥2 distinct codebases bound by a shared contract** — at least one contract/backend source role + at least one client role that must stay in sync with it. **Whether they live in separate git repos or one monorepo is irrelevant** — the load-bearing condition is "multiple codebases that drift apart," not "multiple repos." A monorepo's `contract/` → `ios/`+`android/`+`backend/` seam (the dual-native keystone — one OpenAPI/proto/GraphQL source feeding generated clients) is exactly the drift seam STITCH exists to document. Only stop when there's a **single undifferentiated codebase** with no such seam: *"/stitch needs ≥2 contract-bound codebases; this looks like one codebase — use `/codemap`."*
## Step 1: Resolve paths & output target
- `BACKEND_ROOT` = `<project_root>/<backend folder>` (the one role=backend entry)
- `FRONTEND_ROOTS` = list of `<project_root>/<folder>` for all non-backend roles
- `STITCH_FILE` = `<project_root>/STITCH.md` by default. Override: if `projects_groups[<tag>]` contains a `stitch_path` field, use that (relative to `<project_root>`).
For `create` mode, require `BACKEND_ROOT/CODEMAP.md` and each `frontend_root/CODEMAP.md`. If any missing, list what's missing and recommend running `/codemap create` in each affected repo first.
## Step 2: Load template (create mode only)
Start from `knowledge/stitch/STITCH.template.md`. Fill **Group identity** with:
- Group tag
- Backend repo folder name + `git rev-parse HEAD` if git available
- Frontend repo folder names + revisions
- CODEMAP absolute paths for each repo
- Configured `STITCH_FILE` path
## Step 3: Build sections 2–5 (create + section modes)
Using the loaded CODEMAPs, populate:
- **2. Auth stitch** — token path FE → BE. Source: FE auth slice/hook + BE auth middleware/JWT handler. Table rows: step | location (file) | notes. Mermaid optional, keep minimal.
- **3. Endpoint stitch** — union of FE RTK/fetch calls → BE routes. Normalize paths (strip env prefixes, trailing slashes). Table columns: FE hook/client | HTTP method | FE file | Path | BE urls module | View/handler | Permission | Notes.
- **4. Entity stitch** — when traceable from CODEMAP model/serializer/type tables. Columns: Domain | FE type/schema | BE serializer | Model | Notes.
- **5. Integration stitch** — external services from backend CODEMAP's Integrations section; note FE usage where mentioned. Columns: Service | Env keys | Owner repo | Files | FE usage.
Only populate cells with information that appears in the loaded CODEMAPs. Leave cells blank rather than inventing.
## Step 4: Drift log (create + diff modes)
**Precedence (check in order):**
1. **User-provided script** — check for `<workspace_root>/analyze-stitch.sh` or `<workspace_root>/analyze-stitch.py`. If either exists, invoke with JSON stdin:
```json
{"backend_root": "<abs path>", "frontend_roots": ["<abs path>", ...], "group": "<tag>"}
```
Expect JSON stdout:
```json
{"fe_orphans": [{"call": "...", "file": "..."}, ...], "be_orphans": [{"route": "...", "file": "..."}, ...]}
```
Label output section: *"Drift source: user script (analyze-stitch.*)"*.
2. **CODEMAP-based** (default expected path) — check both CODEMAPs for required endpoint sections:
- **Backend:** look for URLConf tree section (match heading like `## N. URLConf` or similar). Parse endpoint rows.
- **Frontend:** look for API client / RTK Query / endpoint table section. Parse endpoint definitions.
- If both present → normalize to `method + path` tuples, diff the sets. Label: *"Drift source: CODEMAPs (sections: <backend section name>, <frontend section name>)"*.
3. **Missing CODEMAP endpoint data** — **prompt user explicitly** (do NOT silently fall through):
```
STITCH drift detection requires endpoint sections in both CODEMAPs.
Currently missing:
- <backend_path>/CODEMAP.md: <missing section name>
- <frontend_path>/CODEMAP.md: <missing section name> (if applicable)
Recommended: run `/codemap section <missing section>` in the affected repo(s) first
(better accuracy, self-improving as you maintain CODEMAPs).
Fallback: proceed with grep-based drift (coarse — catches presence/absence,
misses HTTP methods, dynamic paths, non-REST conventions). Output will be
labeled "Drift source: fallback grep."
Choose: [C]odemap (stop here, regenerate first) / [G]rep fallback (proceed now)
```
4. **On [G]rep fallback** — grep FE for `/api/` strings (and `api/v1/`, `apiSlice`, `fetch(` variants), grep backend for route definitions (Django `urls.py` patterns, or equivalent). Compare normalized sets. Label output: *"Drift source: fallback grep — CODEMAPs incomplete; see recommendation above"*.
5. **On [C]odemap choice** — exit `/stitch` with instruction: *"Run `/codemap section <name>` in <repo>, then re-invoke `/stitch <mode> <tag>`."*
Populate STITCH.md section 6 (Drift log):
- Header row with drift source labeled
- FE orphans table (FE calls missing BE routes): columns FE call | FE file | Notes
- BE orphans table (BE routes unused by FE): columns BE route | BE file | Notes
## Step 5: Write STITCH.md (create mode)
**Overwrite safety** (mirrors `/distill` Step 4):
- If `STITCH_FILE` exists and non-empty, **first-run notice** explains auto-archive.
- **Default:** move existing `STITCH_FILE` to `<workspace>/.aria-stitch/archive/STITCH-YYYY-MM-DD-HHMMSS.md`, then write fresh.
- **Flags:**
- `--append` — add new dated section below existing (rare for `/stitch`; `section <n>` mode usually preferred; warn user)
- `--out=<path>` — write to alternate path
- `--no-archive` — destructive overwrite, explicit opt-in
## Modes
| Mode | Behavior |
|------|----------|
| `create <group>` | Execute Steps 0-5. Write full `STITCH_FILE`. |
| `verify <group>` | Re-read `STITCH_FILE` tables; check cited file paths still exist on disk; flag stale rows. No rewrite unless user requests. |
| `diff <group>` | Run drift detection only (Step 4). Print drift summary; do not modify `STITCH_FILE`. |
| `section <group> <n>` | Rebuild section `n` in-place in `STITCH_FILE`. Skips overwrite safety (only that section changes). |
## Rules
- Tables over narrative.
- Every file path cited must exist on disk when written.
- Do not invent endpoints not evidenced in CODEMAP or code.
- If `--append` is used for `create`, warn user: *"Append on /stitch is rare; `section <n>` is usually the right mode for incremental updates."*
---
## /handoff
# /handoff — Express Session Handoff
Generate a passoff package so the next reader can pick up cleanly. Two audiences:
- **Next-session handoff** (default + `auto`) — For future-you in a new session (typically when context is high and you need to restart). Synthesizes the session, applies PROGRESS.md / CLAUDE.md / memory updates, commits, runs `/extract`, and emits a **paste-ready next-session opener as the headline artifact**.
- **Coworker brief** (`brief`) — For another person. Produces a copy/paste prose block (Slack/email-ready) summarizing the session. Does NOT update PROGRESS/CLAUDE/memory, does NOT commit, does NOT run /extract. Output-only — paste it and you're done.
For "I'm done, close it out cleanly" with no passoff intent, use `/wrapup` instead.
**Four modes:**
- **Default (`/handoff`)** — Generate ALL drafts (session summary, PROGRESS entry, CLAUDE.md edits, memory updates, commit message, next-session prompt) into one scroll, ask once for combined-go, then apply atomically. Per-item edits allowed.
- **`auto` (`/handoff auto`)** — Implicit-yes on all gates. Run silently. Apply all drafts without confirmation. Emit final report only. Use when the session is short and unambiguous.
- **`brief` (`/handoff brief`)** — Generate a coworker-facing prose brief (80-150 words, copy/paste-ready). Skips PROGRESS/CLAUDE/memory/commit/extract entirely. Emits the brief as the only artifact.
- **`snap` (`/handoff snap`)** — Like `auto` (silent, apply all drafts, emit the next-session opener), but archives the raw transcript via `/snapshot` for later extraction **instead of** running `/extract` now. Use when context is high: you still get the full handoff package + opener + commit, but defer the expensive, compaction-risky knowledge synthesis to a later session (or the next `/audit-knowledge` digest pass, which reads the snapshot automatically).
**The next-session opener is the headline artifact** in default + auto + snap modes — always produced, even when no other surface changed. That is what distinguishes `/handoff` from `/wrapup`. Brief mode is a different shape — handoff to a person, not to a session.
**`snap` is `auto` plus one swap.** snap follows auto's behavior exactly (silent, implicit-yes, apply all drafts, emit the opener) — wherever a step below applies to `auto`, it applies identically to `snap`. The single difference is the capture step (Step 6): `snap` runs `/snapshot` (archive the transcript for later) while `auto` runs `/extract` (synthesize now). Nothing else differs. (snap is NOT brief — it produces the full next-session package, not a coworker prose block.)
## Step 0: Resolve Config and Parse Mode
Read `.cursor/aria-knowledge.local.md` and extract `knowledge_folder`. If missing, stop: "aria-knowledge is not configured. Run /setup to get started."
Parse the argument:
- No arg, or arg is empty → `mode = combined-go` (default)
- Arg matches `auto` (case-insensitive) → `mode = auto`
- Arg matches `brief` (case-insensitive) → `mode = brief`
- Arg matches `snap` (case-insensitive) → `mode = snap`
- Any other arg → stop: "Unknown argument '{arg}'. Use '/handoff', '/handoff auto', '/handoff brief', or '/handoff snap'."
Use `{knowledge_folder}` as the base path for all file operations.
## Step 1: Identify Project Context
Same logic as `/wrapup` Step 1 — detect:
- **Project root** — directory containing PROGRESS.md and/or CLAUDE.md (search upward from cwd)
- **PROGRESS.md path** — if exists
- **CLAUDE.md path(s)** — root + relevant subfolder
- **Memory files** — any `project_*.md` files in `~/.claude/projects/` matching the current path
- **Git repos** — run `git status` in each git repository within the project to detect uncommitted changes
Additionally, determine the **project marker** for the next-session opener (Step 9):
- Match the working directory against the project codes documented in the user's root CLAUDE.md (e.g., `cs`, `ss`, `df`, `kn`, `ar`, `jp` if those conventions exist).
- If no SessionStart-hook project-code convention is detected, use the project's folder name as the marker.
- If no project is identifiable, use the literal `[no-project]` — the opener will still work, just without auto-routing.
If no PROGRESS.md or CLAUDE.md is found, note this — proceed with the steps that apply.
## Step 2: Synthesize Session Work (silent)
Build the session synthesis internally — do NOT print it yet:
1. **Files changed** — list files created/modified/deleted during this session (from conversation context, not git)
2. **Key decisions** — architectural choices, approach selections
3. **Current state** — what's working, in-progress, blocked
4. **Next steps** — what the user indicated should happen next, or what logically follows
5. **Open threads** — anything explicitly deferred or flagged for later
This synthesis feeds every subsequent step.
**Mode branch:** If `mode = brief`, jump to Step 2B (Brief Output) and stop there. Skip Steps 3-8 entirely. Otherwise continue to Step 3.
## Step 2B: Brief Output (brief mode only)
**Skip this step entirely if `mode != brief`.**
Brief mode produces a single copy/paste artifact — a coworker-facing prose brief. No PROGRESS update, no CLAUDE.md edit, no memory write, no commit, no /extract call. Just the prose, formatted for paste into Slack / email / chat.
### 2B.1: Build the brief from Step 2 synthesis
Compose an 80-150 word prose block following this template (cap at 200 words). Fill placeholders from the Step 2 synthesis; if a section has no relevant content for this session, omit the section line entirely (don't leave empty bullets).
```
Hey [coworker] —
Quick brief on {topic from synthesis} from {YYYY-MM-DD}:
**What happened:** {2-3 sentence summary drawn from synthesis "Current state" + "Files changed"}
**Key decisions:**
- {decision 1 from synthesis "Key decisions"}
- {decision 2}
- {decision 3 if relevant — cap at 4 bullets}
**What's next:** {1-2 sentences from synthesis "Next steps" + "Open threads"}
**Where to pick up:** {file path, PR link, ticket ref, or doc link — omit this whole line if N/A}
Let me know if you want me to walk through any of this.
```
Notes for filling the template:
- Keep `[coworker]` as a literal placeholder — user fills the name at paste time. Don't prompt for a name.
- Tone: warm-but-professional default. Write as if briefing a peer who shares context but wasn't in the room. Avoid corporate hedging and avoid forced casualness.
- "**What happened**" is the heaviest section — get the 2-3 sentence summary right. If the session was short or unclear, say so plainly rather than padding.
- "**Key decisions**" should be the genuinely-decided things, not options-still-on-the-table. 0 decisions is fine — if so, omit the section entirely.
- "**Where to pick up**" only appears if there's a concrete artifact reference (file, PR, ticket, doc URL). Otherwise omit the line.
- Stay under 200 words. Above that, the format breaks down and reads as a memo, not a brief.
### 2B.2: Emit the brief
Emit the brief inside a code fence so it copies cleanly. Format:
```
## Coworker Brief — {YYYY-MM-DD}
Paste this directly into Slack / email / chat:
```
{full brief from 2B.1}
```
That's it — no further handoff steps run in brief mode. If you also want to update PROGRESS.md, memory, or run /extract, invoke `/handoff` (default) or `/handoff auto` separately.
```
**Exit after emission.** Do not run any subsequent steps.
## Step 3: Draft All Updates (silent)
In parallel, draft every artifact this handoff might write. Do NOT apply anything yet.
### 3a: PROGRESS.md entry
If PROGRESS.md exists: draft a new session entry matching the existing format (heading style, date format, structure). If today's entry already exists, draft an *append-to-existing* delta instead of a duplicate entry.
### 3b: CLAUDE.md updates
If CLAUDE.md exists: check if anything from this session contradicts, outdates, or is missing from it. If updates are needed, draft the specific edits (show old → new diffs). If nothing needs updating, mark as `current` — do NOT force updates for the sake of updating.
### 3c: Memory updates
Check `~/.claude/projects/.../memory/project_*.md` files matching the current project path. Compare against the session synthesis. If memory is stale, draft an update (specific old → new lines). If no update needed, mark as `current`.
### 3d: Commit message(s)
For each git repository with uncommitted changes: draft a conventional-commit message based on the session synthesis. List specific files to stage (NOT `git add -A`). Skip repos with no changes.
### 3e: Next-session opener (the always-on artifact)
Build a fenced block intended for paste into the next session. Format:
```
{project-marker}
Resume {project-name} from {YYYY-MM-DD} handoff.
Suggested next session: {model · effort}
({one-line rationale grounded in the first action})
Read first:
- {PROGRESS.md path} (latest entry)
- {primary AGENTS.md path}
- {any relevant memory file paths}
Where we left off:
- {1-3 bullets summarizing current state}
Open threads:
- {bulleted list from synthesis "Open threads"}
First action:
- {derived from synthesis "Next steps", phrased as an imperative}
```
The opener is always produced, even when the session was short or no other artifacts changed — it's the headline deliverable.
#### Choosing the `Suggested next session:` value
The recommendation is the current session's judgment about what the **next session's hardest first action** needs. Pick the rubric row matching that first action (from the Step 2 synthesis: `Next steps` + `Open threads` + `Current state`). Both axes descend together as one difficulty gradient, so the matched row doubles as the rationale skeleton.
| Next session's character | Recommend |
|---|---|
| Novel architecture · deeply ambiguous · high asymmetric failure cost · gnarly debugging | `Opus · xhigh` (`max` only if truly hard — session-only, may overthink) |
| Design + hard multi-step implementation, real ambiguity | `Opus · high` |
| Standard implementation with a clear-ish plan, moderate complexity | `Opus · medium` |
| Planning is the hard part, execution mechanical | `opusplan` |
| Well-specified implementation, moderate mechanical work | `Sonnet · high` |
| Routine mechanical execution (sweeps, renames, doc edits, plan already written) | `Sonnet · medium` |
| Trivial lookups / status checks | `Haiku` |
Rules for the line:
- **De-version.** Write only the model family (`Fable` / `Opus` / `Sonnet` / `Haiku`) — a bare family name means the **latest version** of that family. Never write a version number.
- **Always include a one-line rationale** on the indented line below, grounded in the first action.
- **Effort ladder:** `low · medium · high · xhigh · max` (Fable, Opus, and Sonnet support effort; `Haiku` does **not** — emit `Haiku` with no `· effort` suffix). `opusplan` (Opus plans → Sonnet executes) is its own token, no effort suffix.
- **Uncertain / no strong signal → `Opus · high`**, rationale "general session, no strong signal."
- **Spans tiers → recommend the higher tier** and say so in the rationale.
- **`Fable` is the tier above Opus** (displayed "Fable 5"). Recommend `Fable · xhigh` in place of the top row's `Opus · xhigh` only when the hardest first action is at the extreme end of *difficulty* — novel architecture, gnarly cross-system debugging, high-asymmetric-failure-cost reasoning — where a wrong/shallow answer is costly enough to justify ~2× Opus's price. Context size is **not** the trigger: Fable and Opus share the same 1M window, so a large-but-tractable task (big `/codemap`, multi-doc synthesis) stays on `Opus`. The `Opus` rows otherwise stand and the uncertainty fallback stays `Opus · high`.
This line is advisory — it does not set the model. The user selects via `/model` and `/effort`; a running next-session model uses the effort cue + a mismatch self-check.
### 3f: SESSION.md (handoff state)
Skip if `session_state` is not `true` in `.cursor/aria-knowledge.local.md` (read in Step 0). When enabled, draft `{project_root}/SESSION.md` as a **handoff-state** snapshot per `aria-atlas/docs/TEMPLATE_SESSION.md` (full rewrite; create if absent — the one create-exception to the skip-gracefully rule):
- Header: `lastEvent: handoff`; `at:` current UTC (`date -u +%Y-%m-%dT%H:%M:%SZ`); `currentFocus:` one line; `nextAction:` the imperative first action from 3e; `branch:`/`headCommit:` from `git -C {project_root} rev-parse --abbrev-ref HEAD` / `... rev-parse --short HEAD` (omit if not a git repo); `by:` `author_tag` (omit if unset); **`sessionId:` REQUIRED** — the next handoff's demote decision keys on it, and an absent value makes that guard unevaluable (which historically read as "the ledger doesn't apply", and the handoff got skipped). Read it from the existing front-matter or the session context; if it genuinely cannot be resolved, write `sessionId: unknown` rather than omitting the line.
- Body: `## Where we left off` + `## Next session pickup` (2-4 sentences each); `## Next session prompt` = **the 3e opener verbatim** inside the fenced block (it may contain nested ``` fences — preserve them).
The 3e opener is authored once and reused here — single source, no divergence between the closing report's opener and the SESSION.md prompt block.
**NEVER SKIP THIS STEP TO AVOID CLOBBERING SOMEONE ELSE'S STATE.** A SESSION.md may legitimately hold several still-valid next-session prompts, and the ledger below exists precisely so you never have to choose between overwriting a handoff and abandoning yours. Skipping is the *worst* outcome: your opener is lost entirely, which is the one thing this design is built to prevent. If a prior handoff is present, demote it and write yours — both survive.
**Multi-session ledger (several valid prompts can coexist — nothing is ever lost):** if the existing SESSION.md has `lastEvent: handoff` and its `sessionId` differs from this session's (or is absent — treat an unidentifiable handoff as *someone else's*), DEMOTE it before overwriting the active slot. Source `bin/lib-session-state.sh` and, reading the prior file's values first, call:
- `kt_ss_ledger_add "{project_root}" "<prior sessionId>" "<prior at>" "<prior currentFocus>" "<prior nextAction>" "<prior next-session-prompt>"` — moves the prior active entry into `## Pending handoffs` (newest-first). **Pass the prompt at FULL fidelity — do not collapse it to one line.** An unconsumed prompt is still-valid work; collapsing it degrades a mandate nobody has used yet. Block boundaries are declared by an explicit `<!-- aria:entry-end -->` terminator, so a stored prompt may safely contain column-0 `## ` lines and nested fences.
- `kt_ss_ledger_prune "{project_root}"` — drops any entry a resume already marked `consumed`. **Unconsumed entries always survive**, at full fidelity.
**Never demote a `lastEvent: in-progress` marker.** That is a live session's own breadcrumb, not a handoff — it carries no prompt, so a ledger entry for it would be empty. Overwrite it and move on.
THEN write the new active header + `## Next session prompt` (the full rewrite below). `## Pending handoffs` is managed by these helpers — the rewrite replaces only the front-matter + active body, never the pending section. (Files written before this rename carry `## Prior sessions`; the helpers keep using it for those, so nothing is orphaned.)
**Before writing, check what this session left behind (two cheap reads, both report-only):**
1. **Recorded Rule 22 bypasses.** Read `${TMPDIR:-/tmp}/aria-r22-bypass-<session_id>` if it exists — each line is an in-place file mutation made through the shell, which routed around the Edit/Write gate and so landed with no scope assessment recorded. The PreToolUse hook only *warns* (denying would block legitimate work), so a warning that was ignored leaves no other trace. Report the count and the idioms in the closing summary — not as a failure, as a fact the next reader should have. If the file is absent, say nothing.
2. **Pending handoffs.** If `## Pending handoffs` (or a legacy `## Prior sessions`) holds entries still marked `unconsumed`, state how many and name their sessions in the closing summary. A prompt that is stored but never surfaced is lost in practice — this is the second of three checkpoints (the others are `/wrapup` and resume).
**Tracked or ignored — read `session_state_tracked` (default `false`):**
- **`false` (default) — ignore it, never commit it.** SESSION.md is ephemeral per-session state (atlas reads from disk; PROGRESS.md is the durable log). If `{project_root}` is a git repo and SESSION.md is **not already tracked**, ensure `.gitignore` ignores it. **Never stage SESSION.md** — exclude it from the Step 5 / 3d commit.
- **`true` — it is a tracked artifact.** Do **NOT** add an ignore line, and **DO** stage it with the Step 5 / 3d commit. If an ignore line already exists, remove it: leaving one makes the config assert something git is not doing. Choose this when SESSION.md carries a decision trail you need versioned — most often in a repo with no `PROGRESS.md`, where SESSION.md *is* the durable log and the default's rationale does not hold.
⛔ **Test tracking with `git -C {project_root} ls-files --error-unmatch SESSION.md`, never "is the pattern already in `.gitignore`?"** An ignore rule is a **no-op on an already-tracked path**, so a pattern check can never become true for a tracked file and the clause **appends on every run** — one observed `.gitignore` had accumulated four identical `SESSION.md` lines. ⚠ `git check-ignore` cannot serve as the test either: it consults the index, so it reports a **tracked** file as *not ignored*.
## Step 4: Single Combined-Go Review (default mode only)
**Skip this step entirely if `mode = auto` or `mode = snap`.** (Both run silently with implicit-yes — no review gate.)
In default mode, present all drafts together in one scroll under clear section headers, then ask **once**:
```
## Handoff Review — combined-go
[3a: PROGRESS.md entry draft]
[3b: CLAUDE.md updates draft, or "no changes needed"]
[3c: Memory updates draft, or "no changes needed"]
[3d: Commit messages + staged file lists per repo, or "no uncommitted changes"]
[3e: Next-session opener]
[3f: SESSION.md (handoff state) draft, or "skipped — session_state off"]
---
**Apply all of the above?**
- `yes` — apply all drafts, run /extract, emit final report
- `edit {section}` — let user revise a specific section before applying (3a, 3b, 3c, 3d, 3e, or 3f)
- `skip {section}` — apply everything except the named section
- `abort` — apply nothing, exit cleanly
```
Wait for explicit response. Allow multiple `edit` / `skip` directives in sequence (re-show the scroll after each revision). The final action is `yes` or `abort`.
## Step 5: Apply Drafts
Apply approved drafts in order. For `auto` and `snap` modes, this runs immediately after Step 3 with no review gate.
1. **3a:** Append the PROGRESS.md entry (or merge into today's existing entry)
2. **3b:** Edit AGENTS.md per the diffs
3. **3c:** Edit memory file(s) per the diffs
4. **3d:** For each git repo:
- Stage the listed files (specific paths, never `git add -A`)
- Commit with the drafted message
- **Never push.** This applies in both modes regardless of how the repo is hosted.
5. **3f:** If a prior unconsumed `handoff` entry exists (different or absent `sessionId`), run `kt_ss_ledger_add` (**full-fidelity prompt — never collapsed**) + `kt_ss_ledger_prune` FIRST (demote + prune), THEN write `{project_root}/SESSION.md` (handoff state — full rewrite of front-matter + active body + `## Next session prompt`, create if absent; `## Pending handoffs` is managed by the helpers, not the rewrite). Never demote a `lastEvent: in-progress` marker, and **never skip 3f to avoid clobbering** — demote instead, so both survive. Skip only if `session_state` is off or the user `skip`-ped 3f.
If any step fails (e.g., commit hook rejects), surface the failure inline and stop — do not silently continue.
## Step 6: Capture Session Knowledge
**If `mode = snap`:** Do NOT run `/extract`. Instead invoke the `/snapshot` skill to archive the raw transcript to `intake/task-boundary-captures/` for later extraction. This is snap mode's defining difference: capture is deferred, not synthesized now. Like auto's "extract always runs" invariant, the snapshot ALWAYS runs — there is no skip path. The snapshot is the deferred-extraction handoff: a later `/extract`, or the next `/audit-knowledge` digest pass (which reads `intake/task-boundary-captures/` automatically), synthesizes knowledge from it when context isn't a constraint. Capture `/snapshot`'s output (the snapshot path) for inclusion in Step 8. Use snap when context is high and running `/extract` now would risk compaction mid-synthesis. (`/snapshot` requires Bash, which the Step-0 runtime gate already guaranteed.)
**Otherwise (default + `auto` modes):** ALWAYS invoke `/extract` programmatically. This applies to default mode (after the user has approved the combined-go review in Step 4) AND `auto` mode unconditionally. No judgment-skip allowed — even if the session feels short, conversational, or seems to have nothing new to extract, run `/extract` anyway. The handoff skill must not pre-judge whether extraction is worthwhile; `/extract` has its own dedup logic (per its Rules section: "Never ask for confirmation — scan and dump") that correctly handles the "nothing to add" case by reporting `No uncaptured knowledge found`. Auto mode's "implicit-yes on all gates" rule converts to **"extract always runs"** here — there is no skip path. Capture `/extract`'s summary report for inclusion in Step 8.
(Brief mode never reaches Step 6 — it exits at Step 2B before any handoff side-effects, per the Rules section.)
## Step 7: Verify Handoff Readiness
Run the same checklist `/wrapup` Step 7 uses:
```
## Handoff Checklist
- PROGRESS.md — [updated / already current / not found / skipped]
- CLAUDE.md — [current / updated / not found / skipped]
- Memory — [updated / already current / not found / skipped]
- Git — [committed N file(s) / no changes / uncommitted (skipped)]
- /extract — [N items captured / nothing new / deferred: transcript snapshotted to intake/task-boundary-captures/ for later extraction (snap mode)]
- SESSION.md — [written: handoff (prompt embedded) / skipped (session_state off)]
- Tracked artifacts — [all fresh / N stale (consider /codemap update or /stitch verify for {tags}) / not checked]
- Next-session opener — [emitted below]
```
**Tracked artifacts check (added v2.16.1):** if active project detected (from Step 1's identification), stat `{project_root}/CODEMAP.md` and `{project_root}/STITCH.md` against `codemap_staleness_threshold_days` / `stitch_staleness_threshold_days` from config (defaults 14 / 30). Report status. Don't block on staleness — surface for visibility so next session starts with awareness.
Flag any gaps but don't block — the user may have skipped sections intentionally.
## Step 8: Final Report
Emit the closing report. **The next-session opener is the headline artifact** — surface it prominently and inside a code fence so it copies cleanly.
```
## Handoff Complete — {default | auto | snap} mode
[Handoff Checklist from Step 7]
[/extract summary, 1-2 lines]
---
### Next-session opener — paste this to resume
```
{full opener from Step 3e}
Read on resume: {primary AGENTS.md path} for current state.
```
```
The `Read on resume:` line MUST sit INSIDE the opener fence (the last line of the pasteable block), never after the closing ```` ``` ````. It is part of the artifact the user pastes into the next session — if it lands outside the fence it is silently dropped on paste. (Step 3e's opener already carries a `Read first:` pointer inside the fence; this `Read on resume:` line is the Step-8 report's echo of it and must stay equally inside.)
## Rules
- **/wrapup is the interactive default; /handoff is the express lane.** Don't deprecate or replace /wrapup. They serve different cadences.
- **Always emit the next-session opener (default + auto + snap modes; not brief).** In default + auto + snap, even when nothing else changed (no PROGRESS update, no commit, no memory edit), the opener is the headline deliverable. Brief mode emits the coworker brief instead — different artifact, different audience.
- **The opener always carries a `Suggested next session:` line (default + auto + snap modes; not brief).** De-versioned model family (`Fable`/`Opus`/`Sonnet`/`Haiku`, never a version number) + effort level + a one-line rationale grounded in the first action. It is advisory, not model-setting. Brief mode does not carry it. See Step 3e's rubric for the row mapping.
- **`auto` mode applies everything without confirmation.** The user explicitly opted into that risk by typing `auto`. Do not introduce confirmation gates in auto mode — that defeats the purpose.
- **`brief` mode produces output only — no side effects.** No PROGRESS update, no CLAUDE.md edit, no memory write, no commit, no /extract. The brief is a copy/paste artifact for a person, not durable state. Users who want both a brief AND state updates run `/handoff brief` then `/handoff` (or `/handoff auto`) separately — two passes, two artifacts.
- **Brief mode keeps `[coworker]` as a literal placeholder.** Don't prompt the user for a recipient name. They'll fill it at paste time. This avoids friction and supports "send to multiple people" use cases.
- **Brief mode caps at 200 words.** Above that, the format breaks down and reads as a memo, not a brief. Target 80-150 words. Omit empty sections rather than padding.
- **Combined-go preserves verification.** Default mode shows all drafts in one scroll before applying. Per-section `edit` / `skip` keeps the per-item escape hatch.
- **Never push, in any mode.** Local commits only (and brief mode doesn't commit at all). If the user wants to push, they do it separately.
- **Stage specific files, not `git add -A`.** Avoid capturing sensitive files (.env, credentials) that happen to be untracked.
- **Match existing formats** — when appending to PROGRESS.md or editing CLAUDE.md, match the heading style, date format, and structure of existing entries. Don't impose a new format.
- **Skip gracefully** — if a file doesn't exist (no PROGRESS.md, no CLAUDE.md, no memory), skip that step and note it. Don't create files that aren't already part of the project's conventions.
- **Don't invent work** — the session synthesis must reflect what actually happened in the conversation. If the session is short or unclear, say so in the synthesis (default + auto) or in the brief's "What happened" line (brief) rather than padding.
- **Delegate extraction** — /handoff calls /extract for capture in default + auto modes; it does not duplicate /extract's dedup or routing logic. Brief mode skips /extract entirely. Snap mode calls /snapshot instead of /extract (see below).
- **`snap` defers, never drops, capture.** In snap mode Step 6 runs `/snapshot` instead of `/extract` — the snapshot ALWAYS runs (no skip path, same as auto's "extract always runs" invariant). The raw transcript is preserved so a later /extract or /audit-knowledge digest can synthesize it; snap never means "skip knowledge capture," only "capture cheaply now, synthesize later." snap is otherwise byte-for-byte auto behavior: silent, implicit-yes, emit the next-session opener, local commit only, never push. snap is NOT brief — it produces the full next-session package, not a coworker prose block, and unlike brief it DOES update PROGRESS/CLAUDE/memory and commit.
- **One handoff per session** — if the user runs /handoff again in the same session, check what was already done in the prior run and skip completed work. Don't duplicate PROGRESS entries or commits. Multiple `/handoff brief` runs are fine (each produces a fresh brief reflecting current state).
---
## /wrapup
# /wrapup — Session Close-Out
Close out the current session cleanly: review what got done, update project tracking files, commit changes, capture session knowledge, and confirm everything is documented. This is the "I'm done" skill — no next-session opener is produced. For passoff (future-you or a coworker), use `/handoff` instead.
**Three modes:**
- **Default (`/wrapup`)** — Per-step gated review. Each tracked surface (session summary, PROGRESS, CLAUDE.md, memory, commit, /extract prompt) prompts for explicit confirmation before writing.
- **`auto` (`/wrapup auto`)** — Implicit-yes on all gates. Run silently. Apply all drafts and chain `/extract` without confirmation. Emit final report only. Use when the session is short and unambiguous, or when you've already authorized a combined-go (`yes to all`, `yes to all with extract`).
- **`snap` (`/wrapup snap`)** — Like `auto`, but archives the raw transcript via `/snapshot` for later extraction **instead of** running `/extract` now. Use when context is high: you still get the full silent close-out + commit, but defer the expensive, compaction-risky knowledge synthesis to a later session (or the next `/audit-knowledge` digest pass, which reads the snapshot automatically).
**`snap` is `auto` plus one swap.** Everywhere a step below says "If `mode = auto` (or `snap`)", `snap` follows auto's behavior exactly — implicit-yes, silent, apply all drafts, no per-step prompts. The single difference is the capture step (Step 8): `snap` runs `/snapshot` (archive the transcript for later) while `auto` runs `/extract` (synthesize now). Nothing else differs.
## Step 0: Resolve Config and Parse Mode
Read `.cursor/aria-knowledge.local.md` and extract `knowledge_folder`. If the file doesn't exist, stop: "aria-knowledge is not configured. Run /setup to get started."
Parse the argument:
- No arg, or arg is empty → `mode = gated` (default)
- Arg matches `auto` (case-insensitive) → `mode = auto`
- Arg matches `snap` (case-insensitive) → `mode = snap`
- Any other arg → stop: "Unknown argument '{arg}'. Use '/wrapup', '/wrapup auto', or '/wrapup snap'."
Use `{knowledge_folder}` as the base path for all file operations in subsequent steps.
## Step 1: Identify Project Context
Detect the active project by scanning the working directory for project markers:
1. Search upward from cwd for `PROGRESS.md` and `CLAUDE.md` files
2. Also check for `CODEMAP.md` (indicates a mapped codebase)
3. Check for project-level memory files in `~/.claude/projects/` matching the current path
**Multi-project-root disambiguation (required for correct SESSION.md placement):** if the upward search resolves to a *multi-project/workspace root* — e.g. `~/Projects`, or any directory whose `AGENTS.md` or `CLAUDE.md` indexes multiple child projects rather than describing one project, and which has no project-specific `PROGRESS.md` — do NOT treat that root as the project (writing `SESSION.md` there would be wrong). Instead infer the active project from **this session's actual work**: the files edited, the repos committed to, or the project the user named. Use that project's own root (its nearest `CLAUDE.md`/`PROGRESS.md`) for every per-project write, especially `SESSION.md`. This mirrors the SessionStart re-entry instruction's "which project" signal. If the session genuinely spans no single project, skip the SESSION.md write (Step 6.5) and note it.
Record:
- **Project root** — the directory containing PROGRESS.md and/or CLAUDE.md
- **PROGRESS.md path** — if it exists
- **CLAUDE.md path(s)** — root-level and any subfolder-level ones relevant to the session
- **Memory files** — any `project_*.md` files in the Claude memory directory for this project path
- **Git repos** — run `git status` in any git repositories within the project to detect uncommitted changes
If no PROGRESS.md or CLAUDE.md is found, note this — the session may be in a project that doesn't use these conventions. Continue with the steps that are applicable.
## Step 2: Review Session Work
Summarize what was accomplished in this session:
1. **Files changed** — list files created, modified, or deleted during this session (from conversation context, not git — git may include changes from before this session)
2. **Key decisions** — architectural choices, design decisions, approach selections made during the session
3. **Current state** — what's working, what's in progress, what's blocked
4. **Next steps** — what the user indicated should happen next, or what logically follows
Present this summary to the user:
```
## Session Summary
**Project:** [project name/path]
**Focus:** [1-line description of session goal]
**Work completed:**
- [bullet list of what was done]
**Decisions made:**
- [bullet list of key decisions]
**Next steps:**
- [what follows from here]
```
**If `mode = auto` (or `snap`):** skip the prompt and proceed with the drafted summary as-is.
**Otherwise (gated mode):** Ask: "Does this summary look right? (yes / edit)"
If the user wants to edit, incorporate their corrections before proceeding.
## Step 3: Update PROGRESS.md
If a PROGRESS.md exists for this project:
1. Read the current PROGRESS.md
2. Check if a session entry already exists for today's work (the user or a previous /wrapup may have already added one)
3. If no entry exists, draft a new session entry using the project's existing format (match the heading style, content structure, and level of detail of previous entries)
4. Show the draft to the user
**If `mode = auto` (or `snap`):** append the drafted entry without prompting (equivalent to **yes**).
**Otherwise (gated mode):** Ask: "Add this session entry to PROGRESS.md? (yes / edit / skip)"
- **yes** — append the entry
- **edit** — let the user modify, then append
- **skip** — leave PROGRESS.md as-is
If PROGRESS.md doesn't exist, skip this step and note it in the final report.
## Step 4: Check CLAUDE.md Currency
If a CLAUDE.md exists for this project:
1. Read the CLAUDE.md
2. Check if anything from this session contradicts, outdates, or is missing from it — examples:
- New conventions established that aren't documented
- File paths or structures that changed
- Known issues that were resolved or new ones discovered
- Tool/integration changes
3. If updates are needed, show the proposed changes
**If `mode = auto` (or `snap`):** apply the drafted CLAUDE.md updates without prompting (equivalent to **yes**). If no updates are needed, note that in the final report and move on.
**Otherwise (gated mode):** Ask: "Update CLAUDE.md with these changes? (yes / edit / skip)"
If no updates are needed, say so and move on. Don't force updates for the sake of updating.
## Step 5: Update Memory
Check if project memory files (in `~/.claude/projects/` for the current project path) need updating:
1. Read the relevant `project_*.md` memory file(s)
2. Compare against the session summary — is the memory's "Current State" still accurate?
3. If the memory is stale, draft an update
**If `mode = auto` (or `snap`):** apply the drafted memory update without prompting (equivalent to **yes**). If no memory file exists or no update is needed, note that in the final report and move on.
**Otherwise (gated mode):** Ask: "Update project memory? (yes / edit / skip)"
If no memory file exists or no update is needed, skip and note it.
## Step 6: Commit Prompt
For each git repository detected in Step 1:
1. Run `git status` to check for uncommitted changes
2. If there are changes, show a summary:
```
**Uncommitted changes in [repo path]:**
- [N] modified files
- [N] new files
- [N] deleted files
[list the file names]
```
**If `mode = auto` (or `snap`):** stage all changes (per-file, not `git add -A` — exclude anything that looks like a secret or unrelated work-in-progress), draft a conventional commit message from the session work, and commit without prompting. Skip the message-confirmation step. Still **local commit only — never push.**
**Otherwise (gated mode):** Ask: "Want to commit these changes? (yes / no / select files)"
- **yes** — stage all changes, draft a conventional commit message based on the session work, show it for confirmation, then commit
- **no** — skip committing
- **select files** — let the user specify which files to stage, then proceed with commit
If no uncommitted changes exist, say "No uncommitted changes" and move on.
**Important:** Do not push to remote. Only commit locally. If the user wants to push, they can do so separately. This applies to both modes.
## Step 6.5: Write SESSION.md (wrapup state)
Skip this step entirely unless `session_state: true` in `.cursor/aria-knowledge.local.md` (the config you read in Step 0). When enabled:
Write `{project_root}/SESSION.md` (project root from Step 1) as a **wrapup-state** snapshot, following the contract at `aria-atlas/docs/TEMPLATE_SESSION.md`. **Full rewrite** (wrapup is an authoritative close). This is a deliberate exception to the "don't create files" rule — create it if absent.
**Consume on clean close (multi-session ledger):** a `/wrapup` is a clean close, not a handoff — it adds NO pending entry for the wrapped session itself (there is no next-session prompt to retain). If the existing SESSION.md has a `## Pending handoffs` block (or a legacy `## Prior sessions` one), source `bin/lib-session-state.sh` and call `kt_ss_ledger_prune "{project_root}"` to drop any entries a resume already marked `consumed`. Unconsumed handoffs survive at full fidelity — wrapping up one session never silently discards another's pending pickup.
**Before closing, check what this session left behind (two cheap reads, both report-only):**
1. **Recorded Rule 22 bypasses.** Read `${TMPDIR:-/tmp}/aria-r22-bypass-<session_id>` if it exists — each line is an in-place file mutation made through the shell, which routed around the Edit/Write gate and so landed with no scope assessment recorded. The PreToolUse hook only *warns* (denying would block legitimate work), so a warning that was ignored leaves no other trace. Report the count and the idioms in the close-out summary — not as a failure, as a fact worth knowing before the session ends. If the file is absent, say nothing.
2. **Pending handoffs.** If `## Pending handoffs` (or a legacy `## Prior sessions`) still holds `unconsumed` entries, state how many and name their sessions. A clean close does not consume another session's pickup, so this is the last chance to notice one before the session is gone — the third of three checkpoints (the others are `/handoff` and resume).
**Never skip this step to avoid clobbering another session's state.** Skipping loses more than writing does: the prune only ever removes entries already marked consumed, so running it cannot destroy pending work. If you are unsure whether another session owns the file, run the prune and write — that is the safe direction, not the risky one.
**Tracked or ignored — read `session_state_tracked` (default `false`):**
- **`false` (default) — ignore it, never commit it.** SESSION.md is ephemeral per-session state (atlas reads it from disk; PROGRESS.md is the durable log). If `{project_root}` is a git repo and SESSION.md is **not already tracked**, ensure `.gitignore` ignores it. **Never `git add` SESSION.md** — it must not appear in the Step 6 commit.
- **`true` — it is a tracked artifact.** Do **NOT** add an ignore line, and **DO** stage it with the Step 6 commit. If an ignore line already exists, remove it: leaving one makes the config assert something git is not doing. Choose this when SESSION.md carries a decision trail you need versioned — most often in a repo with no `PROGRESS.md`, where SESSION.md *is* the durable log and the default's rationale does not hold.
⛔ **Test tracking with `git -C {project_root} ls-files --error-unmatch SESSION.md`, never "is the pattern already in `.gitignore`?"** An ignore rule is a **no-op on an already-tracked path**, so a pattern check can never become true for a tracked file and the clause **appends on every run** — one observed `.gitignore` had accumulated four identical `SESSION.md` lines. ⚠ `git check-ignore` cannot serve as the test either: it consults the index, so it reports a **tracked** file as *not ignored*. (That inversion is itself useful — "not ignored" from `check-ignore` on a file you believe is ignored means it is tracked.)
Header fields:
- `lastEvent: wrapup`
- `at:` current UTC — `date -u +%Y-%m-%dT%H:%M:%SZ`
- `currentFocus:` one line from the Step 2 summary (where the project stands)
- `nextAction:` one line, or `complete` for a clean close with nothing pending
- `branch:` / `headCommit:` from `git -C {project_root} rev-parse --abbrev-ref HEAD` and `git -C {project_root} rev-parse --short HEAD` (omit both if not a git repo)
- `by:` the `author_tag` config value (omit if unset)
- `sessionId:` omit unless known
Body:
- `## Where we left off` — 2-4 sentences from the Step 2 summary
- `## Next session pickup` — 2-4 sentences
- `## Next session prompt` — **leave the fenced block empty** (wrapup carries no opener; that's what distinguishes it from `/handoff`)
**If `mode = auto` (or `snap`):** write without prompting. **Otherwise (gated):** show the drafted file and ask "Write SESSION.md (wrapup state)? (yes / edit / skip)".
## Step 7: Verify Wrapup Readiness
Run through a checklist and report status:
```
## Wrapup Checklist
- [x/!/ ] PROGRESS.md — [updated / already current / not found / skipped]
- [x/!/ ] CLAUDE.md — [current / updated / not found / skipped]
- [x/!/ ] Memory — [updated / already current / not found / skipped]
- [x/!/ ] Git — [committed / no changes / uncommitted changes (user skipped)]
- [x/!/ ] SESSION.md — [written: wrapup / skipped (session_state off) / not applicable]
- [x/!/ ] Tracked artifacts — [all fresh / N stale (consider /codemap update or /stitch verify) / not checked]
```
**Tracked artifacts check (added v2.16.1):** if active project detected (from Step 1's identification), stat `{project_root}/CODEMAP.md` and `{project_root}/STITCH.md` against `codemap_staleness_threshold_days` / `stitch_staleness_threshold_days` from config (defaults 14 / 30). Report status with `x` (fresh), `!` (stale), or blank (not checked). Don't block on staleness — surface for next-session awareness.
If any item shows a gap (uncommitted changes skipped, PROGRESS.md not updated), flag it — but don't block. The user may have good reasons to defer.
## Step 8: Capture Session Knowledge
**If `mode = snap`:** Do NOT run `/extract`. Instead invoke the `/snapshot` skill to archive the raw transcript to `intake/task-boundary-captures/` for later extraction. This is snap mode's defining difference: capture is deferred, not synthesized now. Like auto, this always runs — there is no skip path. The snapshot is the deferred-extraction handoff: a later `/extract`, or the next `/audit-knowledge` digest pass (which reads `intake/task-boundary-captures/` automatically), synthesizes knowledge from it when context isn't a constraint. Use snap when context is high and running `/extract` now would risk compaction mid-synthesis. (`/snapshot` requires Bash, which the Step-0 runtime gate already guaranteed.)
**If `mode = auto`:** ALWAYS invoke the `/extract` skill. No judgment-skip allowed — even if the session feels short, conversational, or seems to have nothing new to extract, run `/extract` anyway. The model running this step must not pre-judge whether extraction is worthwhile; `/extract` has its own dedup logic (per its Rules section: "Never ask for confirmation — scan and dump") that correctly handles the "nothing to add" case by reporting `No uncaptured knowledge found`. The wrapup skill must not make that judgment on `/extract`'s behalf. Auto mode's "implicit-yes on all gates" rule converts to **"extract always runs"** here — there is no skip path in auto mode.
**Otherwise (gated mode):** Ask: "Run /extract to capture session knowledge before ending? (yes / no)"
- **yes** — invoke the /extract skill. Once the user has said yes, the same "always run" rule applies — do not subsequently skip based on session-content judgment. /extract handles its own dedup; the user authorized the run.
- **no** — skip
## Step 9: Report
Output a brief closing summary:
```
## Session Wrapup Complete
[1-2 lines: what was updated]
[If mode = snap: **Knowledge capture:** transcript snapshotted to intake/task-boundary-captures/ for later extraction (run /extract in a fresh session, or let the next /audit-knowledge digest pass synthesize it). /extract was NOT run this session.]
**Next session pickup:** Read [path to PROGRESS.md or CLAUDE.md]
```
Use the heading **`Session Wrapup Complete`** for `/wrapup` runs — distinct from `/handoff`'s **`Session Handoff Complete`** heading. The two skills have distinct intents per the v2.19.0 intent split (wrapup = close-out with no passoff; handoff = passoff package with next-session opener) and their closing-report headings should reflect that.
## Rules
- **Confirm before writing in gated mode** — every file modification (PROGRESS.md, AGENTS.md, memory, git commit) requires explicit user approval; show the proposed change first. In `auto` mode, the explicit user approval comes from the `/wrapup auto` invocation itself (or a combined-go signal like `yes to all`) and per-step prompts are skipped.
- **Match existing format** — when adding entries to PROGRESS.md, match the heading style, date format, and content structure of existing entries. Don't impose a new format.
- **Don't invent work** — the session summary should reflect what actually happened in the conversation, not what might have happened. If the conversation is short or unclear, say so.
- **Git safety** — never force push, never amend, never push to remote. Local commits only. Stage specific files, not `git add -A` (avoid capturing sensitive files).
- **Skip gracefully** — if a file doesn't exist (no PROGRESS.md, no CLAUDE.md, no memory), skip that step and note it. Don't create files that don't already exist as part of the project's conventions.
- **SESSION.md is the one create-exception.** Unlike PROGRESS.md/CLAUDE.md/memory (skip-gracefully if absent), SESSION.md is *always written* when `session_state` is on — created at the project root if it doesn't exist. It's a new convention that must bootstrap. This is the only file /wrapup creates rather than skips.
- **Delegate extraction** — /wrapup prompts for /extract but does not perform extraction itself. The /extract skill has its own deduplication and formatting logic.
- **`snap` defers, never drops, capture.** In snap mode /wrapup runs `/snapshot` instead of `/extract` — it must always run the snapshot (no skip path, same as auto's "extract always runs" invariant). The raw transcript is preserved so a later /extract or /audit-knowledge digest can synthesize it; snap never means "skip knowledge capture," only "capture cheaply now, synthesize later." snap is otherwise byte-for-byte auto behavior (silent, implicit-yes, local commit only, never push).
- **One passoff per session** — if the user runs /wrapup again in the same session, check what was already done and skip completed steps. Don't duplicate entries.
---
## /prospect
# /prospect — Plan pre-mortem with risk enforcement
Run a structured pre-mortem on a plan or approach that has been *created but not yet executed*. Forward-looking counterpart to `/retrospect`. Produces a 10-section markdown report with per-step verdicts, risk status, action recommendations, and process pre-mortem when the plan-formation itself was thin. Writes findings to `knowledge/logs/prospect/` and runs aria's standard intake.
The discipline this enforces: before the first edit lands, every planned step gets named, its evidence base examined, its smallest viable version identified, and its action gated on the strength of the underlying hypothesis. Mirrors `/retrospect`'s shape so the same review muscle works in both directions.
## When to use
- After a multi-step plan is articulated (in chat, in TodoWrite, in a `.md` plan file) but no code has been written yet
- After `/brainstorming` concludes with an action plan
- After `/distill` produces a task spec that's about to be executed
- After a ticket's Technical Intake is drafted and the implementer is about to begin
- Before kicking off a long autonomous run (e.g., `combined go`) on a non-trivial plan
- As a soft-suggested response to "let me implement…", "I'll just code it…", "ok ship it" when no validation exists yet
If code has already been written/committed (even in-session), use `/retrospect` instead — that pivots from forward-looking to backward-looking validation.
## Step 0: Inputs & Mode Detection
Parse the invocation arguments. The first positional argument is the **scope keyword**; subsequent positional arguments are scope-specific. Six scopes plus a no-args default:
| Scope | Trigger | Backward-compat flag (still accepted) | Plan source |
|---|---|---|---|
| **plan** (default) | `/prospect plan` or `/prospect` | (was the no-arg default) | Current conversation's articulated plan — combine the active TodoWrite list, the most recent assistant plan/approach message, and any in-session plan file Claude has written. If ambiguous, ask user "Which of these is the plan you want me to pre-mortem?" with a short list. |
| **session** | `/prospect session` | `--session` | Synonym for **plan**. Reserved for cases where the user wants to emphasize "everything articulated this conversation" rather than a single plan artifact. |
| **todos** | `/prospect todos` | `--todos` | Just the active TodoWrite list — a thin mode for quick checks |
| **file** | `/prospect file <path>` | `--plan <path>` | Read the markdown file at `<path>` as the plan |
| **ticket** | `/prospect ticket <id>` | `--ticket <id>`, `linear`/`--linear` (legacy) | Read the ticket's Technical Intake (and Product Intake for goal context) via the connected project-tracker MCP — Linear, Jira/Atlassian, Asana, Monday, ClickUp, Notion-as-tracker, GitHub Issues. If no tracker MCP is connected, ask the user to paste. |
| **branch** | `/prospect branch <name>` | `--branch <name>` | Uncommitted/unpushed local changes on the branch — `git diff <main-branch>...<name>` — treated as a plan-in-progress (NOT shipped yet) |
**Argument parsing rules:**
- If the first positional arg matches a scope keyword (case-insensitive), use it. Otherwise treat it as an arg to the default `plan` scope.
- Backward-compat flag forms (`--plan`, `--ticket`, `--branch`, `--todos`, `--session`) remain accepted indefinitely. Both `/prospect ticket ABC-123` and `/prospect --ticket ABC-123` resolve identically.
- **Legacy vendor spellings still work.** The `ticket` scope was named `linear` before the surface was made tracker-agnostic, so `/prospect linear <id>` and `--linear <id>` resolve exactly as `ticket`/`--ticket` do, and `--linear-post` resolves as `--tracker-post`. They are aliases, not separate behaviour — never advertise them as the canonical form.
- Modifier flags (apply to any scope): `--ticket-post` (post the prospect verdict to detected tickets at end; alias `--linear-post`), `--no-source` (skip Step 3.5's Evidence-Sourcing Pass), `--lens=overbuild` (run the over-build review pass — see "Over-build lens" section; opt-in, off by default).
After mode detection, gather:
1. **Goal** — Ask the user: "What is this plan supposed to accomplish? (One sentence is fine.)" If they don't reply, fall back to the plan's first heading or stated objective.
2. **Tickets** — Scan plan text/commits/branch name with regex `\b([A-Z]{2,}-\d+)\b` for ticket IDs (the pattern is vendor-neutral — it matches DEV-123, PROJ-45, JIRA-9 alike). If found AND a project-tracker MCP is available, fetch each ticket's Product/Technical Intake + acceptance criteria to use as the goal-anchor in §4.6. If a project-tracker MCP is unavailable, note "ticket context unavailable" but continue.
3. **Pre-execution evidence** — Ask the user: "For each step in this plan, do you have evidence the step is necessary and that the underlying assumption is correct? (✅ measured / ⚠ inferred / ❌ contradicted / ❓ untested)" Show the per-step list and accept inline replies. If user can't supply evidence for any step, mark those ❓ — those steps will resolve to DEFER unless §4.7 produces supporting hypothesis confidence.
If scope is `branch` (or invoked via `--branch`) and the diff is non-trivial (>50 LOC across >3 files), warn: "Branch already has substantive code — consider `/retrospect range main..HEAD` instead, which is calibrated for already-written changes." Continue if user confirms.
## Step 0.5: Active Knowledge Surfacing
If the user's config (`.cursor/aria-knowledge.local.md`) has `active_knowledge_surfacing: true` (default as of v2.15.0), surface relevant tagged knowledge BEFORE Steps 1-3 so loaded files inform pattern selection and evidence sourcing. If the field is `false`, skip this step entirely (note `Active surfacing: disabled` in the Anchor block).
**Algorithm:**
1. **Build query.** Combine, separated by spaces: the Goal sentence from Step 0; the plan's first heading or the first 3 TodoWrite items; any detected ticket IDs (e.g., `ABC-123`); the file basename if scope is `file`; the branch name if scope is `branch`.
2. **Read the index.** `Read` `<knowledge_folder>/index.md` (resolve `<knowledge_folder>` from the config's `knowledge_folder` field). Parse the `## Tag Index` section for `### tagname` headers — that's the matching vocabulary (~77 known tags as of v2.15.0). Ignore the `## Other Tags` section (freeform tier, intentionally excluded from auto-surfacing).
3. **Tokenize.** Lowercase the query, strip punctuation to spaces, dedupe to a word set.
4. **Match.** Exact word-vs-tag equality only — no substring, no fuzzy. Collect the set of matched tags.
5. **Threshold gate.** If fewer than 2 tags matched, note `Active surfacing: 0 matches (below threshold)` in the Anchor block and skip to Step 1. Single-tag matches are too noisy.
6. **Collect files.** Under each matched tag's `### tag` section, gather the `- path — description` lines. Dedupe by path. Cap at top-5 by first-appearance order.
7. **Ledger filter (best-effort).** Run `ls -t /tmp/aria-active-* 2>/dev/null | head -1` via Bash to find the current session's ledger (the most recently modified file matching that pattern). If found, read it and drop any matched paths already listed there — they were surfaced by an earlier hook/skill in this session. If no ledger exists, proceed unfiltered.
8. **Read matched files.** For each remaining path (up to 5), `Read` the full file into context.
9. **Summarize.** Before Step 1's Anchor Block, emit a 3-line surfacing block:
```
Active Knowledge Surfacing:
Tags matched: <tag1> <tag2> ...
Files loaded: <N> (<file1>, <file2>, ...)
Relevance: <one sentence per file: why this informs the prospect>
```
10. **Carry-forward.** These loaded files become input to Step 2 (Load Pattern Libraries — past prospects/retros tagged with the same topic may already catalog the relevant patterns) and Step 3.5 (Evidence-Sourcing Pass — they may already validate or falsify assumptions in the plan, converting ⚠/❓ to ✅/❌ before the verdict round).
11. **Tracked artifacts surfacing (added v2.16.1).** After Step 10's carry-forward, ALSO surface CODEMAP + STITCH for the plan's project. The shared lib at `scripts/aria/lib-tracked-artifacts.sh` implements equivalent logic for hooks; this step inlines the algorithm for skill-context portability.
a. **Detect project tag.** Try in order: `--group=<tag>` from Step 0 if provided → first ticket-ID prefix that maps to a `projects_list` tag → first `projects_list[<tag>].path` whose `path` appears as substring in the plan source path (from Step 0 `Source:` field). If no detection, skip the rest of Step 11.
b. **Resolve project root via Bash.** Parse `projects_list:` from `.cursor/aria-knowledge.local.md` frontmatter (comma-separated `tag:path`). For the detected tag, compute `project_root = $HOME/Projects/<path>`. If directory doesn't exist, skip.
c. **CODEMAP directory load** (if `{project_root}/CODEMAP.md` exists). Compute boundary via `awk '/^## [0-9]+\.|^---$/ && NR>5 {print NR; exit}' "{project_root}/CODEMAP.md"`; Read limit = `(end - 1)` (fallback 50 if awk empty). Compute `age = (today - mtime).days`; read `codemap_staleness_threshold_days` (default 14). If `age > 2*threshold`, refuse and emit `[refused — run /codemap update first]`. Else if `age > threshold`, annotate `[STALE — consider /codemap update]`. Else `fresh`. Unless refused: `Read {project_root}/CODEMAP.md offset=0 limit=<end-1>`.
d. **STITCH load** (only if `{project_root}/STITCH.md` exists — multi-repo signal). Same staleness logic with `stitch_staleness_threshold_days` (default 30). Unless refused: `Read {project_root}/STITCH.md` (full file).
e. **Ledger dedup.** Locate session ledger via `ls -t /tmp/aria-active-* 2>/dev/null | head -1`. Before loading in (c)/(d), grep ledger for each artifact path; if found, silent skip (already surfaced by earlier T-1/T-2/T-3 trigger) and emit `Tracked artifacts: (already loaded earlier this session for {tag})` in the surfacing block. After loading, append loaded paths to the ledger.
f. **Output.** Extend the Step 9 surfacing block with a 4th line:
```
Tracked artifacts: CODEMAP directory + STITCH for {tag} ({N} / {M} days fresh)
```
Variants: `CODEMAP directory only` for single-repo (no STITCH); `(no CODEMAP for {tag})` if missing; `[STALE — consider /codemap update]` annotation; `(already loaded earlier this session)` if ledger-deduped; `(none — no project detected)` if (a) returned nothing.
g. **Carry-forward.** The loaded artifacts become available to Steps 3+ — particularly Step 3 (Enumerate Steps; CODEMAP directory aids file-path resolution for plan steps) and Step 3.5 (Evidence-Sourcing Pass; CODEMAP sections can validate or falsify assumptions about codebase structure).
Skip Step 11 entirely if `active_knowledge_surfacing: false` (already gated above).
## Step 1: Print the Anchor Block
Before producing any verdict, emit the anchor so the rest of the report can be traced to inputs:
```
Anchor:
Goal: <stated goal>
Mode: <plan | session | todos | file | ticket | branch>
Source: <plan file path | TodoWrite snapshot | ticket-id | branch-name | session messages>
Scope: <step count, files-to-touch estimate, repos-affected>
Tickets: <ABC-123 (Acceptance: ...), ABC-456 (...) | (none) | (unavailable)>
Evidence: <user-supplied per-step evidence table | (untested)>
```
## Step 2: Load Pattern Libraries
Read the canonical pattern library at `<knowledge_folder>/rules/retrospect-patterns.md` (resolved per `.cursor/aria-knowledge.local.md` `knowledge_folder`). The retrospect pattern library is intentionally shared — most failure-mode patterns (theory-driven refactor, scope creep, abstraction-first, etc.) apply forward as well as backward.
If a `<knowledge_folder>/rules/prospect-patterns.md` file exists, also load that (forward-only patterns may emerge over time and be catalogued separately). Do not require it.
If the plan is detected to belong to a known project (file paths or ticket IDs match a configured `projects_list[<tag>].project_root`), additionally read `<knowledge_folder>/projects/<tag>/retrospect-patterns.md` if it exists, and `<knowledge_folder>/projects/<tag>/prospect-patterns.md` if it exists.
Hold all loaded pattern lists in context for use in §4.4 (Failure-Mode Pattern Check). Do not run pattern detection yet — this step is just loading.
## Step 3: Enumerate Steps & Preliminary Triage
For the loaded plan, enumerate each *step*. A step is one of:
- A numbered or bulleted action item in the plan
- A TodoWrite entry
- A discrete change in a Technical Intake's "How" section
- A logical sub-task implied by the plan even if not explicitly numbered
Number them `#1, #2, …` in execution order. For each step, capture:
- One-line description (verb + object, e.g., "Add `--branch` arg to /prospect")
- Files-to-touch (path list, best estimate from plan; "TBD" allowed)
- Estimated LOC range (S < 20, M 20-100, L > 100)
- Underlying assumption (one sentence — "this works because …")
- **Preliminary Risk?** — initial classification using Step 5's taxonomy (✅ / ⚠ / ❌ / ❓ / 🚫). This is a draft that Step 3.5 will attempt to upgrade; it is NOT the value emitted in §4.3.
If a step has no identifiable underlying assumption (it's pure execution, e.g., "rename file"), write "Mechanical — no hypothesis." Mechanical steps default to ✅ Pre-validated and skip Step 3.5.
After preliminary triage, list every step whose Preliminary Risk? is ⚠ Theory-driven, ❓ Unsupported, or 🚫 Unverifiable-yet — these are the *candidates* for Step 3.5's evidence-sourcing pass. ❌ Falsified steps skip Step 3.5 (they go directly to KILL in §4.3 unless the user contests the falsification).
## Step 3.5: Evidence-Sourcing Pass
For each candidate from Step 3 (every step with Preliminary Risk? of ⚠ / ❓ / 🚫), attempt to upgrade or falsify the risk by sourcing evidence. The goal: convert as many ⚠/❓/🚫 to ✅ or ❌ as possible *before* §4.3 emits final verdicts and §4.10 surfaces residual asks.
This step can be skipped with the `--no-source` flag (e.g., for a quick pass where the user just wants the structural review). When skipped, all Preliminary Risk? values pass through to §4.3 unchanged and §4.10 lists every gap as NOT-ATTEMPTED.
### 3.5.1: Generate the Evidence Question
For each candidate step, name the **single most decisive question** whose answer would upgrade the Risk? to ✅ Pre-validated or ❌ Falsified. Format:
```
Step #N: <description>
Preliminary Risk?: <⚠/❓/🚫 with sub-tag>
Decisive question: <one-line — "what would change this verdict?">
Answer source: AUTO-SOURCEABLE | USER-INPUT | MIXED
Sourcing plan: <one-line — what tool/lookup/ask will be used>
```
Source categorization:
| Category | Means | Examples |
|---|---|---|
| **AUTO-SOURCEABLE** | The skill can answer it itself with available tools | Codebase reads (Read/Grep/Glob), git log/diff/blame, public web docs (WebFetch/WebSearch), Bash probes (curl, gh, grep on logs), MCP queries that don't require new credentials (e.g., `supabase__list_tables`, `linear__get_issue` if MCP is connected) |
| **USER-INPUT** | Requires Mike's judgment, local-only knowledge, or a decision he hasn't made yet | Acceptance criteria interpretation, scope/priority calls, choosing between viable design options, info that lives only in his head (a conversation with a teammate, a constraint he hasn't documented) |
| **MIXED** | Auto-sourceable to narrow the option space, then user picks | "Grep finds 3 candidate canonical-nav definitions; ask user which is current" |
If a step has multiple decisive questions, list them as 3.5.1.a, 3.5.1.b, etc., and run each through 3.5.2 / 3.5.3 independently.
### 3.5.2: Auto-Source What's Accessible
For each AUTO-SOURCEABLE (or the auto-portion of MIXED) question, execute the sourcing plan and record findings. Permissible tools:
- **Codebase**: Read, Grep, Glob — for file content, references, structure
- **Version control**: Bash with `git log`, `git diff`, `git show`, `git blame`
- **Public web**: WebFetch (specific URL — library docs, official spec) and WebSearch (when the URL is unknown). Per Rule 33, prefer official sources over inferred ones; per Rule 27, verify identifiers/versions are still current
- **Local probes**: Bash for `curl`, `gh`, log-tail, file-existence checks
- **MCP queries** that don't require new credentials and that the user has already authorized in this session (a project tracker — Linear, Jira, Asana, etc. — Supabase, and so on). If a query would require new auth or interactive consent, demote to USER-INPUT instead
Record findings in this format:
```
Step #N — sourcing result:
Question: <repeat decisive question>
Tool used: <Read | Grep | WebFetch <url> | Bash <command> | mcp__<server>__<tool>>
Finding: <one-paragraph factual summary, with file:line citations or URL anchors>
Verdict: UPGRADED-TO-✅ | UPGRADED-TO-❌ | NO-MOVEMENT (still ⚠/❓/🚫) | INCONCLUSIVE
New Risk?: <new tag, or unchanged if NO-MOVEMENT>
```
Constraints:
- **Rule 33 — verify against current docs**: When sourcing third-party API/SDK behavior, read the official current docs (via WebFetch or `context7` MCP if loaded), not memory or analogy.
- **No credential reads** without explicit per-session permission (per `feedback_ask_before_credentials`). If a question requires reading `.env` or similar, demote to USER-INPUT.
- **No destructive probes** — read-only commands only. `git status`, `git log`, `curl GET`, `grep`, `find` are fine. No `rm`, `git reset`, `git push`, `gh pr merge`, etc.
- **Time-box per step**: if a single question consumes more than ~5 tool-call rounds without converging, mark INCONCLUSIVE and demote the residual to USER-INPUT.
- **Evidence quality bar**: a single corroborating source upgrades to ⚠ Theory-driven (with sub-tag `single-source-inferred`). Two independent sources are required to upgrade to ✅ Pre-validated. One contradicting authoritative source falsifies to ❌.
### 3.5.3: Surface USER-INPUT Asks
For each USER-INPUT question (and the user-portion of MIXED), pause the pre-mortem and surface the ask using the format below. Per `feedback_per_item_review_cadence`, default to ONE ask at a time unless the user requests batch. Per `feedback_hold_gate_steps`, this is a synchronous barrier — do not proceed until the user responds or explicitly chooses Skip.
Standard ask format:
```
[ASK #M of K] Step #N — <step description>
Why this matters:
<one-line — what changes about Step #N's verdict if we know this>
What I tried autonomously (if MIXED):
<one-line — e.g., "Grep'd /df-working/playground for canonical examples; found 3 candidates at <paths>. Need your call on which is current.">
Citations / context inline:
<file:line excerpts, URL pulls, or quoted plan fragments — whatever the answer depends on>
Options:
1) <option, with one-line consequence — e.g., "Use blueprint-loader.ts:645 (last touched 36c9a5f25). Step #N stays as planned, Risk? upgrades to ✅.">
2) <option, with one-line consequence>
3) Other — describe in reply
4) Skip — leave Step #N at <preliminary risk>; it will DEFER in §4.10
```
Rules for the ask:
- Frame neutrally. No baked recommendation in the framing (per `feedback_neutral_option_framing`). A separate "Recommendation: <N> — <reason>" line is acceptable AFTER the options block, but not required, and never inside an option's text.
- Each ask requires its own explicit pick (per `feedback_per_question_explicit_pick`). Don't combine multiple decisive questions into one ask.
- "Skip" is always available and always defaults the step to DEFER. Per `feedback_no_self_fabricated_go_signals`, the skill never invents a decision the user didn't make.
- Bare-number replies pick that option (per `feedback_terse_numeric_answers`). "1" = option 1.
After the user responds, record:
```
Step #N — user-input result:
Question: <repeat>
User pick: <option N | "other: <user's text>" | "skip">
Resulting Risk?: <new tag, or unchanged if skip>
```
### 3.5.4: Pass Summary
When all candidates are processed, emit a one-block summary before moving to Step 4:
```
Evidence-Sourcing Pass complete.
Candidates examined: <N>
Auto-sourced (✅ upgrade): <N>
Auto-sourced (❌ falsify): <N>
User-resolved (✅ upgrade): <N>
User-resolved (❌ falsify): <N>
No movement (still ⚠/❓/🚫): <N>
Skipped by user: <N>
Skipped by --no-source: <N>
Tool calls used: ~<N>
```
The post-pass Risk? values feed §4.3. The residual ⚠/❓/🚫 plus their attempt-status feed §4.10.
## Step 4: Produce the 10-Section Pre-Mortem Report
Render a markdown document with the 10 sections below in order. Each section heading uses `### N. <title>` format. Sections that don't apply to the current scope are emitted with a one-line "N/A: <reason>" — never silently skipped.
### 4.1. Section 1 — Anchor & Inputs
Re-emit the anchor block from Step 1, verbatim, as Section 1 of the report. This makes the report self-contained when read outside the chat.
### 4.2. Section 2 — Plan-Specificity Gate
For each step from Step 3, ask: is the step concrete enough that an implementer could execute it without further design decisions?
Acceptable evidence of concreteness:
- Specific file path(s) named (or a precise pattern that resolves to a small set)
- Specific function/section/component named
- Acceptance signal stated (test passes, log line emitted, screen renders, etc.)
If a step is still goal-stage ("improve performance," "clean up the auth flow," "make it work"), mark it 🌫 **Under-specified** in this section. Under-specified steps do NOT receive a Risk? status in §4.3 — instead, they're flagged here and their action defaults to DEFER-PENDING-DESIGN.
If the entire plan is under-specified, emit "STOP: plan is goal-stage. Run `/distill` to convert to executable spec, then re-run `/prospect`." and skip remaining sections.
### 4.3. Section 3 — Per-Step Verdict
For each step from Step 3 that passed §4.2 (concrete or partially concrete), emit a horizontal-rule-separated block with these fields. Mirror the formatting style of `/retrospect`'s per-fix verdict.
Required fields:
- **Concreteness tag** — one of ✅ specific / ⚠ partial / ⚠ scope-large / ⚠ assumption-stacked / ⚠ duplicates-existing
- **Necessary?** — YES / NO / UNCLEAR with one-sentence reason. Unnecessary steps map to KILL.
- **Smallest viable version** — "the smallest version of this step that would address the goal." If the step is already minimal, write "This is the minimal version." Forces Rule 13. If smaller version exists, the step's action defaults to SHRINK unless user has a stated reason for the larger scope.
- **Maintenance cost (if executed)** — "what future contributors must know / maintain because of this change." Forces Rule 12 / Rule 14.
- **Risk?** — one of the 5 statuses from Step 5 (or 🌫 if §4.2 flagged it under-specified). This is the **post-Step-3.5 final value**, not the preliminary classification from Step 3. If Step 3.5 upgraded or falsified the risk, that change is reflected here.
- **Action** — one of: PROCEED / SHRINK / SPLIT / DEFER / KILL / DEFER-PENDING-DESIGN (under-specified only)
Optional fields:
- **Evidence sourced** — when Step 3.5 produced a verdict-changing finding, summarize it in one line with citation. Example: "Auto-sourced via Read blueprint-loader.ts:645-662 — confirms canonical nav rule applies; upgraded ⚠→✅." If Step 3.5 produced no movement OR was skipped, omit this field.
- **Rule cite** — if a complication maps to a Universal Rule overstep, cite it inline (e.g., "violates Rule 14 — abstraction beyond purposeful layers")
Render each step as a block, not a wide table:
```
Step #N: <description>
Concreteness: <tag>
Necessary?: <YES/NO/UNCLEAR> — <reason>
Smallest version: <description>
Maintenance cost: <description>
Risk?: <status> (post-Step-3.5)
Evidence sourced (optional): <one-line with citation>
Action: <action>
Rule cite (optional): <rule>
────────────────────────────────────────
```
**Hard rule:** A step's Action cannot be PROCEED unless its Risk? status is ✅ Pre-validated, OR ⚠ Theory-driven WITH an explicit one-line "Acceptable risk because: <reason>" appended to the Action line. ❌ Falsified → KILL. ❓ Unsupported → DEFER. 🚫 Unverifiable-yet → SHRINK (smallest version that produces evidence). 🌫 Under-specified → DEFER-PENDING-DESIGN.
The theory-driven carve-out exists because every plan is theory-driven by definition — you're imagining, not measuring. The carve-out forces the planner to *name the risk* rather than block all forward motion.
### 4.4. Section 4 — Failure-Mode Pattern Check
Run the plan against all loaded pattern libraries from Step 2. Detection is judgment-based — read each pattern's "Detection cues" and assess whether the plan, hypotheses, or session transcript exhibits them.
Patterns from `retrospect-patterns.md` apply forward in their *prospective* form. For example:
- "Theory-driven refactor" pattern → fires if a step rewrites working code based on a hypothesis about a problem location, not a confirmed observation
- "Scope creep" pattern → fires if the plan now spans more than the goal requires
- "Abstraction-first" pattern → fires if the plan introduces a new abstraction layer before establishing >2 concrete use cases
- "Pushback-as-cue" pattern → does not fire forward (it's a backward-looking cue), N/A
For each pattern hit, emit:
```
[PATTERN] <pattern-name> (source: rules/retrospect-patterns.md | rules/prospect-patterns.md | projects/<proj>/...)
Evidence: <what in the plan/transcript triggered the hit>
Counter-discipline: <one-line reminder of the pattern's counter-discipline>
```
If no patterns hit, emit: "No catalogued failure-mode patterns detected. (See §9 for novel patterns.)"
### 4.5. Section 5 — Cross-Step Tally
Emit raw counts only — no interpretation in v1.
```
Tally:
Steps planned: <N>
Pre-validated (✅): <N> (post-Step-3.5)
Theory-driven (⚠): <N> (post-Step-3.5)
Falsified (❌): <N> (post-Step-3.5)
Unsupported (❓): <N> (post-Step-3.5)
Unverifiable-yet (🚫): <N> (post-Step-3.5)
Under-specified (🌫): <N>
Theory-driven refactors: <N>
Tied to ticket acceptance: <N>
Discovered-during-planning: <N>
Pattern hits this run: <N>
Evidence-Sourcing Pass:
Candidates examined: <N>
Auto-sourced (✅ upgrade): <N>
Auto-sourced (❌ falsify): <N>
User-resolved (✅ upgrade): <N>
User-resolved (❌ falsify): <N>
No movement: <N>
Skipped by user: <N>
Skipped by --no-source: <N>
```
A "theory-driven refactor" is a step that proposes rewriting working code based on a hypothesis rather than a confirmed bug location or measurement. Discovered-during-planning means a step the plan added beyond the original goal (often surfaces scope creep).
Interpretation of these counts is left to §4.6, §4.7, and §4.9.
### 4.6. Section 6 — Frame Check
Three questions, in order. Answer each in 1–2 sentences with the supporting evidence.
1. **Is the problem statement right?** Does the plan target the user's actual problem, or has it drifted to an adjacent problem during planning?
2. **Is the bug/feature correctly scoped?** Single goal, or has the plan accreted multiple goals presenting as one?
3. **Is the success signal stated?** When the plan finishes, what observable evidence will indicate it worked? If you can't state it, you can't validate it after.
If the answer to #1 is "no," explicitly note: "Re-frame triggered. Hold all steps targeting the *drifted* problem statement pending re-frame discussion. Return to the original goal."
If the answer to #3 is "no," explicitly note: "No success signal stated. Add a measurable post-execution check before any step PROCEEDs."
### 4.7. Section 7 — Diagnosis Confidence
List the **driving hypotheses** behind the plan (only run this section if any step's *post-Step-3.5* Risk? is ⚠ Theory-driven, ❓ Unsupported, or §4.6 #1 triggered re-frame). For each hypothesis:
```
Hypothesis: <one-line statement of what the plan assumes is true>
Used by steps: <#1, #3, #5>
Evidence FOR: <observations consistent with this hypothesis — INCLUDE Step 3.5 sourced findings with citations>
Evidence AGAINST: <observations inconsistent — INCLUDE Step 3.5 sourced findings>
Sourcing attempted: <YES (see Step 3.5 result for steps #N) | NO (auto-sourcing skipped or not categorized as auto-sourceable)>
Confidence: LOW / MEDIUM / HIGH
To upgrade to ✅: <specific signal to look for — feeds §4.10>
```
Evidence FOR/AGAINST must integrate any findings from Step 3.5's sourcing pass. If Step 3.5 produced a finding that moved the step to ⚠ Theory-driven (e.g., `single-source-inferred` upgrade from ❓), cite that finding here as soft Evidence FOR. If Step 3.5 produced a contradicting finding that the user contested or overrode, note both perspectives.
Hypotheses that the plan-formation conversation OR Step 3.5 already ruled out (alternative explanations considered and discarded) are listed separately under "Hypotheses ruled out during planning or sourcing" with one-line reasons and source citations where applicable. This is *learning*, not waste — captures what was considered AND what evidence retired it.
If all steps are ✅ Pre-validated and §4.6 didn't trigger, this section emits "N/A: all steps pre-validated."
### 4.8. Section 8 — Action Verdict
Per step, the action determined in §4.3. Render as a clear list:
```
Action verdict:
Step #1: <ACTION> — <one-line reason>
Step #2: <ACTION> — <one-line reason>
...
```
For SHRINK actions, provide the exact smaller-scope description (from §4.3's "Smallest version"). For SPLIT actions, provide the proposed sub-step breakdown with a checkpoint between sub-steps. For DEFER actions, name the specific evidence/decision needed first. For KILL actions, give the one-line rationale and confirm there's no orphaned downstream step that depended on it.
End with an **Overall verdict** in 1–3 sentences: PROCEED / PROCEED-WITH-CHANGES / HOLD / KILL. PROCEED-WITH-CHANGES means at least one step requires SHRINK or SPLIT before any execution. HOLD means at least one step is DEFER and blocks downstream steps.
### 4.9. Section 9 — Process Pre-mortem
What the plan-formation process should have done differently. Format per item:
```
What planning produced: <observed plan shape>
What it should produce: <better plan shape>
Trigger condition: <how to detect this situation in the future>
Pattern reference: <pattern-name from library | (novel)>
```
Examples of plan-formation issues this section catches:
- Plan jumped to solution without stating the problem (skip Rule 22 step 1)
- Plan considered only one solution (skip Rule 22 step 4)
- Plan has steps with no acceptance signal (skip Rule 22 step 6)
- Plan exceeds the agreed scope (no-unsolicited-scope-reduction's reverse — silent scope expansion)
- Brainstorming was skipped before a creative-work plan
- /distill was skipped before a complex task plan
If a behavior matches an existing pattern in the library, cite it. If a behavior is *novel*, prompt the user:
> "Identified a new plan-formation failure pattern: `<pattern-name>`. Add to:
> 1) Canonical (`rules/prospect-patterns.md` — creates if missing) — applies project-agnostic
> 2) Project-specific (`projects/<proj>/prospect-patterns.md`)
> 3) No — surface in this report only
> Choose: "
If user chooses 1 or 2, append a new entry to the corresponding file using the same "Pattern entry format" defined in `rules/retrospect-patterns.md`. The new entry's "First identified" field is today's date and the current prospect's filename.
### 4.10. Section 10 — Pre-Execution Evidence Ask (Residual)
Anti-speculation barrier. This section lists ONLY the **residual** evidence asks — the questions Step 3.5 either could not source autonomously, the user deferred, or were not attempted. Items resolved during Step 3.5 (✅ upgrades or ❌ falsifications) DO NOT appear here — they're recorded in §4.3 (`Evidence sourced` field), §4.7 (Evidence FOR/AGAINST), and §4.5 (Evidence-Sourcing Pass tally).
For each remaining ⚠/❓/🚫 step, emit the residual ask with its **attempt-status**:
```
Before Step #N can PROCEED (Hypothesis A: <short label>):
Attempt status: NOT-ATTEMPTED | ATTEMPTED-FAILED | DEFERRED-BY-USER | SKIPPED-BY--no-source
Why residual: <one-line — e.g., "Auto-source attempted via WebFetch <url>; doc was 404. Demoted to USER-INPUT.">
What's needed: - <specific check 1 — file read, log query, schema inspection, decision required, etc.>
- <specific check 2>
- <add a unique [<TAG>-PRE] marker in the planned change so post-execution validation is possible>
Who can resolve: <USER | AUTOMATED-RETRY-LATER | EXTERNAL-PARTY <name>>
```
Attempt-status meanings:
- **NOT-ATTEMPTED** — Step 3.5 did not generate a sourcing plan for this question (rare; usually means a misclassified candidate)
- **ATTEMPTED-FAILED** — Step 3.5 ran tools but the answer wasn't found / source was unreachable / two corroborating sources couldn't be obtained
- **DEFERRED-BY-USER** — Step 3.5 surfaced a USER-INPUT ask and the user picked "Skip"
- **SKIPPED-BY--no-source** — entire pass was skipped via the `--no-source` flag
If all steps are ✅ Pre-validated post-Step-3.5, emit: "N/A: all steps pre-validated (Step 3.5 closed all gaps). Proceed to execution."
If Step 3.5 was skipped via `--no-source`, prefix every entry with "(Sourcing pass skipped — re-run `/prospect` without `--no-source` to attempt autonomous resolution.)"
End the section with this verbatim warning when any DEFER or HOLD action exists:
> **Do not begin execution until at least one item in this section is satisfied for each DEFER step. If execution is started without new evidence, the eventual `/retrospect` will mark those steps unvalidated. Plan accordingly.**
## Step 5: Risk Status Taxonomy (reference)
When assigning Risk? in §4.3, choose one of the 5 statuses below. Under-specified (🌫) is a precondition gate handled in §4.2, not a status.
| Status | Definition | Required sub-tag (in report) |
|---|---|---|
| ✅ **Pre-validated** | Evidence already supports the planned step's underlying assumption | **Evidence type**: log/measurement \| reproduction-then-confirm \| code-read-and-traced \| existing-test-coverage. "Plausible argument" is **not** evidence. |
| ⚠ **Theory-driven** | Plan rests on a hypothesis that's reasonable but unmeasured | **Sub-tag**: `single-source-inferred` \| `analogous-system-reasoning` \| `documentation-claim-untested` |
| ❌ **Falsified** | Known evidence contradicts the planned step's assumption | **Sub-tag**: `prior-attempt-failed` \| `evidence-shows-otherwise` \| `documented-anti-pattern` |
| ❓ **Unsupported** | No evidence yet, but the skill can describe the specific check that would gather it | (none) |
| 🚫 **Unverifiable-yet** | Cannot be validated until execution begins (requires running the change to know) | (none) |
When emitting a Risk? value, always include the required sub-tag where applicable. Examples:
- `Risk?: ✅ Pre-validated (code-read-and-traced: blueprint-loader.ts:645-662 confirms canonical nav rule applies)`
- `Risk?: ⚠ Theory-driven (single-source-inferred: only one API route was inspected; assumption that all NDJSON routes share the shape is inferred)`
- `Risk?: ❌ Falsified (prior-attempt-failed: 2026-04-22 retrospect on this same approach showed it doesn't address the bug)`
- `Risk?: ❓ Unsupported: needs <specific check>`
- `Risk?: 🚫 Unverifiable-yet: requires running migration in staging to surface schema collision`
## Step 6: Write Outputs
After Step 4 produces the report, write outputs to the configured destinations:
### Always
- Render the full report to terminal (chat).
### Default
- **Persistent log:** Write the full report to `<knowledge_folder>/logs/prospect/<YYYY-MM-DD>-<scope>-<slug>.md` where `<scope>` is the resolved scope keyword from Step 0 (`plan`, `session`, `todos`, `file`, `linear`, or `branch`) and `<slug>` is derived from the goal or referenced ticket(s). Resolve `<knowledge_folder>` from the config's `knowledge_folder` field. Create the `logs/prospect/` subfolder lazily on first use. Mirror retrospect's logging convention. Existing files written under the older `<YYYY-MM-DD>-<slug>.md` pattern are grandfathered (no rename).
Prepend a structured YAML frontmatter block to the report before writing. Schema:
```yaml
---
type: prospect
date: <YYYY-MM-DD>
scope: <plan | session | todos | file | ticket | branch>
goal: <one-line stated goal from §4.1 Anchor>
tickets: [<ABC-123>, <ABC-456>] # empty list if none
steps_count: <N>
sourcing_pass:
candidates: <N>
upgraded_validated: <N>
upgraded_falsified: <N>
no_movement: <N>
patterns_hit: [<pattern-name-1>, <pattern-name-2>] # from §4.4; empty list if none
overall_verdict: <PROCEED | PROCEED-WITH-CHANGES | HOLD | KILL> # from §4.8
related: [<paths to overlapping prior runs — see below>]
tags: [prospect, <scope>, <project-tag-if-detected>, <pattern-tag-if-applicable>]
---
```
**`related` auto-detection (Q1.2=1, ticket-based):** Before writing, glob `<knowledge_folder>/logs/prospect/*.md` AND `<knowledge_folder>/logs/retrospect/*.md` for files whose frontmatter `tickets:` array shares at least one ticket ID with the current report's tickets. Record their paths (relative to `<knowledge_folder>/`) in the `related:` array. If no tickets in the current report, leave `related:` empty. Only the most recent 10 are kept if many overlap (cap on bloat).
**`tags:` field:** always includes `prospect` and the scope keyword. Add a project tag when the plan is detected to belong to a configured project (commits/files match a `projects_list[<tag>].project_root`). Add pattern-name tags for any §4.4 hits. These tags make the file discoverable via `/index` and `/context` (per Q1.3=1 — `/index` extends its scan to `logs/{prospect,retrospect}/`).
- **Aria intake:** Suggest entries for the four backlogs based on the report content:
- Insights → observations like "step #N's hypothesis was thinly supported because <evidence>"
- Decisions → "Pre-mortem moved step #N from PROCEED to SHRINK; smallest version is <X>" with rationale
- Approaches → instrumentation patterns to use during execution (e.g., "[<TAG>-PRE] marker pattern for forward verification")
- Working rules → if §4.9 identified a plan-formation behavior that should become a Universal Rule, suggest it (do not persist without user approval per Rule 23)
Project-scoped intake goes to `projects/<proj>/`; agnostic intake goes to the shared knowledge tree. Follow the standard aria intake confirmation flow (suggest, user reviews, write on approval).
### Opt-in
- **Tracker comment:** Only when invoked with `--ticket-post` (legacy alias `--linear-post`). Post the Overall verdict from §4.8 + the action verdict list to each ticket detected. Use a project-tracker MCP `save_comment`. Never post the full report — too much detail for the ticket.
### Pattern library write-backs
If §4.9 produced a novel pattern and the user approved adding it, the pattern entry is written to either:
- `<knowledge_folder>/rules/prospect-patterns.md` (canonical — created if missing), or
- `<knowledge_folder>/projects/<proj>/prospect-patterns.md` (project-specific — created if missing)
Pattern write-backs are *separate* from intake — they go directly to the patterns file, not through backlog review.
## Step 7: Soft-Suggest Trigger Logic (Claude-side judgment)
When the skill is *not* directly invoked, Claude monitors user messages and the conversation state for cues that suggest a pre-mortem is warranted. When detected AND the current session has produced a multi-step plan with no execution yet, Claude offers — never auto-executes — `/prospect`.
Cues (non-exhaustive, judgment-based):
- "let me implement…", "I'll just code it", "ok let's do it", "ship it", "going to start now"
- "combined go" or any compound execution authorization across a multi-step plan that hasn't been validated
- A long planning conversation has produced a coherent plan but no edits have happened yet
- Brainstorming concluded with an action plan
- /distill produced a task spec
- A ticket's Technical Intake was just drafted and the user is about to begin
- The plan touches >3 files OR >1 repo OR has any step rated L (>100 LOC)
- The plan rewrites working code without naming a measured problem location
Standard offer (paraphrase as appropriate):
> "Before you start: this plan has <N> steps and at least one rests on an unmeasured hypothesis. Want me to run `/prospect` first? It'll force a per-step risk check + smallest-version pass before any code lands. Cheap insurance against a `/retrospect` later."
Cue weight is judgment, not regex. When the cue is faint, just acknowledge and proceed. When the cue is clear, offer. Never auto-execute from a cue — always ask.
This logic is the forward-looking twin of `/retrospect`'s `pushback-as-cue` pattern — they share the same trigger surface but fire at opposite ends of the development cycle.
## Over-build lens (opt-in: `--lens=overbuild`)
Off unless `--lens=overbuild` is passed. When on, after the standard per-step pass, check each PLANNED step against `rules/overbuild-patterns.md`:
1. Load the ladder + smells.
2. For each step, find the lowest ladder rung that would resolve its need. If the step proposes a higher rung than necessary (e.g. adds a dependency where a one-liner works), it fails that rung.
3. A failing step yields a SHRINK verdict (existing vocabulary) citing the failed rung + the concrete leaner alternative; an unnecessary step yields KILL. A step whose smaller form cannot be named is NOT flagged.
4. Findings fold into §4.3 per-step verdicts (the Action becomes SHRINK/KILL with the over-build reason) — no separate section needed, since prospect is already per-step.
Forward counterpart of `/retrospect --lens=overbuild`; same rubric, applied to a plan instead of a diff.
## Step 8: Validation Gates
Before finalizing the pre-mortem, verify:
1. **Anchor printed?** §4.1 must contain Goal, Mode, Source, Scope, Tickets, Evidence lines.
2. **Plan-specificity gate run?** §4.2 must address every step from Step 3.
3. **Evidence-Sourcing Pass run (or explicitly skipped)?** Step 3.5 must have addressed every preliminary ⚠/❓/🚫 candidate from Step 3 — each must end with one of: UPGRADED-TO-✅ / UPGRADED-TO-❌ / NO-MOVEMENT / INCONCLUSIVE / DEFERRED-BY-USER / SKIPPED-BY--no-source. No silent skips. The pass summary (Step 3.5.4) must be emitted.
4. **Per-step verdicts complete?** Every step has all required fields (Concreteness, Necessary?, Smallest version, Maintenance cost, Risk?, Action). Risk? values reflect post-Step-3.5 state. Missing field = incomplete report.
5. **Risk hard rule respected?** No step has Action: PROCEED unless post-Step-3.5 Risk? is ✅ Pre-validated, OR ⚠ Theory-driven WITH explicit "Acceptable risk because: …" appended. Verify before emitting.
6. **Pattern check ran?** §4.4 must reference all loaded pattern libraries (canonical retrospect + canonical prospect if exists + project-specific if applicable).
7. **Tally consistent?** Counts in §4.5's risk-status block must match the per-step data in §4.3 (post-Step-3.5). Counts in §4.5's Evidence-Sourcing Pass block must match Step 3.5.4's summary.
8. **Hypotheses present when needed?** §4.7 is required if any step is post-Step-3.5 ⚠ Theory-driven, ❓ Unsupported, or §4.6 #1 triggered re-frame. Evidence FOR/AGAINST must integrate Step 3.5 findings where applicable.
9. **Action verdict complete?** §4.8 must have an action for every step in §4.3, plus an Overall verdict.
10. **Residual evidence asks correctly scoped?** §4.10 must list ONLY residual items (NOT-ATTEMPTED / ATTEMPTED-FAILED / DEFERRED-BY-USER / SKIPPED-BY--no-source). Items resolved by Step 3.5 (✅ upgrades or ❌ falsifications) must NOT appear in §4.10. Cross-check: every §4.10 entry's step must have post-Step-3.5 Risk? of ⚠/❓/🚫.
11. **Outputs written?** Confirm the persistent log was written to disk and intake suggestions were surfaced.
If any check fails, self-correct once. If self-correction can't close the gap (e.g., the user must supply evidence), surface the gap explicitly in the report rather than silently skipping.
---
## /retrospect
# /retrospect — Release retrospective with validation enforcement
Run a structured retrospective on a shipped commit range (or single commit, or current session). Produces a 10-section markdown report with per-fix verdicts, validation status, action recommendations, and re-diagnosis when fixes failed. Writes findings to `knowledge/logs/retrospect/` and runs aria's standard intake. Source spec: `docs/specs/2026-05-03-retrospect-skill-design.md`.
## When to use
- After a release ships and the bug is partially or fully unresolved
- When the user reports a regression and a recent change set could be the cause
- Before proposing another fix to a bug that's already been "fixed" once
- As a soft-suggested response to user pushback ("review what you did," "are these changes necessary")
## Step 0: Inputs & Mode Detection
Parse the invocation arguments. The first positional argument is the **scope keyword**; subsequent positional arguments are scope-specific. Seven scopes plus a no-args default:
| Scope | Trigger | Backward-compat flag (still accepted) | Bundle source |
|---|---|---|---|
| **(no args)** | `/retrospect` | — | Auto-range: last push on current branch — `git log @{push}..HEAD` if upstream is set, else `git log -10` and ask user to confirm range |
| **commit** | `/retrospect commit <hash>` | `--commit <hash>` | Single commit |
| **range** | `/retrospect range <ref1>..<ref2>` | `--range <ref1>..<ref2>` | `git log <ref1>..<ref2>` |
| **pr** | `/retrospect pr <num>` | `--pr <num>` | `gh pr view <num> --json commits` then resolve to commit SHAs |
| **session** | `/retrospect session` | `--session` | Files Claude has touched in the current conversation (read from session state, not git). No deploy yet — all fixes auto-tag 🚫 unvalidatable. |
| **release** (NEW) | `/retrospect release` | (none) | Commits since the most recent semver tag. `git describe --tags --abbrev=0` to find the tag, then `git log <tag>..HEAD`. If no tags exist on the repo, fall back to auto-range and warn the user. |
| **deployment** (NEW) | `/retrospect deployment` | (none) | Commits since the last deployment marker — see "Deployment detection cascade" below. |
**Argument parsing rules:**
- If the first positional arg matches a scope keyword (case-insensitive), use it. Otherwise treat it as auto-range and try to parse the args under the legacy flag form.
- Backward-compat flag forms (`--range`, `--pr`, `--session`, `--commit`) remain accepted indefinitely. Both `/retrospect range a..b` and `/retrospect --range a..b` resolve identically.
- Modifier flags (apply to any scope): `--ticket-post` (post the retrospective verdict to detected tickets at end; legacy alias `--linear-post` still accepted), `--no-source` (skip Step 3.5's Evidence-Sourcing Pass), `--lens=overbuild` (run the over-build review pass — see "Over-build lens" section; opt-in, off by default).
### Deployment detection cascade (Q2.1=3)
When invoked as `/retrospect deployment`, attempt to resolve the deployment marker by trying these signals in order. First success wins; on no-success, fall through to the prompt.
1. **GitHub Releases** — `gh release view --json publishedAt,tagName 2>/dev/null` (most recent release). If returned, treat the release tag as the marker; bundle = `git log <tag>..HEAD`.
2. **Semver tags** — `git tag --sort=-creatordate | head -1`. If a tag matches `v?\d+\.\d+\.\d+` (and step 1 returned nothing), use it as the marker.
3. **Last commit on `main` (or `master`)** — `git log -1 --format=%H origin/main` (or `origin/master` if `main` doesn't exist). Treat as the marker; bundle = `git log <sha>..HEAD`. This catches projects without releases or tags.
4. **Prompt user** — if none of the above resolved (e.g., no remote, no tags, no `gh` auth), ask: "I couldn't auto-detect the last deployment for `/retrospect deployment`. Provide a marker (commit hash, tag, or ISO timestamp), or type `auto-range` to fall back to last-push behavior."
Print the resolved marker source ("Detected via gh release: v1.4.2 (2026-05-01)") in the Anchor block (§4.1) so the user can verify what the skill thought "deployment" meant.
After mode detection, gather:
1. **Goal** — Ask the user: "What was this release/range supposed to fix? (One sentence is fine.)" If they don't reply, fall back to commit message subjects + PR description.
2. **Tickets** — Scan commit messages with regex `\b([A-Z]{2,}-\d+)\b` for ticket IDs (the pattern is vendor-neutral — it matches DEV-123, PROJ-45, JIRA-9 alike). If any are found AND a project-tracker MCP is available, fetch each ticket's Product/Technical Intake + recent comments. If a project-tracker MCP is unavailable, note "ticket context unavailable" but continue.
3. **Post-deploy outcome** — Ask the user: "For each fix, what's the post-ship evidence? (✅ closed / ⚠ partial / ❌ failed / ❓ untested)" Show the per-commit list and accept inline replies. If user can't supply evidence for any fix, mark those ❓ and note that §10 will recommend instrumentation.
If scope is `session` (or invoked via `--session`), skip post-deploy outcome (no production yet) and tag all fixes 🚫 unvalidatable; their actions will resolve to HOLD-PENDING-DEPLOY.
## Step 0.5: Active Knowledge Surfacing
If the user's config (`.cursor/aria-knowledge.local.md`) has `active_knowledge_surfacing: true` (default as of v2.15.0), surface relevant tagged knowledge BEFORE Steps 1-3 so loaded files inform pattern selection and evidence sourcing. If the field is `false`, skip this step entirely (note `Active surfacing: disabled` in the Anchor block).
**Algorithm:**
1. **Build query.** Combine, separated by spaces: the Goal sentence from Step 0; the first 3 commit subjects in the bundle range; PR title if scope is `pr`; any detected ticket IDs (e.g., `ABC-123`); the resolved deployment marker label if scope is `deployment`; the range descriptor (e.g., `v0.4.2..HEAD`).
2. **Read the index.** `Read` `<knowledge_folder>/index.md` (resolve `<knowledge_folder>` from the config's `knowledge_folder` field). Parse the `## Tag Index` section for `### tagname` headers — that's the matching vocabulary (~77 known tags as of v2.15.0). Ignore the `## Other Tags` section (freeform tier, intentionally excluded from auto-surfacing).
3. **Tokenize.** Lowercase the query, strip punctuation to spaces, dedupe to a word set.
4. **Match.** Exact word-vs-tag equality only — no substring, no fuzzy. Collect the set of matched tags.
5. **Threshold gate.** If fewer than 2 tags matched, note `Active surfacing: 0 matches (below threshold)` in the Anchor block and skip to Step 1.
6. **Collect files.** Under each matched tag's `### tag` section, gather the `- path — description` lines. Dedupe by path. Cap at top-5 by first-appearance order.
7. **Ledger filter (best-effort).** Run `ls -t /tmp/aria-active-* 2>/dev/null | head -1` via Bash to find the current session's ledger. If found, read it and drop any matched paths already listed there. If no ledger exists, proceed unfiltered.
8. **Read matched files.** For each remaining path (up to 5), `Read` the full file into context. **Prefer files under `logs/retrospect/`** if any matched — they're prior retros on overlapping tags, which is the loop-closure case (past retros inform new retros on the same topic). If both a retro and a non-retro file match, prioritize the retro within the top-5 cap.
9. **Summarize.** Before Step 1's Anchor Block, emit a 3-line surfacing block:
```
Active Knowledge Surfacing:
Tags matched: <tag1> <tag2> ...
Files loaded: <N> (<file1>, <file2>, ...)
Relevance: <one sentence per file: why this informs the retrospective>
```
10. **Carry-forward.** These loaded files become input to Step 2 (Load Pattern Libraries — past retros may already have catalogued the relevant failure-mode patterns) and Step 3.5 (Evidence-Sourcing Pass — they may already provide validation or falsification for fixes in this range).
11. **Tracked artifacts surfacing (added v2.16.1).** After Step 10's carry-forward, ALSO surface CODEMAP + STITCH for the analyzed range's project. The shared lib at `scripts/aria/lib-tracked-artifacts.sh` implements equivalent logic for hooks; this step inlines the algorithm for skill-context portability.
a. **Detect project tag from changed file paths.** Run `git diff --name-only <range>` for the analyzed bundle. For each changed file, check whether any `projects_list[<tag>].path` appears as substring. Count matches per tag; pick the tag with the most matches. Tie-breaker: explicit project flag if provided. If no detection (e.g., bundle touches knowledge folder only, no project files), skip the rest of Step 11.
b. **Resolve project root via Bash.** Parse `projects_list:` from `.cursor/aria-knowledge.local.md` frontmatter (comma-separated `tag:path`). For the detected tag, compute `project_root = $HOME/Projects/<path>`. If directory doesn't exist, skip.
c. **CODEMAP directory load** (if `{project_root}/CODEMAP.md` exists). Compute boundary via `awk '/^## [0-9]+\.|^---$/ && NR>5 {print NR; exit}' "{project_root}/CODEMAP.md"`; Read limit = `(end - 1)` (fallback 50 if awk empty). Compute `age = (today - mtime).days`; read `codemap_staleness_threshold_days` (default 14). If `age > 2*threshold`, refuse and emit `[refused — run /codemap update first]`. Else if `age > threshold`, annotate `[STALE — consider /codemap update]`. Else `fresh`. Unless refused: `Read {project_root}/CODEMAP.md offset=0 limit=<end-1>`.
d. **STITCH load** (only if `{project_root}/STITCH.md` exists — multi-repo signal). Same staleness logic with `stitch_staleness_threshold_days` (default 30). Unless refused: `Read {project_root}/STITCH.md` (full file).
e. **Ledger dedup.** Locate session ledger via `ls -t /tmp/aria-active-* 2>/dev/null | head -1`. Before loading in (c)/(d), grep ledger for each artifact path; if found, silent skip (already surfaced by earlier T-1/T-2/T-3 trigger) and emit `Tracked artifacts: (already loaded earlier this session for {tag})` in the surfacing block. After loading, append loaded paths to the ledger.
f. **Output.** Extend the Step 9 surfacing block with a 4th line:
```
Tracked artifacts: CODEMAP directory + STITCH for {tag} ({N} / {M} days fresh)
```
Variants: `CODEMAP directory only` for single-repo (no STITCH); `(no CODEMAP for {tag})` if missing; `[STALE — consider /codemap update]` annotation; `(already loaded earlier this session)` if ledger-deduped; `(none — no project detected)` if (a) returned nothing.
g. **Carry-forward.** The loaded artifacts become available to Steps 3+ — particularly Step 3 (Enumerate Fixes; CODEMAP sections map changed files to features/responsibilities) and Step 3.5 (Evidence-Sourcing Pass; CODEMAP can validate that a fix actually touches the expected feature surface).
Skip Step 11 entirely if `active_knowledge_surfacing: false` (already gated above).
## Step 1: Print the Anchor Block
Before producing any verdict, emit the anchor so the rest of the report can be traced to inputs:
```
Anchor:
Goal: <stated goal>
Mode: <auto-range | commit | range | pr | session | release | deployment>
Range: <commit range descriptor, e.g. v0.4.2..HEAD, 12 commits, 38 files>
Tickets: <ABC-123 (Acceptance: ...), ABC-456 (...) | (none) | (unavailable)>
Outcome: <user-supplied per-fix status table | (untested) | (per-session — no deploy)>
```
## Step 2: Load Pattern Libraries
Read the canonical pattern library at `<knowledge_folder>/rules/retrospect-patterns.md` (resolved per `.cursor/aria-knowledge.local.md` `knowledge_folder`).
If the bundle is detected to belong to a known project (commits include paths under a configured `projects_list[<tag>].project_root`), additionally read `<knowledge_folder>/projects/<tag>/retrospect-patterns.md` if it exists.
Hold both pattern lists in context for use in §4.4 (Failure-Mode Pattern Check). Do not run pattern detection yet — this step is just loading.
## Step 3: Enumerate Fixes & Preliminary Triage
For the loaded bundle, enumerate each *fix*. A fix is one of:
- A commit whose message describes a fix or change (`fix:`, `feat:`, `refactor:`, etc.)
- A logical sub-change within a multi-concern commit (rare; usually 1 commit = 1 fix)
Number them `#1, #2, …` in commit order. For each fix, capture:
- Short SHA
- Subject line
- Files touched (path list)
- LOC added/deleted
- **Preliminary Bundle-verified?** — provisional ✅ verified / 🤷 unverified / N/A (session mode). Use the user-supplied evidence from Step 0 #3 as a starting point. Step 3.5's bundle-marker pass will attempt to upgrade 🤷 by sourcing the deployed bundle.
- **Preliminary Validated?** — provisional classification using the Step 5 taxonomy (✅ / ⚠ partial / ❌ / ❓ / 🚫). Use the user-supplied post-deploy outcome from Step 0 #3 as the starting point. Step 3.5's outcome pass will attempt to upgrade ⚠/❓/🚫 by sourcing post-deploy evidence (logs, repro tests, ticket comments). These are DRAFT values — the FINAL Validated? values emitted in §4.3 reflect post-Step-3.5 state.
If `session` scope, enumerate by file-touch sets that resolve a single concern (Claude's judgment from session context). Preliminary Bundle-verified? = N/A and Preliminary Validated? = 🚫 unvalidatable (no deploy yet).
After preliminary triage, list every fix whose Preliminary Bundle-verified? is 🤷 — these are candidates for Step 3.5's **bundle-marker pass**. Separately, list every fix whose Preliminary Validated? is ⚠ partial / ❓ unvalidated / 🚫 unvalidatable — these are candidates for Step 3.5's **outcome pass**. ❌ Invalidated fixes skip Step 3.5 (they go directly to REVERT/REDO-MINIMAL in §4.3 unless the user contests the falsification). ✅ Validated fixes also skip Step 3.5 (already confirmed).
## Step 3.5: Evidence-Sourcing Pass
Two sub-passes that run in order before the report is rendered. Goal: convert as many 🤷 / ⚠ / ❓ / 🚫 candidates from Step 3 to ✅ or ❌ as possible *before* §4.2 emits its bundle-verification verdict and §4.3 emits its per-fix Validated? verdict.
This step can be skipped with the `--no-source` flag for a quick structural review. When skipped, all preliminary statuses pass through to §4.2 and §4.3 unchanged and §4.10 lists every gap as NOT-ATTEMPTED.
The synchronous-barrier discipline applies: if a sub-pass surfaces a USER-INPUT ask, hold for response (per `feedback_hold_gate_steps`). Default to one ask at a time (per `feedback_per_item_review_cadence`).
### 3.5.1: Bundle-Marker Sub-Pass (resolves 🤷)
For each fix from Step 3 with Preliminary Bundle-verified? = 🤷, attempt to confirm the fix's code is present in the deployed bundle.
**Auto-source candidates** (most retrospects fall here):
- **Bundle fetch + grep** — `Bash curl -s <deployed-bundle-url> | grep -F '<unique-marker-string>'`. The unique marker is either a function name from the diff, a comment string the fix added, or a `[<TAG>-DIAG]` instrumentation marker. Source the deployed-bundle-url from the user (Step 0) or from common conventions (`<deploy-domain>/static/js/main.<hash>.js` for Vercel/Webpack builds).
- **WebFetch on bundle URL** — same as above but via `WebFetch` when the URL is public and HTML-wrapped.
- **CI artifact check** — `Bash gh run view <run-id> --log` (resolved from the commit's CI status) → confirm the deploy job ran AND the artifact hash matches.
- **Source-map verification** — fetch the deployed source-map, confirm it references the post-fix line numbers.
- **Deploy log inspection** — Vercel logs MCP, Bitbucket pipeline output via `gh api`, etc.
**USER-INPUT escape hatch:**
- If no bundle URL is known and the user hasn't provided one, ask: "Need a deployed-bundle URL or unique in-bundle marker for fix #N (<short-sha>: <subject>) to verify it shipped. Provide URL+marker, paste a curl-grep result, or skip (fix stays 🤷)."
Record per fix using the same shape as /prospect's 3.5.2:
```
Fix #N — bundle-marker result:
Question: Did fix #N's code reach the deployed bundle?
Tool used: <Bash curl <url> | WebFetch <url> | gh run view <id> | mcp__vercel__get_logs | ASK <surfaced-to-user>>
Finding: <one-paragraph factual summary, with URL anchor or grep hit>
Verdict: UPGRADED-TO-✅ verified | UPGRADED-TO-❌ NOT-IN-BUNDLE | NO-MOVEMENT (still 🤷) | INCONCLUSIVE
New Bundle-verified?: <updated tag>
```
When this sub-pass completes, every fix has a final Bundle-verified? value (✅ verified / 🤷 unverified / ❌ not-in-bundle). §4.2 emits this state directly. ❌ NOT-IN-BUNDLE is a strong signal — the fix definitively did not ship; its action defaults to REVERT or RESHIP-AND-VERIFY in §4.3.
### 3.5.2: Outcome Sub-Pass (resolves ⚠ partial / ❓ unvalidated / 🚫 unvalidatable)
For each fix from Step 3 with Preliminary Validated? = ⚠ / ❓ / 🚫 AND Bundle-verified? ≠ 🤷 (a fix that didn't ship can't be outcome-validated), attempt to confirm the fix's outcome in production.
**Generate the decisive question** — what observable evidence would change the verdict from ⚠/❓/🚫 to ✅ Validated or ❌ Invalidated? Format:
```
Fix #N: <subject>
Preliminary Validated?: <⚠/❓/🚫>
Decisive question: <one-line — "what observable evidence would close this?">
Answer source: AUTO-SOURCEABLE | USER-INPUT | MIXED
Sourcing plan: <tools/queries to run>
```
**Auto-source candidates (retrospect-specific):**
- **Production log queries** — `mcp__vercel__get_logs`, `mcp__supabase__get_logs`, `Bash gh run view`, log-tail via SSH. Look for: (a) absence of the error event the fix targeted, (b) presence of the success event, (c) post-deploy regression signals.
- **Ticket comments** — via whichever project-tracker MCP is connected (Linear, Jira/Atlassian, Asana, Monday, ClickUp, Notion-as-tracker, GitHub Issues), using that server's list-comments verb — e.g. `mcp__linear__list_comments <ticket-id>` on Linear. Probe for what is actually connected rather than assuming a vendor; skip this source if none is. For any ticket cited in commit messages. QA, support, and product comments often record post-deploy outcome ("verified fixed in PROD," "still reproducing," etc.). High-signal source.
- **Repro test execution** — if the bug had a documented repro and the test infrastructure is local, run it: `Bash <test-command>` (read-only or sandboxed). NEVER run repro tests that mutate production data.
- **GH commit/check status** — `Bash gh api repos/<owner>/<repo>/commits/<sha>/check-runs` → CI green / red post-fix.
- **Web fetch on monitoring dashboards** — only if URLs are known and public (rare; usually demoted to USER-INPUT).
- **`git log` on the touched files since deploy** — if other commits have already modified the same lines after the fix, that may constitute a regression signal.
**USER-INPUT escape hatch (and when to demote to it):**
- Reproduction tests that require staging access, real user accounts, or interactive QA — demote.
- Subjective acceptance ("does this feel fast enough now?") — demote.
- Anything requiring credentials Step 3.5 doesn't have authorization to use — demote.
Surface format mirrors /prospect's 3.5.3 exactly:
```
[ASK #M of K] Fix #N — <subject> (<short-sha>)
Why this matters:
<one-line — what changes about Fix #N's verdict if we know this>
What I tried autonomously (if MIXED):
<one-line — e.g., "Queried Vercel logs since deploy; found 0 occurrences of the targeted error and 4 of an adjacent error. Ambiguous — need your read on whether the adjacent one is a regression.">
Citations / context inline:
<log excerpts, ticket comment quotes, test output — whatever the answer depends on>
Options:
1) <option, with one-line consequence>
2) <option, with one-line consequence>
3) Other — describe in reply
4) Skip — leave fix #N at <preliminary status>; it will HOLD-PENDING-EVIDENCE in §4.10
```
Same rules as /prospect's 3.5.3: neutral framing, separate Recommendation line allowed below options, per-question explicit pick, bare-number replies pick, "Skip" defaults to HOLD-PENDING-EVIDENCE / HOLD-PENDING-DEPLOY / HOLD-PENDING-VERIFICATION as appropriate.
Record per fix:
```
Fix #N — outcome result:
Question: <repeat decisive question>
Tool used: <listed>
Finding: <factual summary with citations>
User pick (if any): <option N | "skip" | "other: <text>">
Verdict: UPGRADED-TO-✅ Validated | UPGRADED-TO-⚠ partial | UPGRADED-TO-❌ Invalidated | NO-MOVEMENT | INCONCLUSIVE
New Validated?: <updated tag with sub-tag where applicable>
```
### 3.5.3: Constraints (apply to both sub-passes)
- **Rule 33 — verify against current docs**: When sourcing third-party API/SDK behavior post-deploy, read the official current docs.
- **No credential reads** without explicit per-session permission (per `feedback_ask_before_credentials`).
- **No destructive probes** — read-only commands only. No `rm`, `git reset`, `git push`, `gh pr merge`, no migration runs, no PROD writes of any kind.
- **No PROD-mutating repro tests** — if a repro requires writing to production data, demote to USER-INPUT.
- **Time-box per fix**: ~5 tool-call rounds maximum per decisive question. INCONCLUSIVE after that; demote residual to USER-INPUT.
- **Evidence quality bar**: a single corroborating source upgrades only to ⚠ partial (with sub-tag `closed-part-of-target`). Two independent sources required for ✅ Validated. One contradicting authoritative source falsifies to ❌.
### 3.5.4: Pass Summary
When both sub-passes complete, emit a one-block summary before moving to Step 4:
```
Evidence-Sourcing Pass complete.
Bundle-marker sub-pass (§3.5.1):
Candidates examined: <N>
Upgraded to ✅ verified: <N>
Upgraded to ❌ not-in-bundle: <N>
No movement (still 🤷): <N>
Skipped by user: <N>
Outcome sub-pass (§3.5.2):
Candidates examined: <N>
Upgraded to ✅ Validated: <N>
Upgraded to ⚠ partial: <N>
Upgraded to ❌ Invalidated: <N>
No movement (still ⚠/❓/🚫): <N>
Skipped by user: <N>
Total tool calls: ~<N>
Skipped by --no-source: <N> (entire pass)
```
The post-pass values feed §4.2 (Bundle-verified?) and §4.3 (Validated?). The residual 🤷 / ⚠ / ❓ / 🚫 plus their attempt-status feed §4.10.
## Step 4: Produce the 10-Section Retrospective Report
Render a markdown document with the 10 sections below in order. Each section heading uses `### N. <title>` format. Sections that don't apply to the current scope are emitted with a one-line "N/A: <reason>" — never silently skipped.
### 4.1. Section 1 — Anchor & Inputs
Re-emit the anchor block from Step 1, verbatim, as Section 1 of the report. This makes the report self-contained when read outside the chat.
### 4.2. Section 2 — Bundle-Verification Gate
For each fix from Step 3, emit the **post-Step-3.5.1** Bundle-verified? status. §4.2 is the bundle-deployment ledger; its values come directly from Step 3.5.1's Bundle-Marker Sub-Pass, which already attempted to source the marker autonomously and surfaced any USER-INPUT asks that were needed.
Three possible statuses per fix:
- **✅ Verified** — bundle contains the fix's marker. Evidence comes from one of: unique in-bundle marker grep on the deployed asset (e.g., `curl https://<deployed-url>/<bundle> | grep <marker>`), deploy log + bundle hash matching the commit's CI artifact, or source-map verification. Step 3.5.1's "Tool used" + "Finding" cite the source.
- **🤷 Bundle-unverified** — Step 3.5.1 attempted to source the marker but could not confirm. Reasons captured from 3.5.1's verdict (NO-MOVEMENT, INCONCLUSIVE, or DEFERRED-BY-USER). Fix does NOT receive a Validated? status in §4.3 — its action defaults to HOLD-PENDING-VERIFICATION and §4.10 will list the residual evidence ask.
- **❌ Not-in-bundle** — Step 3.5.1 *positively confirmed* the fix did NOT ship (e.g., bundle returned 200 but the marker grep was empty, OR the deploy log shows a failed/superseded job). This is a strong signal: the fix's action defaults to RESHIP-AND-VERIFY (REDO-MINIMAL bound to a re-deploy) or REVERT (if the fix was a refactor whose absence isn't blocking) in §4.3. Validated? is N/A (can't validate code that didn't ship).
Render as a clear list per fix:
```
Fix #N: <subject> (<short-sha>) → <✅ Verified | 🤷 Bundle-unverified | ❌ Not-in-bundle>
Source: <Step 3.5.1 tool/finding citation, e.g., "curl <url> | grep '[CSB-DIAG-042]' → 1 hit">
(For 🤷) Reason: <Step 3.5.1 verdict tag>
(For ❌) Implication: <RESHIP-AND-VERIFY or REVERT, deferred to §4.3 action>
```
If the scope is `session`, this section emits "N/A: session scope (no deploy yet)." Step 3.5.1 is also a no-op in this scope.
### 4.3. Section 3 — Per-Fix Verdict
For each fix from Step 3 that passed §4.2 (bundle verified or session mode), emit a horizontal-rule-separated block with these fields. Mirror the formatting style of the baseline review tag taxonomy (✅, ⚠ partial, ⚠ over-engineered, ⚠ theory-wrong, ⚠ counterproductive).
Required fields:
- **Status tag** — one of ✅ / ⚠ partial / ⚠ over-engineered / ⚠ theory-wrong / ⚠ counterproductive
- **Necessary?** — YES / NO / UNCLEAR with one-sentence reason
- **Complications introduced** — concrete list, or "None"
- **Minimal alternative** — "the smallest version of this change that would have addressed the goal." If the actual fix is the minimal version, write "This is the minimal version." Forces Rule 13.
- **Maintenance cost** — "what future contributors must now know / maintain because of this change." Forces Rule 12 / Rule 14.
- **Validated?** — one of the 5 statuses from Step 5, **post-Step-3.5.2**. The Step 3.5.2 outcome sub-pass had its chance to upgrade this from the preliminary value emitted in Step 3; this field reflects the FINAL state. Use 🤷 if §4.2 flagged the fix Bundle-unverified (Step 3.5.2 doesn't run on those — outcome can't be validated for code that didn't ship). Use N/A if §4.2 emitted ❌ Not-in-bundle.
- **Action** — one of: KEEP / REVERT / REDO-MINIMAL / RESHIP-AND-VERIFY / FOLLOWUP-TICKET / HOLD-PENDING-EVIDENCE / HOLD-PENDING-DEPLOY (session scope only) / HOLD-PENDING-VERIFICATION (bundle-unverified only). RESHIP-AND-VERIFY is for ❌ Not-in-bundle fixes whose code is correct but didn't ship — re-deploy and re-run /retrospect after.
Optional fields:
- **Evidence sourced** — when Step 3.5.2 produced a verdict-changing finding, summarize it in one line with citation. Example: "Auto-sourced via mcp__vercel__get_logs since deploy 2026-05-04 — 0 occurrences of error E_FOO; upgraded ❓→✅." If Step 3.5.2 produced no movement OR was skipped, omit this field.
- **Rule cite** — if a complication maps to a Universal Rule overstep, cite it inline (e.g., "violates Rule 14 — abstraction beyond purposeful layers")
Render each fix as a block, not a wide table:
```
Fix #N: <subject> (<short-sha>)
Status: <tag>
Necessary?: <YES/NO/UNCLEAR> — <reason>
Complications: <list or None>
Minimal alternative: <description>
Maintenance cost: <description>
Validated?: <status> (post-Step-3.5.2)
Evidence sourced (optional): <one-line with citation>
Action: <action>
Rule cite (optional): <rule>
────────────────────────────────────────
```
**Hard rule (action gating):**
- KEEP requires post-Step-3.5.2 Validated? of ✅ or ⚠ partial.
- ❓ Unvalidated → HOLD-PENDING-EVIDENCE (forces §4.10 residual ask).
- 🚫 Unvalidatable → HOLD-PENDING-DEPLOY (session scope) or HOLD-PENDING-EVIDENCE (otherwise).
- 🤷 Bundle-unverified (§4.2) → HOLD-PENDING-VERIFICATION (Step 3.5.1 already attempted; user must verify).
- ❌ Not-in-bundle (§4.2) → RESHIP-AND-VERIFY (default) or REVERT (only if fix was non-blocking refactor that's safer to drop than re-ship).
- ❌ Invalidated (§4.3) → REVERT or REDO-MINIMAL (per the smallest-alternative analysis).
The hard rule mirrors /prospect's discipline: actions cannot upgrade past their evidence floor. Step 3.5 has already attempted to lift evidence floors; what remains is genuine residual uncertainty that gates action selection.
### 4.4. Section 4 — Failure-Mode Pattern Check
Run the bundle against both pattern libraries loaded in Step 2 (canonical + project-specific if applicable). Detection is judgment-based — read each pattern's "Detection cues" and assess whether the bundle, commit messages, or session transcript exhibits them.
For each pattern hit, emit:
```
[PATTERN] <pattern-name> (source: rules/retrospect-patterns.md | projects/<proj>/retrospect-patterns.md)
Evidence: <what in the bundle/transcript triggered the hit>
Counter-discipline: <one-line reminder of the pattern's counter-discipline>
```
If no patterns hit, emit: "No catalogued failure-mode patterns detected. (See §9 for novel patterns.)"
### 4.5. Section 5 — Cross-Change Tally
Emit raw counts only — no interpretation in v1.
```
Tally:
Fixes shipped: <N>
Validated (✅): <N> (post-Step-3.5.2)
Partially validated (⚠): <N> (post-Step-3.5.2)
Invalidated (❌): <N> (post-Step-3.5.2)
Unvalidated (❓): <N> (post-Step-3.5.2)
Unvalidatable (🚫): <N> (post-Step-3.5.2)
Bundle-verified (✅): <N> (post-Step-3.5.1, §4.2)
Bundle-unverified (🤷): <N> (post-Step-3.5.1, §4.2)
Not-in-bundle (❌): <N> (post-Step-3.5.1, §4.2)
Theory-driven refactors: <N>
Required by ticket acceptance: <N>
Discovered-during-process: <N>
Pattern hits this run: <N>
Evidence-Sourcing Pass — bundle-marker sub-pass (§3.5.1):
Candidates examined: <N>
Upgraded to ✅ verified: <N>
Upgraded to ❌ not-in-bundle: <N>
No movement (still 🤷): <N>
Skipped by user: <N>
Evidence-Sourcing Pass — outcome sub-pass (§3.5.2):
Candidates examined: <N>
Upgraded to ✅ Validated: <N>
Upgraded to ⚠ partial: <N>
Upgraded to ❌ Invalidated: <N>
No movement (still ⚠/❓/🚫): <N>
Skipped by user: <N>
Sourcing pass overall:
Total tool calls: ~<N>
Skipped by --no-source: <N> (entire pass)
```
A "theory-driven refactor" is a fix that rewrote working code based on a hypothesis rather than a confirmed bug location. Discovered-during-process means the fix addresses a bug found while working on something else (not the original goal).
Interpretation of these counts is left to §4.6, §4.7, and §4.9.
### 4.6. Section 6 — Re-frame Check
Three questions, in order. Answer each in 1–2 sentences with the supporting evidence.
1. **Is the original problem statement still right?** Or did the bundle reveal that the user-reported bug is a different bug than the bundle was aimed at?
2. **Was the bug correctly scoped?** Single bug, or multiple bugs presenting as one?
3. **Is the user-reproducible scenario still the right test?** Or has the testing surface drifted?
If the answer to #1 is "no," explicitly note: "Re-frame triggered. Hold all fixes targeting the *previous* problem statement pending re-diagnosis (§4.7)."
### 4.7. Section 7 — Re-diagnosis
List the **surviving hypotheses** for the actual root cause (only run this section if any fix's *post-Step-3.5.2* Validated? is ❌ Invalidated or ⚠ partial, OR §4.6 triggered re-frame). For each hypothesis:
```
Hypothesis: <one-line statement>
Used by fixes: <#1, #3, #5>
Evidence FOR: <observations consistent with this hypothesis — INCLUDE Step 3.5.2 sourced findings with citations>
Evidence AGAINST: <observations inconsistent — INCLUDE Step 3.5.2 sourced findings>
Sourcing attempted: <YES (see Step 3.5.2 result for fixes #N) | NO (auto-sourcing skipped, declined, or not categorized as auto-sourceable)>
Confidence: LOW / MEDIUM / HIGH
To confirm: <specific signal to look for — feeds §4.10>
```
Evidence FOR/AGAINST must integrate any findings from Step 3.5.2's outcome sub-pass. If Step 3.5.2 produced a finding that moved a fix to ⚠ partial (e.g., `closed-part-of-target` upgrade from ❓), cite that finding here as Evidence FOR the partial-success hypothesis AND as Evidence AGAINST the full-fix-worked hypothesis. If Step 3.5.2 produced a contradicting finding the user contested or overrode, note both perspectives.
Discarded hypotheses (ones the bundle's outcomes OR Step 3.5.2's sourcing proved wrong) are listed separately under "Hypotheses ruled out by this retrospective or sourcing pass" with one-line reasons and source citations where applicable. This is *learning*, not waste — captures what was considered AND what evidence retired it.
If all fixes are post-Step-3.5.2 ✅ validated and §4.6 didn't trigger, this section emits "N/A: all fixes validated (Step 3.5.2 closed all gaps)."
### 4.8. Section 8 — Action Verdict
Per fix, the action determined in §4.3. Render as a clear list:
```
Action verdict:
Fix #1: <ACTION> — <one-line reason>
Fix #2: <ACTION> — <one-line reason>
...
```
For REVERT actions, provide the exact `git revert <sha>` command. For REDO-MINIMAL actions, provide the minimal alternative diff (from §4.3). For FOLLOWUP-TICKET actions, draft a ticket title + Product/Technical Intake skeleton. For RESHIP-AND-VERIFY actions (introduced when §4.2 emitted ❌ Not-in-bundle), provide: (a) the re-deploy command appropriate to the project (`gh workflow run deploy.yml --ref main`, `vercel --prod`, project-specific deploy script — pick from `aria-config.md`'s `projects_list[<tag>]` if present, otherwise prompt user), and (b) a one-line directive: "After re-deploy, re-run `/retrospect deployment` to confirm the bundle now contains the fix and validate outcome."
End with an **Overall recommendation** in 1–3 sentences.
### 4.9. Section 9 — Process Retrospective
What the prior decision-making should have done differently. Format per item:
```
What I did: <observed behavior>
What I should have done: <better behavior>
Trigger condition: <how to detect this situation in the future>
Pattern reference: <pattern-name from library | (novel)>
```
If a behavior matches an existing pattern in the library, cite it. If a behavior is *novel* (not in either pattern library), prompt the user:
> "Identified a new failure-mode pattern: `<pattern-name>`. Add to:
> 1) Canonical (`rules/retrospect-patterns.md`) — applies project-agnostic
> 2) Project-specific (`projects/<proj>/retrospect-patterns.md`)
> 3) No — surface in this report only
> Choose: "
If user chooses 1 or 2, append a new entry to the corresponding file using the format defined in `rules/retrospect-patterns.md` ("Pattern entry format" section). The new entry's "First identified" field is today's date and the current retrospective's filename.
### 4.10. Section 10 — Next-Step Evidence Ask (Residual)
Anti-speculation barrier. This section lists ONLY the **residual** evidence asks — the questions Step 3.5 either could not source autonomously, the user deferred, or were not attempted. Items resolved during Step 3.5 (✅ upgrades, ⚠ partials with Step 3.5.2 evidence, or ❌ falsifications) DO NOT appear here — they're recorded in §4.3 (`Evidence sourced` field), §4.7 (Evidence FOR/AGAINST), and §4.5 (Evidence-Sourcing Pass tally blocks).
For each remaining 🤷 / ⚠ / ❓ / 🚫 fix, emit the residual ask with its **attempt-status**:
```
For Fix #N (<subject>) — current status: <🤷 / ⚠ partial / ❓ / 🚫>
Attempt status: NOT-ATTEMPTED | ATTEMPTED-FAILED | DEFERRED-BY-USER | SKIPPED-BY--no-source
Sub-pass: BUNDLE-MARKER (§3.5.1) | OUTCOME (§3.5.2)
Why residual: <one-line — e.g., "Auto-source attempted via Bash curl <bundle-url>; URL returned 403. Demoted to USER-INPUT; user picked Skip.">
What's needed: - <specific instrumentation step 1 — log query, repro test, ticket comment, deploy log inspection, etc.>
- <specific instrumentation step 2>
- <add a unique [<TAG>-DIAG] marker so deployment verification is possible>
Who can resolve: <USER | AUTOMATED-RETRY-LATER (e.g., re-query logs after 1h) | EXTERNAL-PARTY <name> (e.g., QA team, customer report)>
```
Then group the residual asks by surviving hypothesis (from §4.7) so the user can see which hypothesis each piece of evidence would advance:
```
To confirm Hypothesis A (<short label, from §4.7>):
- Fix #N residual: <one-line>
- Fix #M residual: <one-line>
To confirm Hypothesis B (<short label>):
- ...
```
Attempt-status meanings (mirror /prospect's §4.10):
- **NOT-ATTEMPTED** — Step 3.5 did not generate a sourcing plan for this question (rare; usually means a misclassified candidate).
- **ATTEMPTED-FAILED** — Step 3.5 ran tools but the answer wasn't found / source was unreachable / two corroborating sources couldn't be obtained / log time window had no signal.
- **DEFERRED-BY-USER** — Step 3.5 surfaced a USER-INPUT ask and the user picked "Skip".
- **SKIPPED-BY--no-source** — entire pass was skipped via the `--no-source` flag.
If all fixes are post-Step-3.5 ✅ Validated and §4.2 emitted ✅ Verified for all, emit: "N/A: all fixes validated and bundle-verified (Step 3.5 closed all gaps). Proceed to next work."
If Step 3.5 was skipped via `--no-source`, prefix every entry with "(Sourcing pass skipped — re-run `/retrospect <scope>` without `--no-source` to attempt autonomous resolution.)"
End the section with this verbatim warning:
> **Do not ship another speculative fix until at least one item in this section is satisfied. If a new fix is proposed without new evidence, re-run `/retrospect`.**
## Step 5: Validation Status Taxonomy (reference)
When assigning Validated? in §4.3, choose one of the 5 statuses below. Bundle-unverified (🤷) is a precondition gate handled in §4.2, not a status.
| Status | Definition | Required sub-tag (in report) |
|---|---|---|
| ✅ **Validated** | Evidence shows the fix achieved its stated goal in the deployed state | **Evidence type**: log event \| reproduction-then-fix-verified \| production instrumentation \| deployed-state check. "Code review confirmed" is **not** validation. |
| ⚠ **Partially validated** | Evidence shows the fix changed something, but didn't fully close the bug | **Sub-tag**: `closed-part-of-target` \| `closed-different-bug` |
| ❌ **Invalidated** | Evidence shows the fix did NOT close the bug, or introduced a regression | **Sub-tag**: `didnt-fix` \| `introduced-regression` |
| ❓ **Unvalidated — evidence requestable** | No evidence yet, but the skill can describe the specific test/check that would validate | (none) |
| 🚫 **Unvalidatable** | Cannot be validated from current vantage point (requires production traffic, edge case not yet reproduced) | (none) |
When emitting a Validated? value, always include the required sub-tag where applicable. Examples:
- `Validated?: ✅ Validated (log event: apify_schema_mismatch confirmed absent post-deploy)`
- `Validated?: ⚠ Partially validated (closed-part-of-target: rehost cap raised but profile-image source still missing)`
- `Validated?: ❌ Invalidated (didnt-fix: bug reproduces on test #3)`
- `Validated?: ❓ Unvalidated — evidence requestable: needs <specific check>`
- `Validated?: 🚫 Unvalidatable: requires production traffic to surface`
## Step 6: Write Outputs
After Step 4 produces the report, write outputs to the configured destinations:
### Always
- Render the full report to terminal (chat).
### Default (configurable in `.cursor/aria-knowledge.local.md` under `retrospect:` block — to be added when needed)
- **Persistent log:** Write the full report to `<knowledge_folder>/logs/retrospect/<YYYY-MM-DD>-<scope>-<slug>.md` where `<scope>` is the resolved scope keyword from Step 0 (`commit`, `range`, `pr`, `session`, `release`, `deployment`, or `auto-range` for the no-args default) and `<slug>` is derived from the goal or referenced ticket(s). Resolve `<knowledge_folder>` from the config's `knowledge_folder` field. Create the `logs/retrospect/` subfolder lazily on first use. Existing files written under the older `<YYYY-MM-DD>-<slug>.md` pattern are grandfathered (no rename).
Prepend a structured YAML frontmatter block to the report before writing. Schema:
```yaml
---
type: retrospect
date: <YYYY-MM-DD>
scope: <commit | range | pr | session | release | deployment | auto-range>
goal: <one-line stated goal from §4.1 Anchor>
tickets: [<ABC-123>, <ABC-456>] # empty list if none
fixes_count: <N>
sourcing_pass:
bundle_marker:
candidates: <N>
upgraded_verified: <N>
upgraded_not_in_bundle: <N>
no_movement: <N>
outcome:
candidates: <N>
upgraded_validated: <N>
upgraded_partial: <N>
upgraded_invalidated: <N>
no_movement: <N>
patterns_hit: [<pattern-name-1>, <pattern-name-2>] # from §4.4; empty list if none
overall_outcome: <closed | partial | unresolved | mixed> # derived from §4.5 tally + §4.8 overall recommendation
related: [<paths to overlapping prior runs — see below>]
tags: [retrospect, <scope>, <project-tag-if-detected>, <pattern-tag-if-applicable>]
---
```
**`overall_outcome` derivation:** `closed` if every fix's post-Step-3.5 Validated? is ✅; `unresolved` if any fix is ❌ Invalidated or ❌ Not-in-bundle; `partial` if any fix is ⚠ partial AND none are ❌; `mixed` for any other combination.
**`related` auto-detection (Q1.2=1, ticket-based):** Before writing, glob `<knowledge_folder>/logs/prospect/*.md` AND `<knowledge_folder>/logs/retrospect/*.md` for files whose frontmatter `tickets:` array shares at least one ticket ID with the current report's tickets. Record their paths (relative to `<knowledge_folder>/`) in the `related:` array. If no tickets in the current report, leave `related:` empty. Cap at 10 most-recent overlaps.
**`tags:` field:** always includes `retrospect` and the scope keyword. Add a project tag when the bundle is detected to belong to a configured project (commits/files match a `projects_list[<tag>].project_root`). Add pattern-name tags for any §4.4 hits. These tags make the file discoverable via `/index` and `/context` (per Q1.3=1 — `/index` extends its scan to `logs/{prospect,retrospect}/`).
- **Aria intake:** Suggest entries for the four backlogs based on the report content:
- Insights → observations like "fix #N's theory was wrong because <evidence>"
- Decisions → "Reverted fix #N; reapplied minimal version" with rationale
- Approaches → instrumentation patterns that worked (e.g., "[<TAG>-DIAG] marker pattern for bundle verification")
- Working rules → if §4.9 identified a behavior that should become a Universal Rule, suggest it (do not persist without user approval per Rule 23)
Project-scoped intake goes to `projects/<proj>/`; agnostic intake goes to the shared knowledge tree. Follow the standard aria intake confirmation flow (suggest, user reviews, write on approval).
### Opt-in
- **Tracker comment:** Only when invoked with `--ticket-post` (legacy alias `--linear-post`). Post a *summary* (the Overall recommendation from §4.8 + the action verdict list) to each ticket detected in commit messages. Use a project-tracker MCP `save_comment`. Never post the full report — too much detail for the ticket.
### Pattern library write-backs
If §4.9 produced a novel pattern and the user approved adding it, the pattern entry is written to either:
- `<knowledge_folder>/rules/retrospect-patterns.md` (canonical), or
- `<knowledge_folder>/projects/<proj>/retrospect-patterns.md` (project-specific)
Pattern write-backs are *separate* from intake — they go directly to the patterns file, not through backlog review.
## Step 7: Soft-Suggest Trigger Logic (Claude-side judgment)
When the skill is *not* directly invoked, Claude monitors user messages for cues that suggest a retrospective is warranted. When detected AND the current session has shipped recent fixes (commits in the last hour or since the last `/retrospect`), Claude offers — never auto-executes — `/retrospect`.
Cues (non-exhaustive, judgment-based):
- "still broken," "still happening," "didn't fix," "no change"
- "regression," "same outcome," "reproducing again," "same bug"
- The user shares a test session log, transcript, or repro evidence that indicates failure
- Audit-shaped requests: "review what you did," "audit the changes," "are these necessary," "what did you change"
Standard offer (paraphrase as appropriate):
> "It sounds like the last release didn't fully close the bug. Before I propose another fix, want me to run `/retrospect` on the change set first? That'll force a validation check + re-diagnosis pass before we ship anything new."
Cue weight is judgment, not regex. When the cue is faint, just acknowledge and proceed. When the cue is clear, offer. Never auto-execute from a cue — always ask.
This logic also fires the `pushback-as-cue` pattern (see `rules/retrospect-patterns.md`) — they share the same trigger surface.
## Over-build lens (opt-in: `--lens=overbuild`)
Off unless `--lens=overbuild` is passed. When off, this skill behaves exactly as documented above — zero behavior change. When on, after the standard per-fix pass, run one additional pass over the in-scope diff:
1. **Load the rubric.** Read `rules/overbuild-patterns.md` (same `knowledge_folder`/template path used for `retrospect-patterns.md`). Hold the ladder + smell list.
2. **Walk each diff hunk** against the ladder, then the smell detection cues.
3. **Marker-respect.** If a hunk carries an `aria:simplification` marker matching `aria:simplification — .+ | limitation: .+ | upgrade: .+`, report it as `resolved (marked)` and do NOT flag it. An obvious simplification with NO marker is the `unmarked-simplification` smell.
4. **Emit findings** in the existing per-fix block style, each REQUIRING: the failed ladder rung, the matched smell name, and a concrete leaner alternative. A finding that cannot name the smaller version is suppressed (matches this skill's existing "name the smaller version or it's not a finding" discipline).
5. **Verdict mapping.** Over-build findings map to the existing verdict vocabulary: `dependency-for-a-oneliner`/`framework-for-a-function` → recommend revert-and-shrink; `unmarked-simplification` → recommend add-the-marker (not a revert).
Findings append to the report as an `### Over-build lens` subsection; they never alter the non-lens verdicts.
## Step 8: Validation Gates
Before finalizing the retrospective, verify:
1. **Anchor printed?** §4.1 must contain Goal, Mode, Range, Tickets, Outcome lines. For `deployment` scope, must also include the resolved marker source (per Step 0's deployment-detection cascade).
2. **Evidence-Sourcing Pass run (or explicitly skipped)?** Step 3.5 must have addressed every preliminary 🤷 candidate (3.5.1 bundle-marker sub-pass) and every preliminary ⚠/❓/🚫 candidate (3.5.2 outcome sub-pass) — each must end with one of: UPGRADED-TO-✅ / UPGRADED-TO-❌ / NO-MOVEMENT / INCONCLUSIVE / DEFERRED-BY-USER / SKIPPED-BY--no-source. No silent skips. The pass summary (Step 3.5.4) must be emitted.
3. **Bundle-verification gate run?** §4.2 must address every fix from Step 3 with one of three values (✅ Verified / 🤷 Bundle-unverified / ❌ Not-in-bundle), reflecting post-Step-3.5.1 state. Each per-fix render must cite the Step 3.5.1 source.
4. **Per-fix verdicts complete?** Every fix has all required fields (Status, Necessary?, Complications, Minimal alternative, Maintenance cost, Validated?, Action). Validated? values reflect post-Step-3.5.2 state. Missing field = incomplete report.
5. **Validation hard rule respected?** No fix has Action: KEEP unless post-Step-3.5.2 Validated? is ✅ or ⚠ partial. ❌ Not-in-bundle (§4.2) → RESHIP-AND-VERIFY or REVERT. 🤷 Bundle-unverified → HOLD-PENDING-VERIFICATION. ❓/🚫 → HOLD-PENDING-EVIDENCE / HOLD-PENDING-DEPLOY. Verify the full hard-rule ladder from §4.3 before emitting.
6. **Pattern check ran?** §4.4 must reference both pattern libraries (canonical + project-specific if applicable).
7. **Tally consistent?** Counts in §4.5 must match the per-fix data in §4.3 (post-Step-3.5.2) and §4.2 (post-Step-3.5.1). The two `Evidence-Sourcing Pass` blocks in §4.5 must match Step 3.5.4's summary verbatim.
8. **Hypotheses present when needed?** §4.7 is required if any fix's *post-Step-3.5.2* Validated? was ❌ Invalidated or ⚠ partial, or §4.6 triggered re-frame. Evidence FOR/AGAINST must integrate Step 3.5.2 findings where applicable.
9. **Action verdict complete?** §4.8 must have an action for every fix in §4.3. RESHIP-AND-VERIFY actions must include the project-appropriate re-deploy command + the re-run-/retrospect directive.
10. **Residual evidence asks correctly scoped?** §4.10 must list ONLY residual items (NOT-ATTEMPTED / ATTEMPTED-FAILED / DEFERRED-BY-USER / SKIPPED-BY--no-source). Items resolved by Step 3.5 (✅ upgrades, ⚠ partials with §3.5.2 evidence, or ❌ falsifications) must NOT appear in §4.10. Cross-check: every §4.10 entry must have current status of 🤷 / ⚠ / ❓ / 🚫.
11. **Outputs written?** Confirm the persistent log was written to disk at `<knowledge_folder>/logs/retrospect/<YYYY-MM-DD>-<scope>-<slug>.md`, with structured YAML frontmatter (type, date, scope, goal, tickets, fixes_count, sourcing_pass, patterns_hit, overall_outcome, related, tags) prepended. Confirm intake suggestions were surfaced.
12. **`related:` cross-refs computed?** If the report contains tickets, the `related:` frontmatter array must list overlapping prior runs (capped at 10) from `logs/prospect/` and `logs/retrospect/`. If empty (no tickets), confirm the empty-list state explicitly.
If any check fails, self-correct once. If self-correction can't close the gap (e.g., the user must supply evidence), surface the gap explicitly in the report rather than silently skipping.
---
## /foundational-review
# /foundational-review — "Is this the right thing, built the right way?"
Run a repeatable, model-agnostic review of a project at the FOUNDATIONS — problem–solution fit, architecture soundness, product coherence — and convert the findings into a prospect-hardened, cold-executable plan with owner routing. This is the productized form of the foundational review chain (the canonical process doc this skill reads at Step 1).
This skill is **orchestration + artifact templates only**. The substance — the A–F review questions, the operating rules, the failure-mode library, the pairing contract — lives in the canonical process doc, which Step 1 reads in full. Do not reproduce that doc's content from memory; read it live every run so the chain evolves in one place.
**What this is NOT** (the process doc opens with this — internalize it):
- **Not a code review** — code review asks "are these changes correct?" on a diff. This asks "should this shape exist?" on a decision.
- **Not a readiness audit** — `/readiness-audit` checks a surface against a checklist and produces a findings list. This produces a VERDICT resting on named PREMISES plus the full execution handoff. Siblings, not substitutes (see the pairing contract in the Step 1 doc and Step 0's pairing check).
- **Not a retrospective** — `/retrospect` validates shipped work backward. This judges a standing architecture/product forward, before an expensive-to-undo step.
## When to use
Run it before any IRREVERSIBLE decision — the anchor:
- A version freeze or stable-contract claim
- A format / schema / spec tag (e.g., a `v1.0` spec freeze)
- A public API surface or a repo public-flip
- A major re-scope, pivot, or "should we keep building this at all" moment
**No irreversible decision named → do not run this chain.** Redirect:
- A plan that's about to execute → `/prospect`
- A surface to check clean/legal/consistent for shipping → `/readiness-audit`
- Already-shipped work to validate backward → `/retrospect`
## Step 0: Invocation Block
Parse `<scope-root>` (first positional), `--decision "<text>"`, and `--extend`. Then assemble the invocation block. If args are thin, collect interactively — **one ask at a time**:
```
Invocation:
Scope root: <path — the project/workspace under review>
Read-first: <docs to read before judging: CLAUDE.md, PROGRESS.md, specs, prior audits>
THE irreversible decision: <the single expensive-to-undo step this review gates — REQUIRED>
Section-F inputs: <strategy/positioning/licensing docs; mark CONFIDENTIAL ones>
Constraints: <read-only repos, build rules, no-push, team-owned repos needing tickets>
Reviewer model: <highest-ceiling available; see Model Routing below>
Extend?: <yes if --extend — runs the system-design extension loop after Step 6>
```
**Hard gate — the decision anchor.** If no irreversible decision can be named (the `--decision` arg is absent AND the user can't state one when asked), STOP and redirect:
> This chain is decision-anchored — it needs one expensive-to-undo step to anchor the verdict and the irreversibility inventory. You haven't named one. Did you mean:
> 1) `/prospect <plan>` — pre-mortem a plan that's about to execute
> 2) `/readiness-audit <scope-root> --for "<event>"` — audit a surface for ship-readiness
> 3) I'll name the irreversible decision now: <reply with it>
Do not invent a decision to keep the chain running.
**Pairing check.** If THE irreversible decision is a SHIP / FREEZE / PUBLIC-FLIP, the canonical pairing rule says run BOTH the audit (for the surface) and the chain (for the decision). Ask:
> This decision is a ship/freeze/flip. The pairing contract recommends running `/readiness-audit` first (cheaper, produces the evidence base the review can lean on). Run `/readiness-audit <scope-root> --for "<event>"` now, then resume this chain? (`y` / `n` / `already-have-one`)
On `y`, invoke `/readiness-audit` via the `Skill` tool, then resume at Step 1 with the audit as admissible evidence (per the composition contract — the review re-derives every inherited claim, never trusts the audit's attributions/counts without a fresh sweep).
### Model Routing
- **Reviewer = the highest-ceiling model available** (escalate to the top tier only when the decision is extreme-stakes; the default top model otherwise). The ceiling is spent on alternatives steelmanning (§A), portfolio/product judgment (§F), and the irreversibility inventory. **The default model can run the full chain** — it compensates with stricter evidence-sourcing in the /prospect passes.
- **Executor = the default model by default.** Every plan task (Step 4) carries `OWNER: <default model>` unless its *execution* needs extreme judgment — justify any top-tier owner in one line, or it's rewritten to the default.
- **Gate owner = the human.** Anything needing sign-off becomes a named gate `G-A..` in the spec (Step 3), never an inline assumption.
- **Effort ladder (when the reviewer is the top-tier model):** default `xhigh` for every substantive review — these passes are semi-agentic (read files, trace seams) and `xhigh` is built for that read-trace-reason loop; it also matches *why* you escalated to the ceiling. Use `high` only for light surfaces (small/on-hold/PRD-only projects). Reserve `max` for the single hardest correctness-dominated pass (e.g. auth-across-tenancy, ledger/tax invariants) — elsewhere it over-deliberates without adding signal. Running the top model at plain `high` is the worst-value point: if `high` feels like enough, the default model at `high` would have sufficed.
## Step 1: Load the Canonical Process & Survey
1. **Load the canonical chain.** Read the canonical process doc, preferring a user copy when present:
- If `<knowledge_folder>/approaches/foundational-review-chain.md` exists (resolve `<knowledge_folder>` from `.cursor/aria-knowledge.local.md`), read THAT — a user may keep a richer, project-specific copy there.
- Otherwise read the plugin-bundled copy at `knowledge/approaches/foundational-review-chain.md` (always present).
It carries the operating rules, the A–F questions, the artifact conventions, the failure-mode library, and the pairing/extension specs. Hold it in context — every later step references it by section rather than restating it.
2. **Load the failure-mode libraries** (mirrors /prospect Step 2): `<knowledge_folder>/rules/retrospect-patterns.md`, and `<knowledge_folder>/rules/prospect-patterns.md` + `<knowledge_folder>/projects/<tag>/{retrospect,prospect}-patterns.md` if they exist for the detected project.
3. **Survey the tree.** Read the read-first docs, then walk the actual source tree. **READ BEFORE JUDGING** (operating rule 1): every load-bearing claim is cited as `file:line`, verified THIS session — never asserted from memory, docs, or prior-session summaries.
4. **Bound the scope** (operating rule 2): state in-scope and out-of-scope explicitly; fence sibling workstreams by reference. For a multi-repo family, **group by coupling mechanism**: shared-runtime repos → review as ONE super-project (hub-out from the shared dependency, ~20% grounding / ~80% on the seams); protocol- or file-contract repos → review each side standalone + review the *contract* once from the producer side; a vendored copy → not a review seam at all. The same portfolio can contain all three.
5. **Run empirical probes** read-only (`git log`, `grep`, file-existence). **VERIFY FORMATS, NOT JUST LOCATIONS** (operating rule 6): any later plan step that parses/renames/sweeps is preceded by an enumeration sweep at planning time.
## Step 2: Findings Document
Write `<scope-root>/FABLE-REVIEW-<YYYY-MM-DD>.md` (keep the reviewer-named filename for provenance even when the default model runs it; state the actual model in the header). Structure:
```
# Foundational Review — <project> — <YYYY-MM-DD>
Reviewer model: <actual model> | THE irreversible decision: <text>
## Verdict
<foundationally-sound | sound-with-changes | re-scope | reconsider>
+ the single most important reason (one sentence).
## Premises
| ID | Premise the verdict rests on | If it changes… |
|----|------------------------------|----------------|
| P1 | … | … |
## A. Problem–Solution Fit
Steelman 2–3 alternative framings (include "do nothing / minimal" and "kill / fold elsewhere");
for each: wins, costs, verdict + PRESERVED rationale (why-rejected stays answerable).
## B. Foundational Correctness
Name each weak premise being patched around + the fix-at-base.
## C. Built Right
Data model · abstraction layers (flag any beyond ~3 that don't earn their cost) · coupling/seams ·
testing & validation · characteristic failure modes (especially SILENT ones).
## D. Gaps
Smallest set of missing capabilities that makes it "v-next worthy." Assign existing owners; don't re-own.
## E. Over-build
What exists that the use case doesn't justify. Cut = archive-with-pointer or policy-freeze, not deletion.
Includes DOC decay (stale plans, falsified bug reports a cold executor would burn time on).
## F. Product / Portfolio Coherence
Boundaries (free/premium, public/private) · monetization/positioning · family coherence · licensing/IP posture.
## Irreversibility Inventory
| What becomes expensive after the tag | Why | Precision the handoff needs |
## Uncertainty Flags
Predictions ≠ findings — say which is which.
```
**Scale to the finding:** one change → findings + a short plan; large review → findings + several specs + plans. A re-scope/reconsider verdict's spec/plan may be a wind-down or pivot plan — that's a valid output.
## Step 3: Design Spec(s)
Per substantive change, write `docs/superpowers/specs/<date>-<topic>-design.md` (use the project's existing `docs/superpowers/` convention):
```
## Decisions
D1: what · why · smallest version · rejected alternatives (with preserved rationale)
…
## Gates
G-A: decision needed · what it blocks · default-if-unanswered (or "hard gate, no default")
…
## Non-goals
## Sibling-workstream fencing
"<sibling plan> owns <X> — not duplicated here."
```
Minor items stay as plan rows; only substantive changes get a spec.
## Step 4: Plan(s)
Per spec, write `docs/superpowers/plans/<date>-<topic>.md`. Tasks must be COLD-EXECUTABLE with no access to the reviewer's reasoning:
- Exact paths, supplied code/content, commands + expected output, acceptance criteria, dependency sequence.
- `OWNER:` per task (default executor; justify any top-tier owner in one line).
- An **execution-notes preamble**: build rules, restore steps, no-push, named-path commits, known-baseline counts (test counts, file counts).
- **Team-owned repos:** the unit of execution is a tracker ticket (Product + Technical Intake), NOT a code edit.
## Step 5: Compose /prospect
Run `/prospect` on each plan (file scope) via the `Skill` tool:
> Use the `Skill` tool to invoke `prospect` with `file <plan-path>`.
Then **APPLY verdict-changing amendments IN PLACE** — don't just score the plan. Validated runs' /prospect passes falsified *format* assumptions (line-anchored tokens that were `var()` indirections; hex colors that were RGB triplets; a 4-file rename that was a 14-file surface). After amending, stamp the verdict + amendment list into the plan header. The prospect log lands in `<knowledge_folder>/logs/prospect/` via the composed skill — do not duplicate that routing here.
## Step 6: Failure-Mode Self-Check, Commit & Kickoff
**Pre-commit self-check (mandatory).** Run the plan(s) against the "Failure modes this process has already caught" list in the Step 1 process doc — this is the chain's equivalent of /prospect's pattern check. At minimum confirm:
- `fix-without-call-site-audit` — no rename/sweep step written only from the sites the review happened to verify
- Un-sourced FORMAT assumptions — every parse/rename/sweep preceded by an enumeration sweep
- Misattributed file claims inherited from a sibling audit — re-derived by sweep, never inherited
- "Verification" runs that mutate build artifacts — every test-build step carries an explicit RESTORE + `git diff --stat` check
- Plan/idea-dump decay — no stale/falsified backlog items inside the plan
- History rewriting in mechanical sweeps — live-vs-historical surfaces split before any `sed`
**Commit** the artifacts (named paths, no push — pushing is the owner's separate gate per operating rule 8). If `Bash`/`git` is unavailable, emit copy-paste commit messages instead.
**Kickoff.** End with a paste-ready executor kickoff:
```
## Executor Kickoff
Plan(s): <path(s)>
Task order: <#1 → #2 → …, note dependency gates>
Owner routing: <default executor; any top-tier tasks + one-line justification>
Definition of done: <observable signals>
Open gates: <G-A.. that must be answered before/at which task>
Report back: <what to surface on completion>
```
## Step 7 (optional): Extension Loop `--extend`
Run only when the owner wants the deeper "is the SYSTEM itself good engineering?" layer. Follow the process doc's "extension loop" section:
1. **System-design assessment** appended to the findings doc (§11-style): novelty ranked by defensibility; honest weaknesses; abstraction-architecture verdict; ranked improvement ladder; assessment uncertainties (mark untested claims benchmarkable). Look for ONE unifying critique — a single lens explaining all improvements signals the analysis converged.
2. **Durable capture** — the unifying critique → a canonical pattern file (`projects/<tag>/patterns/` or `approaches/`); session arc → project memory + memory index. Reuse `/extract` routing rather than duplicating it.
3. **Second chain** — improvement ladder → roadmap SPEC (waves W0 enforce-existing · W1 evidence probes each with a NAMED FALSIFICATION OUTCOME · W2 architecture evolutions gated on W1) → `/prospect` → wave-executable PLAN → `/prospect` → commit. Probes-before-architecture is the spine.
## Step 8: Outputs & Intake
- **Findings:** `<scope-root>/FABLE-REVIEW-<YYYY-MM-DD>.md`
- **Specs/plans:** the project's `docs/superpowers/{specs,plans}/`
- **Prospect logs:** via the composed `/prospect` (Step 5), in `<knowledge_folder>/logs/prospect/`
- **Extension captures:** via `/extract` (Step 7), if `--extend`
- **Aria intake:** suggest backlog entries (insights / decisions / approaches) per the standard intake confirmation flow — suggest, user reviews, write on approval.
## Step 9: Validation Gates
Before declaring the chain complete, verify:
1. **Decision anchor present?** THE irreversible decision is named in the findings header. (If absent, the chain should have redirected at Step 0.)
2. **Read-before-judging honored?** Every load-bearing claim in the findings cites `file:line` verified this session.
3. **Verdict from the fixed vocabulary?** One of foundationally-sound · sound-with-changes · re-scope · reconsider, with its single most-important reason.
4. **Premises table present** with "if it changes" for each.
5. **Sections A–F all answered** (or marked N/A with reason) + irreversibility inventory + uncertainty flags.
6. **Specs carry D-decisions + gates;** plans are cold-executable with OWNER per task + execution-notes preamble.
7. **/prospect composed on every plan,** amendments applied IN PLACE, verdict stamped into the plan header.
8. **Pre-commit failure-mode self-check ran** against the process doc's list.
9. **Artifacts committed (named paths, no push);** kickoff emitted.
10. **Pairing honored** — if the decision was a ship/freeze/flip, either a `/readiness-audit` was run/referenced or the user explicitly declined.
If any check fails, self-correct once; if it can't be closed (e.g., a gate is unanswered), surface the gap explicitly rather than silently skipping.
---
## /readiness-audit
# /readiness-audit — "Is it clean / legal / consistent to ship?"
A checklist-against-a-surface audit that recurs, needs no irreversible-decision anchor, and answers "is it ready to ship for THIS event," not "should this shape exist." The companion format is canonicalized in the **"Companion format: the readiness audit"** section of the foundational review chain process doc (read at Step 1).
This skill is **orchestration + artifact templates only**. The format spec and the pairing contract live in the canonical process doc, which Step 1 reads. Do not reproduce them from memory.
**Sibling of `/foundational-review`** — audit = recurring, checklist-shaped, surface-anchored; chain = per-decision, verdict-shaped. When the event is a SHIP / FREEZE / PUBLIC-FLIP, the pairing contract says run BOTH: this audit for the surface, the chain for the decision (audit first — cheaper, produces the evidence base). Step 8 surfaces that pairing.
## When to use
- Before a release, a public-repo flip, a handover, or any "is this ready for X" moment that's about the *surface* (clean, legal, consistent), not the *shape*.
- As the recurring instrument — re-runnable whenever the surface changes; no decision anchor needed.
If the question is "should this shape exist / is this the right thing built the right way" before an expensive-to-undo step → use `/foundational-review`. If it's a plan about to execute → `/prospect`.
## Step 0: Inputs
Parse `<scope-root>` (first positional) and `--for "<event>"`. If thin, collect interactively:
```
Inputs:
Scope root: <path under audit>
Ready for WHAT: <the event: public release | repo public-flip | handover | v1.0 tag | …>
Locked decisions: <any already-decided constraints that become audit premises>
Inherited audits: <prior audits whose findings/claims this one re-derives, never trusts>
```
"Ready for WHAT" is the frame that makes a finding a blocker vs. a nice-to-have — get it explicit before exploring.
## Step 1: Load Canonical Companion Format & Bound Scope
1. Read the **"Companion format: the readiness audit"** section of the canonical process doc, preferring a user copy when present:
- If `<knowledge_folder>/approaches/foundational-review-chain.md` exists (resolve `<knowledge_folder>` from `.cursor/aria-knowledge.local.md`), read THAT.
- Otherwise read the plugin-bundled copy at `knowledge/approaches/foundational-review-chain.md` (always present).
It defines the tier structure, the agent-claim-correction discipline, and the composition contract with the chain.
2. State in-scope / out-of-scope explicitly. Fence sibling workstreams ("the chain's plan owns architecture-freeze risks — not duplicated here").
## Step 2: Parallel Exploration (read-only)
Dispatch exploration agents per surface — typically:
- **source/build** — what builds, what's dead, what the artifact actually contains
- **licensing/hygiene** — license headers, secrets, internal URLs, third-party-asset rights
- **docs/tooling** — stale docs, broken references, release pipeline presence
- **over-build** (opt-in: when `--for` mentions bloat/over-engineering, or always when the scope is a code repo) — one read-only Explore agent walks `rules/overbuild-patterns.md`'s ladder + smells across the surface's source, reporting candidate over-build sites (each: `file:line`, matched smell, failed ladder rung, concrete leaner alternative). Respects `aria:simplification` markers — a marked site is reported "resolved", never flagged. Read-only per the guardrail below: it reports what it would change, never mutates a build artifact.
Use the `Task` tool (Explore agent type) per surface, in parallel. **Guardrail — read-only verification probes only.** No probe may mutate a build artifact. (A real incident: an agent "verified a build" by RUNNING it and overwrote a generated source file.) If a check would require building/running, the agent reports *what it would run + expected output*, and the controller decides whether to run it under the diff-check discipline in Step 3.
## Step 3: Controller Re-Verification (the defining discipline)
This is what separates a readiness audit from a pile of agent claims. **Re-verify every load-bearing agent claim with a direct controller-level check** before it enters the findings. In real runs, multiple agent claims were corrected by direct checks.
For each claim an agent surfaced:
- Re-derive it yourself (`Read`/`Grep`/`git`), at `file:line`.
- If it holds → it becomes a finding with a verified Evidence cell.
- If it's wrong → record the correction as a **decision-trail row**: `Agent claimed X → direct check showed Y → corrected.`
- **Inherited claims from prior audits are re-derived, never trusted** (per the composition contract — a real review corrected an inherited file misattribution and a mis-scoped variable claim).
**Artifact diff-check (mandatory).** If any verification required building or running anything, immediately run `git diff --stat` (and restore) to prove no tracked artifact was mutated. Record the diff-check result in the audit. A test-build step that can't show a clean diff is a finding against the audit itself.
## Step 4: Tiered Findings
Each finding carries a **verified Evidence cell** (citation from Step 3, not an agent assertion):
```
## Tier 0 — Blockers (must fix before the event)
| # | Finding | Evidence (file:line / probe result) | Owner |
## High
## Medium
## Low (hygiene)
```
The tier is set by the "ready for WHAT" frame from Step 0 — a stale internal URL is Tier 0 for a public flip, Low for an internal handover.
## Step 5: Conceptual Observations (no code change)
Observations worth recording that don't propose a code change — design smells, future risks, "we should decide X someday." Kept separate from findings so the remediation plan stays actionable.
## Step 6: Phased Remediation Plan
**Findings are NOT a shipping list** (cite `audit-findings-as-shipping-list-without-triage`). Triage them into phases with owners:
```
## Remediation Plan
Phase 1 (pre-event): <Tier 0 + the High items that block the event> — owners
Phase 2 (fast-follow): <remaining High + Medium> — owners
Phase 3 (backlog): <Low / hygiene> — owners
```
One owner per item. If the chain (`/foundational-review`) is also running, fence by reference: each item has exactly one owning document.
## Step 7: End-to-End Verification Recipe
The exact sequence to prove the surface is ready *after* remediation — the commands/checks + expected output a cold executor runs to confirm. This is the audit's definition-of-done.
## Step 8: Gates & Pairing
- **Gates** — for genuine decisions, surface via `AskUserQuestion`: the decision, the options, the consequence of each. Don't bake a default for a real decision.
- **Pairing** — if the event is a SHIP / FREEZE / PUBLIC-FLIP, note that the pairing contract recommends running `/foundational-review --decision "<event>"` on the *decision*, with this audit's verified findings as admissible evidence and its locked decisions as review premises. Gates from both should land in ONE gate table (the chain's spec) so nothing ships on a gate answered in only one document.
## Step 9: Output & Commit
- Write `<scope-root>/…/audit-<YYYY-MM-DD>-<event>-readiness.md` (match the project's docs convention for the location).
- Commit local (named path, no push). If `Bash`/`git` is unavailable, emit a copy-paste commit message.
- Suggest aria intake entries (insights / decisions) per the standard confirmation flow.
## Step 10: Validation Gates
Before declaring the audit complete, verify:
1. **"Ready for WHAT" stated** and used to set tier severity.
2. **Every finding has a verified Evidence cell** sourced at Step 3, not an unverified agent claim.
3. **Controller re-verification ran** — agent claims re-derived; corrections recorded as a decision trail.
4. **Artifact diff-check recorded** if any verification built/ran anything (`git diff --stat` clean + restore).
5. **Inherited claims re-derived,** never trusted from a prior audit.
6. **Findings tiered,** not flat; remediation is PHASED with one owner per item (not a shipping list).
7. **End-to-end verification recipe present.**
8. **Real decisions surfaced as gates** (AskUserQuestion), no fabricated defaults.
9. **Pairing surfaced** if the event is a ship/freeze/flip.
10. **Report committed** (named path, no push).
If any check fails, self-correct once; if it can't be closed, surface the gap explicitly.
---
## /interview
# /interview — Elicit Knowledge Through Dialogue
Interview the user to draw out knowledge that lives in their head (and in their artifacts), then stage it as structured markdown in the `intake/` tree. Unlike `/extract` (reads the current conversation) or `/intake` (captures/scans external sources — URLs, snippets, files), `/interview` *asks questions* — the answers become the knowledge. Output is staged for **manual review** (the `/meeting-notes` model), never auto-promoted.
## Step 0: Resolve Config
Read `.cursor/aria-knowledge.local.md` and extract `knowledge_folder`. If the file doesn't exist, stop: "aria-knowledge is not configured. Run /setup to get started."
## Step 1: Resolve Mode, Topic, Grounding
Parse arguments: first positional = **mode** (`project` | `knowledge` | `deep-dive`). Remaining text = **topic**. `--ground=<path|glob|url>[,...]` = optional grounding artifacts. `--socratic` and `--battery` = cadence overrides (Step 3); both are also accepted bare (`socratic`, `battery`). They are **mutually exclusive** — if both appear, take `--socratic` (the more conservative grain) and say in one line that you did.
If no mode given, ask: "Which interview — `project` (scope a new build), `knowledge` (get a topic into the KB), or `deep-dive` (extract rationale behind something you already built)?"
**Derive the slug** from the topic (kebab-case, ≤6 words). If no topic yet, ask for a one-line subject.
### GATE — deep-dive requires a basis (explicit early-return)
**If mode is `deep-dive` AND no `--ground` was provided AND no basis is named in the topic:**
> STOP. Do not ask any interview questions. Emit:
>
> "`deep-dive` extracts the rationale behind something that already exists, so it needs a basis to review and build questions around. Point me at what to study — source code, a directory, a design doc / plan / spec, a project folder, a data file (e.g. a spreadsheet), or a URL."
>
> Wait for the user to supply a basis. Re-enter this gate with their answer. Do NOT proceed to Step 2 until `deep-dive` has at least one grounding artifact.
For `project` and `knowledge`, grounding is optional — proceed to Step 2 regardless.
## Step 2: Ingest Grounding (if any)
For each grounding artifact: file/dir/glob → Read/Glob/Grep; URL → WebFetch; project folder → read its CLAUDE.md + top-level structure; data file → read/parse. Record key observations — these become the evidence you cite in questions. (`deep-dive` always has ≥1; `project`/`knowledge` may have none.)
## Step 3: Cadence — `guided` by default, `--battery` to opt out
**`guided` is the default for all three modes.** Do not recommend a cadence, do not ask which one — just run `guided` unless `--battery` was passed. The old behavior (recommend `battery` for anything grounded or broad) made the all-at-once path the effective default, which is the wrong default: a numbered wall of questions has to be answered by scrolling and referencing numbers, and because a batch is derived before any answer arrives, it necessarily keeps asking things an earlier answer already made irrelevant.
**`--battery` is the escape hatch, not a co-equal option.** Mention it **once, in one line, and only** when the derived question set is large enough that sequential dialogs would genuinely be worse than a list (roughly >12 questions — in practice a broad `deep-dive`). Then proceed with `guided` without waiting for a reply:
> "This derives ~N questions. Staying guided — small dialogs, adapting as we go. Reply `--battery` any time if you'd rather take the whole set at once as a list."
**`--socratic` pins the grain, it is not a third cadence.** There are two cadences, and they differ on **when the questions are derived**, not on how many appear at once: `guided` re-derives after every answer, `battery` derives the whole set before the first one. `--socratic` is `guided` with the dialog grain pinned to exactly one question — same adaptive loop, no clustering. Reach for it when the user has said they want one at a time, or when they distrust the clustering judgment; `guided` already collapses to one question wherever the independence test fails, so this is a manual override of that judgment, not a different process. Record it in Step 6 as `cadence: socratic` — the artifact then states the grain the answers were given at, which a later reader needs.
## Step 4: Interview
### `guided` cadence (the default)
Ask in **small dialogs of 1-4 questions**, using the platform's question/picker affordance — one dialog, wait, then derive the next from what came back. Never present a numbered list the user has to answer by number.
**Every question carries suggested answers.** Offer 2-4 concrete candidate answers per question, drawn from the mode's question bank and — where grounding artifacts exist — from the evidence itself (a real value read out of the code or doc beats an invented placeholder). The free-text / custom answer is always available in the affordance, so a suggestion set is a **shortcut, never a constraint**; say so once at the start and don't repeat it per question. **Never invent a suggestion to fill a slot** — two real candidates are better than four with two fabricated, and a fabricated option in an elicitation interview is worse than none because it can be picked.
**Cluster size is contextual, not fixed.** Put 2-4 questions in one dialog only when they are **mutually independent** — no answer to one can change the wording, the options, or the relevance of another in the same dialog. Anything that branches goes alone. When unsure, ask it alone: the cost of an extra dialog is one round-trip, while the cost of a batched question its neighbour invalidated is precisely the failure this cadence exists to remove.
**`--socratic` pins the grain to one.** When it was passed, ask exactly one question per dialog for the whole interview and skip the independence test entirely — there is nothing to cluster. Everything else is unchanged: same suggested answers, same custom fill, same re-derive-after-each-dialog loop, same coverage ledger. Do not batch "just these two, they're obviously independent" — the flag exists precisely to take that call away from you.
**Adapt, and drop what died.** After each dialog, re-derive before asking again: strike questions the last answers made irrelevant, add the ones they opened, re-cluster the remainder. Never carry a pre-derived list forward unchanged — that is `battery` wearing `guided`'s clothes.
Maintain a running **coverage ledger** (which mode-bank items are satisfied / thin / waved-off).
### `battery` cadence (only when `--battery` was passed)
Derive a question set for the mode (banks below), grounded with cited evidence where artifacts exist. Cluster by **leverage** (highest-impact first), number them, present all at once, invite answers in any order/prose. **State the known limitation once, up front:** the set is fixed before your first answer, so some questions will read as irrelevant by the time you reach them — skip those outright rather than answering around them.
**Hybrid stop (both cadences):** cover the mode's checklist floor, probe where answers are thin, and always honor an early "done". Periodically surface coverage: "Covered: X, Y. Still thin: Z. Say 'done' to stop early."
## Step 5: Assemble & Confirm (confirm-before-write)
Assemble the staged-file draft (frontmatter + the mode's body template, filled from answers). Show the full draft and ask: "Here's what I captured — write it to `{path}`? (`y` to write, or tell me what to change)". Do not write until `y`.
## Step 6: Stage
Target by mode:
- `project` → `{knowledge_folder}/intake/projects/{YYYY-MM-DD}-{slug}.md`
- `knowledge` / `deep-dive` → `{knowledge_folder}/intake/interviews/{YYYY-MM-DD}-{slug}.md`
Lazy-create the subfolder if missing. Write the file. Report the path and note: "Staged for manual review — promote later via /extract or by hand (not auto-swept by /audit-knowledge)."
---
## Question Banks (by mode)
**project** — Problem & motivation · Users / who it's for · Scope (in) · Scope (explicitly out) · Constraints (technical/time/dependency) · Stack / approach leanings · Success criteria · Risks & open questions
**knowledge** — Claim / position · Basis & evidence · Confidence (firm/working/speculative) · Contested points & counter-views · Connections to existing knowledge ([[links]]) · What would change my mind
**deep-dive** (the DF-session method) — cluster by leverage, cite evidence per question, hunt negative space:
- Load-bearing invariants — what's immovable vs revisable, and why
- Origin of each decision — where did this come from (research / inheritance / invention)?
- **Negative space** — what was considered and deliberately NOT built?
- What would force a rebuild — name the scenarios that invalidate the current design
- Open threads
---
## Output Templates
Each file opens with this frontmatter (fill `mode`/`cadence`/`slug`; list `grounding` artifacts or omit if none):
```yaml
---
type: interview
mode: project | knowledge | deep-dive
cadence: guided | socratic | battery # socratic = guided pinned to one question per dialog
date: YYYY-MM-DD
slug: <kebab-topic>
grounding:
- <artifact>
status: staged
---
```
### project → body
```markdown
# Project Intake: <name>
## Problem & motivation
## Users / who it's for
## Scope — in
## Scope — explicitly out
## Constraints
## Stack / approach leanings
## Success criteria
## Risks & open questions
```
### knowledge → body
```markdown
# <topic>
## Claim / position
## Basis & evidence
## Confidence
## Contested points & counter-views
## Connections to existing knowledge
## What would change my mind
```
### deep-dive → body (preserve Q AND A)
```markdown
# Deep-Dive: <system>
## Grounding reviewed
## Q&A by leverage cluster
## Load-bearing invariants
## Negative space
## What would force a rebuild
## Open threads
```
---
## /recap
# /recap — Read-Only Orientation
Render a compact `What / Where / Status` table of recent work to situate the user at a glance. **Read-only**: no disk writes, no logs, no verdicts. The orient-side counterpart to `/handoff` (which packages state for the *next* reader) — recap re-orients the *current* reader. Distinct from `/retrospect`, which validates with per-fix verdicts; `/recap` only summarizes, and may *offer* to escalate to `/retrospect` but never runs verdict work itself.
## Step 0: Resolve Mode
Parse the first argument (case-insensitive):
- `arc` → arc mode
- `commit` (optionally followed by a `<hash>`) → commit mode
- `push` → push mode
- `pull` → pull mode
- `project` → **project mode** — consume a *second* argument as the breadth selector:
- no second arg → **project-nearest** (the current session's main project)
- `all` → **project-roster** (every project in `projects_list`)
- any other token `<name>` → **project-named** (the `<name>:` tag in `projects_list`)
- anything else / no arg → **session mode** (default)
## Mode Resolution
### Session mode (default)
Synthesize what happened in THIS conversation — files created/modified, decisions made, current state — **from conversation context, not git** (same source as `/handoff` Step 2). Headline frame: "this session · {N} changes".
### Arc mode (`arc`)
Read the project's `PROGRESS.md` (nearest one from cwd). The arc boundary = the most recent dated/`## ` arc heading; everything from that heading forward (plus this session's work) is "the arc". **State the inferred boundary in the headline** (e.g. "arc since PROGRESS 2026-06-21 entry") so the user sees what was treated as the arc. If no PROGRESS.md, fall back to session mode and say so.
### Commit mode (`commit [<hash>]`)
`git show <hash|HEAD> --stat` for the subject + changed files. Headline: "commit {short-sha} · {subject} · {N} files".
### Push mode (`push`)
`git log @{push}..HEAD --stat` (commits *I* sent up). If no upstream is configured, fall back to `git log -10` and say so. Headline: "last push · {N} commits · {M} files".
### Pull mode (`pull`)
Commits that *came down to me* on the last pull. Resolve the range:
1. Try `git log ORIG_HEAD..HEAD --stat` (git sets `ORIG_HEAD` before a pull/merge).
2. If `ORIG_HEAD` is unset OR the range is empty (it was overwritten by an intervening merge/rebase/reset), scan `git reflog` for the most recent `pull`/`merge` entry and use that entry's pre-state as the range start.
3. **Always print the resolved range** (e.g. "last pull · ORIG_HEAD..HEAD · 5 commits from origin/main") so the user can verify what "last pull" meant.
**push vs pull:** `push` = what *I* sent up (`@{push}..HEAD`, my commits); `pull` = what *came down to me* (`ORIG_HEAD..HEAD`, others' commits I merged). Opposite directions.
### Project mode (`project [<name>|all]`)
Where the modes above orient you *temporally* (this session, this repo's git), `project` mode orients you *laterally* — the current state of one project, or of the whole portfolio. A terminal-table analogue of the aria-atlas dashboard. The second argument selects breadth (resolved in Step 0).
**Roster resolution (named + roster sub-modes).** Read `.cursor/aria-knowledge.local.md` and parse the `projects_list:` frontmatter key — comma-separated `tag:path` entries; expand a leading `~` in any path. This is the same roster `/aria-assist` reads; **be read-only on `projects_list` — never write it.** If `projects_list` is empty/absent:
- **project-roster** → hard stop: "Roster unconfigured — run `/setup` to populate `projects_list`." Do not guess a roster.
- **project-named** → fall back to treating `<name>` as a literal filesystem path if it exists; else the same unconfigured message.
**Resolve the project path per breadth:**
- **project-nearest** (no second arg) → walk up from cwd to the nearest `CLAUDE.md`/`PROGRESS.md` (the same Step-1 resolver the other aria-knowledge skills use). No `projects_list` needed.
- **project-named** (`<name>`) → the typed `<name>` IS the `projects_list` tag (`/recap project cs` → the `cs:` entry). Unknown tag → list the available tags and stop (no fuzzy matching).
- **project-roster** (`all`) → iterate every `tag:path` entry.
**Per-project read (tolerant — a missing/malformed file degrades to a blank/omitted row, never throws):**
- **`SESSION.md`** (nearest, then sub-project roots if present): `lastEvent` (`in-progress`/`wrapup`/`handoff`) → *current state*; the embedded next-session prompt → an *in-flight* fragment.
- **`PROGRESS.md`** (nearest): the most recent dated/`## ` arc heading + its open (TODO / in-progress) items.
- **Git — only if the directory is a git repo AND Bash is available:** `git -C <path> log -1 --stat` (last commit) + `git -C <path> status --short` (dirty tree). **If not a git repo → silently omit the commit and working-tree rows** (per-project version of the Runtime-Gate Bash check).
**Output — single project (nearest / named).** Full orientation, keeping the standard `What / Where / Status` table. Each substantive item carries an indented `↳` **context sub-row** with a short sentence (so "T0 completed" reads with what T0 *was*). Repo rows are absent entirely when the project is not a git repo. Headline: `project <tag-or-path> · <lastEvent or '—'> · last touched <date>`. **Always print the resolved project path** so the user can verify which project was read.
```
Recap — project cs · handoff · last touched 2026-06-25
| What | Where | Status |
|------|-------|--------|
| Current state: handoff — next-session opener embedded | SESSION.md | ready |
| Latest arc: native Space comment-replies | PROGRESS.md 2026-06-24 | in-progress |
| ↳ Space comment-replies: threaded replies under Space posts, native SwiftUI | | |
| Last commit: feat: tap-through routing for Space notifs | a1b2c3d | done |
| ↳ routed notification taps to the correct Space/post detail view | | |
| Working tree: 2 files modified, uncommitted | git status | open |
| Open: RenderPreview gate for comment-reply cell | PROGRESS open items | open |
```
**Output — all projects (roster).** Terse rows + one short in-flight fragment, **recency-sorted** (most-recent `SESSION.md`/`PROGRESS.md` mtime first). Cap visible rows (~8–12); summarize the tail as a `+N more (older)` row. Headline shows the total count only (recency-sort, no activity-tier thresholds).
```
Recap — all projects · 18 total
| Project | State | In flight | Touched |
|---------|---------|------------------------------|---------|
| cs | handoff | native Space comment-replies | 06-25 |
| aria | in-prog | recap project mode | 06-25 |
| df | wrapup | df-editor row affordance | 06-24 |
| … | … | … | … |
| +12 more (older) | | | |
```
- `State` = SESSION.md `lastEvent`, or `—` if no SESSION.md. `In flight` = a ~6-word fragment from the next-session prompt or the latest PROGRESS heading (blank if neither). `Touched` = most-recent mtime of SESSION.md / PROGRESS.md (MM-DD).
- **Escalation offer** (never auto-run): single-project → "Want a `/retrospect` on this for validation?"; roster → "Want a `/aria-assist` PM review across these?"
## Output — one consistent shape (all modes)
Emit a headline frame line, then the table:
```
Recap — {headline frame}
| What | Where | Status |
|------|-------|--------|
| {high-level item} | {file / area / skill} | {done / in-progress / open} |
| … | … | … |
```
- `What` = the change/action at a high level. `Where` = the file/area/skill touched. `Status` = done / in-progress / open.
- **Self-descriptive or annotate (all modes).** If a `What` cell isn't understandable on its own — a bare artifact name (`group G`, `T0`, a flag/config key, a ticket ID) — append a short detail clause (≤~8 words) so the row reads without prior context: `group G — 9 recap-mode contract assertions`, not `group G`; `T0 — auth-token refactor`, not `T0`. A cell that already reads plainly (`Bump version to 2.37.1`) needs no addition. This sharpens "Glance, not essay" rather than fighting it: every row must be understandable at a glance, which a bare token isn't. (Single-project mode does this via the `↳` context sub-row; terse roster rows use the inline `— detail` form to stay one line.)
- Git modes populate rows from commit subjects + changed paths; session/arc from the conversation/PROGRESS synthesis.
- Cap at ~8–12 rows; if more, add a final `| +N more … | | |` summary row.
- **Close with an optional offer** (never auto-run): "Want a `/retrospect` on this for validation?"
## Rules
- **Read-only — never write.** No logs, no files, no SESSION.md, nothing. `allowed-tools` excludes `Write`/`Edit` by design; honor it.
- **Orient, don't judge.** No verdicts, no validation, no per-fix scrutiny — that's `/retrospect`. Recap states what happened; it may offer to escalate.
- **Be honest about inference.** `pull` prints its resolved range; `arc` states its inferred boundary; `project` prints the resolved project path (single) or the roster total (all). Never present a guessed scope as certain.
- **Glance, not essay.** Keep the table scannable; summarize the tail rather than listing 40 commits.
- **Not `/handoff`.** Recap orients the current reader; it does not package state for a next session or write any artifact.
---
## /auto
# /auto — Drive an autonomous execution arc
**Cursor port — `/auto` runtime differences (intentional):** Cursor has no `CronCreate`, no `/statusline` usage payload, and no `auto-runloop.sh`. **D1** (usage window): ask rather than infer — there is no statusline to read. **D2** (scheduled resume): instruction-only; if a recurring prompt is needed, Cursor `/loop` is the analog, never CronCreate. **`self-restart` is a no-op** (the Claude Code wrapper is `claude -p` only). Standing directives D3–D7 still apply. `/preflight` still satisfies the commit gate. Rule 22 uses the edit-intent marker, not transcript deny.
Drive a piece of work end-to-end under the autonomous decision-routing posture, stopping only where a human decision is genuinely load-bearing. This is the *entry point* that wires together the process skills you already have — `brainstorming`, `/prospect`, `superpowers:test-driven-development` / `superpowers:subagent-driven-development`, `/retrospect` — into one continuous arc, so a single invocation runs the whole chain instead of you re-approving each step.
It does NOT re-define the decide-vs-ask policy. That policy is **Rule 35** (decision routing) in `template/rules/working-rules.md`, scaled by the **`autonomy`** config posture. `/auto` *applies* Rule 35 to a concrete arc and adds the operational discipline an unattended run needs: what to *never* stop for, how to read the binding budget, how to pick the next unit of work, and how (optionally) to self-perpetuate across usage resets. Distilled from real autonomous runs — the friction points below are ones that actually bit.
## When to use
- The user hands off a goal, plan, spec, ticket, or `SESSION.md`/handoff and signals latitude to run without per-step approval ("combined go", "continue autonomously", "go with your recommendation", "do as much as you can", "just build it", "take this and run", "run overnight"). A bare "go" alone is ambiguous — treat it as a `/auto` arc only when the surrounding context is clearly "drive this work autonomously," not when it's conversational ("go ahead and read that", "go with option B").
- After `brainstorming` or `/distill` concludes and the user says "ok, build it."
- At the start of a long or unattended arc the user wants driven to a durable checkpoint with minimal interruption.
**When NOT to use** (route to the right skill instead):
- One plan you want pressure-tested before any code → `/prospect`.
- Work that's already written/shipped and you want it validated → `/retrospect`.
- A session you're trying to pass off to the next session or a coworker → `/handoff`.
- A finished session with nothing pending → `/wrapup`.
`/auto` is the *driver*; those are the *gates and bookends* it calls. It doesn't replace them — it sequences them.
## Standing Directives — always on, never need asking for
These bind every `/auto` run in every mode. They are not modifiers and cannot be turned off.
- **D1 — Usage: the 5-hour figure binds; the 7-day figure is ignored.** When a statusline is
visible, gate only on the 5-hour number. The 7-day number is never a reason to slow,
shrink, defer, or stop. At **90%** 5h, arm or re-arm the resume schedule (Step 6). At
**95%** 5h, PAUSE: checkpoint, commit, then wait for the reset if a resume is armed, else
`/handoff`. When no statusline is visible (the desktop runtime reports an unreliable
figure), do not infer a number and do not gate on one — ask.
- **D2 — A scheduled prompt never starts with `/`.** Applies to every scheduling mechanism.
A leading `/token` is parsed as an unknown command and the whole mandate is silently
discarded. Lead with prose; name a skill mid-sentence if you must reference one. The
prompt must instruct the next scheduled run to start prose-first too. Enforced by
`bin/pre-cron-check.sh`, not by this paragraph — the prose form of this rule shipped once
and was violated twice afterward.
- **D3 — Foundational is always the answer**, unless the foundational fix would itself
derail the arc. Never take the patching branch to protect schedule (Rules 18 and 38).
Every firing of that carve-out is a D7 ledger entry.
- **D4 — Local commits only; push is never grantable.** No modifier — including `full` —
pre-authorizes a push. Push stays a legitimate stop in every mode.
- **D5 — Report the live model name at every checkpoint**, so a silent model swap is visible.
- **D6 — A non-blocking stop never idles the run.** Note it, keep working, surface it at
handoff.
- **D7 — The judgment ledger.** Any decision that could not be **Validated** (checked
against ground truth, not asserted), **Deterministic** (same inputs, same verdict for
anyone re-running it), **Traced** (the check is nameable and re-runnable), and
**Confirmed after** (what was predicted actually held once built) is logged. All four
hold → an ordinary `[DECISION]` line. **Any one fails → a ledger entry.** The ledger is a
filter over the `[DECISION]` trail, not a parallel system.
Write to `<knowledge_folder>/logs/auto/<YYYY-MM-DD>-<slug>-judgments.md`, resolving
`knowledge_folder` from `.cursor/aria-knowledge.local.md`. Create `logs/auto/` lazily.
Entry shape:
### J<N> — <the decision, one line>
- **Chose:** <what was done>
- **Alternative not taken:** <what was rejected>
- **Why not deterministic:** <which of the four tests failed, and how>
- **Would be falsified by:** <the concrete check that would prove it wrong>
- **Blast radius / reversal:** <files · commit · how to undo>
- **Type:** judgment | D3-carve-out
- **Disposition:** pending → accepted | revisit | reverted
At arc close the ledger is reported **first**, ahead of the landed-work summary, and the
user is explicitly prompted to review each entry (accept / revisit / revert); dispositions
are written back into the file. **Stamp a disposition the moment the answer arrives, not
at close** — an entry still reading "pending" after it has been answered misreports what
needs the user's attention, which is the exact cost this ledger exists to remove. An
answer can arrive obliquely: an instruction that keeps or widens the thing under review
resolves it as surely as an explicit accept. If the arc ends via a context wall, a scheduled handoff,
or a restart rather than a clean close, carry the ledger path in the `/handoff` opener and
`SESSION.md` so the resuming session surfaces it before starting new work. **An empty
ledger is stated, never omitted:** "0 judgment calls — every decision was deterministically
validated." Silence and zero must stay distinguishable.
## Step 0: Parse mode, posture, and the queue-complete toggle
`/auto` is an **explicit, in-the-moment grant of autonomous latitude** — invoking it *means* "drive this autonomously, now." It overrides the standing `autonomy` config for the duration of the arc and never changes that config. Four modes, three stackable modifiers, and a toggle:
| Mode | Trigger | What it does |
|---|---|---|
| **arc** (default) | `/auto <goal>` or `/auto arc <goal>` | Full chain: brainstorm → spec → /prospect → plan → /prospect → execute → /retrospect. The default whenever a goal is given without a mode keyword. (A **bare** `/auto` with no goal opens `config` instead — see Parsing.) |
| **execute** | `/auto execute <plan-path \| ticket-id \| "the plan">` | A plan/spec already exists. Skip ideation; run /prospect → build (TDD/SDD) → /retrospect. |
| **plan** | `/auto plan [<goal>]` | Produce a prospected, cold-executable plan and STOP. Runs brainstorm → spec → /prospect → plan → /prospect. **No code.** The mirror of `execute`. |
| **config** | `/auto config [<goal>]` | Guided setup: walk every run setting one at a time as a picker (so nothing has to be remembered), assemble the run-config, then drive the arc with it. Configures THIS run only — never persists (that's `/setup`'s job). See Step 0¾. |
**Modifiers** (stackable, any position, case-insensitive):
- **`full`** — maximum authority on every axis **except push**: tools/MCP/plugins
pre-approved · Workflow fan-out ON (default is hard-OFF) · cumulative subagent cap
10 → 30 · fan-out budget-fraction gate 25% → 40% · self-decide every
objectively-validatable fork. (**Arming a resume is NOT an authority grant** — it belongs
to the presence axis below, and `full` deliberately says nothing about it.) `full` is defined by its
boundary: **every grant except the one that leaves the machine** (D4). It **raises the
three Step 5 fan-out stopgaps but does not remove them** — raised, finite, still live,
because an unattended max-authority run is the case most exposed to unbounded spend, and
the budget-fraction gate is what protects D1's 95% pause.
*(There is deliberately no `preflight` mode, and it is **not** an alias for `config` any more.
It was one until v2.44.1, when `/preflight` shipped as a real skill — the executed pre-completion
checklist — and the two meanings collided head-on: the same word named a settings picker in one
place and a verification gate in another. Retired rather than repurposed, because every candidate
new meaning is already owned: "run the checklist" is `/preflight` (and an arc now runs it
automatically when the commit gate demands it), "check the plan before executing" is `/prospect`,
already in the chain. A third spelling would add a word and no capability — the same reasoning
that retired `loop`. Retired, **not** deleted: the parser must recognise `preflight` and redirect,
never let it fall through to a goal, since `/auto full preflight` would otherwise launch an arc to
build something called "preflight".)*
*(There is deliberately no `loop` modifier. An earlier draft had one meaning
"unattended + continue + self-restart", but once arming moved to the presence axis where it
belongs, `loop` reduced to a strict alias for `unattended continue` — adding a word and no
capability. It had already produced the drift a redundant word invites, claiming
resume-arming that `full` also claimed, and it was the worst of the mid-prose collisions:
`/auto fix the render loop bug`. The overnight run is `/auto full unattended continue` —
one word per axis, no special cases.)*
- **`attended` / `unattended`** — the **presence** axis: is a human reachable right now?
Two values, and **the arc contract always states which is in force**, because it changes
what happens to every question the run produces. Neither is inferred silently: if the
invocation does not say, `config` asks (Step 0¾ knob 7), and a bare run defaults to
`attended` — assuming someone is there is the safe error, since the cost is one surfaced
question rather than an hour of unreviewed autonomy.
- **`attended`** — a **non-blocking** residual is surfaced **immediately** rather than
noted and batched to the handoff. D6 ("a non-blocking stop never idles the run") is
thrift when you are asleep and waste when you are at the desk: an answer worth ten
seconds of yours can otherwise cost an hour of second-best work. `attended` narrows D6,
it does not repeal it — the run still never *idles* waiting, it asks and keeps working
and takes the answer when it arrives. Blocking residuals halt as always.
- **`unattended`** — nobody is reachable. Non-blocking residuals are noted and carried to
the handoff (D6 unchanged), and a resume that fires does so **silently, without**
expecting anyone to see it. Presence does **not** decide *whether* a resume is armed —
unfinished work at the usage wall does (Step 6). Under `attended`, the same resume is
armed and simply **announces itself** when it fires.
**Two different walls need two different mechanisms — do not conflate them:**
| Wall | Mechanism | Effect |
|---|---|---|
| **Usage** — the 5h window is exhausted | the **resume schedule** (Step 6) | Re-fires *this* session after the reset, local work intact |
| **Context** — the window hits 90% | **`self-restart`** + `bin/auto-runloop.sh` | Relaunches a **fresh process** with a clean window |
A resume schedule cannot rescue a context wall (it re-enters the same full session), and a
fresh process does not help when the limit is usage. `unattended` arms the resume for the
usage wall; the context wall needs `self-restart` **explicitly**, because it cannot be
made to work implicitly. **`self-restart` is inert unless the external
wrapper is already running** — invoked directly in a normal session it writes the restart
signal and stops with nothing to consume it, so say so rather than implying the run is
self-healing.
- **`tickets`** — tracker-bound. Work selection comes from the connected tracker by
priority; comment on the ticket at every commit; never claim a ticket without verified
validation.
**Resolving the tracker — never hardcode a vendor.** Probe at runtime for a connected
`~~project-tracker` MCP and adapt, as `/digest` does: Linear · Asana · Atlassian/Jira ·
Monday · ClickUp · Notion-as-tracker · GitHub Issues. Probing is prose-only; there is no
helper API (ADR-015). If `ticketing_plugins` is set in `.cursor/aria-knowledge.local.md`
(comma-separated `tag:plugin-command` pairs, read directly from the file the way
`/audit-knowledge` reads it), it wins — that is the user's explicit declaration.
**Never verify that a mapped command is actually installed:** enumerating installed
plugins couples this skill to runtime internals that can change, and a loud failure at
invocation beats a silently-absent hint. Detect ticket IDs with the vendor-neutral `\b([A-Z]{2,}-\d+)\b`. With
no tracker connected and no mapping, say so once and fall back to the Step 4
work-selection order — `tickets` never hard-fails an arc.
**Three orthogonal axes — set each independently.** `full` sets *how much latitude*;
`continue`/`stop` set *how long*; `attended`/`unattended` set *whether a human is reachable*.
Keeping them separate is what makes every combination expressible:
- `/auto full` — max authority, scoped: stops when the queue clears
- `/auto full unattended continue` — max authority, overnight: never idles, nothing is asked,
resume armed for the usage wall (add `self-restart` only if the wrapper is actually running)
- `/auto full attended` — max authority and you are at the desk: everything pre-approved
except what genuinely cannot be determined, and *that* reaches you the moment it arises
- `/auto attended` — default authority, but residuals come to you live rather than at handoff
**On-queue-complete toggle** (a trailing `continue` or `stop` keyword, default **stop**): what to do once the *planned* queue is done.
- **`stop`** (default) — checkpoint + `/handoff` when the queue is clear; do NOT pick up new work. The right choice for a scoped "just do X" run. Default to this if unset — don't over-reach the remit.
- **`continue`** — keep finding the next valuable work autonomously (see Step 4); for unattended / overnight runs. Don't stop at the arc boundary.
**Context-self-restart flag** (a trailing `self-restart` keyword, default **off**): only meaningful with `continue`. When set, a context-window wall does NOT terminally stop the arc — instead the skill writes a restart-signal file that the external `bin/auto-runloop.sh` wrapper watches, so the arc resumes in a FRESH process (clean context). See Step 3¾. Requires the wrapper to be running and a permission allowlist (the wrapper spawns `claude -p --dangerously-skip-permissions`, which the auto-mode classifier blocks unless allowlisted). Without the flag, a context wall behaves exactly as today (terminal stop + `/handoff`).
**Parsing.** If the first arg case-insensitively matches `arc`, `execute`, `plan`, or `config`, that's the mode; otherwise the mode is `arc`.
**`preflight` is a RETIRED mode keyword and must never fall through to a goal.** It used to alias `config`. If the first arg is `preflight`, do **not** start an arc with the goal "preflight" — recognise it, run nothing, and route: the pre-completion checklist is the standalone **`/preflight`** skill; the per-run settings picker is **`/auto config`**. Falling through here would be the worst outcome available — under `full`, a retired word silently becomes a work order.
**Modifiers are recognised only at the ENDS — never mid-prose.** Scan the contiguous run of modifier tokens at the start (after any mode keyword) and the contiguous run at the end; **once goal prose begins, every remaining token is goal.** This matters because the modifier names are ordinary English words: an anywhere-in-args scan turns `/auto fix the **render loop** bug` into an unattended self-restarting run, and "do a full review" or "close the tickets" the same way. Worked cases:
- `/auto full unattended tickets clear the payments queue` → mode `arc`, modifiers `{full, unattended, tickets}`, goal "clear the payments queue"
- `/auto fix the render loop bug` → mode `arc`, **no modifiers**, goal "fix the render loop bug"
- `/auto ship the CSV exporter continue self-restart` → goal "ship the CSV exporter", toggle `continue`, flag `self-restart`
A trailing `continue`/`stop` sets the on-queue-complete toggle; a trailing `self-restart` sets the context-restart flag (honored only alongside `continue`).
**A bare invocation falls through to `config`.** When there is **no mode keyword and no goal text**, open the guided picker (Step 0¾) rather than inferring a goal. **Modifiers and toggles do not count as goal context** — they say *how* to run, never *what* to run — so `/auto`, `/auto full`, `/auto unattended` and `/auto full continue` all open the picker, **pre-seeded** with whatever was typed so nothing is re-asked. The alternative is guessing a goal from `SESSION.md`, which is precisely the stale-resume hazard "Verify before you trust" warns about: a saved prompt may describe work already shipped. When a goal *is* named as "continue from SESSION.md / the latest handoff," resolve it through Step 4's work-selection order as before.
**Two ways to handle unspecified settings — you remember nothing either way:**
- **Default (`/auto [goal]`)** — pick the safe default for everything unspecified and **surface them all in the arc contract** (Step 0.5) before driving. You *react* to the shown list; you never have to *recall* what's configurable. Minimal friction.
- **Guided (`/auto config`)** — when you'd rather set the knobs deliberately, the walkthrough (Step 0¾) **presents each one as options, one at a time**, so the option list lives in the picker, not your memory. You opt into this per run.
**Relation to the `autonomy` config.** The standing `autonomy` config (`default`/`balanced`/`autonomous`, owned by `/setup`) sets how autonomous I am on sessions where you *didn't* invoke `/auto`. `/auto` is the in-the-moment override for *this* arc — it runs at full self-decide latitude regardless of the config value, including at `autonomy: default`. You never need to flip the config to use `/auto`; the invocation is the grant. Don't bother reading the config value — it can't change `/auto`'s behavior, and the arc contract (Step 0.5) already announces the autonomous posture, so a separate "your standing default is X" note would be redundant ceremony. `/auto` never writes the config either — changing the standing posture is `/setup`'s job, exclusively (one writer, no drift).
## Step 0¾: Guided config walkthrough (`config` mode only)
Runs ONLY when invoked as `/auto config`. Skip entirely for `arc`/`execute`. Purpose: let the user set each run setting deliberately **without having to remember any of them** — present each as a picker, one at a time, with the safe default pre-marked. Use the platform's question/picker affordance (one question per knob); accept a bare-number/keyword reply; "skip" on any knob takes its default.
Walk these in order, **one at a time** (do not dump all seven at once — the point is recognition-not-recall, one decision per step):
1. **Goal / source** — this prompt's goal · continue from `SESSION.md`/latest handoff · a plan path · a ticket ID from your connected tracker. (If a goal was passed as `/auto config <goal>`, pre-seed it and confirm.)
2. **On-queue-complete** — `stop` (scoped: checkpoint + /handoff when the queue's clear — *default*) · `continue` (keep finding new work; for unattended/overnight).
3. **Push policy** — commit local, no push (*default*) · commit + push per host convention. (Push remains an ungranted-approval stop regardless — this only sets the intent.)
4. **Fan-out / subagents** — inline-only · bounded individual subagents, ~10 cumulative cap (*default*) · raise the cap to N · allow the Workflow swarm (multi-agent orchestration). (Maps to the Step 5 stopgaps.)
5. **Budget ceiling** — default 25%-of-remaining-window per fan-out burst (*default*) · a different fraction · a hard "stop the arc at X% usage." (Maps to the Step 5 budget-fraction gate + a live abort floor.)
6. **Resume at the usage wall** — arm (*default when the goal is non-trivial*) · off. If the 5-hour window runs out with the goal unfinished, a resume fires **+5 min after** the reset and picks the work back up (Step 6). **Not gated on #2 or #7** — an unfinished goal warrants a resume whether or not you asked for new work afterwards, and whether or not anyone is watching; presence only decides if it announces itself.
7. **Presence** — `attended` (*default*: you are reachable, so a non-blocking residual is surfaced immediately rather than batched to the handoff) · `unattended` (nobody is reachable; residuals are carried to the handoff and the run arms its own resume). **Always ask this one** — it is cheap to answer and it changes what happens to every question the arc produces, so a run should never proceed without knowing it. Pre-seeded from the invocation when `attended` or `unattended` was passed.
After the walkthrough, assemble the picks into the run-config and proceed to Step 0.5 — the **arc contract is then a confirmation of what you just chose**, not a fresh set of defaults. **Nothing persists**: these picks configure THIS arc only; the standing `autonomy` posture and any saved defaults are untouched (changing standing defaults is `/setup`'s job — `/auto` never writes config, in any mode).
## Step 0.4: Load the standing user rules
Before stating the contract, load the user's own standing rules so the whole arc runs aware of them — `/auto` must never drive an autonomous arc blind to the rules the user has already written down. This is separate from Rule 35: Rule 35 (in the plugin-managed `working-rules.md`) is the decide-vs-ask *routing* policy; `user-rules.md` is the user's own *substantive* rules (how they want work done — commit/push discipline, design taste, rejection criteria, ARIA-behavior preferences). `/auto` applies both.
1. **Resolve + read.** Read `.cursor/aria-knowledge.local.md` and extract `knowledge_folder` (same resolution the `/rules` skill uses). Read `{knowledge_folder}/rules/user-rules.md` if it exists. It is **optional and user-owned** — it may be absent (pre-v2.8.1 setups) or present with no `### U` rules yet. If absent, or present but empty of rules, treat it as **no standing user rules** and continue — a missing file is never a stop.
2. **Hold as active constraints for the arc.** When real user rules are present, treat each as a **binding constraint applied wherever it is contextually relevant** across the whole arc — not just consulted once. A user rule about pushing gates the push step; one about design taste informs execution and review; one about rejection criteria shapes what you accept as done. This is stronger than "be aware": where a user rule speaks to the situation at hand, it **governs**.
3. **The one carve-out — validated proof with critical impact wins.** A user rule does NOT govern in the narrow case where applying it would be **actively detrimental** or would **contradict validated empirical proof whose violation carries critical impact** (a safety, correctness, or data-loss consequence you have actually verified against ground truth — not a hunch, and not mere inconvenience). This mirrors the skill's "verify before you trust" discipline: ground truth you have confirmed outranks a standing assumption. When this carve-out fires, do NOT silently override — treat the conflict as a **legitimate stop** (Step 2): surface the specific user rule, the validated proof that opposes it, and the critical impact, and let the user resolve it. The carve-out is an escalation trigger, not a license to ignore a rule.
4. **Compose, don't duplicate.** `/auto` *loads and applies* user-rules.md; it does not restate the rules' content in the report, re-implement `/rules`, or fork the file. If the user asks *what* their rules are mid-arc, defer to `/rules`. This step is purely: make the arc operate under them.
Loading is one read at arc start; the rules then travel with the arc the same way Rule 35 does.
## Step 0.5: State the contract before driving
Before the first action, post a short **arc contract** so the autonomy is legible — the user should never be surprised by what you decided alone vs. what you'll stop for:
> **Arc:** <one-line goal> · **Mode:** <arc | execute> · **On-complete:** <continue | stop>
> **Standing rules loaded:** user-rules.md — <U1…UN applied where contextually relevant | none (absent or no rules yet)> (from Step 0.4).
> **I'll decide myself:** objectively-validatable forks (checked against real code/corpus/docs, held to Rules 13/14/18 — simplest/robust/clean, no unneeded abstraction).
> **I'll handle without stopping:** knowledge placement, tool/permission approvals, backlog/deferral, ticket filing, the normal commit cadence, and running `/preflight` when the commit gate requires it (see Pre-answered below).
> **I'll stop and ask on:** product/UX taste with no objective answer · an irreversible/outward-facing action not covered by policy *that blocks the task* · a true no-visibility fact only you have · a genuine costly fork empirical investigation can't decide.
> **Gates that run but don't count as stopping:** /prospect (pre-code), /retrospect (post-build).
> **Presence:** <attended — non-blocking residuals come to you as they arise | unattended — residuals batched to the handoff, resume armed>. Always stated; never inferred silently.
> **Usage:** gating on 5h only; 7d ignored · arm at 90% · pause at 95% (D1).
> **Push:** local commits only — never pre-authorized by any modifier, including `full` (D4).
> **Tools:** MCP / plugins / skills pre-approved.
> **Foundational:** always preferred; any carve-out is logged (D3 → D7).
> **Judgment ledger:** `<resolved path>` — reported first at close, for your review (D7).
> **Model:** <live model name> — re-reported at each checkpoint (D5).
This contract is the operative form of Rule 35's routing table for *this* arc. You don't re-derive the table — you instantiate it.
## Verify before you trust (the #1 rule — most friction traces here)
Before acting on **any** assertion — a resume prompt, a handoff, a stale doc, your own memory, a cached metric, a `SESSION.md` "next" line — **verify it empirically against the live source.** Run `git log`/`git status`, read the live state file, grep the real code. A resume prompt may be stale (the work is already shipped — don't redo it); a cached usage % may be stale (window reset → trust the reset *timestamp* vs now, not the number). For backend behavior, API contracts, or field shapes, read the source-of-truth repos FIRST (backend + the shipped client) — never assume, and don't file a "backend gap" until the repos genuinely don't answer it. This is the discipline that earns the right to self-decide: "validated" means checked against ground truth, not asserted.
## Pre-answered — handle and keep going (do NOT stop for these)
Rule 35 says route by question type; these are the recurring autonomous-run cases pre-routed to **act**, so an unattended arc doesn't stall on them:
- **Knowledge placement** — never pause to ask *where* something goes. Make it durable in the best location you can judge (memory · /prospect+/retrospect log · contract doc · CLAUDE.md + PROGRESS). Unsure → drop it to the general intake backlog for a future audit to sort. Placement is never a stop.
- **Tool / MCP / permission approvals** — assume the build/test/lint, sim, git, Cron, MCP, and skill verbs are pre-approved (a companion allowlist in the user's `.claude/settings.local.json` makes this real at the harness level — see Notes). If one tool is genuinely blocked, route to the working alternative and note it. Only OS-level GUI popups need a live human — flag once and route around.
- **Backlog / deferral** — a known follow-on (out-of-scope feature, separate-team backend change, device-gated smoke) → file/note it and DEFER. Don't stop to ask whether to defer.
- **Tickets** — create freely in the connected tracker: status backlog/Undefined, assigned to the user for post-session review, both intakes present (Technical Intake marked DRAFT), enriched via comments. Never stop to ask whether/how to file.
- **Known-pattern git/scope** — stage named in-scope files, commit, push (per the contract's push policy). Don't ask permission for the normal commit cadence.
- **A preflight-gated commit** — if the commit gate would deny (`preflight_gate: deny`, or a `preflight_deny_repos` / `preflight_deny_paths` match), run `/preflight` for real and proceed; a recorded verdict of any kind satisfies the gate. Never ask whether to run it, never fake the marker, never disable the gate, and never let three denials degrade it. See Commit discipline.
- **Self-recommended chain choices** — a spec/prospect/plan fork a recommendation already answers → take the recommendation. "Self-recommended + answerable" is not a stop.
## Step 1: Drive the arc
Run the chain by **invoking the real skills** via the `Skill` tool, not by summarizing them. Composition keeps the gates honest: the quality checks are the actual checks, and improvements to those skills flow through automatically.
**Degrade gracefully when a composed skill or tool is absent.** `brainstorming`, `writing-plans`, `test-driven-development`, and `subagent-driven-development` are Superpowers skills (strongly recommended, optional). If one isn't installed, name what's missing, fall back to doing that phase inline (a plain brainstorm, a hand-written plan, manual red-green-refactor), and say the gate ran in degraded form. The `execute <ticket-id>` path needs a connected project-tracker MCP — if unavailable, ask the user to paste the ticket rather than proceed on a missing plan.
### arc mode — full chain
1. **Brainstorm** (`superpowers:brainstorming`) — only if the *shape* of the solution is a real open question. A concrete plan or tightly-scoped goal skips straight to spec (Rule 35: don't deliberate what's already answered).
2. **Spec** (`superpowers:writing-plans` or `/distill`) — surface every autonomous design decision as an explicit `[DECISION]` line so the next gate can ratify it; that's how self-approval stays auditable.
3. **/prospect** the spec/plan. Apply PROCEED-WITH-CHANGES amendments **in place, now**. A KILL/DEFER verdict on a load-bearing step *is* a stop — surface it.
4. **Plan** — if the spec isn't already a cold-executable plan, write one.
5. **/prospect** the plan only if it materially differs from the spec you already pre-mortemed (re-prospecting an unchanged artifact is ceremony, not a check).
6. **Execute** (`superpowers:test-driven-development`, or `subagent-driven-development` for independent multi-task plans). Per-edit Rule 22 still fires — that's the execution-time scope check, separate from the plan-level gates.
7. **/retrospect** the shipped range. Fix what it surfaces if objectively-validatable; surface what it can't resolve.
**Mechanical / contract-driven change** (no design judgment) → skip brainstorm/spec and just build it — still test + gate.
### execute mode — plan exists
Skip steps 1–2. Resolve the plan source (path → `Read`; ticket ID → tracker MCP fetch if available; quoted string → treat as the plan), then run 3 → 6 → 7.
### Verification reality — verify for real, classify honestly
Use the project's **real working verification path** (e.g. RenderPreview for SwiftUI; a live round-trip vs staging where reachable; the actual app, not just unit tests) to confirm the build does what it should. Be HONEST about what's device-/GUI-gated vs headlessly verifiable — **classify it, never fake or silently skip.** Unit-tested + render-verified with only an OS-delivery slice left unobserved is a *documented residual*, not a pass to claim and not a failure to hide. A live end-to-end check is the only thing that proves model == backend; fixtures only prove fixture == model.
### Commit discipline (per task)
Each task = **one atomic commit**. Gate BEFORE committing: run the FULL suite + build + lint as the **bare exit code**, READ green, THEN commit — never chain `&& commit` after a non-test command (a `| grep`/typecheck between the suite and the commit swallows the test exit and commits red). Run ALL relevant gates; they cover disjoint surfaces (app build ≠ test-target compile). Commit only in-scope **named** files (`git add <paths>`, never `-A`); verify `git status` first (parallel sessions may have dirtied the tree). Push only per the contract's push policy; **never force-push**, and verify the ahead-count returns to 0 after pushing.
**The preflight commit gate — satisfy it, never route around it.** A user may configure `pre-commit-preflight-check.sh` to DENY commits (`preflight_gate: deny`, a `preflight_deny_repos` substring matching the target repo, or a `preflight_deny_paths` glob matching a staged path — all read from `.cursor/aria-knowledge.local.md`). Under an autonomous arc that is not an obstacle to work past: **before the first commit of the arc that the gate would deny, actually run `/preflight`** on the work about to be sealed, then commit. The marker is session-scoped, so one genuine run clears the gate for the remainder of the arc — this costs one checklist, not one per task.
Three ways to get this wrong, all forbidden:
- **Never write the marker file** (`${TMPDIR}/aria-preflight-<session_id>`) or otherwise fake a recorded run. That is bypassing a gate, not passing one, and it is exactly the ungranted-authorization case `/auto` must never self-issue.
- **Never flip `preflight_gate` to `off`, and never edit the user's config to widen your own permissions.** `/setup` is the sole writer of those keys.
- **Never let the circuit breaker do the work.** Three consecutive denials degrade the gate to allow-with-warning for the whole session — so "just keep retrying" doesn't stall the arc, it silently *disables the user's gate* and then proceeds. A degraded gate is a worse outcome than a stopped arc.
A recorded **FAIL** verdict satisfies the gate too, and recording one then proceeding is a legitimate, visible choice — the failure this guards against is not running the checks at all. So a preflight FAIL is **not** a stop: record it, note it in the arc's judgment ledger, and keep going unless the finding is itself load-bearing. Re-run `/preflight` at Step 8 before reporting the arc done — that is the moment the skill actually exists for.
**Throughout:** apply Rule 35 at every fork — investigate the resolvable parts first, then surface only the residual that's genuinely about the user. When you surface one, present concrete options + a recommendation (label A/B for a terse reply), then continue from the pick without restarting the chain.
## Step 2: The stop-rule and checkpoints
**Run to a durable checkpoint, not to exhaustion.** A durable checkpoint is committed/persisted work that survives a fresh session — not a mid-edit pause. You don't need permission to *continue past* a checkpoint under an autonomous grant; you report at it.
**Legitimate stops — the ONLY reasons to ask** (everything else has a pre-answered default above):
- **Product / UX taste with no objective answer** (e.g. "should threads nest or flatten?") — design direction is the user's.
- **An action needing approval not already granted** — an irreversible or outward-facing op not covered by policy (a **push** beyond the contract's push policy, a **prod deploy**, external comms, a **destructive op** / deleting non-recoverable data, a **shared-DB migration**), a **scope change** beyond the stated goal, or **credentials / prod-data access**. BUT only HALT if it *blocks* the current task — if it's non-blocking, NOTE it and CONTINUE other work; never idle the whole run on a side-question. (Surface all noted items at `/handoff`.)
- **A true no-visibility fact only the user has** (a constraint not in any repo/doc; a teammate conversation).
- **A genuine fork where both branches are plausible AND the wrong one is costly AND empirical investigation can't decide it.**
**A fork is a stop only AFTER investigation can't decide it — measure before escalating.** The 4th bullet is the trap: "load-bearing fork" reads like a license to stop, but most forks that *feel* load-bearing are just unfinished investigation. Before calling anything a stop, ask "is this an empirical question I can resolve myself?" (Rule 35: investigate the resolvable, ask only the residual.) In particular, a **reactive fix-forward cascade** — the same failure recurring as you patch each instance (model N, then N+1, then N+2…) — is a **smell, not a fork**: step back and probe whether one upstream change dissolves the whole class, pick the objectively-better option, and continue. Don't sink cycles into the reactive path and *then* escalate the choice you could have decided by measuring. Escalate only the genuine residual the 4th bullet describes: both branches plausible, the wrong one costly, AND investigation genuinely can't decide.
A safety-classifier block is never routed around — pivot to a safe local path and report.
## Step 3: Budget — check the LIVE statusline between every task
Know **which budget binds**, because it decides the right resume tool:
- **Context** → at 90%, AUTO-run `/extract` (no judgment — its dedup handles "nothing new"), then keep going. Context-bound work CAN'T be saved by a cron (a cron re-enters the same full session) → `/handoff` to a durable on-disk opener for a fresh session.
- **5-hour / 7-day usage** → keep working toward the limit. Usage-bound work CAN be continued by a session-only cron at the reset boundary (it re-fires *this* session with local work intact).
- Don't gate, defer, or shrink an action on a number you haven't re-read **live this turn** — read the statusline state file, not a stale hook-alert figure (a window may have reset). If you're not gating on budget, don't mention it.
## Step 3¾ (optional): Context-self-restart across a fresh process
Default OFF. Active **only** when this is a `continue` run **AND** the `self-restart` flag was set. Without both, a context wall behaves exactly as the Step 3 Context bullet describes (extract → `/handoff` → terminal stop) — unchanged.
The problem this solves: an unattended `continue` arc that hits the context wall would otherwise halt until a human restarts it. A cron can't fix this (a cron re-enters the *same* full session). The skill itself **cannot** reset its own context either — `/clear` is a REPL built-in that **neither a skill nor a hook can issue** (both verified). The only autonomous path to clean context is a **fresh `claude` process**, which an external wrapper provides.
So when active, at 90% context — instead of terminally stopping — do this and then **stop cleanly**. The skill never issues `/clear` (it cannot — and even if it could, the wrapper's fresh process is the cleaner reset); you do NOT self-resume; you hand the restart to the wrapper:
1. **AUTO-run `/extract`** (same as the default Context path).
2. **Run `/handoff`** to produce a full, self-sufficient, **prose-first** next-session opener at `SESSION.md`. Prose-first is mandatory — the opener must NOT start with a slash command (a leading `/auto` is parsed as an unknown command and the whole mandate is silently discarded); `/handoff`'s opener already leads with prose.
3. **Write the restart-signal file** `<cwd>/.claude/auto-restart-requested` containing **one line: the absolute path to that opener**. Presence of the file = "restart requested"; its content = the opener the wrapper relaunches with. (Mark the write site `[SELFRESTART-PRE]` so a later `/retrospect` can confirm it fired.)
4. **Stop cleanly.** The arc is now a durable on-disk checkpoint; the in-process work is done.
`bin/auto-runloop.sh` (shipped with the plugin) is the external piece: it launched this run, watches for the signal file on exit, consumes it (so a crash can't loop forever), and relaunches a **fresh** `claude -p` headless process with the opener. **The wrapper must already be running** for this to do anything — if `/auto` was invoked directly in an interactive REPL (no wrapper), `self-restart` still writes the signal and stops, but nothing restarts it; note that in the handoff. **Permission caveat:** the wrapper spawns `claude -p --dangerously-skip-permissions`, which the auto-mode classifier blocks unless the user has added a Bash permission allowlist rule for it — surface this when recommending an unattended run.
## Step 4: Work selection (and the On-queue-complete toggle)
**Always validate before executing** (hard gate, every queued item): re-validate the plan + the live state + staleness before starting. `git log` to confirm the item is still un-done (don't redo shipped work); re-read the spec against current code; re-/prospect if the plan is old or the code moved under it. Only execute once validated current.
**Work the existing queue in its intended order:** `SESSION.md` "Next session prompt" / handoff opener → the project's prospected plan/spec → PROGRESS "NEXT" → an existing TODO/ROADMAP/backlog — each through the validate-before-executing gate.
**When the planned queue is complete, obey the toggle:**
- **`stop`** (default) → do NOT pick up new work. Leave a verified-clean checkpoint + `/handoff`.
- **`continue`** → find the next valuable work autonomously, in order: (1) explicit "NEXT/deferred" in CLAUDE.md/PROGRESS; (2) a follow-on the just-finished retrospect surfaced; (3) the next roadmap/backlog item; (4) a `/readiness-audit` or `/retrospect` to surface the next thing; (5) cheap durable prep (contract traces, specs for queued features) that advances a future arc without a taste call. **Never invent a feature** — if nothing explicit is ready, do high-certainty objectively-valuable work that needs no taste call (strengthen the green baseline; `/codemap` or doc-sync if stale; trace + spec the next likely feature, left prospected; a `/readiness-audit`; close now-doable residuals). If even that is exhausted → `/handoff` with "no queued work — awaiting direction" rather than spinning.
If the next unit needs more context headroom than remains → STOP at a clean checkpoint and `/handoff` rather than fragment it. (Exception: on a `continue` + `self-restart` run, take the Step 3¾ path instead — checkpoint, write the restart-signal, and let the wrapper resume in a fresh process.)
## Step 5: Subagents & fan-out — budgeted, with hard stopgaps
DEFAULT to doing the work **inline**. A single agent (you) with inline tools is the efficient baseline; subagents are a deliberate, budgeted escalation. **NEED-IT gate before spawning any subagent:** legit reasons = (a) a broad fan-out read whose raw output would bloat your context but you only need the conclusion (delegate the search, keep the answer); (b) genuinely independent parallel work with no shared state; (c) an adversarial/second-opinion check. NOT legit = "be thorough," a single-file lookup you can do yourself, or work with sequential dependencies. Every subagent costs *your* context (dispatch + returned summary) even though its own tool output stays in its context — so delegate to SAVE context, never to spend it; require a tight structured result. Escalate, don't pre-commit: start inline/single-agent, widen to parallel only if the first pass proves it needs the breadth.
The NEED-IT gate is a *per-spawn quality check* — it is NOT a cumulative budget ceiling. 90 individually-justified spawns still drain the window. So THREE HARD stopgaps sit ON TOP of the NEED-IT gate, each covering a distinct runaway axis (count-in-one-burst · spend-in-one-burst · count-over-time). They are about *aggregate spend and blast radius*, not per-spawn merit. All thresholds below are built-in defaults; the user may override any of them in the invocation (e.g. `/auto … workflow`, `… fanout=40%`, `… agents=20`). There is no standing config key for these today — they are invocation-scoped (a persistent default would belong in `/setup`, not invented here).
- **Workflow is opt-in only — hard OFF by default** (caps one-shot *count*). The Workflow tool (multi-agent orchestration; fans out dozens at once) does NOT fire unless the user's invocation explicitly opted into it (`/auto … workflow`, or a clear "use a workflow / fan out agents / orchestrate this with subagents" in the request). A bare `/auto` — even an unattended `continue` run — runs inline + bounded individual subagents ONLY. The 90-agent sweep cannot happen unbidden. Non-negotiable invariant, not a judgment call.
- **Budget-fraction pre-flight gate** (caps one-shot *spend*). Before launching ANY fan-out (a `parallel()`/`pipeline()` over many items, an N-way audit/research sweep, or an opted-in Workflow), read the **live** remaining usage (the statusline state file — never a stale number, per Step 3) and *estimate* the fan-out's cost. If one shot would spend more than **~25% of the remaining usage window** (default; overridable via `fanout=<pct>`), STOP and surface it: the planned fan-out width, the estimated spend, the remaining window, and options (proceed · shrink to a smaller batch · serialize · skip). This is the direct fix for the "a single wide task drains the budget *between* the between-task checks" hole — the gate fires *before* the spend, sized to what's actually left, so it tightens as the window depletes. A fresh window may permit a wide sweep; the same sweep at 70%-used is refused.
- **Cumulative per-arc subagent cap** (caps *count-over-time* — the drip case). Maintain a running count of total subagents spawned this arc. After **~10 total** (default; overridable via `agents=<N>`), STOP and re-confirm before delegating more — report the count, what they accomplished, and the remaining work, then let the user raise the cap or switch to inline. This catches the slow drip the budget-fraction gate misses: many small individually-justified spawns over a long `continue` run, none of which trips the per-burst gate but which sum to a real drain. The counter is per-arc and resets only on a new `/auto` invocation.
These three are orthogonal — Workflow-opt-in bounds a single huge swarm, the budget gate bounds one expensive burst, the cumulative cap bounds slow accumulation. A run can pass any two and still be caught by the third.
**Grain, once you DO fan out: one agent per shared-context cluster, not per item.** The stopgaps above bound *whether/how much* to delegate; this bounds *how to carve the work* once you're delegating. The intuitive "one agent per task/item" is usually the wrong default — it pays twice: **(1) redundant context re-loading** (N agents each re-derive the context their items share — that spends the very context delegation was meant to save), and **(2) seam-blindness** (a per-item agent can't see its neighbor items, so it makes locally-reasonable, globally-wrong choices at the seams — divergent identifiers, missed cross-item constraints). So the default grain is **one agent per cluster of items that share a context** — a file neighborhood, a repo, a document, a domain — keyed on *what the items share*, not *how many* there are. Split down to per-item ONLY when one of three forces requires it: **write-contention** (two agents would edit the same file set concurrently → serialize in one agent or split into separate waves), **per-item auditability** (the work needs a clean per-task review gate a blended cluster report would obscure — code with compounding failures earns this; inline content usually doesn't), or **context-ceiling** (the cluster won't fit one agent's working window with accuracy to spare — past which you just move seam-blindness inside one agent). Absent one of these, cluster — and if you split, say why.
## Step 6 (optional): Self-perpetuating run via resume cron
**Arm whenever work remains unfinished and the binding budget is usage, not context** (context-bound cannot be resumed by a schedule — see Step 3). **Arming is NOT gated on presence, and NOT gated on `continue`.**
That distinction is load-bearing and was wrong in v2.43.0. `continue` governs whether to find **new** work once the planned queue is clear — it says nothing about **finishing the goal you were already given**. Gating arming on it stranded the common case: a scoped `/auto full attended <goal>` that hit the 95% pause mid-goal simply stopped, goal unfinished, with nothing scheduled to pick it up. The question that decides arming is **"is the work I was given actually done?"**, not "will there be more after it?" and not "is anyone watching?".
**Presence changes only how the resume behaves, never whether it exists:** an `unattended` resume fires silently, without expecting anyone to see it; an `attended` resume **announces itself** when it fires, so you know work restarted while you were away from the keyboard. Both are armed on the same condition.
**Mechanisms are gated on availability first, capability second.** `CronCreate` is the **baseline and the default**: it is the only one present in **every runtime**. Its session-only nature is an accepted constraint, not a defect — an unattended run keeps its session open by design.
| Mechanism | Available | Survives session death | Fresh context | Use for |
|---|---|---|---|---|
| **`CronCreate`** — the default | **Always, every runtime** | No — **session-only**, in-memory; recurring auto-expires at 7 days; fires only while the REPL is idle | No — re-enters this session | Usage-bound resume with the session left open. The normal unattended run. |
| **A persisted scheduled task** | **Desktop runtime only** — probe, never assume | Yes — runs at next app launch if missed | Yes | A resume that must survive the session ending, where the runtime offers it |
| **launchd** (the `pm-schedule.sh` pattern) | macOS only; user opts in | Yes — OS-level | Yes — a fresh `claude` invocation | Truly session-independent recurring work on the CLI |
| **`bin/auto-runloop.sh`** (`self-restart`) | Wrapper must already be running | Wrapper-dependent | Yes — a fresh `claude -p` process | A context wall mid-arc (Step 3¾) |
**Selection rule.** Default to `CronCreate`. Reach past it only when the resume genuinely must survive the session ending — and then **probe what this runtime actually offers** rather than naming a mechanism the user may not have. State which one you chose and why. **Never promise durability the runtime cannot deliver.** The mechanisms are not substitutes: a schedule resumes work at a *time*, `self-restart` recovers from a *context wall*.
**Do not pass `durable: true`** — the tool documents it as having no effect; all jobs are session-only.
Arm it EARLY and re-arm at or before 90% usage — never wait until the end (the session can die first and break the chain): `recurring:false`, fire **5 minutes AFTER the next 5-hour reset boundary** (NOT at the exact reset minute — firing at the boundary risks landing before the window has actually reset/propagated, re-firing into a still-exhausted window and breaking the chain; the +5-min guard band ensures the new window is live). The prompt = a compressed mandate + "VERIFY STATE FIRST, this prompt may be stale" + "re-create this same cron for the next cycle before stopping (again +5 min after the following reset)." **The cron prompt MUST lead with prose — never start it with a slash command** (a leading `/auto` or any `/command` is parsed as an unknown command and the whole mandate is silently discarded; phrase the mandate in prose and, if you must reference a skill, name it mid-sentence). This is the same prose-first hazard as the Step 3¾ restart opener — one rule, two arming sites. Arming a cron is part of the autonomous remit when the user asked for a self-perpetuating run; it is NOT something to do silently on an ordinary scoped arc.
## Step 7: Knowledge capture (as you go — durable, best-guess location, never blocking)
Write memories / `/prospect`+`/retrospect` logs / contract docs **at the moment of learning**. Update CLAUDE.md Status + PROGRESS each milestone (lead with the new state, demote prior detail under a pointer, never delete). Save a stated design/process decision as a memory immediately, with the WHY. Unsure where it goes → general backlog, keep moving (placement is pre-answered above).
## Step 8: Close the arc
Leave a **verified-clean checkpoint** (tests green, tree clean, pushed if policy allows). Report what landed, the `[DECISION]` trail, and every noted-but-not-blocking item you surfaced. Then `/handoff (auto)` with a next-session opener that itself says "VERIFY STATE FIRST — this prompt may be stale." Offer `/wrapup` instead if the work is fully done and nothing carries forward. Don't auto-run a push/deploy inside the close — that's the ungranted-approval case unless the contract's push policy already granted it.
## Notes
- **`/auto` applies policy, it doesn't redefine it.** The decide-vs-ask *logic* is Rule 35; this skill adds *operational* discipline (never-stop list, budget-binding, work-selection, subagent gate, resume-cron). If you want to change *when to ask*, edit Rule 35 — keep the single source of truth.
- **Two rule sources, both loaded, neither restated.** `/auto` operates under two distinct rule files and owns *neither*: **Rule 35** in the plugin-managed `working-rules.md` is the decide-vs-ask *routing* policy (edit it to change *when to ask*); **`user-rules.md`** is the user's own *substantive* standing rules, loaded in Step 0.4 and applied as binding constraints for the arc (edit them via `/rules`-adjacent flows / `/audit-knowledge` promotion, never here). `/auto` reads and applies both; it restates neither. To see the rules' content, use `/rules`.
- **`/auto` never writes config.** It runs autonomously for the arc on the strength of the invocation; the standing `autonomy` posture changes only via `/setup` (one writer, no drift).
- **Tool allowlist companion.** For unattended runs, a pre-authorized `permissions.allow` list in the user's `.claude/settings.local.json` makes "tools are preset" real: the skill tells the model not to ask, the settings tell the harness not to gate. Keep them in sync — when a new tool causes a mid-run stop, add it there.
- **Self-restart wrapper — permission setup (example, follow only when you actually run one).** The `self-restart` flag needs `bin/auto-runloop.sh`, which spawns `claude -p --dangerously-skip-permissions`. That tripwires TWO independent gates, and you must clear BOTH:
1. **The permissions system** — allowlist the wrapper in `.claude/settings.local.json`. Match how you invoke it (`sh <path>` vs `<path>` directly):
```json
{
"permissions": {
"allow": [
"Bash(sh */plugin-claude-code/bin/auto-runloop.sh:*)"
]
}
}
```
(Use the absolute path on your machine; `:*` is the trailing-args shorthand. Verified vs the current settings schema, 2026-06-27.)
2. **The auto-mode classifier** (a SECOND gate, only when auto mode is ON) — it independently hard-denies spawning an unattended `--dangerously-skip-permissions` agent, and **`permissions.allow` does NOT override it** (docs: "the classifier is a second gate that runs after permissions"). So either: run the wrapper from a **normal interactive session with auto mode OFF** (the allowlist alone suffices there), OR have your org's `autoMode` config trust it. Do NOT expect the allowlist rule alone to clear an auto-mode run.
This is intentionally an example to copy when needed, not a setting this plugin writes — enabling `--dangerously-skip-permissions` is a standing security relaxation you should opt into deliberately, at the moment of a real unattended run, never by default.
---
## /roadmap
# /roadmap — Per-Project Feature Roadmap Grid
Render a project's **feature roadmap** as a compact `Feature / Band / Status` table — where each feature sits across the version trajectory (the *Band*) and its one current state (the *Status*) — synthesized from the project's own docs and persisted to a committed `ROADMAP.md`. The version-plane counterpart to `/recap` (which orients *temporally* — what just happened) and to aria-atlas (the live *visual* session dashboard). `/roadmap` answers "where does each feature sit, and what's ready to build next?"
## Step 0: Resolve Mode
Parse the first argument (case-insensitive):
- `refresh` → **refresh mode** — force re-synthesis + rewrite without the staleness prompt. Consume an optional second argument as the project `<name>`.
- any other token `<name>` → **project-named** — the `<name>:` tag in `projects_list`.
- no argument → **project-nearest** — the current project (walk up from cwd).
### Resolver (verbatim from /recap)
- **project-nearest** (no arg) → walk up from cwd to the nearest `CLAUDE.md`/`PROGRESS.md` (the same Step-1 resolver the other aria-knowledge skills use). No `projects_list` needed.
- **project-named** (`<name>`) → read `.cursor/aria-knowledge.local.md` and parse the `projects_list:` frontmatter key — comma-separated `tag:path` entries; expand a leading `~` in any path. The typed `<name>` IS the `projects_list` tag (`/roadmap cs` → the `cs:` entry). **Unknown tag → list the available tags and stop (no fuzzy matching).** This is the same roster `/aria-assist` and `/recap` read; **be read-only on `projects_list` — never write it.**
**Always print the resolved project path** so the user can verify which project was read.
## Step 1: Read Flow (render-then-offer)
The persisted `ROADMAP.md` is the fast read, but it is never trusted blindly — every read checks staleness against its sources and offers a refresh when stale.
```
1. Resolve project (Step 0). PRINT the resolved path.
2. ROADMAP.md exists at the project root?
- NO → synthesize from sources, stamp, write, render (mark FRESH).
- YES → compute staleness (Step 2).
```
### Hand-authored guard (no-clobber + notify)
If `ROADMAP.md` exists but has **no `synthesized_at` stamp** in its frontmatter, treat it as **hand-authored** (the stamp is the discriminator — its absence covers both "hand-written" and "a different concept entirely," e.g. a cross-subproject *portfolio* roadmap like `df/ROADMAP.md`). **Notify the user** — e.g. *"`ROADMAP.md` looks hand-authored (no `synthesized_at` stamp) — rendering as-is, will not overwrite without `/roadmap refresh`"* — render it as-is, and **never auto-overwrite**. Converting it to a synthesized file requires an explicit `/roadmap refresh` with a confirm.
## Step 2: Staleness (source-stamp)
The artifact carries a stamp in YAML frontmatter:
```yaml
---
synthesized_at: 2026-06-25
synthesized_from_commit: a1b2c3d # HEAD at synthesis; OMITTED when not a git repo / no Bash
sources: [CLAUDE.md, PROGRESS.md]
---
```
**Stale** ⇔ any source's mtime is newer than `synthesized_at`, **OR** `git log <synthesized_from_commit>..HEAD` is non-empty. Because `ROADMAP.md` is **committed** and shared, the commit-delta signal correctly catches *anyone's* intervening commits — a teammate's `CLAUDE.md` edit pulled in after synthesis registers as stale. No extra machinery for the multi-author case.
- **Fresh** → render the persisted grid (mark FRESH).
- **Stale** → **render the persisted grid, then** offer refresh, citing why — e.g. *"stale — 4 commits + CLAUDE.md edited since `a1b2c3d`; refresh from synthesis? (y/n)"*. On `y` → re-synthesize + rewrite. A bare invocation **never** auto-rewrites.
**Graceful degradation:** no git repo or no Bash → drop the commit-delta signal, fall back to **mtime-only** staleness, omit `synthesized_from_commit` from the stamp, and say so. Never present a guessed signal as certain.
## Step 3: Synthesize the Grid
Read the sources — the project `CLAUDE.md` (especially the `Last reviewed` footer + status blocks) and `PROGRESS.md` (arc headings + open items). Derive a `Feature / Band / Status` table.
### Band (the *when* — version-trajectory axis)
| Band | Meaning | Resolved from |
|------|---------|---------------|
| Shipped | Released at/before current | versions ≤ current in CLAUDE.md / CHANGELOG |
| Current | The in-flight release | the version the footer/PROGRESS treats as live |
| Next | The immediately planned release | "next up", "vN target", nearest planned |
| Later | Planned / someday | "deferred", "future", "Phase N+" |
Bands **collapse gracefully**: a young project shows only the bands it has and *notes which were empty* rather than rendering blank columns.
### Status (exactly one state per feature)
| Glyph | State | Source |
|-------|-------|--------|
| ✓ | done | transcribed (shipped/complete in prose) |
| ◐ | in-progress | transcribed ("active", "underway", "in-progress") |
| ⛔ | blocked | transcribed — **cites the blocker phrase** |
| ▷ | buildable | **inferred** — Band=Next ∧ no blocker found (overridable) |
**Buildable is the only inference:** ▷ ⇔ `Band = Next` AND **no blocker found** for the feature. Conservative — absence of evidence is NOT buildable unless the feature is in Next; a feature with any named blocker is ⛔, never ▷.
### Rendered shape
```
Roadmap — <project> · synthesized 2026-06-25 from a1b2c3d · FRESH
Resolved path: /Users/.../<project>
| Feature | Band | Status |
|---------|------|--------|
| <feature — short detail if the name isn't self-descriptive> | Current | ✓ done |
| <feature> | Next | ▷ buildable |
| <feature> | Next | ⛔ blocked |
| <feature> | Later | ⛔ blocked |
| +N more (Shipped) | | |
⛔ blockers (cited):
· <feature> — "<the exact blocker phrase from the prose>"
▷ buildable (Next, no blocker found — override if wrong):
· <feature> · <feature>
```
The **evidence blocks below the grid** are the honesty mechanism: every ⛔ **cites** the phrase that justifies it; every ▷ is explicitly "no blocker found — override if wrong", so the one inferred axis stays falsifiable.
**Legibility:** self-descriptive rows (a bare feature name gets a short `— detail` clause when not clear on its own); cap + summarize the tail (~12–15 feature rows, then a `+N more (Shipped)` summary row grouping the shipped tail).
## Step 4: Write (only on synthesis / approved refresh)
When synthesizing (first run) or on an approved refresh, write the grid + frontmatter stamp to `ROADMAP.md` at the project root. **`ROADMAP.md` is committed** (a shareable team artifact — teammates see the synthesized roadmap). The skill **writes only `ROADMAP.md`** and **leaves committing to the user** — it never auto-commits and never touches any source file. The file simply appears in `git status` for the user to stage.
## Rules
- **Write only `ROADMAP.md`** — only on first synthesis, an approved refresh (`y`), or `/roadmap refresh`. Never touch CLAUDE.md / PROGRESS.md / SESSION.md / any source. Never auto-commit.
- **Read-only on `projects_list`** — never write the roster (same rule as `/recap`).
- **Buildable is the only inference** — narrow (Next + no blocker found), overridable, evidence shown.
- **Print the resolved project path + the staleness verdict (and why)** every run. Never present a guessed scope as certain.
- **Degrade loudly** — no git/Bash → mtime-only staleness, say so.
- **Honor the hand-authored guard** — no `synthesized_at` stamp → notify + render as-is + never overwrite without `/roadmap refresh`.
- **Not `/recap`** — recap orients *temporally* (what just happened); roadmap orients on the *version-trajectory plane*. May offer "`/recap` for recent changes."
- **Not `/aria-assist`** — assist *recommends what to do today* (PM judgment + writes proposals); roadmap *renders state* and stops. May offer to escalate to `/aria-assist` for prioritization.
- **Not aria-atlas** — atlas is the live *visual* session-state dashboard; roadmap is the terminal *feature-by-version* artifact.
---
## /preflight
# /preflight — the checklist you run before you report
`/prospect` looks forward at a plan. `/retrospect` looks back at shipped work. **This runs at the
moment in between: you believe you are done, and you are about to say so.**
## What this is NOT
**It is not `superpowers:verification-before-completion`,** which owns the principle — *no completion
claim without fresh verification evidence* — and owns it well. That skill answers **whether** to
verify. This one answers **what to verify**, and specifically: the checks whose *absence produces no
signal at all*.
If you have not internalised the Iron Law, read that skill first. This one assumes it and adds the
cases where you ran a verification, it passed, and the thing was still broken.
## The two failure families this exists for
Every check below descends from one of these. Neither produces an error, a failing test, or a warning.
1. **The check's FORM bounds what it can find.** A search whose pattern bounds its result; a review
whose scope bounds its result; a probe whose anchor bounds its result. The instrument reports
truthfully about the thing it measured — and you asked it the wrong question.
2. **A decision correct in isolation, wrong in context**, where nothing you looked at was measuring
the part that broke. A green build measures compilation, not reachability. A docstring records a
decision without disclosing it. A model's name states one job, not both.
> **The consequence they share: the absence of a problem signal is not evidence there is no problem.**
> Family 1 because your check couldn't see it. Family 2 because nothing was checking.
## How to run it
Work through the checks that apply to your change. **Every applicable check gets a recorded result.**
An unrun check is not a pass — it blocks the verdict.
Each check has **three** outcomes, never two:
| | |
|---|---|
| **PASS** | the check ran and the property holds |
| **FAIL** | the check ran and the property does not hold |
| **INVALID** | the check could not distinguish absence from a broken probe — **report no verdict, fix the probe** |
The third outcome is the whole point. A probe that returns "nothing found" because it was pointed at
a path that does not exist reads exactly like a clean result.
---
## P1 — Requirements diff · *does what shipped match what was asked?*
**A rationalisation in code is not a disclosure.** Nobody diffs docstrings against requirements at
review, so an omission explained in the source is invisible in exactly the place it needed a second
opinion.
1. List every field, endpoint, behaviour and response shape the request named. Mark each
**shipped / not shipped**. Mechanical; it takes a minute.
2. Every "not shipped" goes in a **ticket comment** — not a docstring, not a commit message, not a
PR body.
3. **"There is nowhere to store this" is a question, not a decision.** Say so and stop. Do not narrow
the deliverable to fit the schema you found — that is the signal the request and the schema
disagree, and that is the owner's call.
4. Check what the **UI promises** against what you returned. A surface labelled with a word the data
cannot support is a defect even when every line is correct.
5. Where you substituted something, **name the substitution.** "Returned the resolved email instead
of the requested username" is reviewable; silence is not.
6. **Enumerate the paths, don't re-read the diff.** A fix can close a disclosure on two code paths
and leave it open on a third — re-reading the change will not show you the path you never touched.
---
## P2 — Consumer census · *does this symbol have a second job?*
**A model's name and fields do not tell you its jobs.** A row can be a record *and* a permission at
the same time; a delete that is obviously right for the first is a silent privilege change for the
second, and nothing in the model, the migration or your endpoint will mention it.
```bash
# every consumer, minus vendored code and migrations
grep -rn "<SYMBOL>" --include='*.<ext>' . | grep -v "/<vendor_dir>/\|/migrations/"
# of those, which sit inside an authorization decision?
grep -rn "<SYMBOL>" --include='*.<ext>' . | grep -viE "test|migration" \
| grep -iE "allow|permission|access|authoriz|can_view|visibility|entitle"
```
1. Run it **before** writing the endpoint, not after.
2. **Write out what each consumer uses it for.** The census is worthless unread; writing it is what
surfaces the surprise.
3. If any consumer is an authorization or access check, **a delete on that model is a permission
change** — say so explicitly and get it ruled on rather than shipping it inside a feature.
4. Ask what the **absence** of a row means, not just its presence. Where presence grants something,
deletion revokes it — and "cancel" is then the wrong word for the button.
5. **Beware the inverted fix.** On discovering a row doubles as a grant, do not "clean up" by
deleting it at some lifecycle point. That revokes access. The fix is usually a new column or a
derived flag.
6. **Aliasing:** can two rows point at the same underlying resource? If so, deleting A's "old"
resource may destroy B's live one.
---
## P3 — Reachability · *is it actually wired in?*
**A green build measures compilation, not reachability.** A component nothing imports is tree-shaken
out: it compiles, it deploys, and it is not in the product.
**Frontend**
```bash
# does anything import it? zero here = the feature does not exist
grep -rn "<COMPONENT>" src --include='*.js' | grep -v "<COMPONENT>/"
```
After deploy, read the **source map's module list**, not the bundle's strings — a string search
cannot distinguish "not deployed yet" from "deployed but tree-shaken." Both return zero. **Always
include a control module from the same page**, or "absent" is indistinguishable from a broken probe.
**Backend / any callable**
Ask what makes it reachable, then verify *that*: a route table entry, a registered webhook, a
scheduled job actually installed, a feature flag. **Include a control** — a sibling you did not touch
— so a zero result is interpretable.
⚠ **A test that calls a function directly does not exercise its routing.** If your suite invokes the
handler rather than the URL, the suite stays green with the route deleted. That is a real gap, not a
pedantic one; check the wiring separately and say which you verified.
⚠ **Never write "build green" or "tests pass" as evidence of shipping.** Say "imported and present in
the bundle," or "routed at `<path>`," or make no claim.
---
## P4 — Census bound · *what could this search NOT have found?*
**Report the instrument, not just the result.** A census reports its own bound or it is not a census.
1. **Run ≥2 idioms.** A symbol may be reached by a bare name, a dynamic dispatch, a `**kwargs` spread,
a re-export, or a differently-quoted string. One idiom returning zero is not absence.
2. **Census by BEHAVIOUR, not by name.** Names move; behaviour is what you care about. Resolve an
argument back to its assignment rather than pattern-matching the call.
3. ⛔ **`grep | wc -l` cannot report failure.** A missing path, a genuine absence and a real zero all
print `0`. Read grep's **exit code**, or verify the path first.
4. **Discount definitions, re-exports and tests.** A symbol existing is not a symbol running; what
remains after those are removed is the answer.
5. **State the bound in the report.** "Censused X across A, B, C excluding D — this is a code census,
not a data census." A bound stated is a bound a reader can challenge.
6. **A handed-over finding list is a LOWER bound.** Re-census in your own consumer.
---
## P5 — Non-vacuity · *did the test actually execute the code?*
**The most dangerous green is the one that ran nothing.** A request refused upstream, a fixture
missing a field, an exception swallowed into a success envelope — the assertions never execute and
every one of them passes.
1. **Assert a POSITIVE signal that the code ran**, not merely the absence of a failure: a call count,
a success envelope, an observable side effect. Put it in the shared fixture so it protects every
test, not the one that happened to notice.
2. ⚠ **An HTTP status is not that signal** where the codebase wraps failures in a 200 envelope. Check
the envelope's own success field.
3. **Scope the guard to the right unit.** A blanket "this was never called" can fail for a reason
unrelated to the property under test — and, worse, can pass while the defect is live if the
defect operates by a different route.
4. **Ask what ELSE produces this outcome.** An outcome consistent with your hypothesis is not the
cause. Was the precondition for failure ever actually met?
---
## P6 — Mutation · *have you SEEN the guard go red?*
**Never cite a gate you have not watched fail.** This applies to guards you wrote *and* guards you
inherited.
1. Break the thing the guard protects. Confirm it goes red, and that it reds **for the stated
reason** rather than erroring.
2. **Restore from a byte backup taken before the mutation**, then `cmp`. Do not restore with version
control if the working edit is uncommitted — that discards it.
3. Check the kill lands in the **right scope**. A mutation in module A reddening only module B's
tests means the two are coupled, or one is inert.
4. ⚠ **On a scripted multi-file edit, read the insert/delete ratio.** For a purely additive change,
deletions should be zero or exactly the lines you meant to rewrite. A net-negative diff on an
additive edit is a first-class signal that something was dropped.
---
## Output
Emit a compact table, then the verdict. Do not bury a FAIL in prose.
```
PREFLIGHT — <scope>
P1 requirements diff PASS 3 named / 3 shipped
P2 consumer census FAIL ProfileInvites is read by 2 authz views — delete = permission change
P3 reachability PASS routed at /api/x, control /api/y present
P4 census bound PASS 2 idioms, exit codes read; CODE census, data not checked
P5 non-vacuity PASS envelope asserted + 5 size-writes observed
P6 mutation INVALID probe restored from git, working edit lost — redo from byte backup
VERDICT: NOT READY — 1 FAIL, 1 INVALID
```
**Any FAIL or INVALID ⇒ do not make the claim.** Fix, or state the limitation explicitly in the same
sentence as the claim. An INVALID is not a soft pass — it means you do not know.
**Skipped checks are listed as skipped, with why.** "N/A — no model touched" is a result. Silence is
not.
## Recording
**Always write the session marker — this is what the commit gate reads.** One line per run,
appended:
```bash
printf '%s\t%s\t%s\n' "$(date +%H:%M)" "<VERDICT>" "<scope>" \
>> "${TMPDIR:-/tmp}/aria-preflight-${CLAUDE_SESSION_ID}"
```
`pre-commit-preflight-check.sh` fires on `git commit` and warns when no marker exists for the
session. **Any recorded verdict satisfies it, including NOT READY** — recording a FAIL and
committing anyway is a legitimate, visible choice; the failure being guarded against is not running
the checks at all.
One baseline and two escalations, none a sub-setting of the other:
| | |
|---|---|
| `preflight_gate: off` | never fires |
| `preflight_gate: warn` *(default)* | warns on code commits — **and denies on any `preflight_deny_repos` or `preflight_deny_paths` match** |
| `preflight_gate: deny` | denies every code commit; both lists are irrelevant |
Both lists escalate from **any** baseline, exactly as `critical_paths` escalates Rule 22 regardless of
the surrounding setting. They match different things, and the difference is load-bearing:
- **`preflight_deny_paths`** — space-separated globs matched against **repo-relative** staged paths.
A bare filename therefore will not match that file nested in a subdirectory, and because the docs
filter runs first, a `*.md` entry here can never fire.
- **`preflight_deny_repos`** — comma-separated substrings matched against the repository's resolved
absolute path. This is the only way to say *"always gate this repo"*: staged paths are repo-relative,
so the repo name appears nowhere in the string a path glob sees. Substring matching over-matches a
same-named sibling — for a gate, the safe direction.
Docs-only diffs are silent under every combination, including a gated repo. That is deliberate: a gate
that fires on a README edit is the one that gets switched off wholesale.
Then write the table to `<knowledge_folder>/logs/preflight/<date>-<scope>.md` when the change is
non-trivial, and run aria's standard intake. A preflight that found something is a candidate
insight — the pattern that produced the FAIL is usually more reusable than the fix.
## Composes with
- **`superpowers:verification-before-completion`** — owns the Iron Law. Run it; this adds the *what*.
- **`/prospect`** — same discipline, earlier: before the work rather than before the claim.
- **`/retrospect`** — after shipping. A FAIL here that you shipped anyway belongs there.
- **Rule 22 / Rule 38** — per-edit scope, and closing the class rather than the instance.
> **Why a skill and not a rule.** Every check here descends from a rule someone already knew. The
> failures happened anyway — including one where a defective probe was run within an hour of reading
> the note warning against it. Reading a rule does not execute it. **Run the checks.**
---
## /snapshot
# /snapshot — Task-Boundary Capture (Cursor-native)
Cursor-native command backed by `scripts/aria/capture-task-boundary.sh`. Writes a small markdown snapshot of the current session's repo + hook state to `{knowledge_folder}/intake/task-boundary-captures/`. The same script runs automatically on every `stop` event when `task_boundary_capture` is enabled in config — `/snapshot` is the on-demand entrypoint for invoking it mid-session.
**Explicitly not a raw transcript capture.** Cursor does not expose conversation transcripts to hooks or skills, so `/snapshot` captures only what is observable from outside the conversation:
- timestamp + session id + cwd
- git branch + `git status --short` + changed files + `git diff --stat`
- active batch manifest (if present)
- config path + knowledge_folder
- recent `/tmp/aria-hook-debug.log` lines
If you need narrative content (decisions made, insights worth keeping), use `/extract` instead — it's the right surface for capture-from-conversation.
## How to Run
```bash
echo '{"sessionId":"manual-snapshot"}' | bash scripts/aria/capture-task-boundary.sh
```
Or invoke from the agent: read `.cursor/aria-knowledge.local.md` for `knowledge_folder`, then run the script with a small JSON payload on stdin. The script is idempotent — each invocation writes a new timestamped file under `intake/task-boundary-captures/` and never overwrites.
## When to Use
- Pausing mid-task and want a self-describing record of where the working tree is.
- About to switch branches or run a destructive operation, and want a "what was the state right before" marker.
- Debugging hook behavior — the capture's `recent /tmp/aria-hook-debug.log` section is the cheapest way to confirm hooks are firing.
## Limitations
- No transcript content. If the value you want lives in the conversation (decisions, alternatives ranked, why-not's), this command will not capture it. Use `/extract`.
- Captures are advisory artifacts, not promoted knowledge. `/audit-knowledge` does not auto-promote from `intake/task-boundary-captures/`; treat them as a debugging / forensic surface.
---
## /setup
# /setup — Knowledge Tools Configuration
Walk the user through configuring their knowledge folder and plugin settings. Safe to re-run at any time — only touches what needs updating.
## Step 1: Check for Existing Config
**Read the installed port version first.** Parse `scripts/aria/VERSION` (plain text, one line — e.g. `2.46.2-cursor.0`):
```bash
INSTALLED_VERSION=$(cat "scripts/aria/VERSION" 2>/dev/null | tr -d '[:space:]')
[ -z "$INSTALLED_VERSION" ] && INSTALLED_VERSION="unknown"
```
Then read `.cursor/aria-knowledge.local.md`.
- **If it exists:** show current settings and say *"aria-knowledge v{INSTALLED_VERSION} is already configured. I'll check for updates."* If the existing config has `last_setup_version: X` and X differs from `INSTALLED_VERSION`, also note: *"Plugin upgraded from v{X} → v{INSTALLED_VERSION} since last setup. Diff prompts and any new config keys will surface in the steps below."* Then proceed to Step 2 in **update mode** — scan for missing structure, re-diff templated files, check dependencies.
- **If it doesn't exist:** say *"Let's set up aria-knowledge v{INSTALLED_VERSION}. This will configure your knowledge folder and preferences."* Proceed to Step 2 in **fresh mode**.
**Detect skill-only fields** (update mode only). After parsing the standard hook-parsed keys, also scan for the `projects_groups` multi-line YAML block — a skill-only field consumed by `/distill` and `/stitch` (see `CONFIG.md` for schema). It's **not** in the advanced-options bundle because it's not bash-parsed; surface its presence here so users get awareness without /setup trying to flatten it.
```bash
GROUPS_PRESENT=$(grep -c '^projects_groups:$' .cursor/aria-knowledge.local.md || true)
GROUPS_COUNT=$(awk '/^projects_groups:$/{in_block=1; next} in_block && /^---$/{exit} in_block && /^[^[:space:]]/{exit} in_block && /^ [^[:space:]].*:$/{c++} END{print c+0}' .cursor/aria-knowledge.local.md)
```
If `GROUPS_PRESENT > 0`, add to the announcement: *"Detected `projects_groups` skill-only field with {GROUPS_COUNT} group(s) configured. Preserved as-is (consumed by `/distill` and `/stitch`; see `CONFIG.md` for the schema)."* If `GROUPS_PRESENT == 0`, say nothing — the field is opt-in and most users without multi-repo projects won't have it.
## Step 2: Knowledge Folder Location
Ask the user:
> "Where would you like your knowledge folder? You can:
> (a) Provide a path to an existing folder
> (b) Create a new one — I'll ask where to put it"
If **(a) existing path:**
- Verify the path exists and is a directory
- Proceed to Step 3 in **existing mode**
If **(b) create new:**
- Ask for the desired location (parent directory + folder name)
- Create the directory
- Proceed to Step 3 in **create mode**
## Step 3: Folder Structure Validation
Read the expected structure from `knowledge/`.
**Expected directories:** `intake/`, `intake/notes/`, `intake/attachments/`, `references/sources/`, `intake/task-boundary-captures/`, `intake/subagent-captures/`, `intake/ideas/`, `logs/`, `rules/`, `approaches/`, `decisions/`, `guides/`, `references/`, `archive/`
**Expected files:** `README.md`, `OVERVIEW.md`, `LOCAL.md`, `aliases.md`, `intake/insights-backlog.md`, `intake/decisions-backlog.md`, `intake/extraction-backlog.md`, `intake/rules-backlog.md`, `intake/ideas/README.md`, `logs/knowledge-audit-log.md`, `logs/config-audit-log.md`, `rules/working-rules.md`, `rules/user-rules.md`, `rules/user-examples.md`, `rules/change-decision-framework.md`, `rules/enforcement-mechanisms.md`, `guides/README.md`, `approaches/README.md`, `decisions/README.md`, `references/README.md`, `archive/README.md`
**User-owned files (created once from template, never overwritten or diffed):** `LOCAL.md` (project-specific guide), `aliases.md` (tag aliases — added 2.16.0), `rules/user-rules.md` (your custom rules — ARIA never touches this file), `rules/user-examples.md` (your per-rule examples — `/rules N` reads this; ARIA never touches this file), `guides/README.md`, `approaches/README.md`, `decisions/README.md`, `references/README.md`, `archive/README.md` (directory stubs users may customize).
**In create mode:** Create all directories and copy all template files. After creation, display a **one-time educational note** about the file-class model (this note is only shown on fresh installs — in update/existing mode, skip it):
> **First-setup note: Plugin-Managed vs User-Owned Files**
>
> Your knowledge folder now contains two classes of template files:
>
> - **Plugin-managed** — `README.md`, `OVERVIEW.md`, `rules/working-rules.md`, `rules/change-decision-framework.md`, `rules/enforcement-mechanisms.md`, `rules/retrospect-patterns.md` (and `projects/README.md` when the project tier is enabled). These are diffed on every `/setup` run. Customize them freely — your edits will appear as diff prompts when plugin updates ship. That's how you receive improvements without silent overwrites. Each managed file also carries a `<!-- plugin-managed: -->` comment header so you can spot them at edit time.
> - **User-owned** — `LOCAL.md`, `aliases.md`, `rules/user-rules.md`, `rules/user-examples.md` (your per-rule examples, since v2.14.2), intake backlogs (`insights-backlog.md`, `decisions-backlog.md`, `extraction-backlog.md`, `rules-backlog.md`) and the `intake/ideas/` directory (one file per idea since v2.11), audit logs under `logs/`, directory README stubs (`guides/`, `approaches/`, `decisions/`, `references/`, `archive/`), and per-project READMEs under `projects/{tag}/`. ARIA never diffs or overwrites these. Your customizations live here safely.
>
> See `OVERVIEW.md` "Plugin-Managed vs User-Owned Files" for details. This note appears only on first setup.
**In existing mode:** Scan what's present vs missing.
- For missing **directories**: create them silently.
- For missing **files**: copy from template and note what was added.
- For existing **files**: do NOT overwrite — collect for diffing in Step 4.
- Report: "Created N directories, added N files, found N existing files to check."
**Project tier scaffolding** (if `projects_enabled: true` in current or pending config) is deferred to **Step 7c** — it runs after the config is written so it uses the final values (including answers from Step 6 that aren't in the config file yet during Step 3).
## Step 3b: Legacy `ideas-backlog.md` Detection
ARIA v2.11 moved the ideas backlog from a single `intake/ideas-backlog.md` file to per-file storage under `intake/ideas/`. Users upgrading from v2.10.x or earlier have an orphaned legacy file that v2.11 skills don't read. This step catches the migration on the first post-upgrade `/setup` run.
**Check:** does `{knowledge_folder}/intake/ideas-backlog.md` exist?
- **If no:** skip this step silently. Fresh installs and already-migrated users land here.
- **If yes:** count active entries by running:
```bash
awk '/^---$/{sep++; next} sep>=1 && /^### /{c++} END{print c+0}' "{knowledge_folder}/intake/ideas-backlog.md"
```
- **If count is 0:** the legacy file has no active entries (cleared-history HTML comments only). Prompt: *"Empty pre-2.11 `ideas-backlog.md` found. Delete it? (y/n)"* — on yes, `rm` the file; on no, leave it.
- **If count > 0:** report: *"Pre-2.11 `ideas-backlog.md` detected with {N} active entries. ARIA v2.11 uses per-file ideas in `intake/ideas/`. Options:"*
- `(1) Migrate now` — run `bash scripts/aria/migrate-ideas-backlog.sh "{knowledge_folder}"` and report the output (N files written, original renamed to `ideas-backlog.md.pre-2.11-migration`)
- `(2) Skip for now` — leave the file in place; `/setup` will prompt again on the next run. Note in the Step 8 summary that legacy entries are still stranded.
- `(3) Never migrate` — write a sentinel file at `{knowledge_folder}/intake/ideas/.legacy-skipped` so future `/setup` runs stop prompting. Document that the user accepts stranded pre-2.11 entries.
**Never auto-migrate without user choice.** The migration renames the original file (doesn't delete), so it's reversible, but executing filesystem changes without confirmation violates the user-review principle `/setup` is built around.
**Report** in Step 8 summary: *"Legacy ideas-backlog.md: migrated N entries"* or *"Legacy ideas-backlog.md: skipped (N entries still pending)"* or *"Legacy ideas-backlog.md: not detected"* as appropriate.
## Step 4: File Diffing
For each templated file that already exists in the user's folder, compare against the plugin's shipped version in `knowledge/`.
**Files to diff:** `rules/working-rules.md`, `rules/change-decision-framework.md`, `rules/enforcement-mechanisms.md`, `rules/retrospect-patterns.md`, `README.md`, `OVERVIEW.md`, `projects/README.md` (plugin-managed if present)
**Never diff:** `LOCAL.md` (user-owned), `aliases.md` (user-owned — added 2.16.0), `rules/user-rules.md` (user-owned — your custom rules), `rules/user-examples.md` (user-owned — your per-rule examples), directory README stubs (`guides/README.md`, `approaches/README.md`, `decisions/README.md`, `references/README.md`, `archive/README.md`), backlog files (`intake/insights-backlog.md`, `intake/decisions-backlog.md`, `intake/extraction-backlog.md`, `intake/rules-backlog.md`) and the `intake/ideas/` directory (`intake/ideas/README.md` and all per-file ideas under `intake/ideas/**`), audit log files (`logs/knowledge-audit-log.md`, `logs/config-audit-log.md`), and per-project READMEs (`projects/{tag}/README.md` and any other content under `projects/{tag}/**`) — these contain user data or user-customizable content.
For each file with differences:
1. Notify: "[filename] differs from the plugin version."
2. Show a brief summary of what's different (not the full diff unless asked).
3. Offer options:
- **Keep mine** — no change
- **Use plugin version** — overwrite with template
- **Show diff** — display the full diff, then ask again
If no files differ (or all are new), skip this step silently.
In **update mode** (re-run): always diff, even if the file was previously kept. The plugin version may have changed.
## Step 5: Dependency Check
Check if the `explanatory-output-style` plugin is installed:
```bash
find ~/.claude/plugins -name "explanatory-output-style" -type d 2>/dev/null | head -1
```
- **If found:** "explanatory-output-style plugin detected. Insight capture will be enabled."
- **If not found:** "The explanatory-output-style plugin generates Insight blocks that aria-knowledge can capture automatically. It's an official Anthropic plugin. Want to install it? (recommended, but optional)"
- If user says yes: guide them to install it (the exact install mechanism depends on their Claude Code setup)
- If user says no: "Insight capture will be disabled. You can enable it later by installing the plugin and re-running /setup."
Record the result as `true` or `false`.
## Step 5b: Status-line Meter (Cursor port — skip)
The CLI status-line meter (`/statusline`) is **Claude Code only**. Cursor has no persistent usage meter or `~/.claude/aria-statusline-state.json` snapshot. **Skip this step silently** — do not offer install. Ensure `usage_alert_threshold: off` in the config template unless the user explicitly requests otherwise.
## Step 6: Cadence Configuration
Present current or default cadences:
> "Audit cadences control how often you're prompted to review knowledge:
> - **Knowledge audit:** triggers when either (a) backlog accumulates 20+ entries (primary, activity-driven) or (b) 7 days have elapsed since the last audit (safety net for low-activity weeks). Tier messages differ by size: 20+ "suggested", 35+ "recommended", 50+ "overdue — multi-pass".
> - **Config audit:** every 14 days (checks configs and docs for drift)
> - **Update check:** every 30 days (prompts to run /setup for plugin template updates)
>
> Want to change any? (Enter new values or press enter to keep defaults. Knowledge audit has two knobs: `audit_trigger_threshold` (entries, default 20) and `audit_cadence_knowledge` (days, default 7).)"
Record the values.
### Advanced Options
**Always offer** the advanced-settings review on every `/setup` run — both fresh installs and re-runs. New users need to see what's tunable up front rather than discovering it later; returning users need to surface and adjust values they may not have configured initially (e.g., keys added by plugin updates since their last `/setup`). Auto-mode users still see the bundle; pressing enter to accept defaults is an explicit no-op rather than a silent skip.
**Highlight new-since-last-setup keys (re-runs only):** before showing the bundle below, compare each Advanced Option key against the existing config from Step 1. For any key that exists in this spec but is **not** present in the user's current config (the upgrade case — a plugin update added the key after the user's last `/setup`), append `[NEW]` to that bullet's title in the bundle and prepend a one-line note above the bundle:
> *"Some settings are new since your last `/setup` run — `[NEW]` markers below indicate keys added by plugin updates that aren't yet in your config. Consider whether to set them now."*
Detection is a per-key `grep -q '^{key}:' .cursor/aria-knowledge.local.md`; non-zero exit means the key is missing → flag with `[NEW]`. For fresh installs there is no prior config to compare against, so no `[NEW]` markers appear and no preamble note is shown — the bundle just renders defaults.
**Emit detection summary (v2.15.2+):** before showing the Advanced Options bundle, output a one-line audit-trail line naming the detection result, regardless of outcome. This makes the [NEW]-detection step transcript-visible so users can verify the wizard actually ran the comparison instead of silently skipping it.
- If `[NEW]` keys were found: `[setup] Advanced Options [NEW]-detection: flagged {N} key(s) added since v{last_setup_version} — {key1}, {key2}, ...`
- If no `[NEW]` keys: `[setup] Advanced Options [NEW]-detection: none — all keys present in current config.`
- For fresh installs (no prior config): `[setup] Advanced Options [NEW]-detection: skipped — fresh install, no prior config to compare against.`
The summary line precedes the bundle text. If the user later questions "did the wizard surface my new field?", the transcript carries the explicit yes-or-no.
> "Advanced settings (defaults are fine for most users):
> - **Freeform tag promotion threshold:** 3 (suggest promoting a freeform tag to known after it appears on this many files)
> - **Staleness threshold:** 6 months (flag knowledge files not updated within this period)
> - **Ideas staleness threshold:** 7 days (during `/audit-knowledge`, mark idea files in `intake/ideas/` older than this with `[STALE — still relevant?]` to prompt Accept/Reject/Defer decisions)
> - **Task-boundary capture:** true (save structural snapshot on agent `stop` via `task_boundary_capture`; Cursor has no PreCompact transcript hook)
> - **Active knowledge surfacing:** true (when enabled, four hooks — sessionStart, stop, beforeShellExecution with cd, beforeReadFile — and two skills — /prospect, /retrospect — auto-load context at trigger moments. **Two kinds of context get surfaced (v2.16.1 expansion):** (a) **knowledge files** matched by tag against the user's task/cd-target/skill-input, and (b) **tracked artifacts** — CODEMAP directory + STITCH for the detected project (boundary-detected; not the full CODEMAP). Both surface with staleness annotations against `codemap_staleness_threshold_days` (default 14) and `stitch_staleness_threshold_days` (default 30); grossly-stale artifacts (>2× threshold) refuse to load with a warning. Companion surfaces — /audit-config, /stats, /handoff, /wrapup — also gate their tracked-artifact surfacing on this flag. Set to `false` for passive mode where hooks only suggest `/context <tag>` and all proactive artifact loading is suppressed (users load manually via /context). Active mode honors a session-scoped dedup ledger at `/tmp/aria-active-{session_id}` so the same file/artifact isn't re-Read across triggers. See CONFIG.md for the trigger sites and the ≥2-tag-match threshold + 5-file cap policy.)
> - **Session state file (`SESSION.md`):** false (when on, aria-knowledge writes a per-project `SESSION.md` — `in-progress` at session start, `wrapup`/`handoff` at close — and offers to resume from it at session start; enables re-entry + the aria-atlas status board. Files are created at project roots only when on. Change later via `session_state` in `.cursor/aria-knowledge.local.md`. A companion `session_stale_days` key [default 7] controls when a saved resume prompt is treated as possibly-stale: an older entry triggers a "still relevant? [resume / archive / keep]" prompt at session start instead of being presented as live — it never auto-evicts. A second companion key `session_state_tracked` [default false] decides whether `SESSION.md` is **git-ignored** or **committed**: the default treats it as ephemeral, while `true` treats it as a tracked decision-trail artifact that `/wrapup` and `/handoff` stage with their commit. Set it `true` in repos with no `PROGRESS.md`, where `SESSION.md` *is* the durable log and the ephemeral rationale does not apply. ⚠ Whichever it is set to, both skills test tracking with `git ls-files --error-unmatch`, not by looking for the pattern in `.gitignore` — an ignore rule is a no-op on an already-tracked path, so a pattern check never becomes true and the ignore line is appended on every run.)
> - **Auto-prospect (`auto_prospect`):** off (when `nudge`, writing a plan to `docs/plans/` or `docs/superpowers/plans/` prompts an offer to run `/prospect file <path>`; when `run`, it runs inline. `docs/specs/` is intentionally not a trigger. Change later via `auto_prospect` in `.cursor/aria-knowledge.local.md`.)
> - **Autonomy posture (`autonomy`):** default (decision-routing posture, Rule 35). `default` injects nothing — no behavior change, no context cost. `balanced` injects an investigate-first directive each session: ask on intent/preference/judgment-with-no-gainable-visibility + ungranted explicit approval; act on mechanical/objectively-validatable. `autonomous` injects the full posture: decide objectively-validatable forks yourself (checked against the build-philosophy bar, Rules 13/14/18), run quality gates as checks-not-stops, stop only on a no-visibility judgment call or ungranted explicit approval. Turn it up when you want the agent to spend fewer of your decisions on what it can resolve itself. Change later via `autonomy` in `.cursor/aria-knowledge.local.md`.)
> - **Auto-retrospect (`auto_retrospect`):** off (when `nudge` [recommended], a `git push` of ≥`retrospect_min_commits` commits to a branch in `retrospect_branches` prompts an offer to run `/retrospect range <old>..<new>`; `run` runs it inline — note the post-push session is not disposable, so `run` adds real cost. Gates: `retrospect_min_commits` default 3, `retrospect_branches` default `main,master,production`.)
> - **Usage alert threshold (`usage_alert_threshold`):** off (Claude Code only — requires `/statusline` meter + snapshot file; Cursor has no equivalent. Leave `off` unless you add a custom integration.)
> - **Critical paths:** (empty) comma-separated path patterns that always require HIGH impact assessment (e.g., auth/*,payments/*,migrations/*)
> - **Preflight commit gate (`preflight_gate`):** warn (what happens on a `git commit` with no `/preflight` recorded this session. `off` = never fires; `warn` = a reminder; `deny` = block every code commit. Any recorded preflight — of any verdict — satisfies the gate for the rest of the session, so even `deny` costs one run, not one per commit. Docs-only commits are always silent. An unrecognized value falls back to `warn`, never `off`.)
> - **Preflight deny paths (`preflight_deny_paths`):** (empty) space-separated globs matched against **repo-relative** staged paths. An *escalation*, independent of the gate: `warn` + named paths = warn generally, block on these. Note the docs filter runs first, so `*.md` paths can never be covered here.
> - **Preflight deny repos (`preflight_deny_repos`):** (empty) comma-separated substrings matched against the repository's absolute path — the way to say "always gate this repo", which deny_paths cannot express (staged paths are repo-relative and never contain the repo name). Substring, so it over-matches a same-named sibling; for a gate that is the safe direction. Same independence as deny_paths.
> - **Style-audit lookback (`style_lookback_days`):** 90 (on `/audit style`'s first-ever run, how many days of session-log history to window the initial scan to. Later runs resume incrementally from the style-audit log's last stamp, so this only matters cold-start or after a `window <D>` override. Change later via `style_lookback_days` in `.cursor/aria-knowledge.local.md`.)
> - **Style-audit session cap (`style_max_sessions`):** 50 (the over-cap gate `/audit style` Step 1b stops at before scanning — exceeding it prompts `recent`/`all`/`window <D>`/`cancel` rather than silently truncating. Change later via `style_max_sessions`.)
> - **Style-audit log path (`style_audit_log`):** `{knowledge_folder}/logs/style-audit-log.md` (where `/audit style` stamps its incremental scan boundary after each run. Change later via `style_audit_log`.)
> - **External-fetch gate (`external_fetch_gate`):** off (when `on`, the first `WebFetch`/`WebSearch` per session aimed at a surface your knowledge folder or memory dirs already cover is denied **once**, naming the matched files; the retry passes. Coverage is keyed on the URL's registrable domain, or on vendor-like words in a search query — ordinary English words are filtered out. It is an *interrupt, not a verification*: it cannot confirm you read the file. Change later via `external_fetch_gate` in `.cursor/aria-knowledge.local.md`.)
> - **External-fetch ambient cap (`external_fetch_max_hits`):** 8 (above this many matching files the surface is treated as ambient and the gate stays silent — a host mentioned in 76 files carries no signal, and surfacing them all trains you to dismiss the hook. Change later via `external_fetch_max_hits`.)
> - **Ticketing plugins:** (empty) comma-separated `tag:plugin-command` pairs mapping a project tag to its ticket-drafting plugin (e.g., `proj-a:foo-ticket,proj-b:bar-ticket`). When set, `/audit-knowledge` prints a hint to use that plugin's command when an idea's project matches a mapped tag during the `Accept → tracker` disposition. Hint only — never auto-invokes. Leave empty if you don't use a ticketing plugin or prefer to copy ideas into your tracker manually. Plugin commands are bare names — no leading `/`. Validate input: each pair must contain exactly one `:` separating tag from command; project tags cannot contain `:` or `,`; plugin commands cannot start with `/` (strip leading `/` and warn if found).
> - **Project-specific knowledge tier:** disabled (creates `projects/{tag}/` subdirectories for project-specific decisions and patterns; opt in if you want to organize knowledge by project alongside the cross-project tree. If enabled, you'll be asked an inline follow-up about auto-loading project context on session start.)
>
> Want to change any? (Enter new values or press enter to keep defaults)"
Record the values.
### Skill-only fields (read-only awareness)
Some configuration is consumed by skills (which parse YAML natively in Claude's context) rather than by bash hooks. These fields use multi-line nested YAML blocks that don't fit the single-line bundle prompt above and are **not** offered for interactive editing here — they're either populated by their consuming skill's auto-propose bootstrap (e.g., `/distill --group=<tag>`, `/stitch create <tag>`) or hand-edited per the schema in `CONFIG.md`.
Currently in this category:
- **`projects_groups`** — multi-repo group mapping (backend/web/mobile sub-folder layout per project tag). Read by `/distill` and `/stitch`. Auto-populated on first multi-repo skill invocation; hand-editable per `CONFIG.md` "Skill-only fields" section.
If Step 1 detected this field, restate its current group count here for confirmation: *"Skill-only fields preserved: `projects_groups` ({N} groups). Edit via `CONFIG.md` schema or let `/distill`/`/stitch` auto-propose new groups on first use."* If absent, say: *"No skill-only fields configured. `/distill --group=<tag>` and `/stitch create <tag>` will auto-propose `projects_groups` entries on first use for any multi-repo project."*
This block is read-only — `/setup` never writes new entries here. See **Step 7 / Step 7b** for how the existing block is preserved and validated.
### Project Setup (only if user enables the project-specific knowledge tier)
If the user enables (or keeps enabled) the project-specific knowledge tier in Advanced Options, ask six follow-up questions. In **update mode** where values already exist in the config, show the current value for each question and let the user keep it (press enter) or enter a new value — this is the discoverable path for toggling `auto_load_project_context` on a re-run when the tier was previously enabled:
1. **Project list** — "Comma-separated `tag:relative-path` pairs (e.g., `proj-a:path/to/proj-a,proj-b:proj-b,lib:shared-lib`). Paths are relative to the parent of your knowledge folder (typically `~/Projects/`). Press enter to defer adding projects:"
2. **Project remotes (optional)** — "Optional git-remote URL patterns for fallback project detection when CWD doesn't match a configured path. Comma-separated `tag:url-substring` pairs (e.g., `proj-a:myorg/proj-a-repo`). Press enter to skip:"
3. **Promotion threshold** — "Minimum number of projects that must share a similar pattern before `/audit-knowledge` suggests cross-project promotion (default 2):"
4. **Auto-load project context on session start** — "When your CWD matches a configured project, should SessionStart automatically suggest `/context {tag}`? This is a runtime convenience — the project tier works fine without it, and you can change this later by editing `auto_load_project_context` in `.cursor/aria-knowledge.local.md`. (y/n, default n):"
5. **SessionStart project picker** — "When you open a session from a multi-project parent directory (no project chosen yet), should ARIA suggest a project menu generated from your `projects_list`? Non-blocking — you can always just name a project or start working. (y/n, default n):" → writes `session_start_project_picker`.
6. **Project display labels (optional)** — "Optional friendly names for the picker menu. Comma-separated `tag:Label` pairs (e.g., `api:API Server,web:Web Client`). Empty = bare tags. Press enter to skip:" → writes `projects_labels`.
**Validate input:**
- Project tags cannot contain `:` or `,` (these are the parser delimiters). If invalid, show the offending tag and re-prompt.
- Promotion threshold must be a plain integer ≥ 1. If invalid, re-prompt.
- Auto-load answer must be `y`/`n` (or empty for default). If invalid, re-prompt.
- SessionStart project picker answer must be `y`/`n` (or empty for default). If invalid, re-prompt.
- `projects_labels` is comma-separated `tag:Label` pairs, or empty. Warn (don't error) if a label's tag is not in `projects_list`.
- For each `tag:path` pair, warn (don't error) if the resolved path doesn't exist on disk yet — the user may be configuring projects they haven't created.
**Existing-folder detection:**
Before prompting, scan the user's knowledge folder for an existing `projects/` subdirectory:
- **If found AND `projects_enabled` is unset in config:** Skip the Advanced Options bullet for this feature; instead prompt directly: "Detected existing `projects/` folder with these subdirectories: [list]. Enable project-specific knowledge tier? (y/n)" — if yes, auto-populate `projects_list` from detected subdirectories (prompt for the path mapping per detected tag), then ask question 4 from the Project Setup flow above so the user can opt into `auto_load_project_context` at the same time.
- **If found AND `projects_enabled: false` explicitly in config:** Leave the existing folder untouched; note in verbose output: "An existing `projects/` folder was detected but the projects tier is disabled in config. Folder is preserved; automation is off."
- **If found AND `projects_enabled: true`:** Verify each detected subdirectory is in `projects_list`; prompt to add any missing ones. Then surface the current `auto_load_project_context` value as a status check: "Auto-load project context on session start is currently [on/off]. Change? (y/n, default n — keep current)." — this is the re-run discoverability path for toggling the flag when the tier was previously enabled.
**Never auto-delete or auto-rewrite existing `projects/` content.**
### Shared Knowledge Setup (only if user enables the project tier)
After Project Setup completes (questions 1-6), if `projects_enabled: true` AND `projects_list` is non-empty, ask two follow-up questions about the shared-knowledge feature. In **update mode** where values exist, show current values and let the user keep (press enter) or change.
7. **Which projects do you want to enable shared knowledge for?** — *"This is an opt-in extension that lets you promote selected personal knowledge into per-repo `_project-knowledge/` folders so teammates can see what you've learned. Personal knowledge stays in your own knowledge folder; team copies are independent records committed to your project repos via your normal git workflow. Most users have many repos but only a few with teams to share with — pick only the ones with teammates who'd benefit. Your configured projects: {projects_list tag enumeration}. Enter comma-separated tags (default: empty = feature disabled, all projects stay personal-only):"*
8. **Author tag for shared-knowledge filenames** — only ask if Q7 returned a non-empty tag list. *"Shared-knowledge files use `{YYYY-MM-DD}-{author-tag}-{slug}.md` naming. Pick a short author tag (e.g., `init`, or initials, or first2+last2 of your name). Default: derived from `git config user.name` (first 2 chars of first name + first 2 chars of last name) → '{auto-derived}':"*
**Validate input:**
- Q7 answer is a comma-separated tag list, or empty (= feature disabled). Each tag must already exist in `projects_list`. If a tag is not in `projects_list`, show the offending tag and re-prompt: *"Tag '{tag}' is not in projects_list. Available: {projects_list tags}. Re-enter:"*. Empty input is valid and means feature disabled.
- Q8 author_tag must be 1-12 characters, alphanumerics + hyphens only (the value will appear in filenames). If invalid, show offending characters and re-prompt.
- If Q7 returned a non-empty list but Q8 produces an empty value AND no derivable git user.name exists, warn: *"Author tag is required for shared knowledge. You can set `author_tag` later in `.cursor/aria-knowledge.local.md`, but `/audit-share` will refuse to run until it's set."* Continue setup with `author_tag:` empty.
**Schema note:** the config field `projects_shared_knowledge` is itself the comma-separated tag list (the value IS the scope). Empty/missing = feature disabled. There is no separate boolean toggle; the field's presence and content together encode "enabled and for which projects." A legacy value of `true` (from pre-publish v2.13.0 stubs) is treated the same as empty and triggers Q7 to populate the list properly on `/setup` re-run.
**CLAUDE.md reference handling deferred to first-write.** Earlier drafts of this spec offered to append `_project-knowledge/` references to project AGENTS.md files at setup time. That has been removed: documenting a convention before the folder exists is aspirational, batch-applying across all projects loses per-repo nuance (different repos may have different teams / visibility), and a default-`y` prompt for a teammate-affecting change is more aggressive than ARIA's normal posture. The CLAUDE.md reference offer now happens inside `/audit-share` Step 6.5 the first time a file is actually written to a repo's `_project-knowledge/` folder — at that moment the folder + README exist, the user has just made an active sharing decision, and per-repo confirmation with git-tracked detection can be presented in context. Step 6.5b additionally handles the multi-repo container CLAUDE.md case for tags with `projects_groups` entries.
**Existing `_project-knowledge/` folder detection:**
Before completing this section, scan for existing `_project-knowledge/` folders. Scan locations depend on whether the project is single-repo or multi-repo (matches `/audit-share` Step 2.3 and `/index` Phase 5 conventions):
- **Single-repo project** (no `projects_groups[tag]` entry): probe `<project-root>/_project-knowledge/`.
- **Multi-repo project** (`projects_groups[tag]` set): probe each sub-repo declared in the group (`<project-root>/<sub-repo>/_project-knowledge/`), in declaration order. Skip sub-repos whose path doesn't exist on disk.
For each scan location where a `_project-knowledge/` folder is found:
- **If found AND its parent project tag is NOT in the user's `projects_shared_knowledge` list:** Note in verbose output: *"An existing `_project-knowledge/` folder was detected at `<scan-location>` (parent project tag `{tag}`) but `{tag}` is not in your shared-knowledge list. Add `{tag}` to the list now? (y/n)"* — if yes, append the tag to the Q7 answer and continue.
- **If found AND its parent project tag IS in the list:** No action; the folder will be picked up by `/index` Phase 5 on next rebuild.
- **If found AND `projects_shared_knowledge` is empty:** Note: *"An existing `_project-knowledge/` folder was detected at `<scan-location>` but the shared-knowledge feature is disabled (empty list). Folder is preserved; `/index` and `/context` won't surface it until you enable the feature for tag `{tag}` via `/setup`."*
For multi-repo projects, all of the project's sub-repos are evaluated independently — finding `_project-knowledge/` in one sub-repo doesn't suppress the scan of others. Each surfaces its own note.
## Step 7: Write Config
Write `.cursor/aria-knowledge.local.md` with the collected settings:
```yaml
---
knowledge_folder: [path from Step 2]
audit_cadence_knowledge: [value from Step 6, default 7]
audit_trigger_threshold: [value from Step 6, default 20]
audit_cadence_config: [value from Step 6]
explanatory_plugin: [true/false from Step 5]
audit_cadence_update: [value from Step 6, default 30]
last_setup_version: [INSTALLED_VERSION from Step 1 — the plugin version active when this /setup ran]
freeform_promotion_threshold: [value from Step 6, default 3]
staleness_threshold_months: [value from Step 6, default 6]
ideas_staleness_threshold_days: [value from Step 6, default 7]
auto_capture: [true/false from Step 6, default true]
active_knowledge_surfacing: [true/false from Step 6, default true]
session_state: [true/false from Step 6, default false]
session_stale_days: [integer, default 7]
session_state_tracked: [true/false, default false]
auto_prospect: [off/nudge/run, default off]
auto_retrospect: [off/nudge/run, default off]
autonomy: [default/balanced/autonomous, default default]
retrospect_min_commits: [integer, default 3]
retrospect_branches: [comma-list, default main,master,production]
usage_alert_threshold: [value from Step 6, default 80; or `off` to disable usage injection]
critical_paths: [comma-separated patterns from Step 6, default empty]
planning_paths: [comma-separated patterns from Step 6, default empty]
external_fetch_gate: [on/off from Step 6, default off]
external_fetch_max_hits: [integer from Step 6, default 8]
preflight_gate: [off | warn | deny, from Step 6, default warn]
preflight_deny_paths: [space-separated globs from Step 6, default empty]
preflight_deny_repos: [comma-separated repo-path substrings from Step 6, default empty]
style_lookback_days: [integer from Step 6, default 90]
style_max_sessions: [integer from Step 6, default 50]
style_audit_log: [path from Step 6, default {knowledge_folder}/logs/style-audit-log.md]
ticketing_plugins: [comma-separated tag:plugin-command pairs from Step 6, default empty]
projects_enabled: [true/false from Step 6, default false]
projects_list: [comma-separated tag:path pairs from Step 6, default empty]
projects_remotes: [comma-separated tag:url-pattern pairs from Step 6, default empty]
projects_promotion_threshold: [integer from Step 6, default 2]
auto_load_project_context: [true/false from Step 6, default false]
session_start_project_picker: [true/false from Step 6, default false]
projects_labels: [comma-separated tag:Label pairs from Step 6, default empty]
projects_shared_knowledge: [comma-separated tag list from Shared Knowledge Setup Q7, default empty = feature disabled; each tag must exist in projects_list]
author_tag: [string from Shared Knowledge Setup Q8, default empty when projects_shared_knowledge is empty]
---
```
Add a markdown body below the frontmatter:
```markdown
# Knowledge Tools Configuration
Configured by /setup on [today's date].
```
In **update mode:** preserve any user-added content in the markdown body below the frontmatter when rewriting.
**Formatting rules** — the config file MUST follow these exact conventions or the hook scripts cannot parse it. The hooks parse this file using pure `grep + sed` (no jq/yq/python) — these constraints exist so the substitution patterns in `scripts/aria/config.sh` work correctly, and any deviation breaks parsing silently.
- Frontmatter delimiters must be exactly `---` on their own line (no leading spaces, no trailing content)
- Each key must start at column 1 with no indentation
- Keys use the exact names shown above (no quoting, no trailing spaces)
- Values must NOT be quoted — write `knowledge_folder: /path/to/folder`, not `knowledge_folder: "/path/to/folder"`
- **Empty values:** write `key:` with nothing after the colon (optionally one trailing space). Do NOT write `key: null`, `key: ""`, `key: none`, or `key: []` — the parser treats those as literal string values (`"null"`, `"\"\""`, etc.) and validators won't normalize them to empty
- `knowledge_folder` must be an absolute path (starts with `/`) and must not contain `..`
- Cadence values must be plain integers (no units, no quotes)
- `projects_enabled` must be exactly `true` or `false` (not `True`, `yes`, `1`, etc.)
- `projects_shared_knowledge` is a comma-separated tag list (e.g., `cs,ss`) — empty/missing = feature disabled. Each tag must already exist in `projects_list`. No spaces around commas. Tags cannot contain `:` or `,` (same as `projects_list`). A legacy literal `true` value is treated as empty (triggers `/setup` to repopulate the list properly). Requires `projects_enabled: true` to take effect.
- `author_tag` is a 1-12 char string of alphanumerics + hyphens (used in shared-knowledge filenames); leave empty if `projects_shared_knowledge` is empty
- `projects_list`, `projects_remotes`, and `ticketing_plugins`: comma-separated `tag:value` pairs, no spaces around the colon or comma (e.g., `proj-a:path/to/proj-a,proj-b:proj-b` for paths; `proj-a:foo-ticket,proj-b:bar-ticket` for plugin commands)
- Project tags (used in `projects_list`, `projects_remotes`, `ticketing_plugins`) cannot contain colons or commas (the parser splits on these)
- `ticketing_plugins` plugin-command values are bare command names without the leading `/` (e.g., `foo-ticket`, not `/foo-ticket`) — `/audit-knowledge` prepends the slash when printing the hint
- `last_setup_version` is a semver string read from `scripts/aria/VERSION` at Step 1 — write it as bare digits-and-dots (e.g., `2.12.1`), not quoted, not prefixed with `v`. The session-start hook compares this against the installed plugin version to detect upgrades since the user's last `/setup`
- `projects_promotion_threshold` must be a plain integer ≥ 1 (no units, no quotes)
- `auto_load_project_context` must be exactly `true` or `false` (not `True`, `yes`, `1`, etc.)
- No blank lines between frontmatter entries
- **Skill-only multi-line YAML blocks** (currently `projects_groups`; see `CONFIG.md`) must sit at the **end** of the frontmatter, after every column-1 hook-parsed key. Their indented sub-keys must use 2-space indents for tags and 4-space indents for role values. The blank-line-free rule applies inside the block too — no blank lines between sub-entries.
- **In update mode, preserve every skill-only multi-line YAML block verbatim.** Do not reformat, reorder, or strip sub-entries. The block was either written by an auto-propose bootstrap (`/distill`, `/stitch`) or hand-edited per `CONFIG.md`; `/setup` is read-only for these. If a block exists in the input config, copy it byte-for-byte to the output config; if absent, write nothing for that field.
## Step 7b: Verify Config Round-Trip
After writing the config file, read it back and verify that each value can be extracted using the same patterns that `config.sh` uses. This catches formatting issues before the user discovers them in the next session.
**Verification checks:**
1. Read `.cursor/aria-knowledge.local.md`
2. Extract the frontmatter block (content between the first and second `---` lines)
3. For each key, verify the value matches what was intended:
- `knowledge_folder` — grep for `^knowledge_folder:` and confirm the extracted path matches Step 2's value
- `audit_cadence_knowledge` — confirm it's the integer from Step 6
- `audit_trigger_threshold` — confirm it's the integer from Step 6 (default 20)
- `audit_cadence_config` — confirm it's the integer from Step 6
- `explanatory_plugin` — confirm it's `true` or `false`
- `audit_cadence_update` — confirm it's the integer from Step 6
- `freeform_promotion_threshold` — confirm it's the integer from Step 6
- `staleness_threshold_months` — confirm it's the integer from Step 6
- `ideas_staleness_threshold_days` — confirm it's the integer from Step 6
- `auto_capture` — confirm it's `true` or `false`
- `active_knowledge_surfacing` — confirm it's `true` or `false`
- `session_state` — confirm it's `true` or `false`
- `auto_prospect` / `auto_retrospect` — confirm each is `off`, `nudge`, or `run`
- `usage_alert_threshold` — confirm it's `off` or a plain integer in 1–100 (matches Step 6 input; default 80). Any other value is reset to 80.
- `critical_paths` — confirm it's a comma-separated string of path patterns (or empty)
- `planning_paths` — confirm it's a comma-separated string of path patterns (or empty)
- `preflight_gate` — confirm it is exactly `off`, `warn` or `deny`. Any other value is rewritten to `warn`, never to `off`: a typo must not silently disable a gate the user believes is on.
- `preflight_deny_paths` — confirm it's a space-separated string of globs (or empty). **Independent of `preflight_gate`, not a sub-setting of it**: these paths deny from any baseline, the same way `critical_paths` escalates Rule 22 regardless of surroundings. So `preflight_gate: warn` + named paths = "warn on code commits, but block on these" — the configuration most users want. Empty means no escalation. ⚠ Patterns are matched against **repo-relative** staged paths, and the docs filter drops `*.md`/`*.txt`/`*.rst`/`docs/*` before matching — so a `.md` path here can never fire, and a bare filename will not match that file nested in a subdirectory.
- `preflight_deny_repos` — confirm it's a comma-separated string of substrings (or empty); no spaces around commas. Matched against the repository's resolved absolute path, so it expresses "always gate this repo" — which `preflight_deny_paths` structurally cannot, staged paths being repo-relative. Independent of the gate, exactly like `preflight_deny_paths`. Empty means no escalation.
- `style_lookback_days` — confirm it's the integer from Step 6 (default 90)
- `style_max_sessions` — confirm it's the integer from Step 6 (default 50)
- `style_audit_log` — confirm it's a path string (default `{knowledge_folder}/logs/style-audit-log.md`, with `{knowledge_folder}` resolved to the actual configured path)
- `ticketing_plugins` — confirm it's a comma-separated string of `tag:plugin-command` pairs (or empty); validate no project tag contains `:` or `,`; validate plugin-command values do not start with `/`
- `last_setup_version` — confirm it matches `INSTALLED_VERSION` captured in Step 1 (this run's plugin version); validate it's a semver-shaped string of digits and dots (no `v` prefix, no quotes, no trailing whitespace). If it's missing or doesn't match, rewrite the line and re-verify
- `projects_enabled` — confirm it's `true` or `false`
- `projects_list` — confirm it's a comma-separated string of `tag:path` pairs (or empty); validate no project tag contains `:` or `,`
- `projects_remotes` — confirm it's a comma-separated string of `tag:url-pattern` pairs (or empty); validate no project tag contains `:` or `,`
- `projects_promotion_threshold` — confirm it's a plain integer ≥ 1 (matches Step 6 input)
- `auto_load_project_context` — confirm it's `true` or `false`
- **Empty-sentinel check** — for string-valued keys with an empty default (`critical_paths`, `planning_paths`, `preflight_deny_paths`, `preflight_deny_repos`, `ticketing_plugins`, `projects_list`, `projects_remotes`): confirm the raw extracted value is not the literal string `null`, `""`, `none`, or `[]`. If the key is intended to be empty, the value after the colon must be truly empty (nothing or a single trailing space). Rewrite the key as `key:` and re-verify.
**Skill-only field validation (`projects_groups`)** — if the field is present in the config, run structural-only checks. Do not attempt to flatten or rewrite this field; it's parsed by skills, not bash, so the verification mirrors that consumer.
1. **Block placement** — `projects_groups:` must sit **after** every hook-parsed key. If a column-1 hook-parsed key appears below the block (between it and the closing `---`), the parser scope is at risk. Move the block to the end of the frontmatter and re-verify.
2. **Indentation shape** — sub-tags use 2-space indents; role values use 4-space indents; no blank lines inside the block. Use this awk pattern to extract the block and inspect:
```bash
awk '/^projects_groups:$/{in_block=1; next} in_block && /^---$/{exit} in_block && /^[^[:space:]]/{exit} in_block{print}' .cursor/aria-knowledge.local.md
```
Reject the block if any line inside the block fails to match `^ [^[:space:]].*:$` (tag header) or `^ [^[:space:]].*: .+$` (role value). Report the offending line and stop — do not auto-rewrite (the user may have a custom role layout the skills support but the regex doesn't predict).
3. **Tag cross-check (warn, do not fail)** — every tag inside `projects_groups` should also appear in `projects_list` so `/distill` and `/stitch` can resolve `<project_root>`. If a `projects_groups` tag is not in `projects_list`, emit a warning: *"Warning: `projects_groups` tag `{tag}` is not declared in `projects_list`. `/distill --group={tag}` and `/stitch create {tag}` will fail until `{tag}` is added to `projects_list`. (This may be intentional if you're staging a project not yet path-mapped.)"* Do not block setup.
**If any check fails:** rewrite the file with corrected formatting and verify again. Report which value failed and what was fixed.
**If all checks pass:** proceed to Step 7c silently.
## Step 7c: Project Tier Scaffolding
Runs only if the config just written has `projects_enabled: true` and a non-empty `projects_list`. Skip entirely otherwise — no action, no output.
Scaffold the project tier using the final config values:
1. **Create `projects/` directory** if it doesn't exist.
2. **Copy `knowledge/projects/README.md` to `projects/README.md`** if missing (plugin-managed; will be diffed on future `/setup` runs).
3. **For each entry in `projects_list` (parsed as `tag:path` pairs):**
- Create `projects/{tag}/` if missing.
- Create `projects/{tag}/decisions/`, `projects/{tag}/patterns/`, and `projects/{tag}/rules/` if missing. The `rules/` subdir is the destination for `/audit-knowledge` Step 7's project-tier rule promotion (`{knowledge_folder}/projects/{tag}/rules/working-rules.md`); it stays empty until the first rule is promoted.
- If `projects/{tag}/README.md` does not exist, generate it from this per-project template:
```markdown
---
Last updated: [today's date]
tags: [{tag}, knowledge-structure]
---
# {Project Display Name} Project Knowledge
Project-specific architecture decisions, patterns, and gotchas for {project display name}.
## Structure
- `decisions/` — Architecture Decision Records (ADRs) — numbered sequentially per project (001, 002, ...)
- `patterns/` — Reusable patterns specific to this project
- `rules/` — Project-specific working rules promoted from `intake/rules-backlog.md`; lands `working-rules.md` here
- `guides/` (optional) — Operational knowledge specific to this project; create on demand
- `references/` (optional) — External resources specific to this project; create on demand
## Promotion
When a pattern in this folder is validated in another project, `/audit-knowledge` will surface it as a candidate to promote to `knowledge/approaches/`. See `knowledge/projects/README.md` for the full promotion ladder.
## Related
- [../README.md](../README.md) — projects/ tier overview
- [../../index.md](../../index.md) — tag index
```
- **Project Display Name** is derived from the tag with hyphens converted to spaces and title-cased (e.g., `proj-a` → `Proj A`). If the tag doesn't produce a sensible display name, use the tag as-is and prompt the user to edit the README header.
4. **Never overwrite** existing per-project READMEs or content under `projects/{tag}/` — these are user-owned.
5. **Report** what was scaffolded: "Project tier: created N directories, N per-project READMEs."
## Step 7d: Shared Knowledge Initial Sync
Runs only if the config just written has a non-empty `projects_shared_knowledge` tag list AND a non-empty `author_tag`. Skip entirely otherwise — no action, no output.
This step does NOT auto-create `_project-knowledge/` folders in any repo. Folders are created on demand by `/audit-share` Step 5 (when the user actually shares the first file to that repo). This avoids littering empty folders into repos the user may not actively use.
**Initial sync offer:**
Prompt the user:
> *"Run `/audit-share` now to review your existing personal knowledge for sharing? This is the cold-start sweep — without it, the feature is enabled but nothing is shared yet (every audit-share run is opt-in per item). (Y/n, default y):"*
If yes: invoke `/audit-share` inline as the next action. The user will see the audit-share batch summary and decide what to share. Setup's Step 8 (Confirm) runs after audit-share completes.
If no: continue to Step 8. Note in setup output: *"Shared knowledge enabled but not yet populated. Run `/audit-share` anytime to do an initial sweep, or it'll surface candidates as they accumulate in your knowledge folder."*
## Step 7e: Self-Validation Audit (v2.15.2+)
After Step 7b's round-trip verification, run a coverage audit to catch any `KT_*` fields documented in `scripts/aria/config.sh` but missing from the user's written config. **This is a defense-in-depth check against the wizard's own discipline failures** — if Step 6's Advanced Options bundle silently skipped surfacing a key (e.g., the `active_knowledge_surfacing` gap that bit v2.15.1's first users), this step catches it before the user leaves `/setup` thinking everything is current.
**Algorithm:**
1. Enumerate known user-facing field names by reading `scripts/aria/config.sh` and extracting them from the parse lines. Each known field has the shape:
```bash
KT_FIELDNAME=$(sed -n '/^---$/,/^---$/p' "$KT_CONFIG" | grep '^fieldname:' | sed 's/^fieldname: *//')
```
Use `grep -oE "grep '\\^[a-z_]+:'" plugin-claude-code/scripts/aria/config.sh | grep -oE '[a-z_]+'` to extract the user-facing field names — those are the canonical list of fields the wizard should have covered.
2. For each known field, grep the just-written config `.cursor/aria-knowledge.local.md` for `^{fieldname}:`. If the grep returns zero hits, add to a `MISSING_FIELDS` list.
3. **If `MISSING_FIELDS` is non-empty:**
- Output: `Self-validation found {N} known field(s) missing from your config: {field1}, {field2}, ...`
- For each missing field, look up its default value by reading the matching `KT_FIELDNAME=${KT_FIELDNAME:-default}` line in `scripts/aria/config.sh`. If no default is set, treat as empty.
- Prompt: *"Add all {N} missing fields with their defaults? (y/n/select): {field1}={default1}, {field2}={default2}, ..."*
- If user answers **y**: append each missing field as `fieldname: default` between the last column-1 hook-parsed field and the closing `---` of the frontmatter. Re-run Step 7b's round-trip verification on the additions.
- If user answers **n**: emit a one-liner to the setup output: *"Self-validation skipped: {N} field(s) missing ({list}). Run `/audit-config` later to surface them again, or hand-add to `.cursor/aria-knowledge.local.md`."* Do not block setup.
- If user answers **select**: walk per-field, prompting `Add {fieldname}: {default}? (y/n)` for each. Aggregate decisions; apply approved fields atomically.
4. **If `MISSING_FIELDS` is empty:** print `Self-validation passed: all {N} known fields present in config.`
**Why this exists (v2.15.2 Origin):** the `[NEW]` detection in Step 6's Advanced Options was specced to surface new-since-last-setup keys, but Step 6 is a *soft instruction* to Claude — it's not hook-enforced, so a fast or quiet /setup run can silently skip the detection. Step 7e is a final verification gate that runs against the canonical config.sh source of truth, surfacing any gap regardless of how the wizard got there. Pairs with `/audit-config`'s missing-known-fields cascade check (Step 3b) as the audit-cadence safety net.
## Step 8: Confirm
Output a summary:
```
Setup complete for ARIA v[INSTALLED_VERSION].
- Knowledge folder: [path]
- Knowledge audit: every [N] days
- Config audit: every [N] days
- Update check: every [N] days
- Insight capture: [enabled/disabled]
- Auto-capture on compaction: [enabled/disabled]
- Ticketing plugins: [N mappings configured | not configured (empty — change anytime by re-running /setup; the advanced-options bundle always shows the current value)]
- Shared knowledge: [enabled (author_tag: {tag}) | disabled (opt-in via re-run /setup)]
- Files added: [N]
- Files updated: [N]
- Files kept (user version): [N]
Two habits that make ARIA most effective:
- Run /extract before ending sessions — captures knowledge while the full conversation is in context
- Respond to "Knowledge audit due" prompts — promotes pending items so /context can surface them later
Everything else runs automatically via hooks.
```
## Step (optional): Schedule the morning PM review (Claude Code, macOS only)
If the user wants `/aria-assist` to run automatically each morning, offer to install the launchd job:
> "Want me to schedule the morning PM review? It runs `/aria-assist generate` at your
> `pm_schedule_time` (default 07:30) and notifies you. macOS only; you can remove it later with
> `sh <plugin>/bin/pm-schedule.sh --uninstall`."
On yes (Bash available): `sh scripts/aria/pm-schedule.sh`.
The iMessage notification path needs a one-time **Automation permission** grant
(System Settings → Privacy & Security → Automation); the desktop banner always works.
The schedule also surfaces as a read-only "Morning run" card in aria-atlas (if you use it),
which reads the status from `<knowledge_folder>/pm-reviews/.aria-assist.json` (written by
`pm-schedule.sh` on install/uninstall and refreshed by each run).
---
## /help
# /help — aria-knowledge Commands
Print the command reference table. No config or file access needed.
## Output
```
## aria-knowledge Commands
| Command | Description |
|---------|-------------|
| /setup | Configure knowledge folder, audit cadences, and plugin settings |
| /extract | Capture insights, decisions, and feedback from the current conversation |
| /audit [knowledge\|config\|style\|all] | Umbrella audit dispatcher — routes to the sub-audit named, or runs all in sequence with no arg |
| /audit-knowledge (alias: /knowledge-audit) | Review backlogs, promote to knowledge files, rebuild index |
| /audit-config (alias: /config-audit) | Check project configs and docs for drift and broken references |
| /audit style | Log-mining audit over session transcripts for revealed working-style rules (opt-in — not part of routine cadence) |
| /audit-share | Promote personal knowledge to the team-shared `_project-knowledge/` tier |
| /prospect [plan/session/todos/file/linear/branch] | Forward-looking pre-mortem on a plan before any code — per-step risk verdicts (PROCEED/SHRINK/SPLIT/DEFER/KILL), evidence-sourcing pass, simpler-alternative discipline |
| /preflight [ticket/file] | Executed pre-completion checklist run just before you claim done — six checks (requirements diff, consumer census, reachability, census bound, non-vacuity, mutation), three outcomes each; an unrun check blocks the verdict |
| /retrospect [--range/--pr/--session/--commit] | Structured retrospective on a shipped commit range — per-fix validation, simpler-alternative discipline, re-diagnosis, action verdicts, failure-mode pattern check |
| /recap [arc\|commit\|push\|pull] | Read-only orientation — a scannable What/Where/Status table of recent work (this session by default; or the last arc/commit/push/pull). Summarizes, never validates; writes nothing |
| /roadmap [<name>\|refresh] | Per-project feature roadmap — a Feature/Band/Status grid (Shipped/Current/Next/Later × done/in-progress/blocked/buildable) synthesized from CLAUDE.md + PROGRESS.md, persisted to a committed ROADMAP.md with staleness-aware refresh. Renders + offers refresh when stale; never auto-commits |
| /foundational-review <scope-root> [--decision "..."] [--extend] | Foundational review chain before an irreversible decision (freeze/tag/flip/re-scope): verdict + premises + A–F → design spec → cold-executable plan → composed /prospect → kickoff. Requires a named irreversible decision (else redirects). |
| /readiness-audit <scope-root> [--for "<event>"] | Surface readiness audit (sibling of /foundational-review): parallel exploration → controller re-verification of agent claims → tiered evidence-celled findings → phased remediation. Read-only probes; no decision anchor needed. |
| /context [tags] | Load relevant knowledge files by topic (supports AND/OR, project expansion) |
| /index | Rebuild the tag-based knowledge index with cross-references |
| /rules [number] | Look up a working rule by number or keyword |
| /backlog [type] | View and manage pending intake items |
| /stats | Knowledge base health dashboard — file counts, backlogs, audit status |
| /ask [question] | Research a question, check existing knowledge, save answer as a knowledge doc |
| /intake [url or text] | Clip a single URL/snippet whole → references/sources/ (reviewed at next /audit-knowledge) |
| /intake [path or dir or glob] | Bulk import knowledge from files, directories, or globs into the backlogs |
| /intake extract [source] | Decompose a source (URL/file/doc via ~~docs MCP) into backlog entries |
| /intake doc [url or title] | Capture a single doc with 5-section structured body (claims/keeping/contested/action/reaction) → intake/docs/ |
| /intake thread [id] | Pull a chat/email thread via ~~chat/~~email MCP → references/sources/ |
| /interview <mode> | Elicit knowledge via dialogue (project / knowledge / deep-dive); chooses cadence in-session; stages to intake/ for manual review |
| /codemap [mode] | Feature-organized CODEMAP.md for any codebase (create/inventory/update/section) |
| /distill [text or path] | Tiered task spec from raw text; optional --group for CODEMAP-loaded context |
| /stitch <mode> <group> | Cross-repo binding (auth/endpoints/entities/drift) for a product group |
| /wrapup [auto\|snap] | End-of-session close-out — update PROGRESS/CLAUDE.md, prompt for commit, verify continuity. `auto` runs silently; `snap` runs like auto but archives the transcript via /snapshot for later extraction instead of /extract (use when context is high) |
| /handoff [auto\|brief\|snap] | Express handoff — same coverage as /wrapup, one combined-go review (or `auto` for silent), always emits a paste-ready next-session opener. `brief` mode produces a copy/paste coworker brief (Hey [coworker]-style prose, 80-150 words) instead of next-session opener — no PROGRESS/CLAUDE/memory/commit/extract side effects. `snap` mode runs like auto but archives the transcript via /snapshot for later extraction instead of /extract (use when context is high) |
| /snapshot | On-demand task-boundary capture (git + hook state) to intake/task-boundary-captures/ |
| /statusline [on\|off\|status] | Install/remove the CLI status-line meter — context-window bar + 5h/7d plan-usage % (Claude Code only) |
| /help | This command reference |
Run /setup to configure. See QUICKSTART.md for a walkthrough of your first 3 sessions.
## Model Recommendations
These are recommendations only — ARIA does not force a model. Switch per session via `/model` based on the skill you're about to run.
| Skill | Recommended Model | Why |
|-------|-------------------|-----|
| /extract | Highest-capability Opus, medium-to-high effort | Judgment-heavy: distinguishing reusable signal from ephemeral noise, writing non-obvious Why/How-to-apply lines. |
| /audit-knowledge | Highest-capability Opus, medium-to-high effort | Cross-references backlogs against the promoted index, decides promotion vs. discard, detects emerging themes. |
| /audit-config | Highest-capability Opus, medium-to-high effort | Reads across AGENTS.md files and configs to detect drift and broken references. |
| /preflight | Whatever model is doing the work — no escalation | Deliberately runnable at any tier. The checks are mechanical (census, importers, call counts, mutation); escalating the model would imply the gate is a judgment call, and its whole premise is that judgment already failed once. |
| /retrospect | Highest-capability Opus, medium-to-high effort | Multi-stage judgment per fix: validation status assignment, simpler-alternative identification, hypothesis generation, failure-mode pattern matching, action verdict synthesis. Highest leverage from stronger models. |
| /foundational-review, /readiness-audit | Highest-ceiling available (Fable at extreme stakes, else Opus), xhigh effort | The reviewer model is spent on alternatives-steelmanning, portfolio/product judgment, and the irreversibility inventory; semi-agentic read-trace-reason loop benefits from xhigh. Executor tasks the chain emits route to Opus by default. |
| /ask | Highest-capability Opus, medium-to-high effort (ambiguous topics) or Sonnet (mid-tier) for scoped lookups | Research + draft + categorize. Drop to Sonnet when the question is narrow. |
| /interview | Highest-capability Opus for deep-dive (ambiguous, evidence-cited, leverage-clustered question generation) or Sonnet (mid-tier) for focused guided project/knowledge runs | Deep-dive is judgment-heavy (cite evidence, cluster by leverage, hunt negative space) and re-deriving after every guided dialog compounds that; focused elicitation is lighter. Spans tiers like /ask. |
| /codemap create | Highest-capability Opus (large-context variant preferred) | Full-repo traversal benefits from a large context window so sections aren't truncated mid-generation. |
| /codemap update, /codemap section, /wrapup, /handoff, /intake, /distill, /stitch | Sonnet (mid-tier), medium effort | Structured work with clear prescribed output. |
| /index, /stats, /backlog, /rules, /context, /intake, /snapshot, /statusline, /help, /setup | Sonnet (mid-tier), low effort | Mechanical or retrieval-only — higher models add no measurable lift. |
Always pick the latest release within each tier — ARIA pins capability *tiers*, not version numbers, so this guidance survives model updates.
`Fable` (displayed "Fable 5") is the tier above Opus. Its edge is raw capability/judgment, **not** context size — Fable and Opus share the same 1M-token window. Treat it as a step-up only for the most judgment-heavy, high-stakes runs where a wrong or shallow answer is costly (`/extract`, `/audit-knowledge`, `/retrospect` on genuinely hard sessions). It costs ~2× Opus, so reach for it when difficulty — not data volume — justifies the spend; the Opus rows otherwise stand. (Note: the "large-context variant preferred" qualifier on `/codemap create` above is legacy — any current top-tier model, Opus 4.8 included, already carries the 1M window, so full-repo traversal no longer needs a special variant.)
Any model below Sonnet-equivalent capability is not recommended for any ARIA skill — the judgment/cross-reference demands exceed its strengths.
The honest test: will a stronger model change what ends up in the knowledge base? For `/extract` and `/audit-knowledge`, yes, measurably. For `/index` and `/stats`, no.
```
---
## /audit-share
# /audit-share — Batch-Review Personal Knowledge for Team Sharing
Walk personal knowledge files and IDEAS-BACKLOG.md entries; recommend a target `_project-knowledge/` destination per item; present a batch summary; let the user approve, modify, or skip.
## Step 0: Resolve Config
Read `.cursor/aria-knowledge.local.md` and extract:
- `knowledge_folder` — required
- `projects_enabled` — required (must be `true`)
- `projects_list` — required (parsed into tag→path map)
- `projects_shared_knowledge` — required (comma-separated tag list; non-empty list of tags from `projects_list`); each tag in the list is a project enabled for shared knowledge
- `author_tag` — required (non-empty); fall back to deriving from `git config user.name` (first 2 chars of first + first 2 chars of last) if missing
If the config file doesn't exist: *"aria-knowledge is not configured. Run /setup to get started."*
If `projects_shared_knowledge` is empty/missing (or the legacy literal `true`): *"Shared knowledge has no projects enabled. Run /setup and pick which projects to enable in the 'Which projects do you want to enable shared knowledge for?' prompt."*
If `projects_enabled: false` or `projects_list` empty: *"Shared knowledge requires the project tier. Run /setup to enable projects and configure your project list."*
If `author_tag` is missing AND no derivable git user.name: *"Author tag is required. Set `author_tag` in `.cursor/aria-knowledge.local.md` (e.g., `init`) or configure `git config user.name`."*
## Step 1: Scan Candidates
Walk these directories under `{knowledge_folder}/`:
- `insights/`
- `decisions/`
- `approaches/`
- `rules/`
- `projects/<tag>/` for each tag in `projects_shared_knowledge` (the per-project opt-in list — projects not in this list stay personal-tier and are skipped here)
Plus IDEAS-BACKLOG.md entries from each project root:
- For each tag in `projects_shared_knowledge`, resolve to project root via `~/Projects/<path>` (where `<path>` is the corresponding `projects_list` value).
- Probe `<project-root>/_project-knowledge/IDEAS-BACKLOG.md` first (post-feature location).
- Fall back to `<project-root>/IDEAS-BACKLOG.md` (pre-feature location, will trigger Step 7 migration on first execute).
- Parse the file by `### YYYY-MM-DD — {title}` headers; treat each section as a candidate "entry."
**Skip candidates** when:
- File frontmatter contains a `shared:` array with an entry whose `path` matches the proposed target. (Already shared; no action needed.)
- File type is `feedback` or `references` (out of scope per design Q10 — feedback memories tend to be personal preferences; references are pointers to external systems that may not apply uniformly to teammates).
## Step 2: Suggest Action Per Candidate
For each candidate, determine:
1. **Project tag(s)** — derived from any of three sources, unioned (matches `/index` Phase 4 Decision #9 path-derived convention so audit-share and /index see the same tag set):
- **Path-derived:** if file path is under `{knowledge_folder}/projects/<tag>/`, that `<tag>` is implicit (even if not in YAML frontmatter).
- **Frontmatter `project:` field** if present (split on `,` for multi-value).
- **Frontmatter `tags:` array:** any tag matching a project in `projects_shared_knowledge` is treated as a share signal. A file tagged `[architecture, cs, ss]` with `cs,ss` in `projects_shared_knowledge` produces TWO share recommendations (one to cs, one to ss) — multi-tag files generate one recommendation per matching project, with independent destinations.
- The literal value `cross` (in any source) marks the file as cross-cutting within its product group, not as cross-PROJECT-GROUP. Cross-PROJECT-GROUP relevance is naturally expressed by multi-tag (e.g., `[cs, ss]` = relevant to both cs and ss product groups) and is handled by the multi-tag fan-out above, not by `cross`.
2. **Recommended action** (per recommendation produced in step 1):
- Tag is `cross` → recommend **share-to-cross** (will need user to pick destination repo at execute time, since cross items can land in any repo's `cross/` subfolder; cross destinations may be any tag from `projects_shared_knowledge`).
- Tag matches a tag in `projects_shared_knowledge` → recommend **share-to-{project}**.
- Tag exists in `projects_list` but is NOT in `projects_shared_knowledge` → recommend **skip** with reason "project not enabled for shared knowledge (use `/setup` to enable)".
- Otherwise (no tag detected, or tag not in projects_list) → recommend **skip**.
3. **Target path** — compute as follows. Multi-repo projects (those with a `projects_groups` entry) require sub-repo selection because the projects_list path resolves to a container, not a git repo:
- **Single-repo project** (no `projects_groups[tag]` entry):
- Repo-scoped: `<project-root>/_project-knowledge/<YYYY-MM-DD>-<author_tag>-<slug>.md`
- Cross-stack: `<destination-repo-root>/_project-knowledge/cross/<YYYY-MM-DD>-<author_tag>-<slug>.md`
- **Multi-repo project** (`projects_groups[tag]` is set, listing role:sub-repo pairs like `backend: foo-backend`, `web: foo-web`, `mobile: foo-mobile`):
- Run **role-detection heuristic** on file content + frontmatter tags:
- **backend keywords:** django, flask, fastapi, server-side, api, endpoint, jwt, oauth, auth, idor, impersonation, sql, database, migration, model, view, serializer, drf, rest, graphql resolver
- **web keywords:** nextjs, next.js, react, frontend, client-side, app router, spa, redux, rtk, rtk-query, css, tailwind, ui component, hook, page, route component
- **mobile keywords:** ios, android, react native, expo, swift, kotlin, swiftui, jetpack
- Plus any **custom roles** defined in `projects_groups[tag]` — match keywords from the role name itself plus inferable tokens.
- Score = count of matching keywords per role (case-insensitive whole-word match against body + tags).
- **Single dominant role** (one role's score is ≥2× the next, AND ≥3 hits): recommend that role's sub-repo, repo-scoped destination.
- **Multiple roles tied or all low scores**: recommend cross-stack → **primary sub-repo's `_project-knowledge/cross/`**.
- **Primary sub-repo** = first role declared in `projects_groups[tag]` (declaration order, NOT alphabetical), OR an explicit `primary:` field if user has added one to the group entry. For example, given `webapp: { backend: api-server, web: web-client, mobile: mobile-app }`, primary is `api-server`.
- Multi-repo paths:
- Repo-scoped (dominant role detected): `<project-root>/<role-sub-repo>/_project-knowledge/<YYYY-MM-DD>-<author_tag>-<slug>.md`
- Cross-stack (no dominant role): `<project-root>/<primary-sub-repo>/_project-knowledge/cross/<YYYY-MM-DD>-<author_tag>-<slug>.md`
- **Common to both shapes:**
- **Date** is today (the share date), not the original capture date.
- **Author** is `author_tag` from config.
- **Slug** is derived from the source filename (strip date prefix and extension) or from frontmatter `title:`.
- **Collision handling**: if the target path already exists, append `-2`, `-3`, etc. to the slug until unique.
4. **IDEAS-BACKLOG.md entries** are special-cased: each entry promotes by appending to the project's IDEAS-BACKLOG.md, not by creating a new file. Path resolution mirrors step 3's single-vs-multi-repo logic — single-repo: `<project-root>/_project-knowledge/IDEAS-BACKLOG.md`; multi-repo: `<project-root>/<primary-sub-repo>/_project-knowledge/IDEAS-BACKLOG.md` (always primary, since IDEAS-BACKLOG entries are project-wide queue items not per-role). Cross-cutting ideas append to `cross/IDEAS-BACKLOG.md` instead.
5. **Public-repo flag** — for each unique target sub-repo (not container), run `gh repo view --json visibility 2>/dev/null` (cache result for the session). If visibility is `PUBLIC`, mark target as needing sanitization warn-prompt at execute time. If `gh` is unavailable, skip the check (do not block); note in summary as "could not verify repo visibility."
## Step 3: Present Batch Summary
Group recommendations by action; number continuously across groups; flag public-repo targets:
```
audit-share — found N candidates not yet in shared knowledge
## Recommended: share to repo (X)
1. knowledge/insights/foo.md (project: proj-a)
→ ~/Projects/<path>/_project-knowledge/2026-04-28-init-foo.md
2. knowledge/decisions/008-bar.md (project: proj-a)
→ ~/Projects/<path>/_project-knowledge/2026-04-28-init-bar.md
⚠️ public repo — content-safety prompt at execute time
3. ...
## Recommended: share to cross (Y)
9. knowledge/approaches/api.md (project: cross)
→ ⚠️ pick destination repo at execute time
10. ...
## Recommended: skip (Z)
12. knowledge/feedback/baz.md — feedback type, not in scope
13. knowledge/insights/qux.md — no project tag, no destination
...
⚠️ T of these target public repos: <repo-1>, <repo-2>
Decide:
all — execute all recommended actions
numbers (1 3 5) — execute only specified items
modify N — change action / destination / slug for item N
skip — cancel without executing
```
**Section omission**: omit a heading entirely if its count is zero. If no candidates at all, skip Steps 4-7 and report: *"No candidates to share — all eligible knowledge is already shared or out of scope."*
## Step 4: User Decision
Wait for user input. Parse:
- `all` — proceed with all recommendations from Steps 1-3.
- Space- or comma-separated numbers (e.g., `1 3 9` or `1,3,9`) — proceed only with those items.
- `modify N` — sub-prompt for item N:
- *"What would you like to change for item N? [action / destination / slug / skip]"*
- Apply the change; re-summarize the modified item; wait for confirmation; then continue to Step 5.
- `skip` — cancel without executing; jump to Step 8 with a "user-cancelled" report.
## Step 5: Execute Approved Actions
For each approved item, execute in order:
1. **Resolve cross destination** (only if action is share-to-cross and destination not yet set): prompt *"Pick destination repo for cross item: <list of projects_shared_knowledge entries>"*. For multi-repo projects, the cross destination further resolves to that project's primary sub-repo's `_project-knowledge/cross/` folder (not the container). Update target path accordingly.
2. **Sanitization warn-prompt** (only if target repo is public):
```
⚠️ Target repo "<repo-name>" is PUBLIC.
Source file: <personal-path>
Confirm content has no secrets / internal URLs / personal names? [yes / no / show-file]
```
- `yes` — proceed.
- `no` — skip this item; record as `sanitization-blocked` in the summary.
- `show-file` — print full file body for review; re-prompt yes/no.
3. **Read source file** — full content with frontmatter.
4. **Build team copy**:
- Strip personal-only frontmatter fields (e.g., `originSessionId`, `name`, `description`, internal session IDs).
- Add team-copy fields: `origin: <relative-path-from-knowledge-folder>`, `shared_by: <author_tag>`, `shared_at: <YYYY-MM-DD>`, `project: <repo-tag-or-cross>`.
- Preserve `title:`, `tags:`, body content as-is.
5. **Write team copy** at target path (Step 2's computed path). For IDEAS-BACKLOG.md entries, append the section to the target file rather than creating a new file.
6. **Update personal copy frontmatter** — add an entry to the `shared:` array:
```yaml
shared:
- path: <repo>/_project-knowledge/2026-04-28-init-foo.md
date: 2026-04-28
```
If the array doesn't exist, create it. If it exists, append (don't overwrite — supports re-sharing to multiple repos over time).
7. **Auto-create README.md** if this is the first write to this `_project-knowledge/` folder (folder was empty or didn't exist before). Use the template from Step 6 below.
8. **`git add`** the new/changed files. The git operation must run **inside the destination sub-repo's working tree** (`<sub-repo-root>` for multi-repo projects, `<project-root>` for single-repo) — running `git add` from a non-repo container directory silently no-ops. Use `git -C <sub-repo-root> add <relative-path>` to make the working-tree explicit. **Do NOT commit** — user reviews and commits via normal flow.
## Step 6: README Template (Auto-Created on First Write)
When the first file is written to a repo's `_project-knowledge/` folder, create `README.md` alongside it with this content:
```markdown
# _project-knowledge/
This folder holds team-shared project knowledge promoted from individual developers' personal knowledge captures.
**Convention:**
- Files use `{YYYY-MM-DD}-{author-tag}-{slug}.md` naming (e.g., `2026-04-28-init-foo.md`).
- Each file's frontmatter includes `origin:` (where it came from), `shared_by:` (who promoted it), and `shared_at:` (when).
- The `cross/` subfolder holds cross-cutting knowledge that applies across multiple repos in the same product group.
- `IDEAS-BACKLOG.md` is a queue of unscheduled future work — entries are dated sections.
**Tooling (optional):**
- These files are plain markdown — readable and editable without any tool.
- The ARIA Claude Code plugin (https://github.com/mikeprasad/aria-knowledge) provides:
- `/audit-share` to promote personal knowledge here
- `/index` + `/context` to discover and load these files into Claude sessions
- Non-ARIA teammates can read and write directly; no tool dependency.
**Contributing:**
- Edit personal knowledge in your own knowledge store, then promote via `/audit-share` (or copy manually).
- Direct edits to files in this folder are fine; commit through normal PR review.
```
## Step 6.5: CLAUDE.md Reference Offer (First-Write Hook, Per Repo)
The first time `audit-share` writes to a repo's `_project-knowledge/` folder (same trigger as Step 6's README auto-create), offer to add a `_project-knowledge/` reference to that repo's `CLAUDE.md` so non-ARIA teammates can discover the convention. This used to be a setup-time batch prompt; it was deferred here so the documentation appears alongside the first real share rather than as an aspirational forward reference.
Per-repo, gated by these conditions in order:
1. **Probe** for `<repo-root>/CLAUDE.md`. If absent, skip silently (don't auto-create).
2. **Probe** for an existing `## Team-Shared Knowledge` heading inside the CLAUDE.md (or a previous reference to `_project-knowledge/`). If present, skip silently (already documented; don't append again on re-shares).
3. **Detect git tracking** via `git -C <repo-root> ls-files --error-unmatch CLAUDE.md` (exit 0 = tracked, non-zero = untracked or no git). Cache result for the session.
4. **Detect remote visibility** if tracked: `gh repo view --json visibility 2>/dev/null` (cache per repo for the session). If `gh` is unavailable, treat as "unknown remote."
**Prompt user with the appropriate warning tier:**
| Tracking state | Prompt form |
|----------------|-------------|
| Untracked or no git | `Add a _project-knowledge/ reference to <repo-root>/CLAUDE.md? This is a 5-line section explaining the convention to teammates not using ARIA. (y/N)` |
| Tracked, public remote | `⚠️ <repo-root>/CLAUDE.md is committed to a PUBLIC remote — this edit will be visible to anyone on push. Add a _project-knowledge/ reference? (y/N)` |
| Tracked, private remote | `<repo-root>/CLAUDE.md is committed to a remote — teammates will see this edit on next push. Add a _project-knowledge/ reference? (y/N)` |
| Tracked, unknown remote (gh missing or repo not on GitHub) | `<repo-root>/CLAUDE.md is git-tracked — committing this edit will broadcast it to anyone with the remote. Add a _project-knowledge/ reference? (y/N)` |
**Default is N** for all four tiers. Per-repo confirmation matches the cadence of `/setup`'s file-diff prompts.
**On `y`:** append the following block to `<repo-root>/CLAUDE.md` (insert after the title H1 if one exists, otherwise append at end). `git add` the change but do NOT commit.
```markdown
## Team-Shared Knowledge
Team-shared knowledge for this repo lives in `_project-knowledge/` (committed). Files follow `{YYYY-MM-DD}-{author}-{slug}.md` naming with frontmatter origin pointers. Cross-cutting items live in `_project-knowledge/cross/`. See `_project-knowledge/README.md` for the convention.
```
**On `N` or empty input:** skip; record the decision in the Step 8 report ("CLAUDE.md reference declined for `<repo-root>`"). User can add manually later or accept on a future first-write to a different repo.
**Idempotency:** the existing-heading probe in step 2 above prevents duplicate sections if the user accepts on a first share, then later runs `audit-share` again with new content into the same repo. The "first-write hook" trigger only fires when `_project-knowledge/` is newly created OR when CLAUDE.md still lacks the reference.
## Step 6.5b: Container CLAUDE.md Offer (Multi-Repo Group Awareness)
Single-repo projects are fully covered by Step 6.5a above — `<repo-root>/CLAUDE.md` is the only relevant CLAUDE.md, and the canned single-repo text accurately describes that repo's `_project-knowledge/` folder.
Multi-repo projects (those with a `projects_groups` entry) have a second CLAUDE.md worth pointing at: the **container** that holds the sub-repos. Teammates navigating at the container level (above any one sub-repo) benefit from a group-level pointer that names each sub-repo's `_project-knowledge/`. The single-repo canned text is structurally wrong for the container — the container has no `_project-knowledge/` of its own — so this step uses a different text variant.
Run this step after Step 6.5a, gated by these conditions in order:
1. **Detect group membership.** Parse the `projects_groups` block from `.cursor/aria-knowledge.local.md`. For each group tag, walk the role-value pairs (e.g., `backend:`, `web:`, `mobile:`, plus any custom roles); if any role-value resolves to a path equal to the current sub-repo's path, the current sub-repo belongs to that group tag. Record the group tag. If no match, skip Step 6.5b entirely (pure single-repo case).
2. **Resolve container path.** Look up the matched group tag in `projects_list` to get the container's relative path; the container root is `~/Projects/<that-path>`.
3. **Session cache check.** If this group tag has already had its container offer made (or skipped) earlier in the current `audit-share` invocation — e.g., user shared to one sub-repo, then later to a sibling sub-repo in the same group — skip silently. The cache prevents re-prompting across sibling shares within one run.
4. **Probe `<container-root>/CLAUDE.md`.** If absent, skip silently (don't auto-create) and record group as "container CLAUDE.md absent" in the session cache.
5. **Idempotency probe.** Search the container CLAUDE.md for an existing `## Team-Shared Knowledge` heading OR any reference to `_project-knowledge/`. If present, skip silently and record group in the session cache as "container CLAUDE.md already has reference."
6. **Detect git tracking** for the container CLAUDE.md via `git -C <container-root> ls-files --error-unmatch CLAUDE.md`. Cache result.
7. **Detect remote visibility** if tracked: `gh repo view --json visibility 2>/dev/null` (cache per container for the session). If `gh` is unavailable, treat as "unknown remote."
**Prompt user with the appropriate warning tier** (same three-tier shape as Step 6.5a, retargeted at the container path):
| Tracking state | Prompt form |
|----------------|-------------|
| Untracked or no git | `Add a group-level _project-knowledge/ reference to <container-root>/CLAUDE.md? This points teammates at each sub-repo's _project-knowledge/ folder. (y/N)` |
| Tracked, public remote | `⚠️ <container-root>/CLAUDE.md is committed to a PUBLIC remote — this edit will be visible to anyone on push. Add a group-level _project-knowledge/ reference? (y/N)` |
| Tracked, private remote | `<container-root>/CLAUDE.md is committed to a remote — teammates will see this edit on next push. Add a group-level _project-knowledge/ reference? (y/N)` |
| Tracked, unknown remote | `<container-root>/CLAUDE.md is git-tracked — committing this edit will broadcast it to anyone with the remote. Add a group-level _project-knowledge/ reference? (y/N)` |
**Default is N** for all four tiers, matching Step 6.5a's posture.
**On `y`:** append the **group-aware variant** to `<container-root>/CLAUDE.md` (insert after the title H1 if one exists, otherwise append at end). `git add` the change but do NOT commit. Variant text:
```markdown
## Team-Shared Knowledge
Team-shared knowledge for repos in this group lives in each sub-repo's `_project-knowledge/` folder (committed per repo: `{sub1}/_project-knowledge/`, `{sub2}/_project-knowledge/`, ...). Files follow `{YYYY-MM-DD}-{author}-{slug}.md` naming with frontmatter origin pointers. Cross-cutting items live in `_project-knowledge/cross/` in any one repo. See any sub-repo's `_project-knowledge/README.md` for the convention (auto-created on first share via the ARIA plugin).
```
Substitute `{sub1}`, `{sub2}`, etc., with the relative paths of each sub-repo from the matched group entry (preserve role ordering: backend → web → mobile → custom roles in declaration order).
**On `N` or empty input:** skip; record in session cache and Step 8 report. User can add manually later or accept on a future share that triggers a fresh first-write to this group (i.e., the group's container offer fires again on the next `audit-share` invocation if still not satisfied).
**Why session cache, not persistent cache:** the container offer should re-fire across sessions because the user may have changed their mind, the CLAUDE.md may have been edited externally, or sub-repos may have been added/removed. Within a single `audit-share` invocation, sibling sub-repo shares share the cache so the container offer fires at most once per run.
## Step 7: IDEAS-BACKLOG.md Migration (One-Time Per Project)
For each project touched in Step 5, check if migration is needed. Migration target depends on project shape:
- **Single-repo project** (no `projects_groups[tag]` entry): migration target is `<project-root>/_project-knowledge/IDEAS-BACKLOG.md`.
- **Multi-repo project** (`projects_groups[tag]` set): migration target is `<project-root>/<primary-sub-repo>/_project-knowledge/IDEAS-BACKLOG.md` (primary sub-repo = first role in declaration order, matching Step 2.3's resolution).
Migration logic:
- If `<project-root>/IDEAS-BACKLOG.md` exists AND the migration target does NOT:
- If the source location is in a git repo (single-repo case, OR rare multi-repo edge case where the container itself is a repo), use `git mv <source> <target>`.
- Otherwise, use filesystem `mv` (multi-repo containers are typically untracked; the move crosses from container to sub-repo).
- For multi-repo projects, after the filesystem move, run `git -C <primary-sub-repo-root> add _project-knowledge/IDEAS-BACKLOG.md` to stage the new file inside the sub-repo. **Do NOT commit** — user reviews and commits via normal flow.
- Note migration in the summary report (include source → target paths so user can verify).
- If both source and target exist, log a warning: *"Both `<project-root>/IDEAS-BACKLOG.md` and `<migration-target>` exist. Manual reconciliation needed."* Skip migration; user resolves.
- If only the target exists, no migration needed.
- If neither exists, no IDEAS-BACKLOG.md is in play for this project (audit-share may still create the target on first IDEAS-BACKLOG entry promotion via Step 2.4).
## Step 8: Report
Output a summary of what happened:
```
## /audit-share complete
- N candidates reviewed
- A shared:
- A1 to repo: <count>
- A2 to cross: <count>
- A3 modified-then-shared: <count>
- B skipped:
- B1 user-declined: <count>
- B2 not-in-scope: <count>
- B3 sanitization-blocked: <count>
- C deferred (will re-prompt next invocation): <count>
- M IDEAS-BACKLOG.md migrations performed: <list>
Files written:
- <repo-1>/_project-knowledge/<filename> (from <source>)
- <repo-2>/_project-knowledge/<filename> (from <source>)
- ...
Next steps:
- Review staged changes: `cd <repo-1> && git diff --cached`
- Commit and push when ready.
- Run `/index` to refresh the tag index with the new files.
- Run `/context <project-tag>` to verify discovery works.
```
If the user cancelled in Step 4, report: *"audit-share cancelled — no actions taken."*
## Rules
- **Read-then-write** — Steps 1-3 are read-only (build the recommendation list); Step 5 is where writes happen.
- **No auto-commit** — `git add` only; the user reviews staged changes and commits through their normal flow.
- **Sanitization is a warn-prompt, not an auto-block** — the user makes the call. Auto-scanning for secrets/URLs is out of scope for v1 (deferred to v2.x).
- **Frontmatter is the source of truth for "already shared"** — files with `shared:` array entries matching the proposed target are skipped silently.
- **Personal copies are independent records from team copies** — they can drift; re-running `/audit-share` after editing personal will offer to share again (incrementing the array, not overwriting).
- **Cross items are federated, not centralized** — a cross item promoted from one user lands in one repo's `cross/`; another user might promote a similar item to a different repo's `cross/`. Aggregation/dedup is a read-side concern handled by `/index` + `/context`, not write-side here.