---
name: dead-edge-audit
description: "Per-affordance \"does this button lie?\" audit of PROTOTYPE screens, module by module — every interactive element inventoried by mechanical tells (toast-only, no-handler, placeholder, log-only), classified on TWO axes (behavior disposition × remediation class: lifecycle step / creation entry / cross-nav / utility / accepted fire-and-forget), fixed additively in the prototype's own patterns, with an accepted-exceptions ledger so re-audits never re-litigate. Use after a module's screens land (either lane), before a module is declared demo-ready, or as a periodic sweep. Two venues: the design-tool session via CONSOLE_PROMPT.md, or in-repo via the edge-auditor agent under /edge-audit."
allowed-tools: Read, Write, Edit, Grep, Glob, Bash
---

> **Specialization:** generic by design — bind every `<…>` and "per `CLAUDE.md`" reference from the project `CLAUDE.md` (the prototype location + file pattern + entry point, the project's **dead-handler tells** — the grep patterns for its toast/click idioms, the shared utility components, the context-carry navigation pattern, the accepted-toast policy, module codes, the interaction registry path). See `SPECIALIZE.md`. If a needed value is missing, STOP-and-ask.

## The quadrant this fills — and the boundary with its neighbors

| | Prototype side | Built side |
|---|---|---|
| **Journey scope** (between screens) | `journey-continuity-audit` / `journey-auditor` | workflow sweeps' gates |
| **Affordance scope** (within screens) | **this skill / `edge-auditor`** | `interaction-auditor` |

The lenses are orthogonal, not redundant: the journey audit classifies **destinations** (does the edge go somewhere real, carrying context?); this audit classifies **handler behavior** (does the element *do* anything?). A toast-only "Approve" that never navigates is invisible to a journey ledger — no destination to be dead — yet it strands a record's lifecycle. Boundaries stay strict: this skill never walks journeys (cross-file journey findings to `/journey-audit`), never audits built screens (that is `interaction-auditor`, which shares this skill's remediation-class axis and exceptions ledger), never diffs provenance (`design-sync`).

## Law: an audit is only as complete as its taxonomy

Every run MUST open by declaring (a) the buckets in scope and (b) the buckets **explicitly excluded** — and every re-run MUST include the prior run's exclusions in scope. Silent exclusions are how a "closed" audit ships dead buttons: a creation entry point ("Add X") passes a journey-gap-scoped audit while still being dead, because starting a lifecycle wasn't in the taxonomy — and a **create form ships pre-filled with a phantom record** because *initial-state correctness* wasn't in the taxonomy (it renders identically to a valid edit form, so a pixel/handler audit passes it). The exclusion list is a debt register, not a scope courtesy.

## Step 1 — Inventory by mechanical tells (before any judgment)

