<purpose>
Scan the codebase to build or refresh the feature registry. Produces structured
.aperant/features/{area}.json files that verification, review, and planning consume.

This workflow uses parallel agents to analyze different aspects of the codebase,
then synthesizes their findings into a unified feature registry.
</purpose>

<process>

<step name="init">
## 1. Initialize

```bash
mkdir -p .aperant/features
```

Parse the scope argument:
- If an area name was given (e.g., "terminals"): **focused scan** — one agent, one area
- If no argument: **full scan** — discover all areas, spawn parallel agents

Check if `.aperant/features/REGISTRY.md` exists:
- If yes: this is a **refresh** — load existing registry to compare against
- If no: this is an **initial scan**

Read project structure to discover feature areas:
```bash
# Find all route-level components (pages/sections)
ls packages/ui/src/sections/ 2>/dev/null
ls apps/*/src/renderer/src/routes/ 2>/dev/null
ls apps/*/src/pages/ 2>/dev/null
ls apps/*/app/ 2>/dev/null
```

Also read the app's router/navigation to find all routes:
- Search for route definitions, navigation items, sidebar config
- Each route typically corresponds to a feature area
</step>

<step name="discover_areas">
## 2. Discover Feature Areas

For a full scan, identify all feature areas by reading:
1. **Router config** — every route = a feature area
2. **Navigation/sidebar** — every nav item = a feature area
3. **Section directories** — `packages/ui/src/sections/*`
4. **Page components** — `apps/*/src/pages/*` or equivalent

Create a list of areas to scan. Example:
```
dashboard, terminals, chat, pipeline, explorer, pr-reviews,
roadmap, context, memories, competitors, personas, agents,
analytics, settings, inbox, planning
```

For a focused scan, use only the specified area.
</step>

<step name="spawn_agents">
## 3. Spawn Feature Scanner Agents

For each area (or the single focused area), spawn an Agent with subagent_type="Explore":

**Agent prompt template:**
```
Scan the "{area}" feature area in this codebase. Read the actual source code
to enumerate EVERY interactive element.

SCHEMA CONFORMANCE (comes FIRST):
Your JSON output MUST match the `FeatureRegistrySchema` from `@aperant/framework/schemas`.
See `packages/framework/src/schemas/feature-registry.ts` for the exact shape.
Top-level keys are strictly `{ area, route, components, last_scanned, features }`.
Do NOT invent top-level keys like `summary`, `ipc_channels`, `phase_readiness`,
`tabs_inventory` — put that data INSIDE the `features[]` array or omit it.

WRITE TOOL (CRITICAL):
You MUST call the Write tool to create the file at the exact path specified.
Do not return the JSON as text in your response — the orchestrator will not
parse it. If the Write tool appears to fail, retry it. The `.aperant/` directory
is writable.

SINGLE FILE CONSTRAINT:
Write EXACTLY ONE file: `.aperant/features/{area}.json`. Do not write any
additional files — no report.md, no report.txt, no supplementary docs. If you
have extra findings, put them as JSON comment fields inside the main file or
omit them.

EXHAUSTIVENESS (inside the schema-conforming structure):
Schema conformance comes FIRST; exhaustiveness comes second inside the
schema-conforming structure. Once the structure is correct, be exhaustive:
if a settings panel has 7 controls, list all 7. If a toolbar has 5 buttons,
list all 5. Miss nothing within the schema.

Your task:
1. Find the main component files for this area
2. Read each component file thoroughly
3. For every interactive element, record:
   - id: unique identifier (area-feature-name)
   - name: human-readable name
   - component: file path and line number
   - type: button | slider | toggle | input | select | dropdown | display | behavior | shortcut
   - wired: true | false | "stubbed" (is the handler connected to real functionality?)
   - handler: what happens when interacted with (one line)
   - default: default value (for inputs/sliders/toggles)
   - range: value range (for sliders)
   - options: available options (for selects/dropdowns)
   - shortcut: keyboard shortcut if any
   - testable: "testable" | "observable" | "blocked" (can MCP automation test this?)
   - test_hint: how to test this feature (one line)

4. Also identify:
   - WebSocket/IPC connections (what's wired to backend) — encode inside features[]
   - Props that indicate external data dependencies
   - Keyboard shortcuts registered for this area
   - Any reference/design quality expectations

Write your findings as JSON to: .aperant/features/{area}.json

Format:
{
  "area": "{area}",
  "route": "/#/{area}",
  "components": ["path/to/Component.tsx", ...],
  "last_scanned": "{ISO date}",
  "features": [
    {
      "id": "{area}-{feature}",
      "name": "Feature Name",
      "component": "path/to/file.tsx:42",
      "sub_features": [
        {
          "id": "{area}-{feature}-{sub}",
          "name": "Sub Feature Name",
          "component": "path/to/file.tsx:88",
          "type": "button",
          "wired": true,
          "handler": "Calls onSomething() which triggers XYZ",
          "testable": "testable",
          "test_hint": "Click button, verify XYZ appears"
        }
      ]
    }
  ]
}

Report only a confirmation when done: file path and feature count.
```

**Parallelization:**
- For full scan: spawn up to 4 agents at a time (wave-based if >4 areas)
- For focused scan: spawn 1 agent
- Each agent writes directly to `.aperant/features/{area}.json`
- Orchestrator receives only confirmations (file path + count)

**Runtime fallback:**
- If Agent tool is not available: run scanning inline, one area at a time
- Use Glob + Grep + Read to find components, then write JSON directly
</step>

