[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6CRCAVX8RNCF0BZBPTJXJFS":3,"skill-same:01M6CRCAVX8RNCF0BZBPTJXJFS":64},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":36,"linkOnly":37,"markdownContent":38,"markdownBody":39,"frontMatter":40,"refName":44,"commitSha":45,"skillDirectory":46,"forksCount":47,"repositoryHtmlUrl":48,"repositoryHomepageUrl":6,"ownerHtmlUrl":49,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":50,"install":51,"resources":63},"01M6CRCAVX8RNCF0BZBPTJXJFS","fe-verify",null,"Run verification gate (tsc, ESLint, build) on generated code for a feature.","ohmyhotelco\u002Fhare-cc-plugins","ohmyhotelco","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F258133233?v=4","01M6CR4HP2N9F9ZWNX1KRHQ2W1","01M6CR4HSJC5J80RVQMD5K9QRN",2,"MIT","https:\u002F\u002Fgithub.com\u002Fohmyhotelco\u002Fhare-cc-plugins\u002Fblob\u002Fmain\u002Ffrontend-react-plugin\u002Fskills\u002Ffe-verify\u002FSKILL.md",[],[18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35],"verify","fe verify","verification gate","for","true","name","eslint","feature","build","gate","run","generated","write","read","grep","Bash","verification","code","2026-11-10 10:12:30",false,"---\nname: fe-verify\ndescription: \"Run verification gate (tsc, ESLint, build) on generated code for a feature.\"\nargument-hint: \"\u003Cfeature-name>\"\nuser-invocable: true\nallowed-tools: Read, Write, Glob, Grep, Bash\n---\n\n# Verification Gate Skill\n\nRun TypeScript, ESLint, and build verification on generated code (build is mode-aware: `vite build` for declarative\u002Fdata, `react-router build` for framework — see the fe-shared Router-mode command matrix).\n\nConventions (`fe-shared`): `${CLAUDE_PLUGIN_ROOT}\u002Fskills\u002Ffe-shared\u002FSKILL.md`.\n\n## Instructions\n\n### Step 0: Read Configuration\n\n1. Read `.claude\u002Ffrontend-react-plugin.json` → extract `routerMode`, `mockFirst`, `appDir`, `eslintTemplate`, `prettierTemplate`, `i18n`, and `baseDir` as **`sourceBaseDir`** (the source root, e.g. `app\u002Fsrc`)\n2. If `sourceBaseDir` is missing, use default value `\"src\"`; if `appDir` is missing, use default value `\".\"` (project root). `prettierTemplate` defaults to `true`; `i18n` has no default — absent means the i18n axis reports `skipped`.\n3. If the file does not exist:\n   > \"Frontend React Plugin has not been initialized. Please run `\u002Ffrontend-react-plugin:fe-init` first.\"\n   - Stop here.\n4. **Derive `srcPath`** — take `sourceBaseDir` (the config source root read above, **after its default is applied**) and remove the leading `{appDir}\u002F` (`app\u002Fsrc` + `appDir=app` → `src`; `appDir=\".\"` → unchanged; `appDir == baseDir` → `.`). Every `npx …` path argument uses `srcPath`; the repo-relative source root stays available for file operations. See fe-shared § Build Command Working Directory.\n\n### Step 1: Validate Files\n\n1. Check if `docs\u002Fspecs\u002F{feature}\u002F.implementation\u002Ffrontend\u002Fplan.json` exists\n   - If not found:\n     > \"Implementation plan not found.\"\n     > \"Please run `\u002Ffrontend-react-plugin:fe-plan {feature}` first.\"\n     - Stop here.\n\n2. Read `plan.json` → extract its `baseDir` as **`featureDir`** (e.g. `app\u002Fsrc\u002Ffeatures\u002F{feature}`) and the file list from all sections (types, api, stores, components, pages, tests)\n\n> **Never collapse these two into one `baseDir`.** Config `baseDir` is the source root; plan `baseDir` is the feature directory. Reading the second into the same name makes every later `{baseDir}` reference point at the feature directory — which is why the app-wide i18n spec must be looked for under `sourceBaseDir`, not `featureDir`. `fe-gen` keeps them apart as `planBaseDir`; do the same here.\n\n3. Read `docs\u002Fspecs\u002F{feature}\u002F.progress\u002F{feature}.json` → extract `workingLanguage` (default: `\"en\"`), `implementation.status`\n4. Language name mapping: `en` = English, `ko` = Korean, `vi` = Vietnamese\n\n**Communication language**: All user-facing output in this skill must be in {workingLanguage_name}.\n\n1. **Status check** — verify `implementation.status` indicates code has been generated:\n   - If status is `\"planned\"`, `\"gen-failed\"`, or absent:\n     > \"No generated code found (current status: '{status}').\"\n     > \"Please run `\u002Ffrontend-react-plugin:fe-gen {feature}` first.\"\n     - Stop here.\n\n2. **Demotion warning** — if `implementation.status` is `done` or `reviewed`:\n   > \"This feature is currently '{status}'. Re-running verification will reset the status to 'verified' or 'verify-failed', discarding review progress.\"\n   > \"Continue?\"\n   - If the user declines, stop here.\n\n3. **Spec staleness check** — compare spec modification time against `implementation.generatedAt`:\n   - Read `implementation.generatedAt` from the progress file\n   - Check if any spec file in `docs\u002Fspecs\u002F{feature}\u002F{workingLanguage}\u002F` was modified after `generatedAt`\n   - If spec is newer:\n     > \"Warning: Spec files have been modified since code was generated ({generatedAt}).\"\n     > \"Verification results may not reflect the current spec.\"\n     > \"To apply spec changes incrementally (preserving existing fixes): `\u002Ffrontend-react-plugin:fe-plan {feature}` (choose incremental mode) → `\u002Ffrontend-react-plugin:fe-gen {feature}`\"\n     > \"Continue with verification anyway?\"\n     - If the user declines, stop here.\n\n4. **File existence check** — Verify that all files specified in the plan actually exist:\n   - Use Glob to check existence of each file path\n   - If missing files are found, display a warning:\n     > \"Warning: {count} planned files not found:\"\n     > {list of missing files}\n\n### Lock Acquire\n\nAcquire the feature lock `docs\u002Fspecs\u002F{feature}\u002F.implementation\u002Ffrontend\u002F.lock` with `holder: \"fe-verify\"`, per fe-shared § Lock file. **Check the holder's `pid` before treating any lock as stale** — the 30-minute rule sweeps ghost locks, it does not time out a live one. Held by a live holder → report `holder` and `acquiredAt`, then stop.\n\n**Release on every exit below.** Any \"stop here\" from this point on — a user declining a\nconfirmation, a validation refusal, an agent that fails — releases this lock first. The lock is\ntaken before the confirmation prompts, so a refusal that just stops leaves the feature locked and\nevery later command refusing it (fe-shared § Lock file).\n\n### Step 2: Run Verification\n\nRun the following 3 verifications sequentially.\n\n#### 2.1 TypeScript Check\n\n**Framework mode (`routerMode == \"framework\"`)**: first run `npx react-router typegen 2>&1` to generate the RR route types (into `{appDir}\u002F.react-router\u002F`) **before** the composite-aware tsc — see fe-shared § Router-mode command matrix (typecheck row: `react-router typegen` **then** the composite-aware tsc). Library modes (`declarative`\u002F`data`) skip this step; their behavior is unchanged.\n\nDetect composite tsconfig and use the correct command:\n\n1. Read `tsconfig.json` in `{appDir}` (e.g., `app\u002Ftsconfig.json` when `appDir` is `app`, or root `tsconfig.json` when `appDir` is `.`)\n2. If it contains a `\"references\"` array → use `tsc -b`\n3. Otherwise → use `tsc --noEmit`\n\nAll commands below run from `{appDir}`. If `appDir` is not `\".\"`, prefix each with `cd {appDir} &&`.\n\n```bash\n# Framework mode only (routerMode == \"framework\"): generate route types first\nnpx react-router typegen 2>&1\n\n# If tsconfig.json contains \"references\":\nnpx tsc -b 2>&1\n\n# Otherwise:\nnpx tsc --noEmit 2>&1\n```\n\n- exit code 0 → pass\n- exit code != 0 → fail (collect error list)\n- Record error\u002Fwarning counts\n\n**Workspace API package** (`plan.apiLayer == \"workspace-package\"` and `plan.api[]` has any `additions[]`): the app's\ntsconfig may not include the package sources, so also run the package's own composite-aware tsc —\n`cd {projectRoot}\u002F{apiPackage.dir} && npx tsc --noEmit 2>&1` (or `tsc -b` when its `tsconfig.json` has `references`) — and\nrecord it as `packageTsc`. **A `packageTsc` fail is a `fail` for the TypeScript axis.** This and the `packageTests` run in 2.4\nare the two commands **exempt** from the `cd {appDir} &&` prefix above: `apiPackage.dir` is repo-relative. No additions →\n`packageTsc: skipped` (reason `\"no package additions\"`).\n\n#### 2.2 ESLint Check\n\n1. Glob for ESLint config: `.eslintrc*`, `eslint.config.*`\n2. **Config found** → detect config type and run:\n   ```bash\n   # If eslint.config.* exists (flat config, ESLint v9+):\n   npx eslint {srcPath} 2>&1\n\n   # If .eslintrc* exists (legacy config, ESLint v8):\n   npx eslint {srcPath} --ext .ts,.tsx 2>&1\n   ```\n3. **Config not found** → template fallback:\n   a. Read `.claude\u002Ffrontend-react-plugin.json` → check `eslintTemplate`\n   b. If `eslintTemplate === false` → skip (\"ESLint skipped — template disabled\")\n   c. If `eslintTemplate === true` or field absent (default: enabled):\n      - Read `${CLAUDE_PLUGIN_ROOT}\u002Ftemplates\u002Feslint-config.md`\n      - Generate `eslint.config.js` at the project root from the Canonical Config section\n      - Check `package.json` devDependencies for required packages: `eslint`, `@eslint\u002Fjs`, `typescript-eslint`, `eslint-plugin-react-hooks`, `eslint-plugin-react-refresh`, `globals`\n      - If any dependency is missing → skip with message:\n        > \"ESLint skipped (dependencies missing). Install with:\"\n        > ```\n        > pnpm add -D eslint @eslint\u002Fjs typescript-eslint eslint-plugin-react-hooks eslint-plugin-react-refresh globals\n        > ```\n      - If all dependencies are installed → run:\n        ```bash\n        npx eslint {srcPath} 2>&1\n        ```\n\n- exit code 0 → pass\n- exit code != 0 → fail (record error\u002Fwarning counts)\n\n#### 2.3 Build Check\n\nBuild command is **mode-aware** — branch on `routerMode` per fe-shared § Router-mode command matrix (build row). Run from `{appDir}` (prefix with `cd {appDir} &&` when `appDir` is not `\".\"`).\n\n```bash\n# declarative \u002F data (Vite SPA):\nnpx vite build 2>&1\n\n# framework (SSR\u002FSSG):\nnpx react-router build 2>&1\n```\n\n- exit code 0 → pass\n- exit code != 0 → fail (collect error messages)\n\n#### 2.4 Test Check\n\n**Workspace API package (`plan.apiLayer == \"workspace-package\"`, Phase 2 D15).** When `plan.api[]` carries any\n`additions[]`, first run the **package's own** suite — `cd {projectRoot}\u002F{apiPackage.dir} && npx vitest run 2>&1`\n(`apiPackage.dir` is repo-relative, so this command is exempt from the `cd {appDir} &&` prefix in 2.1;\n`apiPackage` is copied into plan.json — read `dir` from there, never from the app config) — and record it as a\nseparate `packageTests` result. A failure here is a `fail` for the test axis even when the app suite below is\ngreen. No additions → `packageTests: skipped` (reason `\"no package additions\"`); the app suite is unaffected.\n\nCheck `tests[]` in plan.json:\n- If `tests[]` is not empty:\n  ```bash\n  npx vitest run {srcPath} 2>&1\n  ```\n  - exit code 0 → pass\n  - exit code != 0 → fail (collect list of failed tests)\n- If `tests[]` does not exist or is empty: skipped (\"no tests planned\")\n\n#### 2.5 i18n Key Coverage\n\nNot a separate run — the app-wide spec (`${CLAUDE_PLUGIN_ROOT}\u002Ftemplates\u002Fi18n-key-coverage.md`) is part of the 2.4 suite.\nThis step reads what that run produced:\n\n1. No `i18n` block in `.claude\u002Ffrontend-react-plugin.json` → `skipped`, reason \"no i18n config\".\n   Stop here.\n2. Glob `{sourceBaseDir}\u002F__tests__\u002Fi18n-key-coverage.test.ts` — the **source root**, where\n   `foundation-generator` writes it. It is an app-wide invariant, never a per-feature file.\n   - **Absent** → `fail`, reason \"i18n key-coverage spec not generated\". `fe-fix` cannot produce it,\n     so name `\u002Ffrontend-react-plugin:fe-gen {feature}` as the remedy.\n   - **Present but its results are not in the 2.4 output** → `fail`, reason \"spec not collected:\n     {path}\". A file that exists but never ran is not a pass — that is the whole reason this step\n     reads the output rather than the filesystem. (When 2.4 itself was `skipped` for having no\n     planned tests, run the spec directly: `npx vitest run {srcPath}\u002F__tests__\u002Fi18n-key-coverage.test.ts 2>&1`.)\n   - **Present and collected** → report its pass\u002Ffail plus the `uncheckable` (dynamic key) count.\n     Uncheckable keys never fail the gate; the count is reported so a growing hole stays visible.\n   - **Present but built for a different config** → `fail`, reason \"i18n spec is stale: built for\n     {spec's languages\u002FlookupFns}, config now says {current}\". The spec records the `i18n` block it\n     was generated from; compare it against the current config. Without this check a spec generated\n     before a language was added keeps passing while never testing that language — a check that\n     verifies nothing but reports success. Remedy: `\u002Ffrontend-react-plugin:fe-gen {feature}`.\n\n#### 2.6 Format Check (advisory)\n\nPer `${CLAUDE_PLUGIN_ROOT}\u002Ftemplates\u002Fprettier-config.md`: glob for a Prettier config, scaffold from the template when\nabsent and `prettierTemplate` is `true`\u002Funset, skip silently when it is `false`, and record\n`skipped` (printing the `pnpm add -D` line) when `prettier` is not installed. Then:\n\n```bash\nnpx prettier --check . 2>&1\n```\n\nExit ≠ 0 is an **advisory warning only** — report the count and name `npx prettier --write .`.\nIt never sets `verify-failed`, and never contributes to Overall FAIL. Do not run `--write` here.\n\n### Result taxonomy\n\nEvery check above reports exactly one of these, and a reason is required for the last two:\n\n| Result | Means |\n|---|---|\n| `pass` | ran, and the output says it passed |\n| `fail` | ran, and the output says it failed |\n| `skipped` | deliberately not applicable — no tests planned, no i18n block, flag off |\n| `not-run` + reason | should have run but could not — tool missing, deps absent, timed out |\n\n`skipped` and `not-run` are **not** passes and must never be folded into one. \"The tool was absent\"\nand \"there was nothing to check\" lead to different next actions, and collapsing either into `pass`\nis the silent-hole failure this taxonomy exists to prevent.\n\n### Step 3: Display Report\n\nDisplay the verification results:\n\n```\nVerification Report for '{feature}':\n\n  TypeScript:  {pass\u002Ffail} ({error count} errors, {warning count} warnings){; package tsc: {pass\u002Ffail} — workspace-package with additions only}\n  ESLint:      {pass\u002Ffail\u002Fskipped\u002Fnot-run} ({error count} errors, {warning count} warnings)\n  Build:       {pass\u002Ffail}\n  Tests:       {pass\u002Ffail\u002Fskipped} ({passed}\u002F{total}){; package: {pass\u002Ffail} — workspace-package with additions only}\n  i18n keys:   {pass\u002Ffail\u002Fskipped} ({uncheckable} dynamic keys uncheckable)\n  Format:      {pass\u002Fwarn\u002Fskipped} ({file count} files differ)   ← advisory\n  E2E:         {pass\u002Fpartial\u002Ffail\u002Fnot-run} ({passed}\u002F{total} scenarios)\n\n  Overall: {PASS\u002FFAIL}\n```\n\nA `skipped` or `not-run` line always prints its reason next to it.\n\nOverall FAIL is set by TypeScript, ESLint, Build, Tests, or i18n keys. **Format never affects it** — it is advisory (`${CLAUDE_PLUGIN_ROOT}\u002Ftemplates\u002Fprettier-config.md`).\n\nThe E2E line reads `implementation.e2e` from `docs\u002Fspecs\u002F{feature}\u002F.progress\u002F{feature}.json`. If the `e2e` field is absent, display `not-run`. E2E results are informational in this report — they do not affect the Overall PASS\u002FFAIL determination (E2E has its own loop).\n\n**If FAIL:**\n- Display up to 10 error messages for each failed item\n- Suggest fixes:\n  > \"After fixing the errors, re-verify with `\u002Ffrontend-react-plugin:fe-verify {feature}`.\"\n  > \"Auto-debug: `\u002Ffrontend-react-plugin:fe-debug {feature}`\"\n  > \"For E2E testing: `\u002Ffrontend-react-plugin:fe-e2e {feature}`\"\n\n### Step 4: Update Progress\n\nRead `docs\u002Fspecs\u002F{feature}\u002F.progress\u002F{feature}.json` and add or update the `verification` field:\n\n```json\n{\n  \"implementation\": {\n    \"status\": \"verified\",\n    \"verification\": {\n      \"status\": \"pass\",\n      \"timestamp\": \"{ISO timestamp}\",\n      \"tsc\": { \"status\": \"pass\", \"errors\": 0, \"warnings\": 0, \"packageTsc\": { \"status\": \"pass | fail | skipped\", \"reason\": \"…\" } },\n      \"eslint\": { \"status\": \"pass\", \"errors\": 0, \"warnings\": 0 },\n      \"build\": { \"status\": \"pass\" },\n      \"tests\": { \"status\": \"pass\", \"passed\": 10, \"total\": 10, \"packageTests\": { \"status\": \"pass | fail | skipped\", \"reason\": \"…\" } },\n      \"i18nKeys\": { \"status\": \"pass\", \"uncheckable\": 3 },\n      \"format\": { \"status\": \"warn\", \"filesDiffering\": 4 }\n    }\n  }\n}\n\nNote: Set `implementation.status` to `\"verified\"` or `\"verify-failed\"`. `verified` requires tsc,\neslint, build, tests, and i18nKeys to each be `pass` or `skipped`; any `fail` or `not-run` sets\n`verify-failed`. `packageTsc` \u002F `packageTests` exist only under `apiLayer == workspace-package` (omit them otherwise);\na `fail` there sets its axis (`tsc` \u002F `tests`) to `fail`. `format` never participates — it is advisory.\n\nEvery `skipped` and `not-run` entry carries a `reason` string. A check that did not run is recorded\nas what it was, never omitted and never rewritten as `pass`.\n```\n\n**Merge rule**: Read the existing progress file, merge changes into the existing `implementation` object preserving all other fields (e.g., `planFile`, `tddPhases`, `review`, `fix`, `debug`), then write back the complete file.\n\n### Lock Release\n\nDelete `docs\u002Fspecs\u002F{feature}\u002F.implementation\u002Ffrontend\u002F.lock`.\n","# Verification Gate Skill\n\nRun TypeScript, ESLint, and build verification on generated code (build is mode-aware: `vite build` for declarative\u002Fdata, `react-router build` for framework — see the fe-shared Router-mode command matrix).\n\nConventions (`fe-shared`): `${CLAUDE_PLUGIN_ROOT}\u002Fskills\u002Ffe-shared\u002FSKILL.md`.\n\n## Instructions\n\n### Step 0: Read Configuration\n\n1. Read `.claude\u002Ffrontend-react-plugin.json` → extract `routerMode`, `mockFirst`, `appDir`, `eslintTemplate`, `prettierTemplate`, `i18n`, and `baseDir` as **`sourceBaseDir`** (the source root, e.g. `app\u002Fsrc`)\n2. If `sourceBaseDir` is missing, use default value `\"src\"`; if `appDir` is missing, use default value `\".\"` (project root). `prettierTemplate` defaults to `true`; `i18n` has no default — absent means the i18n axis reports `skipped`.\n3. If the file does not exist:\n   > \"Frontend React Plugin has not been initialized. Please run `\u002Ffrontend-react-plugin:fe-init` first.\"\n   - Stop here.\n4. **Derive `srcPath`** — take `sourceBaseDir` (the config source root read above, **after its default is applied**) and remove the leading `{appDir}\u002F` (`app\u002Fsrc` + `appDir=app` → `src`; `appDir=\".\"` → unchanged; `appDir == baseDir` → `.`). Every `npx …` path argument uses `srcPath`; the repo-relative source root stays available for file operations. See fe-shared § Build Command Working Directory.\n\n### Step 1: Validate Files\n\n1. Check if `docs\u002Fspecs\u002F{feature}\u002F.implementation\u002Ffrontend\u002Fplan.json` exists\n   - If not found:\n     > \"Implementation plan not found.\"\n     > \"Please run `\u002Ffrontend-react-plugin:fe-plan {feature}` first.\"\n     - Stop here.\n\n2. Read `plan.json` → extract its `baseDir` as **`featureDir`** (e.g. `app\u002Fsrc\u002Ffeatures\u002F{feature}`) and the file list from all sections (types, api, stores, components, pages, tests)\n\n> **Never collapse these two into one `baseDir`.** Config `baseDir` is the source root; plan `baseDir` is the feature directory. Reading the second into the same name makes every later `{baseDir}` reference point at the feature directory — which is why the app-wide i18n spec must be looked for under `sourceBaseDir`, not `featureDir`. `fe-gen` keeps them apart as `planBaseDir`; do the same here.\n\n3. Read `docs\u002Fspecs\u002F{feature}\u002F.progress\u002F{feature}.json` → extract `workingLanguage` (default: `\"en\"`), `implementation.status`\n4. Language name mapping: `en` = English, `ko` = Korean, `vi` = Vietnamese\n\n**Communication language**: All user-facing output in this skill must be in {workingLanguage_name}.\n\n1. **Status check** — verify `implementation.status` indicates code has been generated:\n   - If status is `\"planned\"`, `\"gen-failed\"`, or absent:\n     > \"No generated code found (current status: '{status}').\"\n     > \"Please run `\u002Ffrontend-react-plugin:fe-gen {feature}` first.\"\n     - Stop here.\n\n2. **Demotion warning** — if `implementation.status` is `done` or `reviewed`:\n   > \"This feature is currently '{status}'. Re-running verification will reset the status to 'verified' or 'verify-failed', discarding review progress.\"\n   > \"Continue?\"\n   - If the user declines, stop here.\n\n3. **Spec staleness check** — compare spec modification time against `implementation.generatedAt`:\n   - Read `implementation.generatedAt` from the progress file\n   - Check if any spec file in `docs\u002Fspecs\u002F{feature}\u002F{workingLanguage}\u002F` was modified after `generatedAt`\n   - If spec is newer:\n     > \"Warning: Spec files have been modified since code was generated ({generatedAt}).\"\n     > \"Verification results may not reflect the current spec.\"\n     > \"To apply spec changes incrementally (preserving existing fixes): `\u002Ffrontend-react-plugin:fe-plan {feature}` (choose incremental mode) → `\u002Ffrontend-react-plugin:fe-gen {feature}`\"\n     > \"Continue with verification anyway?\"\n     - If the user declines, stop here.\n\n4. **File existence check** — Verify that all files specified in the plan actually exist:\n   - Use Glob to check existence of each file path\n   - If missing files are found, display a warning:\n     > \"Warning: {count} planned files not found:\"\n     > {list of missing files}\n\n### Lock Acquire\n\nAcquire the feature lock `docs\u002Fspecs\u002F{feature}\u002F.implementation\u002Ffrontend\u002F.lock` with `holder: \"fe-verify\"`, per fe-shared § Lock file. **Check the holder's `pid` before treating any lock as stale** — the 30-minute rule sweeps ghost locks, it does not time out a live one. Held by a live holder → report `holder` and `acquiredAt`, then stop.\n\n**Release on every exit below.** Any \"stop here\" from this point on — a user declining a\nconfirmation, a validation refusal, an agent that fails — releases this lock first. The lock is\ntaken before the confirmation prompts, so a refusal that just stops leaves the feature locked and\nevery later command refusing it (fe-shared § Lock file).\n\n### Step 2: Run Verification\n\nRun the following 3 verifications sequentially.\n\n#### 2.1 TypeScript Check\n\n**Framework mode (`routerMode == \"framework\"`)**: first run `npx react-router typegen 2>&1` to generate the RR route types (into `{appDir}\u002F.react-router\u002F`) **before** the composite-aware tsc — see fe-shared § Router-mode command matrix (typecheck row: `react-router typegen` **then** the composite-aware tsc). Library modes (`declarative`\u002F`data`) skip this step; their behavior is unchanged.\n\nDetect composite tsconfig and use the correct command:\n\n1. Read `tsconfig.json` in `{appDir}` (e.g., `app\u002Ftsconfig.json` when `appDir` is `app`, or root `tsconfig.json` when `appDir` is `.`)\n2. If it contains a `\"references\"` array → use `tsc -b`\n3. Otherwise → use `tsc --noEmit`\n\nAll commands below run from `{appDir}`. If `appDir` is not `\".\"`, prefix each with `cd {appDir} &&`.\n\n```bash\n# Framework mode only (routerMode == \"framework\"): generate route types first\nnpx react-router typegen 2>&1\n\n# If tsconfig.json contains \"references\":\nnpx tsc -b 2>&1\n\n# Otherwise:\nnpx tsc --noEmit 2>&1\n```\n\n- exit code 0 → pass\n- exit code != 0 → fail (collect error list)\n- Record error\u002Fwarning counts\n\n**Workspace API package** (`plan.apiLayer == \"workspace-package\"` and `plan.api[]` has any `additions[]`): the app's\ntsconfig may not include the package sources, so also run the package's own composite-aware tsc —\n`cd {projectRoot}\u002F{apiPackage.dir} && npx tsc --noEmit 2>&1` (or `tsc -b` when its `tsconfig.json` has `references`) — and\nrecord it as `packageTsc`. **A `packageTsc` fail is a `fail` for the TypeScript axis.** This and the `packageTests` run in 2.4\nare the two commands **exempt** from the `cd {appDir} &&` prefix above: `apiPackage.dir` is repo-relative. No additions →\n`packageTsc: skipped` (reason `\"no package additions\"`).\n\n#### 2.2 ESLint Check\n\n1. Glob for ESLint config: `.eslintrc*`, `eslint.config.*`\n2. **Config found** → detect config type and run:\n   ```bash\n   # If eslint.config.* exists (flat config, ESLint v9+):\n   npx eslint {srcPath} 2>&1\n\n   # If .eslintrc* exists (legacy config, ESLint v8):\n   npx eslint {srcPath} --ext .ts,.tsx 2>&1\n   ```\n3. **Config not found** → template fallback:\n   a. Read `.claude\u002Ffrontend-react-plugin.json` → check `eslintTemplate`\n   b. If `eslintTemplate === false` → skip (\"ESLint skipped — template disabled\")\n   c. If `eslintTemplate === true` or field absent (default: enabled):\n      - Read `${CLAUDE_PLUGIN_ROOT}\u002Ftemplates\u002Feslint-config.md`\n      - Generate `eslint.config.js` at the project root from the Canonical Config section\n      - Check `package.json` devDependencies for required packages: `eslint`, `@eslint\u002Fjs`, `typescript-eslint`, `eslint-plugin-react-hooks`, `eslint-plugin-react-refresh`, `globals`\n      - If any dependency is missing → skip with message:\n        > \"ESLint skipped (dependencies missing). Install with:\"\n        > ```\n        > pnpm add -D eslint @eslint\u002Fjs typescript-eslint eslint-plugin-react-hooks eslint-plugin-react-refresh globals\n        > ```\n      - If all dependencies are installed → run:\n        ```bash\n        npx eslint {srcPath} 2>&1\n        ```\n\n- exit code 0 → pass\n- exit code != 0 → fail (record error\u002Fwarning counts)\n\n#### 2.3 Build Check\n\nBuild command is **mode-aware** — branch on `routerMode` per fe-shared § Router-mode command matrix (build row). Run from `{appDir}` (prefix with `cd {appDir} &&` when `appDir` is not `\".\"`).\n\n```bash\n# declarative \u002F data (Vite SPA):\nnpx vite build 2>&1\n\n# framework (SSR\u002FSSG):\nnpx react-router build 2>&1\n```\n\n- exit code 0 → pass\n- exit code != 0 → fail (collect error messages)\n\n#### 2.4 Test Check\n\n**Workspace API package (`plan.apiLayer == \"workspace-package\"`, Phase 2 D15).** When `plan.api[]` carries any\n`additions[]`, first run the **package's own** suite — `cd {projectRoot}\u002F{apiPackage.dir} && npx vitest run 2>&1`\n(`apiPackage.dir` is repo-relative, so this command is exempt from the `cd {appDir} &&` prefix in 2.1;\n`apiPackage` is copied into plan.json — read `dir` from there, never from the app config) — and record it as a\nseparate `packageTests` result. A failure here is a `fail` for the test axis even when the app suite below is\ngreen. No additions → `packageTests: skipped` (reason `\"no package additions\"`); the app suite is unaffected.\n\nCheck `tests[]` in plan.json:\n- If `tests[]` is not empty:\n  ```bash\n  npx vitest run {srcPath} 2>&1\n  ```\n  - exit code 0 → pass\n  - exit code != 0 → fail (collect list of failed tests)\n- If `tests[]` does not exist or is empty: skipped (\"no tests planned\")\n\n#### 2.5 i18n Key Coverage\n\nNot a separate run — the app-wide spec (`${CLAUDE_PLUGIN_ROOT}\u002Ftemplates\u002Fi18n-key-coverage.md`) is part of the 2.4 suite.\nThis step reads what that run produced:\n\n1. No `i18n` block in `.claude\u002Ffrontend-react-plugin.json` → `skipped`, reason \"no i18n config\".\n   Stop here.\n2. Glob `{sourceBaseDir}\u002F__tests__\u002Fi18n-key-coverage.test.ts` — the **source root**, where\n   `foundation-generator` writes it. It is an app-wide invariant, never a per-feature file.\n   - **Absent** → `fail`, reason \"i18n key-coverage spec not generated\". `fe-fix` cannot produce it,\n     so name `\u002Ffrontend-react-plugin:fe-gen {feature}` as the remedy.\n   - **Present but its results are not in the 2.4 output** → `fail`, reason \"spec not collected:\n     {path}\". A file that exists but never ran is not a pass — that is the whole reason this step\n     reads the output rather than the filesystem. (When 2.4 itself was `skipped` for having no\n     planned tests, run the spec directly: `npx vitest run {srcPath}\u002F__tests__\u002Fi18n-key-coverage.test.ts 2>&1`.)\n   - **Present and collected** → report its pass\u002Ffail plus the `uncheckable` (dynamic key) count.\n     Uncheckable keys never fail the gate; the count is reported so a growing hole stays visible.\n   - **Present but built for a different config** → `fail`, reason \"i18n spec is stale: built for\n     {spec's languages\u002FlookupFns}, config now says {current}\". The spec records the `i18n` block it\n     was generated from; compare it against the current config. Without this check a spec generated\n     before a language was added keeps passing while never testing that language — a check that\n     verifies nothing but reports success. Remedy: `\u002Ffrontend-react-plugin:fe-gen {feature}`.\n\n#### 2.6 Format Check (advisory)\n\nPer `${CLAUDE_PLUGIN_ROOT}\u002Ftemplates\u002Fprettier-config.md`: glob for a Prettier config, scaffold from the template when\nabsent and `prettierTemplate` is `true`\u002Funset, skip silently when it is `false`, and record\n`skipped` (printing the `pnpm add -D` line) when `prettier` is not installed. Then:\n\n```bash\nnpx prettier --check . 2>&1\n```\n\nExit ≠ 0 is an **advisory warning only** — report the count and name `npx prettier --write .`.\nIt never sets `verify-failed`, and never contributes to Overall FAIL. Do not run `--write` here.\n\n### Result taxonomy\n\nEvery check above reports exactly one of these, and a reason is required for the last two:\n\n| Result | Means |\n|---|---|\n| `pass` | ran, and the output says it passed |\n| `fail` | ran, and the output says it failed |\n| `skipped` | deliberately not applicable — no tests planned, no i18n block, flag off |\n| `not-run` + reason | should have run but could not — tool missing, deps absent, timed out |\n\n`skipped` and `not-run` are **not** passes and must never be folded into one. \"The tool was absent\"\nand \"there was nothing to check\" lead to different next actions, and collapsing either into `pass`\nis the silent-hole failure this taxonomy exists to prevent.\n\n### Step 3: Display Report\n\nDisplay the verification results:\n\n```\nVerification Report for '{feature}':\n\n  TypeScript:  {pass\u002Ffail} ({error count} errors, {warning count} warnings){; package tsc: {pass\u002Ffail} — workspace-package with additions only}\n  ESLint:      {pass\u002Ffail\u002Fskipped\u002Fnot-run} ({error count} errors, {warning count} warnings)\n  Build:       {pass\u002Ffail}\n  Tests:       {pass\u002Ffail\u002Fskipped} ({passed}\u002F{total}){; package: {pass\u002Ffail} — workspace-package with additions only}\n  i18n keys:   {pass\u002Ffail\u002Fskipped} ({uncheckable} dynamic keys uncheckable)\n  Format:      {pass\u002Fwarn\u002Fskipped} ({file count} files differ)   ← advisory\n  E2E:         {pass\u002Fpartial\u002Ffail\u002Fnot-run} ({passed}\u002F{total} scenarios)\n\n  Overall: {PASS\u002FFAIL}\n```\n\nA `skipped` or `not-run` line always prints its reason next to it.\n\nOverall FAIL is set by TypeScript, ESLint, Build, Tests, or i18n keys. **Format never affects it** — it is advisory (`${CLAUDE_PLUGIN_ROOT}\u002Ftemplates\u002Fprettier-config.md`).\n\nThe E2E line reads `implementation.e2e` from `docs\u002Fspecs\u002F{feature}\u002F.progress\u002F{feature}.json`. If the `e2e` field is absent, display `not-run`. E2E results are informational in this report — they do not affect the Overall PASS\u002FFAIL determination (E2E has its own loop).\n\n**If FAIL:**\n- Display up to 10 error messages for each failed item\n- Suggest fixes:\n  > \"After fixing the errors, re-verify with `\u002Ffrontend-react-plugin:fe-verify {feature}`.\"\n  > \"Auto-debug: `\u002Ffrontend-react-plugin:fe-debug {feature}`\"\n  > \"For E2E testing: `\u002Ffrontend-react-plugin:fe-e2e {feature}`\"\n\n### Step 4: Update Progress\n\nRead `docs\u002Fspecs\u002F{feature}\u002F.progress\u002F{feature}.json` and add or update the `verification` field:\n\n```json\n{\n  \"implementation\": {\n    \"status\": \"verified\",\n    \"verification\": {\n      \"status\": \"pass\",\n      \"timestamp\": \"{ISO timestamp}\",\n      \"tsc\": { \"status\": \"pass\", \"errors\": 0, \"warnings\": 0, \"packageTsc\": { \"status\": \"pass | fail | skipped\", \"reason\": \"…\" } },\n      \"eslint\": { \"status\": \"pass\", \"errors\": 0, \"warnings\": 0 },\n      \"build\": { \"status\": \"pass\" },\n      \"tests\": { \"status\": \"pass\", \"passed\": 10, \"total\": 10, \"packageTests\": { \"status\": \"pass | fail | skipped\", \"reason\": \"…\" } },\n      \"i18nKeys\": { \"status\": \"pass\", \"uncheckable\": 3 },\n      \"format\": { \"status\": \"warn\", \"filesDiffering\": 4 }\n    }\n  }\n}\n\nNote: Set `implementation.status` to `\"verified\"` or `\"verify-failed\"`. `verified` requires tsc,\neslint, build, tests, and i18nKeys to each be `pass` or `skipped`; any `fail` or `not-run` sets\n`verify-failed`. `packageTsc` \u002F `packageTests` exist only under `apiLayer == workspace-package` (omit them otherwise);\na `fail` there sets its axis (`tsc` \u002F `tests`) to `fail`. `format` never participates — it is advisory.\n\nEvery `skipped` and `not-run` entry carries a `reason` string. A check that did not run is recorded\nas what it was, never omitted and never rewritten as `pass`.\n```\n\n**Merge rule**: Read the existing progress file, merge changes into the existing `implementation` object preserving all other fields (e.g., `planFile`, `tddPhases`, `review`, `fix`, `debug`), then write back the complete file.\n\n### Lock Release\n\nDelete `docs\u002Fspecs\u002F{feature}\u002F.implementation\u002Ffrontend\u002F.lock`.\n",{"name":5,"description":7,"allowed-tools":41,"argument-hint":42,"user-invocable":43},"Read, Write, Glob, Grep, Bash","\u003Cfeature-name>",true,"main","eaef2a5b6092b72972ff2f25ef79803753bc9bbf","frontend-react-plugin\u002Fskills\u002Ffe-verify",0,"https:\u002F\u002Fgithub.com\u002Fohmyhotelco\u002Fhare-cc-plugins","https:\u002F\u002Fgithub.com\u002Fohmyhotelco","2026-11-13 16:34:32",{"source":8,"skillName":5,"commands":52,"prompt":62,"zipAvailable":43},[53,56,59],{"tool":54,"command":55},"npx","npx skills add ohmyhotelco\u002Fhare-cc-plugins --skill fe-verify",{"tool":57,"command":58},"bunx","bunx skills add ohmyhotelco\u002Fhare-cc-plugins --skill fe-verify",{"tool":60,"command":61},"pnpm","pnpm dlx skills add ohmyhotelco\u002Fhare-cc-plugins --skill fe-verify","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fohmyhotelco\u002Fhare-cc-plugins\u002Fblob\u002Fmain\u002Ffrontend-react-plugin\u002Fskills\u002Ffe-verify\u002FSKILL.md",[],[65,88,113,137,154,174],{"skillNo":66,"name":67,"nameZh":6,"description":68,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":69,"categories":70,"keywords":71,"publishedAt":36},"01M6M5QVXQNQMJBP1JZP9T440T","hp-shared","Shared conventions for every homepage-plugin agent and skill — stack, code conventions, state-file writes, check commands and how to report them. Preloaded into agents through their `skills:` frontmatter; not meant to be invoked by hand.","https:\u002F\u002Fgithub.com\u002Fohmyhotelco\u002Fhare-cc-plugins\u002Fblob\u002Fmain\u002Fhomepage-plugin\u002Fskills\u002Fhp-shared\u002FSKILL.md",[],[72,73,74,75,76,77,21,78,79,80,81,82,83,84,85,35,86,87],"shared","hp shared","state","stack","plugin","how","report","code conventions","commands","how to","conventions","false","frontmatter","file","hand","check",{"skillNo":89,"name":90,"nameZh":6,"description":91,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":92,"categories":93,"keywords":102,"publishedAt":36},"01M6M5PZTN1M0G4AHJV08VF1HV","fe-shared","Shared conventions for every frontend-react-plugin agent and skill — code conventions, state files and locks, accepted deviations, design principles, build working directory, router-mode command matrix, TypeScript check. Preloaded into agents through their `skills:` frontmatter; not meant to be invoked by hand.","https:\u002F\u002Fgithub.com\u002Fohmyhotelco\u002Fhare-cc-plugins\u002Fblob\u002Fmain\u002Ffrontend-react-plugin\u002Fskills\u002Ffe-shared\u002FSKILL.md",[94,99],{"categoryNo":95,"categoryName":96,"taxonomyType":97,"taxonomyTypeDesc":98},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",{"categoryNo":100,"categoryName":101,"taxonomyType":97,"taxonomyTypeDesc":98},"01JBSEEDCAT000000000000015","视觉设计",[72,103,21,104,79,105,106,107,108,83,35,86,109,87,110,74,76,111,112,26],"fe shared","mode","frontend","design","design principles","files","react","router","principles","command",{"skillNo":114,"name":115,"nameZh":6,"description":116,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":117,"categories":118,"keywords":125,"publishedAt":36},"01M6M5GAW7ZZ3580DVCMH51SFQ","be-spring-shared","Shared conventions for every backend-springboot-plugin agent and skill — tech stack, CQRS architecture, naming, coding standards, TDD, commit and build rules, state files. Preloaded into agents through their `skills:` frontmatter; not meant to be invoked by hand.","https:\u002F\u002Fgithub.com\u002Fohmyhotelco\u002Fhare-cc-plugins\u002Fblob\u002Fmain\u002Fbackend-springboot-plugin\u002Fskills\u002Fbe-spring-shared\u002FSKILL.md",[119,122],{"categoryNo":120,"categoryName":121,"taxonomyType":97,"taxonomyTypeDesc":98},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":123,"categoryName":124,"taxonomyType":97,"taxonomyTypeDesc":98},"01JBSEEDCAT000000000000023","Skill与智能体开发",[72,126,127,108,83,128,129,86,75,130,84,131,132,133,21,134,135,74,76,136],"spring","be spring shared","commit","architecture","coding standards","coding","standards","tech","Rules","cqrs","naming",{"skillNo":138,"name":139,"nameZh":6,"description":140,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":141,"categories":142,"keywords":143,"publishedAt":36},"01M6FNV58QMW8K71SA9FGTXMF4","pp-translate","Use after directly editing the working language spec to sync changes to other supported languages.","https:\u002F\u002Fgithub.com\u002Fohmyhotelco\u002Fhare-cc-plugins\u002Fblob\u002Fmain\u002Fplanning-plugin\u002Fskills\u002Fpp-translate\u002FSKILL.md",[],[144,145,146,25,23,147,31,148,30,149,150,151,85,152,22,153],"pp","pp translate","translate","editing","spec","edit","sync","use","language","changes",{"skillNo":155,"name":156,"nameZh":6,"description":157,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":158,"categories":159,"keywords":161,"publishedAt":36},"01M6FNV0PGRKR2MW0AEPS6XP0Q","pp-sync-stitch","Use when wireframes were manually edited on the Stitch website and need to be re-fetched to the local project.","https:\u002F\u002Fgithub.com\u002Fohmyhotelco\u002Fhare-cc-plugins\u002Fblob\u002Fmain\u002Fplanning-plugin\u002Fskills\u002Fpp-sync-stitch\u002FSKILL.md",[160],{"categoryNo":123,"categoryName":124,"taxonomyType":97,"taxonomyTypeDesc":98},[162,144,163,150,164,165,25,166,167,168,169,151,33,170,171,172,173,23,31,22],"pp sync stitch","stitch","projects","get","project","re","local","MCP","id","wireframes","website","screen",{"skillNo":175,"name":176,"nameZh":6,"description":177,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":178,"categories":179,"keywords":181,"publishedAt":36},"01M6FNTWW2KHS9XRAR72X33G0B","pp-sync-notion","Use when a specification needs to be published or re-synced to Notion after finalization or manual edits.","https:\u002F\u002Fgithub.com\u002Fohmyhotelco\u002Fhare-cc-plugins\u002Fblob\u002Fmain\u002Fplanning-plugin\u002Fskills\u002Fpp-sync-notion\u002FSKILL.md",[180],{"categoryNo":123,"categoryName":124,"taxonomyType":97,"taxonomyTypeDesc":98},[144,182,150,183,25,167,184,185,169,151,186,23,31,30,149,32,22],"notion","pp sync notion","search","manual","specification"]