fe-verify
Run verification gate (tsc, ESLint, build) on generated code for a feature.
- License 许可
- MIT
- 最近更新
- 2026-11-13
- Stars
- ★ 2
- Forks
- 0
01 前言 Front Matter
- name
- fe-verify
- description
- Run verification gate (tsc, ESLint, build) on generated code for a feature.
- allowed-tools
- Read, Write, Glob, Grep, Bash
- argument-hint
- <feature-name>
- user-invocable
- true
03 SKILL.md 正文
Verification Gate Skill
Run TypeScript, ESLint, and build verification on generated code (build is mode-aware: vite build for declarative/data, react-router build for framework — see the fe-shared Router-mode command matrix).
Conventions (fe-shared): ${CLAUDE_PLUGIN_ROOT}/skills/fe-shared/SKILL.md.
Instructions
Step 0: Read Configuration
- Read
.claude/frontend-react-plugin.json→ extractrouterMode,mockFirst,appDir,eslintTemplate,prettierTemplate,i18n, andbaseDirassourceBaseDir(the source root, e.g.app/src) - If
sourceBaseDiris missing, use default value"src"; ifappDiris missing, use default value"."(project root).prettierTemplatedefaults totrue;i18nhas no default — absent means the i18n axis reportsskipped. - If the file does not exist:
"Frontend React Plugin has not been initialized. Please run
/frontend-react-plugin:fe-initfirst."- Stop here.
- Derive
srcPath— takesourceBaseDir(the config source root read above, after its default is applied) and remove the leading{appDir}/(app/src+appDir=app→src;appDir="."→ unchanged;appDir == baseDir→.). Everynpx …path argument usessrcPath; the repo-relative source root stays available for file operations. See fe-shared § Build Command Working Directory.
Step 1: Validate Files
-
Check if
docs/specs/{feature}/.implementation/frontend/plan.jsonexists- If not found:
"Implementation plan not found." "Please run
/frontend-react-plugin:fe-plan {feature}first."- Stop here.
- If not found:
-
Read
plan.json→ extract itsbaseDirasfeatureDir(e.g.app/src/features/{feature}) and the file list from all sections (types, api, stores, components, pages, tests)
Never collapse these two into one
baseDir. ConfigbaseDiris the source root; planbaseDiris 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 undersourceBaseDir, notfeatureDir.fe-genkeeps them apart asplanBaseDir; do the same here.
- Read
docs/specs/{feature}/.progress/{feature}.json→ extractworkingLanguage(default:"en"),implementation.status - Language name mapping:
en= English,ko= Korean,vi= Vietnamese
Communication language: All user-facing output in this skill must be in {workingLanguage_name}.
-
Status check — verify
implementation.statusindicates code has been generated:- If status is
"planned","gen-failed", or absent:"No generated code found (current status: '{status}')." "Please run
/frontend-react-plugin:fe-gen {feature}first."- Stop here.
- If status is
-
Demotion warning — if
implementation.statusisdoneorreviewed:"This feature is currently '{status}'. Re-running verification will reset the status to 'verified' or 'verify-failed', discarding review progress." "Continue?"
- If the user declines, stop here.
-
Spec staleness check — compare spec modification time against
implementation.generatedAt:- Read
implementation.generatedAtfrom the progress file - Check if any spec file in
docs/specs/{feature}/{workingLanguage}/was modified aftergeneratedAt - If spec is newer:
"Warning: Spec files have been modified since code was generated ({generatedAt})." "Verification results may not reflect the current spec." "To apply spec changes incrementally (preserving existing fixes):
/frontend-react-plugin:fe-plan {feature}(choose incremental mode) →/frontend-react-plugin:fe-gen {feature}" "Continue with verification anyway?"- If the user declines, stop here.
- Read
-
File existence check — Verify that all files specified in the plan actually exist:
- Use Glob to check existence of each file path
- If missing files are found, display a warning:
"Warning: {count} planned files not found:" {list of missing files}
Lock Acquire
Acquire the feature lock docs/specs/{feature}/.implementation/frontend/.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.
Release on every exit below. Any "stop here" from this point on — a user declining a confirmation, a validation refusal, an agent that fails — releases this lock first. The lock is taken before the confirmation prompts, so a refusal that just stops leaves the feature locked and every later command refusing it (fe-shared § Lock file).
Step 2: Run Verification
Run the following 3 verifications sequentially.
2.1 TypeScript Check
Framework mode (routerMode == "framework"): first run npx react-router typegen 2>&1 to generate the RR route types (into {appDir}/.react-router/) 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/data) skip this step; their behavior is unchanged.
Detect composite tsconfig and use the correct command:
- Read
tsconfig.jsonin{appDir}(e.g.,app/tsconfig.jsonwhenappDirisapp, or roottsconfig.jsonwhenappDiris.) - If it contains a
"references"array → usetsc -b - Otherwise → use
tsc --noEmit
All commands below run from {appDir}. If appDir is not ".", prefix each with cd {appDir} &&.
# Framework mode only (routerMode == "framework"): generate route types first
npx react-router typegen 2>&1
# If tsconfig.json contains "references":
npx tsc -b 2>&1
# Otherwise:
npx tsc --noEmit 2>&1
- exit code 0 → pass
- exit code != 0 → fail (collect error list)
- Record error/warning counts
Workspace API package (plan.apiLayer == "workspace-package" and plan.api[] has any additions[]): the app's
tsconfig may not include the package sources, so also run the package's own composite-aware tsc —
cd {projectRoot}/{apiPackage.dir} && npx tsc --noEmit 2>&1 (or tsc -b when its tsconfig.json has references) — and
record it as packageTsc. A packageTsc fail is a fail for the TypeScript axis. This and the packageTests run in 2.4
are the two commands exempt from the cd {appDir} && prefix above: apiPackage.dir is repo-relative. No additions →
packageTsc: skipped (reason "no package additions").
2.2 ESLint Check
- Glob for ESLint config:
.eslintrc*,eslint.config.* - Config found → detect config type and run:
# If eslint.config.* exists (flat config, ESLint v9+): npx eslint {srcPath} 2>&1 # If .eslintrc* exists (legacy config, ESLint v8): npx eslint {srcPath} --ext .ts,.tsx 2>&1 - Config not found → template fallback:
a. Read
.claude/frontend-react-plugin.json→ checkeslintTemplateb. IfeslintTemplate === false→ skip ("ESLint skipped — template disabled") c. IfeslintTemplate === trueor field absent (default: enabled):- Read
${CLAUDE_PLUGIN_ROOT}/templates/eslint-config.md - Generate
eslint.config.jsat the project root from the Canonical Config section - Check
package.jsondevDependencies for required packages:eslint,@eslint/js,typescript-eslint,eslint-plugin-react-hooks,eslint-plugin-react-refresh,globals - If any dependency is missing → skip with message:
"ESLint skipped (dependencies missing). Install with:"
pnpm add -D eslint @eslint/js typescript-eslint eslint-plugin-react-hooks eslint-plugin-react-refresh globals - If all dependencies are installed → run:
npx eslint {srcPath} 2>&1
- Read
- exit code 0 → pass
- exit code != 0 → fail (record error/warning counts)
2.3 Build Check
Build 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 ".").
# declarative / data (Vite SPA):
npx vite build 2>&1
# framework (SSR/SSG):
npx react-router build 2>&1
- exit code 0 → pass
- exit code != 0 → fail (collect error messages)
2.4 Test Check
Workspace API package (plan.apiLayer == "workspace-package", Phase 2 D15). When plan.api[] carries any
additions[], first run the package's own suite — cd {projectRoot}/{apiPackage.dir} && npx vitest run 2>&1
(apiPackage.dir is repo-relative, so this command is exempt from the cd {appDir} && prefix in 2.1;
apiPackage is copied into plan.json — read dir from there, never from the app config) — and record it as a
separate packageTests result. A failure here is a fail for the test axis even when the app suite below is
green. No additions → packageTests: skipped (reason "no package additions"); the app suite is unaffected.
Check tests[] in plan.json:
- If
tests[]is not empty:npx vitest run {srcPath} 2>&1- exit code 0 → pass
- exit code != 0 → fail (collect list of failed tests)
- If
tests[]does not exist or is empty: skipped ("no tests planned")
2.5 i18n Key Coverage
Not a separate run — the app-wide spec (${CLAUDE_PLUGIN_ROOT}/templates/i18n-key-coverage.md) is part of the 2.4 suite.
This step reads what that run produced:
- No
i18nblock in.claude/frontend-react-plugin.json→skipped, reason "no i18n config". Stop here. - Glob
{sourceBaseDir}/__tests__/i18n-key-coverage.test.ts— the source root, wherefoundation-generatorwrites it. It is an app-wide invariant, never a per-feature file.- Absent →
fail, reason "i18n key-coverage spec not generated".fe-fixcannot produce it, so name/frontend-react-plugin:fe-gen {feature}as the remedy. - Present but its results are not in the 2.4 output →
fail, reason "spec not collected: {path}". A file that exists but never ran is not a pass — that is the whole reason this step reads the output rather than the filesystem. (When 2.4 itself wasskippedfor having no planned tests, run the spec directly:npx vitest run {srcPath}/__tests__/i18n-key-coverage.test.ts 2>&1.) - Present and collected → report its pass/fail plus the
uncheckable(dynamic key) count. Uncheckable keys never fail the gate; the count is reported so a growing hole stays visible. - Present but built for a different config →
fail, reason "i18n spec is stale: built for {spec's languages/lookupFns}, config now says {current}". The spec records thei18nblock it was generated from; compare it against the current config. Without this check a spec generated before a language was added keeps passing while never testing that language — a check that verifies nothing but reports success. Remedy:/frontend-react-plugin:fe-gen {feature}.
- Absent →
2.6 Format Check (advisory)
Per ${CLAUDE_PLUGIN_ROOT}/templates/prettier-config.md: glob for a Prettier config, scaffold from the template when
absent and prettierTemplate is true/unset, skip silently when it is false, and record
skipped (printing the pnpm add -D line) when prettier is not installed. Then:
npx prettier --check . 2>&1
Exit ≠ 0 is an advisory warning only — report the count and name npx prettier --write ..
It never sets verify-failed, and never contributes to Overall FAIL. Do not run --write here.
Result taxonomy
Every check above reports exactly one of these, and a reason is required for the last two:
| Result | Means |
|---|---|
pass |
ran, and the output says it passed |
fail |
ran, and the output says it failed |
skipped |
deliberately not applicable — no tests planned, no i18n block, flag off |
not-run + reason |
should have run but could not — tool missing, deps absent, timed out |
skipped and not-run are not passes and must never be folded into one. "The tool was absent"
and "there was nothing to check" lead to different next actions, and collapsing either into pass
is the silent-hole failure this taxonomy exists to prevent.
Step 3: Display Report
Display the verification results:
Verification Report for '{feature}':
TypeScript: {pass/fail} ({error count} errors, {warning count} warnings){; package tsc: {pass/fail} — workspace-package with additions only}
ESLint: {pass/fail/skipped/not-run} ({error count} errors, {warning count} warnings)
Build: {pass/fail}
Tests: {pass/fail/skipped} ({passed}/{total}){; package: {pass/fail} — workspace-package with additions only}
i18n keys: {pass/fail/skipped} ({uncheckable} dynamic keys uncheckable)
Format: {pass/warn/skipped} ({file count} files differ) ← advisory
E2E: {pass/partial/fail/not-run} ({passed}/{total} scenarios)
Overall: {PASS/FAIL}
A skipped or not-run line always prints its reason next to it.
Overall FAIL is set by TypeScript, ESLint, Build, Tests, or i18n keys. Format never affects it — it is advisory (${CLAUDE_PLUGIN_ROOT}/templates/prettier-config.md).
The E2E line reads implementation.e2e from docs/specs/{feature}/.progress/{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/FAIL determination (E2E has its own loop).
If FAIL:
- Display up to 10 error messages for each failed item
- Suggest fixes:
"After fixing the errors, re-verify with
/frontend-react-plugin:fe-verify {feature}." "Auto-debug:/frontend-react-plugin:fe-debug {feature}" "For E2E testing:/frontend-react-plugin:fe-e2e {feature}"
Step 4: Update Progress
Read docs/specs/{feature}/.progress/{feature}.json and add or update the verification field:
{
"implementation": {
"status": "verified",
"verification": {
"status": "pass",
"timestamp": "{ISO timestamp}",
"tsc": { "status": "pass", "errors": 0, "warnings": 0, "packageTsc": { "status": "pass | fail | skipped", "reason": "…" } },
"eslint": { "status": "pass", "errors": 0, "warnings": 0 },
"build": { "status": "pass" },
"tests": { "status": "pass", "passed": 10, "total": 10, "packageTests": { "status": "pass | fail | skipped", "reason": "…" } },
"i18nKeys": { "status": "pass", "uncheckable": 3 },
"format": { "status": "warn", "filesDiffering": 4 }
}
}
}
Note: Set `implementation.status` to `"verified"` or `"verify-failed"`. `verified` requires tsc,
eslint, build, tests, and i18nKeys to each be `pass` or `skipped`; any `fail` or `not-run` sets
`verify-failed`. `packageTsc` / `packageTests` exist only under `apiLayer == workspace-package` (omit them otherwise);
a `fail` there sets its axis (`tsc` / `tests`) to `fail`. `format` never participates — it is advisory.
Every `skipped` and `not-run` entry carries a `reason` string. A check that did not run is recorded
as what it was, never omitted and never rewritten as `pass`.
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.
Lock Release
Delete docs/specs/{feature}/.implementation/frontend/.lock.
04 同仓库更多 Skills
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.
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.
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.
pp-translate
Use after directly editing the working language spec to sync changes to other supported languages.
pp-sync-stitch
Use when wireframes were manually edited on the Stitch website and need to be re-fetched to the local project.
pp-sync-notion
Use when a specification needs to be published or re-synced to Notion after finalization or manual edits.