<step name="validate_written_files">
## 4a. Validate Written Files

After all agents complete (or at the end of each wave), verify each expected output:

1. For each area the wave spawned, check that `.aperant/features/{area}.json` exists on disk.
2. If missing: re-launch that agent with an explicit reminder to USE THE WRITE TOOL.
3. If present: parse it as JSON and validate against `FeatureRegistrySchema`:
   ```ts
   import { validateFeatureRegistry } from '@aperant/framework/schemas'
   const result = validateFeatureRegistry(JSON.parse(content))
   if (!result.success) { /* re-launch with error message */ }
   ```
4. For orchestrators running in pure-markdown context (no TS runtime), perform a **lightweight structural check**: parse the JSON and verify the top-level keys include `area`, `route`, `components`, `last_scanned`, `features`, and that `features` is an array where each entry has `id`, `name`, `component`, `sub_features`. Flag deviations in the output report.
5. On validation failure, the orchestrator may: (a) re-launch the agent with the validation error as feedback, OR (b) mark the file as non-conforming and note it in REGISTRY.md. Prefer (a) up to 2 retries, then fall back to (b).
</step>

<step name="synthesize_registry">
## 4. Synthesize Registry Index

After all agents complete, read each `.aperant/features/{area}.json` and produce
`REGISTRY.md`:

```markdown
# Feature Registry

Last scanned: {date}
Scan type: {full | focused on {area}}

## Areas

| Area | Route | Features | Sub-features | Wired | Testable | Last Verified |
|------|-------|----------|--------------|-------|----------|---------------|
| terminals | /#/terminals | 8 | 47 | 42/47 | 35/47 | never |
| chat | /#/chat | 5 | 23 | 20/23 | 18/23 | never |
| ... | | | | | | |

## Coverage Summary

- **Total feature areas:** {N}
- **Total features:** {N}
- **Total sub-features:** {N}
- **Wired (connected to backend):** {N}/{total} ({pct}%)
- **Testable via MCP:** {N}/{total} ({pct}%)
- **Stubbed/dead UI:** {N} (candidates for removal or implementation)

## Files

{list of .aperant/features/*.json with modification dates}
```

Write to `.aperant/features/REGISTRY.md`.
</step>

<step name="detect_changes">
## 5. Detect Changes (refresh only)

If this is a refresh (per-area JSON files already existed before the scan), compare pre- and post-scan files for each scanned area:

1. Before spawning agents, read the existing `.aperant/features/{area}.json` (if any) and snapshot its `features[].sub_features[].id` set plus the `wired` value for each.
2. After the agent writes the new file, compute:
   - **New features**: ids in new file not in snapshot
   - **Removed features**: ids in snapshot not in new file
   - **Wiring changes**: ids present in both where `wired` value differs
3. Aggregate across all scanned areas and report:
   ```
   Feature Registry Refresh:
     Areas scanned:       {N}
     New features:        {N}
     Removed features:    {N}
     Wiring changes:      {N} (X stubbed→wired, Y wired→stubbed)
     Total sub-features:  {N} (delta: {±N})
   ```
4. If the old `REGISTRY.md` existed and no per-area files existed, fall back to the previous "old scan assumed terminals-only" behavior — note that this is an initial full scan replacing a focused scan.
</step>

<step name="commit">
## 6. Commit

Before committing, check whether the target paths are gitignored:

```bash
git check-ignore -q .aperant/features/ && IGNORED=1 || IGNORED=0
```

If `IGNORED=1`, **do NOT commit**. Instead report to the user:
```
Feature registry written to .aperant/features/ but this path is gitignored.
Registry is local-only. To track it, either:
  (a) Add `!.aperant/features/` and `!.aperant/features/**` to .gitignore,
  (b) Or move the registry out of .aperant/ (e.g. to .feature-registry/).
```

If `IGNORED=0`, proceed with the commit:
```bash
git add .aperant/features/
git commit -m "docs: scan feature registry ({type}: {area_count} areas, {feature_count} features)"
```
</step>

<step name="output">
## 7. Final Output

```
Feature Registry: {SCAN_TYPE}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Areas scanned:    {N}
Features found:   {N}
Sub-features:     {N}
Wired:            {N}/{total} ({pct}%)
Testable (MCP):   {N}/{total} ({pct}%)

Registry: .aperant/features/REGISTRY.md
Files:    .aperant/features/*.json

Next: /apt:verify-proof {area} — verify features with visual evidence
```
</step>

</process>

<integration>
## Integration Hooks

### How other skills update the registry

**`/apt:execute` post-step:**
After completing a task that adds or modifies UI features, the executor should:
1. Check if `.aperant/features/REGISTRY.md` exists
2. If yes: read the relevant `{area}.json` file
3. Add new entries for any features that were created
4. Update `wired` status for any features that were connected
5. Update `last_scanned` timestamp

This is a lightweight update — NOT a full rescan. The executor reads its own diff
to know what changed.

**`/apt:verify-proof` pre-step:**
Before generating tests, verify-proof should:
1. Check if `.aperant/features/{area}.json` exists for the target area
2. If yes: use it as the primary source for test generation
3. If no: suggest running `/apt:scan {area}` first, or do inline discovery
4. After verification: update `last_verified` in REGISTRY.md

**`/apt:review` check:**
During code review, check:
1. Are there new component files not covered by any feature registry entry?
2. Flag as: "New component {file} not in feature registry — run `/apt:scan` to update"
</integration>
