[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6J43K0YYN49SGCTTWMFH3BX":3,"skill-same:01M6J43K0YYN49SGCTTWMFH3BX":113},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":45,"linkOnly":46,"markdownContent":47,"markdownBody":48,"frontMatter":49,"refName":50,"commitSha":51,"skillDirectory":52,"forksCount":14,"repositoryHtmlUrl":53,"repositoryHomepageUrl":6,"ownerHtmlUrl":54,"ownerBlogUrl":6,"ownerDescription":55,"updatedAt":56,"install":57,"resources":70},"01M6J43K0YYN49SGCTTWMFH3BX","nav-audit",null,"Static, code-derived navigation \u002F information-architecture audit — the\nsystem-level third lens complementing \u002Fpersona-test (journey-level) and\n\u002Fclick-test (page-level). Builds the whole nav graph from source, checks\nwhether what's OFFERED matches what's NEEDED (grounded in the persona\nregistry), and gates CI only on declared-intent regressions. A verify mode\ndrives the live app to reconcile static-vs-live (per-persona scorecard +\nlive findings); a bootstrap mode emits a review-queue nav-contract.json\nskeleton on first run.\nTriggers on: \"map the nav flow\", \"navigation audit\", \"IA audit\",\n\"is the menu coherent\", \"nav map\", \"information architecture\",\n\"offered vs needed\", \"\u002Fnav-audit\", \"\u002Fia-map\".\nFull command syntax: see the Usage section in this skill.","Lbstrydom\u002Fclaude-engineering-skills","Lbstrydom","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F77991486?v=4","01M6J3X4N2T0FTZT1RKFXDVS0K","Louis Strydom","01M6J3X4SXV0BXWDMXPW90D8BK",0,"MIT","https:\u002F\u002Fgithub.com\u002FLbstrydom\u002Fclaude-engineering-skills\u002Fblob\u002Fmain\u002Fskills\u002Fnav-audit\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000037","律师与法务","PROFESSION","适用职业",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44],"nav audit","audit","app","static","skeleton","mode","scorecard","information architecture","information","lens","architecture audit","click","review","map","journey","intent","only","persona","ci","code","architecture","2026-11-10 10:12:30",false,"---\nname: nav-audit\ndescription: |\n  Static, code-derived navigation \u002F information-architecture audit — the\n  system-level third lens complementing \u002Fpersona-test (journey-level) and\n  \u002Fclick-test (page-level). Builds the whole nav graph from source, checks\n  whether what's OFFERED matches what's NEEDED (grounded in the persona\n  registry), and gates CI only on declared-intent regressions. A verify mode\n  drives the live app to reconcile static-vs-live (per-persona scorecard +\n  live findings); a bootstrap mode emits a review-queue nav-contract.json\n  skeleton on first run.\n  Triggers on: \"map the nav flow\", \"navigation audit\", \"IA audit\",\n  \"is the menu coherent\", \"nav map\", \"information architecture\",\n  \"offered vs needed\", \"\u002Fnav-audit\", \"\u002Fia-map\".\n  Full command syntax: see the Usage section in this skill.\n---\n\n> **Worktree preflight** — in a linked git worktree the synced tooling tree\n> `scripts\u002F.claude-skills\u002F` is absent — it is gitignored, so `git worktree add`\n> does not populate it, and every command below that uses it dies on a bare\n> `MODULE_NOT_FOUND`. Run `npm run skills:hydrate` first. If this repo defines\n> no such script, `references\u002Fworktree-preflight.md` holds the entry to add — ask\n> the user first, since it edits their `package.json`.\n\n## Usage\n\n```\nUsage:\n  \u002Fnav-audit                         — static map + analysis of the repo's nav surface (--scope diff default)\n  \u002Fnav-audit --scope full            — analyse the whole nav graph, not just the changed surface\n  \u002Fnav-audit --bootstrap             — emit a review-queue nav-contract.json skeleton (first run)\n  \u002Fnav-audit --gate                  — exit non-zero on a declared-intent regression on the changed surface\n  \u002Fnav-audit --verify \u003Curl>          — live-verify: multi-state DOM layer attribution → scorecard pass\u002Fmisplaced\u002Fmissing + LIVE FINDINGS\n  \u002Fnav-audit --verify \u003Curl> --breakpoints mobile,desktop --storage-state auth.json   — states to capture (default mobile,desktop) + auth\n  \u002Fnav-audit --verify \u003Curl> --no-activate   — skip the collapsed-menu activation pass (faster; single-state capture only)\n  \u002Fnav-audit --bootstrap --from-url \u003Curl>   — draft nav-contract.json navLayers from the live app (refuses to clobber; --force to replace)\n```\n\n# \u002Fnav-audit — Contract-backed Navigation Verifier (with static assists)\n\n> **This skill's last line is an end marker naming the skill between em-dashes, after\n> the Reference files table.** If that last line is not in your context (context\n> compaction keeps only the start of a skill), re-read\n> `.claude\u002Fskills\u002Fnav-audit\u002FSKILL.md` in full before continuing — the later steps still apply.\n\nThe third UX-quality lens. **persona-test is journey-first, click-test is\npage-first, nav-audit is system-first.**\n\n## Three modes (know which one you're in)\n\n1. **static-only** (`\u002Fnav-audit`, no URL) — route\u002Fdestination **inventory** + the\n   pre-deploy **CI drift-gate** on declared-intent edges. Runs on a PR with no\n   deployed URL. **Low confidence on data-driven nav** (the persona scorecard\n   shows `?` and the audit says \"run --verify\"); honest by design.\n2. **live-verified** (`\u002Fnav-audit --verify \u003Curl>`) — drives the live app across\n   viewports and **authoritatively attributes each destination to its nav layer**,\n   resolving the scorecard to `pass` \u002F `misplaced` \u002F `missing`. This is the\n   trustworthy mode for dynamic\u002Fserver-rendered nav. It ALSO runs the\n   layer-attribution finding classes (competing-models, over-exposure\u002Fredundancy,\n   sequencing) over the **live** evidence and emits them as **Live findings**\n   (`source:'live'`) — so the system-level findings fire on data-driven apps the\n   static taxonomy can't model (v1.3 #4). Findings are state-scoped: a destination\n   in `primary@mobile` and `secondary@desktop` is responsive duplication, NOT\n   over-exposure. To surface destinations hidden behind collapsed menus, a bounded\n   **activation pass** opens hamburgers \u002F collapsed sub-tab rows and re-snapshots\n   (single-level, navigation-guarded, ≤8 per viewport; disable with `--no-activate`).\n3. **contract-backed live** (a committed `nav-contract.json` + `--verify`) — the\n   full IA audit: per-persona offered-vs-needed verdicts grounded in live DOM\n   evidence. `--bootstrap --from-url \u003Curl>` drafts the contract's `navLayers` from\n   the live app so you edit a smart baseline instead of a blank page.\n\nStatic analysis cannot model arbitrary data-driven nav — `--verify` is where the\nauthority lives; the static engine is kept for the pre-deploy gate + completeness\n(orphans\u002Funreachable a live crawl never clicks into), and its graph is a set of\n**hypotheses, not truth** (~80% recall). → `references\u002Fdesign-rationale.md` §Why a\nstatic engine at all — read when asked why static analysis is kept beside `--verify`.\n\n## The two-artifact split (the core idea)\n\nRoute-owned facts colocate **in code** (`export const navMeta = {...}` or a\n`\u002F** @nav … *\u002F` docblock) — they can't drift from the route. Cross-cutting\nproduct intent lives in a tiny committed **`nav-contract.json`** (personas →\nintents → expected anchors → nav-layer definitions). The observed nav graph is\n**tool-generated every run** (gitignored). This split is what stops the contract\nfrom rotting into a 200-route allowlist.\n\n## Flow\n\n### Phase 0 — First run: bootstrap the contract\nIf `nav-contract.json` is absent, run `node scripts\u002Fnav-audit.mjs --bootstrap`.\nIt emits a **review-queue** skeleton: obvious utility routes (`\u002Foauth`, `\u002Fauth`,\n`\u002F404`) flagged, persona-intent candidates seeded from the registry, every\ninferred entry marked `source:inferred` (lower CI authority until a human\nconfirms). **Never a trusted baseline** — review it, fill in real persona\nintents + approved anchors, commit it.\n\nWith `PERSONA_TEST_REPO_NAME` set, bootstrap also seeds `personaIntents` from\n`\u002Fpersona-test` click-path evidence (`source:\"persona-test-evidence\"`, still\nhuman-reviewable); a reader failure seeds nothing and bootstrap proceeds.\n**Auth-gated apps: pass `--storage-state \u003Cauth.json>`** — otherwise `--from-url`\ndrafts from the logged-out shell and mis-picks `primary`. The drafted `navLayers`\nis always a hypothesis to review before committing, never trusted.\n→ `references\u002Fbootstrap-and-auth.md` §Persona-reachability seeding \u002F §Auth-gated\n`--from-url` drafting — read when running `--bootstrap`, or a draft warned\n`unauthenticatedDraft` \u002F `emptyNavShells`.\n\n#### Declare an `authSentinel` so `--verify` can prove the session is live\n\n`--storage-state` alone does **not** prove you captured an authenticated app — an\nexpired token yields a plausible scorecard of the **logged-out shell**. Declare an\n`authSentinel` (`selector` + optional `expectText`) in `nav-contract.json`: something\n**only an authenticated user sees**, counted only if actually RENDERED.\n→ `references\u002Fbootstrap-and-auth.md` §Declare an `authSentinel` — read when using\n`--verify --storage-state`, or authoring\u002Fdebugging the sentinel.\n\nResulting `authLiveness`, and what it does:\n\n| `--storage-state` | `authSentinel` | Observed | `authLiveness` | Effect |\n|---|---|---|---|---|\n| no | any | — | `n\u002Fa` | Normal unauthenticated run — **no degradation** |\n| yes | absent | — | `unverified` | Cannot confirm ⇒ degraded to `unverified` |\n| yes | declared | yes | `live` | Full authoritative verdicts |\n| yes | declared | no | `dead` | Degraded; refresh the token and re-run |\n| yes | declared | selector error | `unverified` | Authoring bug — never reported as `dead` |\n\n**Degradation** replaces authoritative `misplaced`\u002F`missing` verdicts with\n`unverified` (reusing the v1.4 `unverifiableLayers` path) — the run reports that\nit could not tell, rather than asserting a scorecard for the wrong surface.\nAdding or changing `authSentinel` changes the contract digest, so a previously\npersisted verify result correctly reads stale and must be re-run.\n\n### Phase 1 — Extract the nav surface (automatic)\n`node scripts\u002Fnav-audit.mjs [--scope diff|full]` detects nav affordances by\n**behaviour, not framework** — `\u003Ca href>`\u002F`\u003CLink to>`\u002F`\u003CNavLink>`\u002FNext\n`\u003CLink href>`, `navigate()\u002Frouter.push()\u002FswitchView()`, `\u003CNavigate>`, modal\ntriggers. Thin adapters (React Router \u002F Next file-routing \u002F vanilla switchView)\nresolve raw targets to canonical destination ids. Output: a normalized edge list\n+ the discovered route inventory. See `references\u002Fextraction-and-adapters.md`.\n\n### Phase 2 — Build the model + attribute anchors\nCollapse edges by destination (in-degree, affordance-type histogram). Attribute\neach edge to its declared-anchor ancestors via **render-containment** (not exact\nmatch — real nav composes `PrimarySidebar → NavGroup → NavItem → \u003Ca>`). Anchor\nreachability is the static metric — **NOT** taps-to-reach (which static analysis\ncannot measure; that's `--verify`'s job).\n\n### Phase 3 — Run the findings taxonomy\n10 classes, each P0–P3 with a one-line \"offered vs needed\" verdict and FP guards:\nredundancy, coverage-gap, orphan, dead-end, label-inconsistency, surprising-\nmapping, competing-models, sequencing, onboarding-overlap, anchor-regression.\nOnly **coverage-gap** and **anchor-regression** (declared-intent regressions) are\ngate-eligible; everything else is advisory. See `references\u002Ffinding-taxonomy.md`.\n\n### Phase 4 — Report + (optional) gate\nOutput: (1) findings list, (2) destination × in-degree × affordance × anchor ×\nverdict table, (3) a mermaid drilldown (capped, never the headline), (4) the\ngitignored observed edge-list JSON. With `--gate`, exit `1` **only** when a\ndeclared-intent divergence lands on the changed surface (merge-base diff).\nDrift is **aged from cloud run-history** (a >14-day unclassified divergence is a\ngovernance smell), not a local file. See `references\u002Fci-gate-and-verify.md`.\n\n## Output surfaces\n- **PR comment** is the operational surface (advisory divergences).\n- **Dashboard** (REGISTRY.reference) shows the accumulated state: a Per-Persona\n  Reachability Scorecard + a Nav Drift indicator. Two panels only — the full\n  graph is a drilldown, never the homepage.\n\n## Exit codes\n`0` clean \u002F advisory-only · `1` hard-gate divergence (with `--gate`) · `2` tool\nerror · `3` needs-bootstrap (no contract).\n\n## Honest limits\n- The static graph sees **declared** structure, not **runtime-gated** (roles \u002F\n  flags \u002F empty-state) — run `--verify \u003Curl>` to confirm against the live app and\n  surface runtime-only nav.\n- Judges **coherence**, not **desirability** — it surfaces and classifies\n  redundancy; whether it's justified is product judgment.\n- Extraction is AST-based (`@babel\u002Fparser`) with nested\u002Frelative route\n  composition and monorepo app-root namespacing; vanilla apps' template-string\n  HTML is recovered by scanning string\u002Ftemplate literals. Recall is still\n  hypotheses-not-truth (~80%) — `--verify` raises confidence.\n- Per-screen quality + feel are click-test's and persona-test's job.\n- **Coverage honesty**: `coverage-gap` reports (never drops) an unconfirmable\n  surface, and `authLiveness` degrades unproven verdicts to `unverified`; a\n  per-run subject-line rendering is **deferred, by name**.\n  → `references\u002Fdesign-rationale.md` §Coverage honesty — read when asked which\n  §7 (`references\u002Fverification-discipline.md`) obligations are met or deferred.\n\n## Reference files\n\n| File | Summary | Read when |\n|---|---|---|\n| `references\u002Fextraction-and-adapters.md` | Affordance detection, the discovery-only adapter interface, destination normalization, and per-stack notes. | Phase 1 — adding a stack adapter or debugging why an affordance\u002Froute was missed. |\n| `references\u002Ffinding-taxonomy.md` | The 10 finding classes with predicate, required evidence, FP guard, and gate-eligibility. | Phase 3 — interpreting a finding or tuning a false positive. |\n| `references\u002Fcontract-and-bootstrap.md` | The two-artifact split — navMeta\u002Fdocblock grammar, nav-contract.json schema, and the bootstrap review-queue. | Phase 0 — authoring or bootstrapping the contract and route metadata. |\n| `references\u002Fbootstrap-and-auth.md` | Bootstrap persona-reachability seeding, auth-gated --from-url drafting, and the authSentinel declaration rules. | Phase 0 — running `--bootstrap`, a draft warned `unauthenticatedDraft`\u002F`emptyNavShells`, OR using `--verify --storage-state`. |\n| `references\u002Fci-gate-and-verify.md` | Drift-only CI gating, changed-surface scoping, cloud-sourced aging, and the v1.1 --verify runtime mode. | Phase 4 — wiring CI or reasoning about what blocks vs advises. |\n| `references\u002Fdesign-rationale.md` | Why nav-audit keeps a static engine beside --verify, and which existing mechanisms already serve coverage honesty. | Asked why static analysis is kept beside `--verify`, OR which coverage-honesty obligations are met or deferred. |\n| `references\u002Fverification-discipline.md` | Verification discipline — pinned citations, figure provenance, two-direction proof, attribution, consumer-side checks. | Reading the \"Honest limits\" section, to see which existing mechanisms already serve §7's obligations. |\n| `examples\u002Fexample-report.md` | A sample \u002Fnav-audit run: findings, destination table, mermaid drilldown, and the gate-vs-advisory split. | Want to see the shape of the output before running it. |\n| `references\u002Fworktree-preflight.md` | Worktree preflight remedy — the skills:hydrate npm script to add when a linked worktree lacks the tooling tree. | A command dies with `MODULE_NOT_FOUND` in a linked worktree AND this repo has no `skills:hydrate` script. |\n\n— end of \u002Fnav-audit —\n","> **Worktree preflight** — in a linked git worktree the synced tooling tree\n> `scripts\u002F.claude-skills\u002F` is absent — it is gitignored, so `git worktree add`\n> does not populate it, and every command below that uses it dies on a bare\n> `MODULE_NOT_FOUND`. Run `npm run skills:hydrate` first. If this repo defines\n> no such script, `references\u002Fworktree-preflight.md` holds the entry to add — ask\n> the user first, since it edits their `package.json`.\n\n## Usage\n\n```\nUsage:\n  \u002Fnav-audit                         — static map + analysis of the repo's nav surface (--scope diff default)\n  \u002Fnav-audit --scope full            — analyse the whole nav graph, not just the changed surface\n  \u002Fnav-audit --bootstrap             — emit a review-queue nav-contract.json skeleton (first run)\n  \u002Fnav-audit --gate                  — exit non-zero on a declared-intent regression on the changed surface\n  \u002Fnav-audit --verify \u003Curl>          — live-verify: multi-state DOM layer attribution → scorecard pass\u002Fmisplaced\u002Fmissing + LIVE FINDINGS\n  \u002Fnav-audit --verify \u003Curl> --breakpoints mobile,desktop --storage-state auth.json   — states to capture (default mobile,desktop) + auth\n  \u002Fnav-audit --verify \u003Curl> --no-activate   — skip the collapsed-menu activation pass (faster; single-state capture only)\n  \u002Fnav-audit --bootstrap --from-url \u003Curl>   — draft nav-contract.json navLayers from the live app (refuses to clobber; --force to replace)\n```\n\n# \u002Fnav-audit — Contract-backed Navigation Verifier (with static assists)\n\n> **This skill's last line is an end marker naming the skill between em-dashes, after\n> the Reference files table.** If that last line is not in your context (context\n> compaction keeps only the start of a skill), re-read\n> `.claude\u002Fskills\u002Fnav-audit\u002FSKILL.md` in full before continuing — the later steps still apply.\n\nThe third UX-quality lens. **persona-test is journey-first, click-test is\npage-first, nav-audit is system-first.**\n\n## Three modes (know which one you're in)\n\n1. **static-only** (`\u002Fnav-audit`, no URL) — route\u002Fdestination **inventory** + the\n   pre-deploy **CI drift-gate** on declared-intent edges. Runs on a PR with no\n   deployed URL. **Low confidence on data-driven nav** (the persona scorecard\n   shows `?` and the audit says \"run --verify\"); honest by design.\n2. **live-verified** (`\u002Fnav-audit --verify \u003Curl>`) — drives the live app across\n   viewports and **authoritatively attributes each destination to its nav layer**,\n   resolving the scorecard to `pass` \u002F `misplaced` \u002F `missing`. This is the\n   trustworthy mode for dynamic\u002Fserver-rendered nav. It ALSO runs the\n   layer-attribution finding classes (competing-models, over-exposure\u002Fredundancy,\n   sequencing) over the **live** evidence and emits them as **Live findings**\n   (`source:'live'`) — so the system-level findings fire on data-driven apps the\n   static taxonomy can't model (v1.3 #4). Findings are state-scoped: a destination\n   in `primary@mobile` and `secondary@desktop` is responsive duplication, NOT\n   over-exposure. To surface destinations hidden behind collapsed menus, a bounded\n   **activation pass** opens hamburgers \u002F collapsed sub-tab rows and re-snapshots\n   (single-level, navigation-guarded, ≤8 per viewport; disable with `--no-activate`).\n3. **contract-backed live** (a committed `nav-contract.json` + `--verify`) — the\n   full IA audit: per-persona offered-vs-needed verdicts grounded in live DOM\n   evidence. `--bootstrap --from-url \u003Curl>` drafts the contract's `navLayers` from\n   the live app so you edit a smart baseline instead of a blank page.\n\nStatic analysis cannot model arbitrary data-driven nav — `--verify` is where the\nauthority lives; the static engine is kept for the pre-deploy gate + completeness\n(orphans\u002Funreachable a live crawl never clicks into), and its graph is a set of\n**hypotheses, not truth** (~80% recall). → `references\u002Fdesign-rationale.md` §Why a\nstatic engine at all — read when asked why static analysis is kept beside `--verify`.\n\n## The two-artifact split (the core idea)\n\nRoute-owned facts colocate **in code** (`export const navMeta = {...}` or a\n`\u002F** @nav … *\u002F` docblock) — they can't drift from the route. Cross-cutting\nproduct intent lives in a tiny committed **`nav-contract.json`** (personas →\nintents → expected anchors → nav-layer definitions). The observed nav graph is\n**tool-generated every run** (gitignored). This split is what stops the contract\nfrom rotting into a 200-route allowlist.\n\n## Flow\n\n### Phase 0 — First run: bootstrap the contract\nIf `nav-contract.json` is absent, run `node scripts\u002Fnav-audit.mjs --bootstrap`.\nIt emits a **review-queue** skeleton: obvious utility routes (`\u002Foauth`, `\u002Fauth`,\n`\u002F404`) flagged, persona-intent candidates seeded from the registry, every\ninferred entry marked `source:inferred` (lower CI authority until a human\nconfirms). **Never a trusted baseline** — review it, fill in real persona\nintents + approved anchors, commit it.\n\nWith `PERSONA_TEST_REPO_NAME` set, bootstrap also seeds `personaIntents` from\n`\u002Fpersona-test` click-path evidence (`source:\"persona-test-evidence\"`, still\nhuman-reviewable); a reader failure seeds nothing and bootstrap proceeds.\n**Auth-gated apps: pass `--storage-state \u003Cauth.json>`** — otherwise `--from-url`\ndrafts from the logged-out shell and mis-picks `primary`. The drafted `navLayers`\nis always a hypothesis to review before committing, never trusted.\n→ `references\u002Fbootstrap-and-auth.md` §Persona-reachability seeding \u002F §Auth-gated\n`--from-url` drafting — read when running `--bootstrap`, or a draft warned\n`unauthenticatedDraft` \u002F `emptyNavShells`.\n\n#### Declare an `authSentinel` so `--verify` can prove the session is live\n\n`--storage-state` alone does **not** prove you captured an authenticated app — an\nexpired token yields a plausible scorecard of the **logged-out shell**. Declare an\n`authSentinel` (`selector` + optional `expectText`) in `nav-contract.json`: something\n**only an authenticated user sees**, counted only if actually RENDERED.\n→ `references\u002Fbootstrap-and-auth.md` §Declare an `authSentinel` — read when using\n`--verify --storage-state`, or authoring\u002Fdebugging the sentinel.\n\nResulting `authLiveness`, and what it does:\n\n| `--storage-state` | `authSentinel` | Observed | `authLiveness` | Effect |\n|---|---|---|---|---|\n| no | any | — | `n\u002Fa` | Normal unauthenticated run — **no degradation** |\n| yes | absent | — | `unverified` | Cannot confirm ⇒ degraded to `unverified` |\n| yes | declared | yes | `live` | Full authoritative verdicts |\n| yes | declared | no | `dead` | Degraded; refresh the token and re-run |\n| yes | declared | selector error | `unverified` | Authoring bug — never reported as `dead` |\n\n**Degradation** replaces authoritative `misplaced`\u002F`missing` verdicts with\n`unverified` (reusing the v1.4 `unverifiableLayers` path) — the run reports that\nit could not tell, rather than asserting a scorecard for the wrong surface.\nAdding or changing `authSentinel` changes the contract digest, so a previously\npersisted verify result correctly reads stale and must be re-run.\n\n### Phase 1 — Extract the nav surface (automatic)\n`node scripts\u002Fnav-audit.mjs [--scope diff|full]` detects nav affordances by\n**behaviour, not framework** — `\u003Ca href>`\u002F`\u003CLink to>`\u002F`\u003CNavLink>`\u002FNext\n`\u003CLink href>`, `navigate()\u002Frouter.push()\u002FswitchView()`, `\u003CNavigate>`, modal\ntriggers. Thin adapters (React Router \u002F Next file-routing \u002F vanilla switchView)\nresolve raw targets to canonical destination ids. Output: a normalized edge list\n+ the discovered route inventory. See `references\u002Fextraction-and-adapters.md`.\n\n### Phase 2 — Build the model + attribute anchors\nCollapse edges by destination (in-degree, affordance-type histogram). Attribute\neach edge to its declared-anchor ancestors via **render-containment** (not exact\nmatch — real nav composes `PrimarySidebar → NavGroup → NavItem → \u003Ca>`). Anchor\nreachability is the static metric — **NOT** taps-to-reach (which static analysis\ncannot measure; that's `--verify`'s job).\n\n### Phase 3 — Run the findings taxonomy\n10 classes, each P0–P3 with a one-line \"offered vs needed\" verdict and FP guards:\nredundancy, coverage-gap, orphan, dead-end, label-inconsistency, surprising-\nmapping, competing-models, sequencing, onboarding-overlap, anchor-regression.\nOnly **coverage-gap** and **anchor-regression** (declared-intent regressions) are\ngate-eligible; everything else is advisory. See `references\u002Ffinding-taxonomy.md`.\n\n### Phase 4 — Report + (optional) gate\nOutput: (1) findings list, (2) destination × in-degree × affordance × anchor ×\nverdict table, (3) a mermaid drilldown (capped, never the headline), (4) the\ngitignored observed edge-list JSON. With `--gate`, exit `1` **only** when a\ndeclared-intent divergence lands on the changed surface (merge-base diff).\nDrift is **aged from cloud run-history** (a >14-day unclassified divergence is a\ngovernance smell), not a local file. See `references\u002Fci-gate-and-verify.md`.\n\n## Output surfaces\n- **PR comment** is the operational surface (advisory divergences).\n- **Dashboard** (REGISTRY.reference) shows the accumulated state: a Per-Persona\n  Reachability Scorecard + a Nav Drift indicator. Two panels only — the full\n  graph is a drilldown, never the homepage.\n\n## Exit codes\n`0` clean \u002F advisory-only · `1` hard-gate divergence (with `--gate`) · `2` tool\nerror · `3` needs-bootstrap (no contract).\n\n## Honest limits\n- The static graph sees **declared** structure, not **runtime-gated** (roles \u002F\n  flags \u002F empty-state) — run `--verify \u003Curl>` to confirm against the live app and\n  surface runtime-only nav.\n- Judges **coherence**, not **desirability** — it surfaces and classifies\n  redundancy; whether it's justified is product judgment.\n- Extraction is AST-based (`@babel\u002Fparser`) with nested\u002Frelative route\n  composition and monorepo app-root namespacing; vanilla apps' template-string\n  HTML is recovered by scanning string\u002Ftemplate literals. Recall is still\n  hypotheses-not-truth (~80%) — `--verify` raises confidence.\n- Per-screen quality + feel are click-test's and persona-test's job.\n- **Coverage honesty**: `coverage-gap` reports (never drops) an unconfirmable\n  surface, and `authLiveness` degrades unproven verdicts to `unverified`; a\n  per-run subject-line rendering is **deferred, by name**.\n  → `references\u002Fdesign-rationale.md` §Coverage honesty — read when asked which\n  §7 (`references\u002Fverification-discipline.md`) obligations are met or deferred.\n\n## Reference files\n\n| File | Summary | Read when |\n|---|---|---|\n| `references\u002Fextraction-and-adapters.md` | Affordance detection, the discovery-only adapter interface, destination normalization, and per-stack notes. | Phase 1 — adding a stack adapter or debugging why an affordance\u002Froute was missed. |\n| `references\u002Ffinding-taxonomy.md` | The 10 finding classes with predicate, required evidence, FP guard, and gate-eligibility. | Phase 3 — interpreting a finding or tuning a false positive. |\n| `references\u002Fcontract-and-bootstrap.md` | The two-artifact split — navMeta\u002Fdocblock grammar, nav-contract.json schema, and the bootstrap review-queue. | Phase 0 — authoring or bootstrapping the contract and route metadata. |\n| `references\u002Fbootstrap-and-auth.md` | Bootstrap persona-reachability seeding, auth-gated --from-url drafting, and the authSentinel declaration rules. | Phase 0 — running `--bootstrap`, a draft warned `unauthenticatedDraft`\u002F`emptyNavShells`, OR using `--verify --storage-state`. |\n| `references\u002Fci-gate-and-verify.md` | Drift-only CI gating, changed-surface scoping, cloud-sourced aging, and the v1.1 --verify runtime mode. | Phase 4 — wiring CI or reasoning about what blocks vs advises. |\n| `references\u002Fdesign-rationale.md` | Why nav-audit keeps a static engine beside --verify, and which existing mechanisms already serve coverage honesty. | Asked why static analysis is kept beside `--verify`, OR which coverage-honesty obligations are met or deferred. |\n| `references\u002Fverification-discipline.md` | Verification discipline — pinned citations, figure provenance, two-direction proof, attribution, consumer-side checks. | Reading the \"Honest limits\" section, to see which existing mechanisms already serve §7's obligations. |\n| `examples\u002Fexample-report.md` | A sample \u002Fnav-audit run: findings, destination table, mermaid drilldown, and the gate-vs-advisory split. | Want to see the shape of the output before running it. |\n| `references\u002Fworktree-preflight.md` | Worktree preflight remedy — the skills:hydrate npm script to add when a linked worktree lacks the tooling tree. | A command dies with `MODULE_NOT_FOUND` in a linked worktree AND this repo has no `skills:hydrate` script. |\n\n— end of \u002Fnav-audit —\n",{"name":5,"description":7},"main","938efbef34b7c76f0d112f336d1ac4018224d186","skills\u002Fnav-audit","https:\u002F\u002Fgithub.com\u002FLbstrydom\u002Fclaude-engineering-skills","https:\u002F\u002Fgithub.com\u002FLbstrydom","Studying M.Sc Computer Science at Heriot Watt","2026-11-14 22:15:30",{"source":8,"skillName":5,"commands":58,"prompt":68,"zipAvailable":69},[59,62,65],{"tool":60,"command":61},"npx","npx skills add Lbstrydom\u002Fclaude-engineering-skills --skill nav-audit",{"tool":63,"command":64},"bunx","bunx skills add Lbstrydom\u002Fclaude-engineering-skills --skill nav-audit",{"tool":66,"command":67},"pnpm","pnpm dlx skills add Lbstrydom\u002Fclaude-engineering-skills --skill nav-audit","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FLbstrydom\u002Fclaude-engineering-skills\u002Fblob\u002Fmain\u002Fskills\u002Fnav-audit\u002FSKILL.md",true,[71,77,81,85,89,93,97,101,105,109],{"path":72,"resourceType":73,"resourceTypeDesc":74,"sizeBytes":75,"sourceHtmlUrl":76},"examples\u002Fexample-report.md","REFERENCE","参考文档",1869,"https:\u002F\u002Fgithub.com\u002FLbstrydom\u002Fclaude-engineering-skills\u002Fblob\u002Fmain\u002Fskills\u002Fnav-audit\u002Fexamples\u002Fexample-report.md",{"path":78,"resourceType":73,"resourceTypeDesc":74,"sizeBytes":79,"sourceHtmlUrl":80},"gate-contract.json",5250,"https:\u002F\u002Fgithub.com\u002FLbstrydom\u002Fclaude-engineering-skills\u002Fblob\u002Fmain\u002Fskills\u002Fnav-audit\u002Fgate-contract.json",{"path":82,"resourceType":73,"resourceTypeDesc":74,"sizeBytes":83,"sourceHtmlUrl":84},"references\u002Fbootstrap-and-auth.md",3192,"https:\u002F\u002Fgithub.com\u002FLbstrydom\u002Fclaude-engineering-skills\u002Fblob\u002Fmain\u002Fskills\u002Fnav-audit\u002Freferences\u002Fbootstrap-and-auth.md",{"path":86,"resourceType":73,"resourceTypeDesc":74,"sizeBytes":87,"sourceHtmlUrl":88},"references\u002Fci-gate-and-verify.md",5068,"https:\u002F\u002Fgithub.com\u002FLbstrydom\u002Fclaude-engineering-skills\u002Fblob\u002Fmain\u002Fskills\u002Fnav-audit\u002Freferences\u002Fci-gate-and-verify.md",{"path":90,"resourceType":73,"resourceTypeDesc":74,"sizeBytes":91,"sourceHtmlUrl":92},"references\u002Fcontract-and-bootstrap.md",5136,"https:\u002F\u002Fgithub.com\u002FLbstrydom\u002Fclaude-engineering-skills\u002Fblob\u002Fmain\u002Fskills\u002Fnav-audit\u002Freferences\u002Fcontract-and-bootstrap.md",{"path":94,"resourceType":73,"resourceTypeDesc":74,"sizeBytes":95,"sourceHtmlUrl":96},"references\u002Fdesign-rationale.md",2079,"https:\u002F\u002Fgithub.com\u002FLbstrydom\u002Fclaude-engineering-skills\u002Fblob\u002Fmain\u002Fskills\u002Fnav-audit\u002Freferences\u002Fdesign-rationale.md",{"path":98,"resourceType":73,"resourceTypeDesc":74,"sizeBytes":99,"sourceHtmlUrl":100},"references\u002Fextraction-and-adapters.md",5059,"https:\u002F\u002Fgithub.com\u002FLbstrydom\u002Fclaude-engineering-skills\u002Fblob\u002Fmain\u002Fskills\u002Fnav-audit\u002Freferences\u002Fextraction-and-adapters.md",{"path":102,"resourceType":73,"resourceTypeDesc":74,"sizeBytes":103,"sourceHtmlUrl":104},"references\u002Ffinding-taxonomy.md",2653,"https:\u002F\u002Fgithub.com\u002FLbstrydom\u002Fclaude-engineering-skills\u002Fblob\u002Fmain\u002Fskills\u002Fnav-audit\u002Freferences\u002Ffinding-taxonomy.md",{"path":106,"resourceType":73,"resourceTypeDesc":74,"sizeBytes":107,"sourceHtmlUrl":108},"references\u002Fverification-discipline.md",28340,"https:\u002F\u002Fgithub.com\u002FLbstrydom\u002Fclaude-engineering-skills\u002Fblob\u002Fmain\u002Fskills\u002Fnav-audit\u002Freferences\u002Fverification-discipline.md",{"path":110,"resourceType":73,"resourceTypeDesc":74,"sizeBytes":111,"sourceHtmlUrl":112},"references\u002Fworktree-preflight.md",3413,"https:\u002F\u002Fgithub.com\u002FLbstrydom\u002Fclaude-engineering-skills\u002Fblob\u002Fmain\u002Fskills\u002Fnav-audit\u002Freferences\u002Fworktree-preflight.md",[114,128,139,158,172,200],{"skillNo":115,"name":116,"nameZh":6,"description":117,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":118,"categories":119,"keywords":121,"publishedAt":45},"01M6J45X4FDB2AD5BGS4R4QY8X","stated-absent","Negative fixture — the contract's stated string never appears in this file.","https:\u002F\u002Fgithub.com\u002FLbstrydom\u002Fclaude-engineering-skills\u002Fblob\u002Fmain\u002Ftests\u002Ffixtures\u002Fgate-honesty\u002Fnegative\u002Fstated-absent\u002Fskills\u002Fstated-absent\u002FSKILL.md",[120],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[122,123,124,125,126,127],"stated absent","STRING","fixture","contract","never","file",{"skillNo":129,"name":130,"nameZh":6,"description":131,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":132,"categories":133,"keywords":135,"publishedAt":45},"01M6J45TAX2PPHETZ5TJZG20QQ","nonexistent-implementation","Negative fixture — contract references an implementation file that does not exist.","https:\u002F\u002Fgithub.com\u002FLbstrydom\u002Fclaude-engineering-skills\u002Fblob\u002Fmain\u002Ftests\u002Ffixtures\u002Fgate-honesty\u002Fnegative\u002Fnonexistent-implementation\u002Fskills\u002Fnonexistent-implementation\u002FSKILL.md",[134],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[136,137,124,138,125,127],"implementation","nonexistent implementation","references",{"skillNo":140,"name":141,"nameZh":6,"description":142,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":143,"categories":144,"keywords":150,"publishedAt":45},"01M6J45P5GN52YFMZ81T7NN1VF","document-only-with-params","Negative fixture — a document-only gate illegally carries params (schema must refuse this fake-check pattern).","https:\u002F\u002Fgithub.com\u002FLbstrydom\u002Fclaude-engineering-skills\u002Fblob\u002Fmain\u002Ftests\u002Ffixtures\u002Fgate-honesty\u002Fnegative\u002Fdocument-only-with-params\u002Fskills\u002Fdocument-only-with-params\u002FSKILL.md",[145],{"categoryNo":146,"categoryName":147,"taxonomyType":148,"taxonomyTypeDesc":149},"01JBSEEDCAT000000000000002","文档处理","SCENARIO","使用场景",[40,151,152,153,154,124,155,156,157],"document","document only with params","with","pattern","gate","schema","check",{"skillNo":159,"name":160,"nameZh":6,"description":161,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":162,"categories":163,"keywords":167,"publishedAt":45},"01M6J45J1CCZ7AXDGQKBA9VC8M","lying-skill","Fixture skill for the gate-honesty self-check — every gate here is deliberately dishonest.","https:\u002F\u002Fgithub.com\u002FLbstrydom\u002Fclaude-engineering-skills\u002Fblob\u002Fmain\u002Ftests\u002Ffixtures\u002Fgate-honesty\u002Flying-skill\u002Fskills\u002Flying-skill\u002FSKILL.md",[164],{"categoryNo":165,"categoryName":166,"taxonomyType":148,"taxonomyTypeDesc":149},"01JBSEEDCAT000000000000023","Skill与智能体开发",[168,124,169,155,170,171,157],"lying skill","self","for","fixture skill",{"skillNo":173,"name":174,"nameZh":6,"description":175,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":176,"categories":177,"keywords":187,"publishedAt":45},"01M6J45CW0DNN7K2W39E3PQHEY","visual-audit","Math-first, deterministic visual\u002Fpaint inspection — the 4th UX lens, complementing\npersona-test (journey), click-test (page), and nav-audit (system). Drives Playwright +\ncomputed styles + bounding boxes + CDP forced-pseudo-state to audit declared-token\nconformance, light\u002Fdark theme parity, layout physics, and affordance signifiers. A VLM\nis advisory-only and never gates. CI gate is drift-only on the changed contracted surface.\nTriggers on: \"visual audit\", \"check styling consistency\", \"theme parity\", \"dark mode parity\",\n\"design token drift\", \"paint audit\", \"button\u002Fcard consistency\", \"focus ring audit\",\n\"\u002Fvisual-audit\".","https:\u002F\u002Fgithub.com\u002FLbstrydom\u002Fclaude-engineering-skills\u002Fblob\u002Fmain\u002Fskills\u002Fvisual-audit\u002FSKILL.md",[178,181,184],{"categoryNo":179,"categoryName":180,"taxonomyType":148,"taxonomyTypeDesc":149},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":182,"categoryName":183,"taxonomyType":148,"taxonomyTypeDesc":149},"01JBSEEDCAT000000000000015","视觉设计",{"categoryNo":185,"categoryName":186,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000032","设计师",[188,189,25,190,191,192,193,194,195,33,35,126,196,197,29,42,198,38,40,41,199],"visual audit","visual","inspection","layout","math","focus","design token","cdp","physics","card","design","drift",{"skillNo":201,"name":202,"nameZh":6,"description":203,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":204,"categories":205,"keywords":212,"publishedAt":45},"01M6J4569S40ZASFWW698CHBKP","ux-lock","Generate Playwright e2e specs. Two modes:\n  1. LOCK mode — pin a fix's DOM contract so it doesn't regress (default).\n  2. VERIFY mode — check that a \u002Fplan plan was actually implemented\n     by parsing its Acceptance Criteria section and driving the live URL.\nVERIFY mode grades a plan against a RUNNING app. To judge the plan document\nitself, before anything is built, that is \u002Faudit-plan.\nTriggers on: \"ux lock\", \"lock ux\", \"lock in the fix\", \"write regression spec\",\n\"generate e2e test for\", \"regression test for commit\", \"lock this fix\",\n\"verify the plan\", \"verify plan implementation\", \"check the plan was built\",\n\"audit frontend implementation\", \"did we ship the plan\".\nFull command syntax: see the Usage section in this skill.","https:\u002F\u002Fgithub.com\u002FLbstrydom\u002Fclaude-engineering-skills\u002Fblob\u002Fmain\u002Fskills\u002Fux-lock\u002FSKILL.md",[206,207,210,211],{"categoryNo":179,"categoryName":180,"taxonomyType":148,"taxonomyTypeDesc":149},{"categoryNo":208,"categoryName":209,"taxonomyType":148,"taxonomyTypeDesc":149},"01JBSEEDCAT000000000000009","前端开发",{"categoryNo":185,"categoryName":186,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[213,214,215,216,26,217,218,29,219,220,221,222,223,224,225,226,227,25,228,229,230],"ux","lock","ux lock","syntax","regression","e2e test","default","fix","frontend","acceptance","URL","commit","plan","command","generate","write","judge","verify plan"]