quarkus-tdd
Test-driven development for Quarkus 3.x LTS using JUnit 5, Mockito, REST Assured, Camel testing, and JaCoCo. Use when adding features, fixing bugs, or refactoring event-driven services.
- License 许可
- Apache-2.0
- 最近更新
- 2026-11-08
- Stars
- ★ 0
- Forks
- 0
01 前言 Front Matter
- name
- fm-verify
- description
- Use after fm-gen to run the technical gate on a migrated page — build, TypeScript (composite-aware), Vitest, and ESLint (hard); Prettier --check is advisory — from the app's appDir, and advance the page to verified.
- allowed-tools
- Read, Write, Glob, Grep, Bash, Agent
- argument-hint
- <page> [--regate] [--app pc|mobile|hana]
- user-invocable
- true
03 SKILL.md 正文
Verify a Migrated Page (Technical Gate)
The first hard gate after generation: build + types + unit/component tests. (E2E is fm-e2e,
legacy parity is fm-parity.) All user-facing output in workingLanguage.
Instructions
Step 0: Config
Read config (absent → run fm-init; stop). Resolve app, its appDir, monorepoRoot,
packagesDir (Step 6 maps the plan's sharedDeps[] through it for the gate-evidence hash), pluginRoot (absolute, per-machine — read from .claude/frontend-migration-plugin.local.json, never the shared config; where scripts/gate-tree-hash.sh lives — absent → record no tree and report the freshness axis unverifiable, never an inline pipeline).
legacyDir (Step 6b hands it to the Codex auditor), workingLanguage. Confirm the page is at least generated in tracker.json — but refuse a page
at flipped or done, and refuse while flipPrOpenedAt is present (unless --regate, Step 0a, admits it): "at least generated" is a
monotonic comparison and flipped/done both satisfy it, so
without this guard a re-verify would write verified over a live page and desync the tracker from
the edge flag (CLAUDE.md → Per-page State Machine). For flipped or a present flipPrOpenedAt,
point the user at /frontend-migration-plugin:fm-verify {page} --regate (Step 0a — re-gate without
a status change), or at /frontend-migration-plugin:fm-route {page} --revert to abandon the flip.
For done, do not — --revert refuses a done page (the legacy page is deleted, so there is no
rollback target); reopening it is a manual decision.
Warn before demoting, and clear the route fields. If the page is already at verified,
e2e-passed or parity-passed, this skill's Record step moves it backwards and discards that gate
progress — so clear routePrepared and flagKey on that write, the same set fm-gen,
fm-fix, fm-delta and fm-extract clear for the same reason. Left standing, the page re-passes
the chain and reaches --flag-on with a routePrepared from before the change, so Step 1-pre is
satisfied by the stale --flag-off and the route-stage Codex audit that step exists to force never
re-runs. This is the recovery fm-route Step 1a now names, so it is the path that reaches it. Say so and get confirmation first — the same courtesy fm-gen Step 2 extends. Without it, the documented recovery from a stale-evidence block (re-run the gates) silently destroys parity-passed on the way through.
Confirm apps[app] before using it (CLAUDE.md → Configuration): the app entry must exist and carry the keys this stage reads. Config-file presence is not app presence — mobile/hana are scaffolded, and a --app naming an unconfigured one must stop here with a clear message rather than fail deep inside an agent on an unresolved path.
Step 0a: --regate (in-flight or flipped page) — CLAUDE.md → Per-page State Machine → In-flight window
With --regate, require flipPrOpenedAt present (status parity-passed) or status flipped;
refuse anything else and name the plain fm-verify {page} run, which is what advances a status.
Run Steps 1–5 unchanged. Step 6 then records evidence only: it never writes status,
routePrepared, flagKey, flipPrOpenedAt or flippedAt, and skips the demotion warning above.
On a pass it writes verifiedAt, gateEvidence.verify and the manifest exactly as the pass branch
does, and deletes regateFailed.verify. On a failure it writes
regateFailed.verify = { "at", "commit", "summary" } (the failing summary) instead of
verify-failed. fm-route --confirm-live blocks while verify's evidence is stale or regateFailed.verify is present.
Step 1: Lock
This skill mutates tracker.json, so acquire docs/migration/{app}/{page}/.lock (stale only when
its holder is gone — CLAUDE.md → Lock file) before running the gate. If held and fresh, report who holds it and stop.
Step 2: Resolve the run directory
All commands run from {monorepoRoot}/{appDir}. If appDir is ".", run from root.
Before the first tool runs, compute the pre-run tree hash — same script, same watch-path
union as Step 6. Step 6 compares its record-time hash against this one: a gate may record a pass
only if its watch paths did not move while it ran (CLAUDE.md → Gate Result Accounting E).
Step 3: TypeScript (composite-aware)
Read tsconfig.json in {appDir}:
- has a
referencesarray →npx tsc -b 2>&1 - otherwise →
npx tsc --noEmit 2>&1
Step 4: Build & tests
npx vite build 2>&1(or the app's build script).npx vitest run 2>&1.
Step 4a: i18n key coverage (observed-in-the-run check — the run itself is Step 4)
The key-coverage spec foundation-generator scaffolds (templates/i18n-copy-parity.md) runs inside
Step 4's npx vitest run, so a missing/locale-gapped key — or a markup/entity value rendered as
plain text (K3) — already fails the gate there; there is no separate command here. What this
step adds is that the spec ran, so the check cannot be silently removed — by deletion or by
falling out of the harness's collection:
- Config has an
i18nblock, the app has the spec, and the spec's results appear in Step 4's vitest output → note it aspresentand surface itsuncheckable(dynamic-key) count from that output. A rising count is a signal, not a pass. File existence is not the test — a spec vitest never collected produces no output to read, and recordingpresentfrom the file alone is the pass-you-did-not-observe Step 5 forbids. - Config has an
i18nblock but the spec is absent → gate failure; point atfm-gen {page} --force(there is no per-phase entry point;--forcere-runs every phase, andfoundation-generatorregenerates the spec when the app has none). - Config has an
i18nblock, the spec exists, but its results are not in the output → gate failure, and do not recordregenRequiredAt: the spec is already there, so a full regeneration would not change whether vitest collects it. Two cases, told apart by Step 4's exit code because their remedies differ:- vitest exited 0 → the spec was genuinely not collected. Record the failing summary as
i18n key-coverage spec not collected: <path>—migration-fixer'sverify-fixmode owns the harness and repairs the include pattern. It still re-runs all four tools, but the repair is what makes the nextfm-verifyobserve the spec — without itfm-fixwould report a pass and land the page back on this same failure. - vitest exited non-zero → the run died before per-file reporting, so "not collected" cannot
be told from "never reached". Record the coverage axis as
not-observedand let the hard-tool failure carry the gate; its remedy is the one that applies.
- vitest exited 0 → the spec was genuinely not collected. Record the failing summary as
- No
i18nblock in config → recordskipped(never a silent pass); note thatfm-initcan add it.
Route-target spec (foundation-generator 3c) — the same observed-in-the-run reading, lighter
consequences. Its failures already fail Step 4's vitest run. Find it by name, route-targets.test.ts
under {appDir}, and record routeTargets as one of:
present— its results appear in the output. Surface itsuncheckablecount.absent— no such file.not-observed— the file exists but vitest exited non-zero before per-file reporting. The hard-tool failure carries the gate, as in the i18n branch.- not collected — the file exists but its results are not in the output while vitest exited 0.
This is a gate failure, exactly like the i18n branch above: summary
route-target spec not collected: <path>, noregenRequiredAt, repaired byverify-fix. File existence is not the test.
Only absent is reported rather than failed, because the spec postdates many already-generated
apps. The next page this app generates scaffolds it (foundation-generator
checks per app, not per page); fm-gen {page} --force does it now. Say so in the report and name
the command.
Step 4b: Lint (hard) & format (advisory)
Follow CLAUDE.md → "Lint & Format Gate" (detection / scaffold-if-flag-on / skip-if-deps-missing).
- ESLint — hard.
npx eslint . 2>&1. Exit ≠ 0 is a gate failure.skipped(config absent &eslintTemplate: false, or deps missing) does not fail the gate. - Prettier — advisory.
npx prettier --check . 2>&1. Exit ≠ 0 is recorded as a warning only; it never blocksverified. Surface the unformatted file list and suggestnpx prettier --write ..
Step 4c: Design-token traps (advisory — templates/visual-parity-checklist.md)
The computed-value parity probe catches a mis-resolved Tailwind token, but it runs later and is
skipped/blocked exactly where clusters and blocked pages ship — so surface the known traps here,
at the first gate, where they are cheap to fix. Grep this page's generated files (tracker.json
sourcePaths[] — never the legacy tree) for the enumerated trap literals:
grep -nE '\brounded-lg\b|\bfont-inherit\b|\bborder-[trbl]-solid\b' -- <sourcePaths...> 2>/dev/null || true
Report each hit file:line · <literal> as an advisory warning (like Prettier — it never sets
verify-failed or blocks): rounded-lg computes 16px in this repo (use rounded-[8px] for a bespoke
legacy radius), and font-inherit / per-side border-style emit no CSS in Tailwind v4. Point at the
checklist's "Design-token traps" section and the page's style-spec.json computed value. A project's
own docs/migration/**/css-parity-checklist.md, if present, may list more literals to grep. This is a
reminder to check the computed value against the spec, not a verdict.
Step 5: Read and judge (evidence before claims)
Apply the 5-step gate: RUN → READ the full output (exit codes, error/test counts) → VERIFY → CLAIM. Do not report a pass you did not observe. Capture the failing output verbatim if any step fails.
Step 6: Record
Tracker lock. Take docs/migration/.tracker.lock around every tracker.json write below —
after the lock this step already holds, released right after the write (CLAUDE.md → Lock file). Write it per CLAUDE.md → Serialization.
Update tracker.json (Read-Modify-Write). Under --regate, apply Step 0a instead of every status
write below — the evidence writes are the same:
-
tsc + build + vitest + eslint all pass (or eslint
skipped) and the i18n key-coverage spec ispresentorskippedand the route-target spec ispresentorabsent→apps[app].pages[page].status = "verified", withverifiedAt, the tool summary, the spec'suncheckablecount underi18nCoverage, the route-target spec's state underrouteTargets({ "state": "present", "uncheckable": N }or{ "state": "absent" }), and any Prettier advisory underformatWarnings(all three are reporting surfaces forfm-progressand a human reading the tracker — no gate branches on any of them; a Prettier advisory never fails anything). ClearregenRequiredAt— a passing gate means the full regeneration it records as owed is discharged, however it was met. Also recordapps[app].pages[page].gateEvidence.verify = { "at": <ISO-8601>, "commit": <sha>, "tree": <hash> }exactly as CLAUDE.md → "Gate Result Accounting" E prescribes —commitfromgit rev-parse --short HEAD(<sha>+dirtywhengit status --porcelainis non-empty),treeby running the script once with--manifestand hashing its output — never by re-implementing its records with an inline pipeline:REPO=$(git rev-parse --show-toplevel) MAN="$REPO/docs/migration/{app}/{page}/gate-tree/verify.tsv" mkdir -p "$(dirname "$MAN")" {pluginRoot}/scripts/gate-tree-hash.sh --manifest \ --exclude docs/migration/{app}/{page}/gate-tree/verify.tsv -- <watch path>... > "$MAN.tmp" TREE=$(git hash-object --no-filters -- "$MAN.tmp")One execution produces both. The script's aggregate is
git hash-objectof exactly these records (its own construction), so hashing the manifest file yields the sametree— atomically. Two separate executions could straddle a change and record a hash the manifest does not describe. On a non-zero script exit, do not hash: exit 2 putunverifiablein the redirect (freshness axisunverifiable), exit 1 is an error.Watch paths are the union of the three axes CLAUDE.md → "Gate Result Accounting" F defines, minus every axis-1 entry under
{appDir}/e2e/, and minus any untracked axis-1 entrygit check-ignore -qaccepts (F) (the e2e carve-out:fm-e2ehas not realized them yet, and a re-run rewrites them); resolvepackagesDirandmonorepoRootin Step 0 and read the plan'ssharedDeps[]here. The redirect target must be the real repo root, not{monorepoRoot}— this skill runs from{appDir}. Compare this hash with the pre-run hash from Step 2: if they differ, the watch paths moved while the gate ran — record no pass, leave the status unchanged, discard"$MAN.tmp", and say to re-run. Only when the pass is recorded, promote the manifest — an overwritten manifest beside a refused pass would pair the old recordedtreewith a file list from a different tree — and stage it with the tracker: the two are one piece of evidence, andfm-routeStep 1a blocks while either is uncommitted:REPO=$(git rev-parse --show-toplevel); MAN="$REPO/docs/migration/{app}/{page}/gate-tree/verify.tsv" mv "$MAN.tmp" "$MAN" && git add -- "$MAN" "$REPO/docs/migration/tracker.json"If
git addfails (the index lock held by a concurrent page, an ignored path), say so: the pass stands, the pair is unstaged, andfm-routeStep 1a blocks until it is committed.If it prints
unverifiable(exit 2 — no watch paths resolved), record notreeand say so: the page is unverifiable on this axis, whichfm-routeacknowledges rather than blocks. Never store the wordunverifiable, and never store a hash the script did not print. KeepverifiedAtfor backward compatibility. -
any hard tool fails (tsc / build / vitest / eslint) →
verify-failed, with the failing summary. A Prettier advisory alone never setsverify-failed. -
while
i18nis configured, the spec exists but its results are not in the output, and no hard tool failed (eslintskippedcounts here exactly as it does in the pass branch above — it is neither a pass nor a failure, and an app that has not installed the lint deps yet is the ordinary early state) →verify-failed, with the failing summaryi18n key-coverage spec not collected: <path>and noregenRequiredAt(Step 4a). Both halves are load-bearing: without the status the page keeps its entry status andfm-fix— the remedy Step 7 names — refuses it; without the summary intracker.jsonthe harness repair inmigration-fixer'sverify-fixmode has nothing to key on, since verify writes no report file. -
the route-target spec exists but its results are not in the output, and no hard tool failed →
verify-failed, summaryroute-target spec not collected: <path>, noregenRequiredAt— the same handling and the same reasons as the i18n not-collected branch above. -
the spec is absent while
i18nis configured →verify-failedand recordregenRequiredAt.fm-fixcannot produce the spec (Step 7), and the next-step advisors already override the*-failedwildcard tofm-gen --forcewhen that field is set — this is what puts them on the remedy this skill's own report names.fm-genStep 5.3 clears it once a full regeneration succeeds, and this skill's own PASS branch clears it too.
Release the lock.
Step 6b: Codex audit (advisory) — see CLAUDE.md → "Codex Independent Audit"
If codexAudit is enabled and this stage is in codexAuditStages (absent → all seven; the
key narrows coverage, it never means "none"), after the lock is released spawn
codex-auditor
(Agent) for the verify stage (params: app, page, stage="verify", appDir, legacyDir,
generated code + test paths + the verify summary, outPath = docs/migration/{app}/{page}/codex-audit.json,
workingLanguage) — an independent second opinion to Claude's own reviewers. Advisory — never
changes the page status. Surface its verdict below.
Step 7: Report
In workingLanguage: per-tool result (tsc / build / vitest / eslint) with the evidence (exit code,
counts), the i18n key-coverage result (present + uncheckable count / absent / not collected /
not-observed / skipped), the route-target spec result (present + uncheckable count /
absent with its remedy / not collected / not-observed), the
Prettier advisory if any, and the Codex audit verdict (advisory). Next step: on pass →
/frontend-migration-plugin:fm-cascade {page} when the page injects markup it does not author (CMS
rich text, i18n values containing HTML, editor output — the case no element-indexed style spec and no
jsdom test can cover), otherwise /frontend-migration-plugin:fm-e2e {page}; on fail →
/frontend-migration-plugin:fm-fix {page}
— except the absent i18n key-coverage spec (Step 4a), whose remedy is
/frontend-migration-plugin:fm-gen {page} --force. fm-fix cannot produce that spec: its
verify-fix mode re-runs tsc/build/vitest/eslint, all of which pass, so it would report a
successful fix, set the page back to generated, and land on this same failure again.
Under --regate the next step follows the evidence, not a status: on a pass,
/frontend-migration-plugin:fm-e2e {page} --regate; on a failure,
/frontend-migration-plugin:fm-fix {page} --mode review --findings <file> with the failure as the
finding (then re-gate), or fm-route {page} --revert to take the page back to legacy.
04 同仓库更多 Skills
remotion-video-creation
Best practices for Remotion - Video creation in React. 29 domain-specific rules covering 3D, animations, audio, captions, charts, transitions, and more.
hotcook-recipes
暂无简介
code-helper
Code review and assistance skill
research-assistant
Web research assistant skill using Brave Search MCP
x-api
X/Twitter API integration for posting tweets, threads, reading timelines, search, and analytics. Covers OAuth auth patterns, rate limits, and platform-native content posting. Use when the user wants to interact with X programmatically.
workspace-surface-audit
Audit the active repo, MCP servers, plugins, connectors, env surfaces, and harness setup, then recommend the highest-value ECC-native skills, hooks, agents, and operator workflows. Use when the user wants help setting up Claude Code or understanding what capabilities are actually available in their environment.