For every interactive element (buttons, clickable rows, chip-links, anchors, icon buttons, form submits) record `screen · label · region (header/row/aside/modal-footer) · what the handler actually does`. Grep the project's tells (per `CLAUDE.md`; typical shapes):
- **toast-only** — the handler only fires the prototype's toast/notify idiom (changes nothing)
- **no-handler** — a CTA component with no click binding at all
- **placeholder** — `href="#"`, empty-body handlers, handlers that only log
- **disabled-without-reason** — disabled with no inline reason (violates the guarded-CTA invariant)
- **phantom-prefill** (a form lying about its *initial state*, not a handler) — a **create/new/register/add** form (sidecar `formMode: create`, or a screen whose label/route is New/Create/Register/Add + a create submit) whose inputs render **record-identifying sample values** (a real-looking name, email, address, phone, DOB, member id) instead of placeholders + system-proposed defaults. The create form must open **empty**; the prototype's "show mock data at full fidelity" convention is **exempted for a create form's field values**. A *system-proposed* value (proposed id, today's date, a defaulted dropdown) is acceptable — the tell is record-identifying data on a blank create form. The inverse — an `edit` form opening blank/reset — is the same tell in mirror.
Also flag **cross-screen dishonesty**: an action that changes local state while the same record still shows the old status elsewhere (the lie is between renders, not in the handler).

## Step 2 — Classify on two axes

**Axis 1 — behavior disposition** (what the handler does): `real | honest-stub (marked, or declared fire-and-forget) | toast-only | no-handler | placeholder`.
**Axis 2 — remediation class** (who fixes it, how — severity in parentheses):
1. **Lifecycle step (High):** a mid-flow action that strands an in-progress record — approve/return, dispute, receive, post, complete, dispatch. The record has a lifecycle; this button is a step that goes nowhere.
2. **Creation entry point (High):** "Add/New/Create X" that only toasts. These *start* a lifecycle; if they lie, the module cannot be demoed end-to-end. Historically the bucket most likely to survive earlier audits.
3. **Cross-navigation stub (Med):** the label implies navigation ("View history", "Open record") but nothing navigates. Fix with the prototype's context-carry pattern per `CLAUDE.md` (destination preselects the subject).
4. **Utility stub (Med):** Export, Print, Refresh, Scan, Import, Settings-style. Fix with the shared utility components per `CLAUDE.md` first — never reinvent per screen.
5. **Accepted fire-and-forget (Low / leave):** a toast that is an *honest simulation* of an out-of-scope side effect ("email sent", "paged on-call"). Every one left must be justified in the exceptions ledger AND declared in the screen's sidecar (`effect: "notify-sim"`).

## Step 3 — Fix additively, in the prototype's own grammar (per `CLAUDE.md`)

All fixes are additive state-in-component — never mutate the mock data arrays:
- **Lifecycle steps:** status-override maps (`statusOf(x) = flow[x.id] || x.status`) + a per-status CTA config (label/icon/effect per step) for multi-step chains.
- **Creation flows:** modal intake → prepend to a session list rendered ahead of the base data → select it → give it an honest in-progress status so downstream states stay true.
- **Cross-nav:** the app-level context-carry pattern (navigate + preselect the subject at the destination).
- **Utilities:** the shared components. **Trap:** in no-build prototypes all screen files share one global scope — grep any new top-level `const` name before declaring it.
Fixes too large for a micro-fix (a creation flow that needs a real designed screen) are not fixed here — they become design-backlog rows (routing below).

## The exceptions ledger (stop re-litigating)

The interaction registry (per `CLAUDE.md`) carries an **§ Accepted toasts** section: `screen · element · justification · date · sidecar-declared? ✓`. Every run loads it first (those rows are in scope only to re-verify the justification still holds) and appends what it newly accepts. `interaction-auditor` reads the same section on the built side — an accepted prototype toast reconstructs as a Marked-stub/declared notify, never resurfacing as an Unmarked-stub finding.

## Routing the findings (kit integration)

| Finding | Route |
|---|---|
| Fixable micro-fix, screen **unbuilt** | Orchestrator applies it (Step-3 grammar) + refreshes the source's provenance hash |
| Anything on a **BUILT ✅** screen's prototype source | Drift by definition → `design-sync` (classification, preserve-wiring) |
| **Graduated** screen | Forward-only: file against the built side (`interaction-auditor` remediation path) |
| Fix needs a **new designed screen** (real intake/workspace) | Design-backlog row (+ `design-prompt-authoring` brief → `/design-build` or console lane) |
| The handler is fine but the **destination/journey** is the problem | Cross-file to `/journey-audit` — not this audit's fix |
| Accepted fire-and-forget | Exceptions ledger + sidecar declaration |

## Prevention at birth (why this audit should shrink over time)
`design-builder` and the console Global preamble forbid toast-only lifecycle/creation CTAs in NEW screens — a deferral is a Marked-stub (disabled-with-reason) or a declared `notify-sim`, recorded in the sidecar at birth. This audit's steady state is verifying the ledger, not harvesting debt.

## Done when (per run)
- Scope + exclusions declared up front; prior exclusions included; the exceptions ledger loaded, re-verified, and appended.
- Every inventoried element carries both axes; every High finding is fixed, filed (backlog/design-sync/forward-only), or explicitly human-deferred — never silent.
- Fixed screens re-pass the tells grep (zero toast-only/no-handler/placeholder on lifecycle + creation CTAs) and their sidecars reflect any new CTAs/states.
- The findings table + the "left as toast" list ship with the run's report.
