[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6CR8D3WN4WMPNTDYGYR2P9X":3,"skill-same:01M6CR8D3WN4WMPNTDYGYR2P9X":71},{"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":22,"publishedAt":43,"linkOnly":44,"markdownContent":45,"markdownBody":46,"frontMatter":47,"refName":51,"commitSha":52,"skillDirectory":53,"forksCount":54,"repositoryHtmlUrl":55,"repositoryHomepageUrl":6,"ownerHtmlUrl":56,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":57,"install":58,"resources":70},"01M6CR8D3WN4WMPNTDYGYR2P9X","fm-gen",null,"Use after fm-plan to generate the RR v7 page from migration-plan.json via a strict per-phase TDD pipeline (foundation -> api -> store -> component -> page -> integration), with resume and demotion safety.","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-migration-plugin\u002Fskills\u002Ffm-gen\u002FSKILL.md",[17],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000038","人力资源","PROFESSION","适用职业",[23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42],"fm gen","generate","via","with","store","-app","true","rr","app","json","tdd","component","plan","resume","api","foundation","Bash","use","pipeline","page","2026-11-10 10:12:30",false,"---\nname: fm-gen\ndescription: \"Use after fm-plan to generate the RR v7 page from migration-plan.json via a strict per-phase TDD pipeline (foundation -> api -> store -> component -> page -> integration), with resume and demotion safety.\"\nargument-hint: \"\u003Cpage> [--app pc|mobile|hana] [--force]\"\nuser-invocable: true\nallowed-tools: Read, Write, Edit, Glob, Grep, Bash, Agent\n---\n\n# Generate a Page Migration (TDD Coordinator)\n\nExecutes `migration-plan.json` phase by phase. Each phase runs in a separate agent session\n(`Agent` tool — phases are strictly sequential, each depends on the previous). All user-facing\noutput in `workingLanguage`.\n\n## Instructions\n\n### Step 0: Config & plan\nRead config (absent → run `fm-init`; stop). Require `docs\u002Fmigration\u002F{app}\u002F{page}\u002Fmigration-plan.json`\n(missing → run `fm-plan {page}`; stop) **with `gateAcceptance`** (absent → the plan is incomplete;\nre-run `fm-plan {page}`; stop). Require `docs\u002Fmigration\u002F{app}\u002F{page}\u002Fstyle-spec.json` (missing → run\n`fm-style-spec {page}`; stop) — the foundation phase copies its assets and the component\u002Fpage phases\nbuild to its style values. Read `targetDir`, `appDir`, `packagesDir`, `monorepoRoot`, `legacyDir`,\n`workingLanguage`, `eslintTemplate`, `prettierTemplate`, and the plan's `buildOrder` + `blockers`.\n\n**Confirm `apps[app]` before using it** (${CLAUDE_PLUGIN_ROOT}\u002FCLAUDE.md → Configuration): the app entry must exist and carry the keys this stage reads. Config-file presence is not app presence — `mobile`\u002F`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.\n\n### Step 1: Blockers\nIf the plan has `blockers` (unextracted shared candidates), resolve each against\n`tracker.packages` — **not** against the plan, which `migration-planner` writes once and no skill\nrewrites. A blocker is unresolved while its candidate's `packages.\u003Cpkg>.status` is anything other\nthan `extracted`. Stop only then, and tell the user to run `\u002Ffrontend-migration-plugin:fm-extract`\nfirst. Reading the plan array as the live gate would refuse forever: `fm-extract` never writes the plan\n— its tracker writes are `packages.*` (Step 5.1) and the dependent pages' invalidation (Steps 2b\nand 5.3) — so a successful extraction leaves the plan byte-identical.\n\n### Step 2: Resume \u002F demotion\n- If `generation-state.json` exists, offer to resume from the last incomplete phase. With `--force`\n  (how `fm-fix` sends a page back after `regenRequired`), regenerate every phase — and **rewrite it\n  with every phase pending in Step 3, once the page lock is held and after the refusals below**,\n  never here: this bullet runs unlocked, so rewriting it now would let a run that is about to be\n  refused (`flipped`, `done`, a flip in flight, or a declined demotion) destroy the record anyway,\n  and would reset the ledger of another session's generation already in progress — a completed state file would otherwise make\n  the resume path a no-op, and a `--force` run that dies in an early phase would leave the later\n  phases still marked done, so the next plain resume would skip them and reach `generated`\n  carrying exactly the code the fix refused to certify.\n- Demotion warning: if the page status is `verified`\u002F`e2e-passed`\u002F`parity-passed`, warn that\n  re-generating resets it to `generated` and discards downstream gate progress. Confirm before\n  proceeding.\n- **`flipped` and `done` are refused, not warned; so is an in-flight flip PR.** `done` is past\n  `flipped` (the legacy page is deleted), so `--revert` is not an escape from it — refuse and\n  require manual intervention — **not** `--revert`, which refuses a `done` page too. On any status,\n  a present `flipPrOpenedAt` means the flip artifact is prepared and PR2 handed over against the\n  current code: refuse and point at `fm-route --revert` first, or the regeneration lands under a PR\n  that no longer describes it.\n- **`flipped`** — stop and tell the user to\n  run `\u002Ffrontend-migration-plugin:fm-route {page} --revert` first — **`--revert`, not `--flag-off`**:\n  flag-off prepares the routing artifact with the flag OFF and *keeps the current status*\n  (`fm-route` Step 4), so it would leave the page at `flipped` and this refusal would repeat forever.\n  `--revert` is the rollback that takes the path out of rotation and returns the page to\n  `parity-passed`. The path is serving production\n  traffic, so regenerating it is a rewrite under live load — and the status reset would desync the\n  two facts that must agree: the tracker's `flipped` and the edge flag `fm-route` owns. Provenance\n  resolves a capture's `side` from that status (`${CLAUDE_PLUGIN_ROOT}\u002Ftemplates\u002Fcapture-provenance.md`), so a demoted-but-\n  still-flipped page makes a capture from the production domain — which is serving v2 — resolve as\n  `legacy`. That is the wrong-side baseline the v0.14.0 provenance layer exists to stop, and unlike\n  `unresolved` it is *accepted as evidence*.\n\n### Step 3: Lock\n**With `--force`, rewrite `generation-state.json` with every phase pending here — and every\nphase's persisted `filesChanged[]` cleared** (a full regeneration owns all its files; a stale list\nsurviving into a force run that fails early would feed Step 5 paths from the previous\ngeneration) — once the lock\nis held and the Step 2 refusals have passed. Doing it in Step 2 would let a run that is about to\nbe refused destroy the ledger, and would reset the ledger of another session's generation already\nin progress. Without it, a `--force` run that dies in an early phase leaves the later phases still\nmarked done, and the next plain resume skips them.\nAcquire `docs\u002Fmigration\u002F{app}\u002F{page}\u002F.lock` (stale only when its holder is gone — see ${CLAUDE_PLUGIN_ROOT}\u002FCLAUDE.md → Lock file; JSON schema — `holder`\u002F`pid`\u002FISO-8601 `acquiredAt` — in ${CLAUDE_PLUGIN_ROOT}\u002FCLAUDE.md → Lock file).\n\n### Step 4: Run phases (sequential)\nFor each phase in `buildOrder`, launch the right agent (`subagent_type: \"frontend-migration-plugin:\u003Cagent>\"`), passing **the agent's full\nparameter list from its own file** — not only the additions called out below (subagent isolation\nmeans an omitted param is simply missing, and the agent cannot ask). Inspect the result before the\nnext:\n- `foundation` → **foundation-generator** (types + MSW + harness + **style-spec assets copied** +\n  lint\u002Fformat config scaffold; pass `styleSpecPath`, `legacyDir`, `monorepoRoot`, `legacyDirs`\n  (every `apps.*.legacyDir`), `eslintTemplate`, `prettierTemplate`)\n- `api` \u002F `store` \u002F `component` \u002F `page` → **tdd-cycle-runner** (Red-Green per unit; pass\n  `styleSpecPath` — the component\u002Fpage phases build to the legacy style values)\n- `integration` → **integration-generator** (routes + i18n + MSW global + ESLint on generated\n  code; pass `monorepoRoot`, `eslintTemplate`)\n\nPass the config's **`i18n` block** to `foundation-generator` along with its other params. Its task 3b\n(the key-coverage spec) reads `localesDir`, `languages`, `lookupFns`, and `keyPrefix`, and cannot\ninfer any of them: unlike the plan-derived `languages` that `parity-verifier` and `e2e-test-runner`\ncan read, these exist only in config. Omitting them would skip the spec on a project that *has* i18n\nconfigured, and `fm-verify` Step 4a makes an absent spec a hard failure whose only remedy is\nre-running this phase — which would fail the same way.\n\nAfter each phase, update `generation-state.json` (Read-Modify-Write): mark the phase\n`done`\u002F`failed`, record `currentPhase`, and **persist the phase's `filesChanged[]`** on its entry —\nStep 5's `sourcePaths` union reads these recorded lists, and a resumed session cannot recover an\nunpersisted list without re-running the completed phase. On a **retried** phase, union the retry's\nlist with the entry's existing one — files a failed attempt created still exist when the retry\nreuses them unchanged and does not relist them; `--force` (Step 3) is the only reset. On a phase failure, **stop running further phases and\ncontinue to Step 5** — do not return from the skill here. Step 5 is what writes `gen-failed`,\nrecords `sourcePaths` for the files the completed phases did write, clears the stale gate fields,\nand **releases the lock**. Returning from this step instead would leave the page at `planned` over\nmodified code, with the Step 3 lock held for 30 minutes.\n\n### Step 5: Record\n\n**Tracker lock.** Take `docs\u002Fmigration\u002F.tracker.lock` around every `tracker.json` write below —\nafter the lock this step already holds, released right after the write (${CLAUDE_PLUGIN_ROOT}\u002FCLAUDE.md → Lock file). Write it per ${CLAUDE_PLUGIN_ROOT}\u002FCLAUDE.md → Serialization.\n\n1. Set `generatedAt` and, if all phases succeeded, `tracker.json`\n   `apps[app].pages[page].status = \"generated\"`; any skipped\u002Ffailed phase → `gen-failed`.\n2. Record `apps[app].pages[page].sourcePaths` — the repo-relative paths of the files the phases\n   created or modified (under `appDir`, plus any root-level file a phase legitimately owns),\n   collected from **every** phase's recorded `filesChanged[]` in `generation-state.json` —\n   including phases completed by an earlier resumed run: a resume that rewrites the list from only\n   the current run's phases silently drops watched files. Each list is the phase's **output set**\n   (reused-but-unchanged files included — the agents' contracts say so), which is what makes this\n   full rewrite safe **for the generation-owned files**. Entries merged into `sourcePaths` by\n   `fm-e2e`\u002F`fm-fix`\u002F`fm-delta` (specs, page objects, helpers) are not the phases' to drop:\n   re-add every prior entry whose file still exists on disk — only an entry whose file is gone\n   leaves the list. A phase report missing the list, or\n   carrying paths that do not resolve from the repo root, is **incomplete evidence**: do not\n   record `sourcePaths` from it — re-collect from the phase before recording (an unwatched file\n   evades every later freshness hash). This is the page's\n   **axis 1** of its watch paths — axis 2 is the plan's `sharedDeps[]` mapped to\n   `{packagesDir}\u002F\u003Cpackage>` and axis 3 is the page's `migration-plan.json` itself, and every hash is\n   taken over the **union of all three** (${CLAUDE_PLUGIN_ROOT}\u002FCLAUDE.md → \"Gate Result Accounting\" F). `fm-route --flag-on` (Step 1a) and `fm-progress` hash that union to\n   tell a still-fresh PASS from a stale one, and neither can derive axis 1 otherwise\n   — `componentTree` carries component *names*, not paths. Rewrite the list on every run so a\n   removed file leaves it.\n3. Clear `apps[app].pages[page].gateEvidence` **and the legacy `verifiedAt` \u002F `e2ePassedAt` \u002F\n   `parityPassedAt`** — the page's code has been regenerated, so every prior gate PASS now rests on\n   code that no longer exists. Clearing only `gateEvidence` is the wrong half of the job: that is the\n   field Step 1a treats as evidence, while `fm-route` Step 1's **hard** precondition reads\n   `verifiedAt` and the two gate report files, so the authoritative traces would survive this\n   regeneration and re-authorize a flip on code no gate has seen. The report files are not deleted\n   (`fm-fix` reads them) — the tracker's claim about them is what has to go.\n   Delete the page's `cascade-diff*.json` and clear the tracker `cascade` record too — `fm-route`\n   reads that file directly, and a clean pre-regeneration report would silently vouch for markup\n   this regeneration may have changed.\n   **Clear `routePrepared` and `flagKey` too.** `fm-route` Step 1-pre accepts `routePrepared: true`\n   as proof the code PR was prepared; left standing, a regenerated page reaches `--flag-on` without\n   a fresh `--flag-off`, skipping the route-stage Codex audit that step exists to force.\n     **Clear `regenRequiredAt` — but only when every phase succeeded**, the same condition 5.1\n     writes `generated` under. `fm-fix` (a fix that changed no code) and `fm-verify` (an absent i18n\n     key-coverage spec) both write it to mean \"a full regeneration is still owed\", and only a\n     completed run here — or a later passing `fm-verify` — discharges that. `fm-fix` and `fm-delta`\n     deliberately do not clear it: neither performs a full regeneration. The SessionStart hook and `fm-progress` read it; left\n     standing it outlives the run it asked for and hijacks every later `*-failed` state, while\n     clearing it on a `gen-failed` run would drop the record while the debt stands.\n4. Release the lock.\n\n### Step 5b: Codex audit (advisory) — see ${CLAUDE_PLUGIN_ROOT}\u002FCLAUDE.md → \"Codex Independent Audit\"\nIf `codexAudit` is enabled, this stage is in `codexAuditStages` (**absent → all seven**; the key\nnarrows coverage, it never means \"none\"), and generation succeeded, after the\nlock is released spawn `codex-auditor` (`subagent_type: \"frontend-migration-plugin:codex-auditor\"`) for the `gen` stage (params: `app`, `page`, `stage=\"gen\"`,\n`appDir`, `legacyDir`, the generated diff + `planPath`, `outPath = docs\u002Fmigration\u002F{app}\u002F{page}\u002Fcodex-audit.json`,\n`workingLanguage`). Codex checks mapping fidelity, RR v7 idioms, and secret-boundary violations.\nAdvisory — never changes the page status. Surface its verdict in the report.\n\n### Step 6: Report\nIn `workingLanguage`: phases completed, files created, total tests with RED\u002FGREEN evidence from\neach TDD phase, harness status, and any manual integration steps. Next step: on **`generated`** →\n`\u002Ffrontend-migration-plugin:fm-verify {page}`; on **`gen-failed`** → `\u002Ffrontend-migration-plugin:fm-gen\n{page}` again, which resumes from the incomplete phase. `fm-verify` requires at least `generated`\n(its Step 0) and would refuse — the same carve-out the SessionStart hook and `fm-progress` make.\n","# Generate a Page Migration (TDD Coordinator)\n\nExecutes `migration-plan.json` phase by phase. Each phase runs in a separate agent session\n(`Agent` tool — phases are strictly sequential, each depends on the previous). All user-facing\noutput in `workingLanguage`.\n\n## Instructions\n\n### Step 0: Config & plan\nRead config (absent → run `fm-init`; stop). Require `docs\u002Fmigration\u002F{app}\u002F{page}\u002Fmigration-plan.json`\n(missing → run `fm-plan {page}`; stop) **with `gateAcceptance`** (absent → the plan is incomplete;\nre-run `fm-plan {page}`; stop). Require `docs\u002Fmigration\u002F{app}\u002F{page}\u002Fstyle-spec.json` (missing → run\n`fm-style-spec {page}`; stop) — the foundation phase copies its assets and the component\u002Fpage phases\nbuild to its style values. Read `targetDir`, `appDir`, `packagesDir`, `monorepoRoot`, `legacyDir`,\n`workingLanguage`, `eslintTemplate`, `prettierTemplate`, and the plan's `buildOrder` + `blockers`.\n\n**Confirm `apps[app]` before using it** (${CLAUDE_PLUGIN_ROOT}\u002FCLAUDE.md → Configuration): the app entry must exist and carry the keys this stage reads. Config-file presence is not app presence — `mobile`\u002F`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.\n\n### Step 1: Blockers\nIf the plan has `blockers` (unextracted shared candidates), resolve each against\n`tracker.packages` — **not** against the plan, which `migration-planner` writes once and no skill\nrewrites. A blocker is unresolved while its candidate's `packages.\u003Cpkg>.status` is anything other\nthan `extracted`. Stop only then, and tell the user to run `\u002Ffrontend-migration-plugin:fm-extract`\nfirst. Reading the plan array as the live gate would refuse forever: `fm-extract` never writes the plan\n— its tracker writes are `packages.*` (Step 5.1) and the dependent pages' invalidation (Steps 2b\nand 5.3) — so a successful extraction leaves the plan byte-identical.\n\n### Step 2: Resume \u002F demotion\n- If `generation-state.json` exists, offer to resume from the last incomplete phase. With `--force`\n  (how `fm-fix` sends a page back after `regenRequired`), regenerate every phase — and **rewrite it\n  with every phase pending in Step 3, once the page lock is held and after the refusals below**,\n  never here: this bullet runs unlocked, so rewriting it now would let a run that is about to be\n  refused (`flipped`, `done`, a flip in flight, or a declined demotion) destroy the record anyway,\n  and would reset the ledger of another session's generation already in progress — a completed state file would otherwise make\n  the resume path a no-op, and a `--force` run that dies in an early phase would leave the later\n  phases still marked done, so the next plain resume would skip them and reach `generated`\n  carrying exactly the code the fix refused to certify.\n- Demotion warning: if the page status is `verified`\u002F`e2e-passed`\u002F`parity-passed`, warn that\n  re-generating resets it to `generated` and discards downstream gate progress. Confirm before\n  proceeding.\n- **`flipped` and `done` are refused, not warned; so is an in-flight flip PR.** `done` is past\n  `flipped` (the legacy page is deleted), so `--revert` is not an escape from it — refuse and\n  require manual intervention — **not** `--revert`, which refuses a `done` page too. On any status,\n  a present `flipPrOpenedAt` means the flip artifact is prepared and PR2 handed over against the\n  current code: refuse and point at `fm-route --revert` first, or the regeneration lands under a PR\n  that no longer describes it.\n- **`flipped`** — stop and tell the user to\n  run `\u002Ffrontend-migration-plugin:fm-route {page} --revert` first — **`--revert`, not `--flag-off`**:\n  flag-off prepares the routing artifact with the flag OFF and *keeps the current status*\n  (`fm-route` Step 4), so it would leave the page at `flipped` and this refusal would repeat forever.\n  `--revert` is the rollback that takes the path out of rotation and returns the page to\n  `parity-passed`. The path is serving production\n  traffic, so regenerating it is a rewrite under live load — and the status reset would desync the\n  two facts that must agree: the tracker's `flipped` and the edge flag `fm-route` owns. Provenance\n  resolves a capture's `side` from that status (`${CLAUDE_PLUGIN_ROOT}\u002Ftemplates\u002Fcapture-provenance.md`), so a demoted-but-\n  still-flipped page makes a capture from the production domain — which is serving v2 — resolve as\n  `legacy`. That is the wrong-side baseline the v0.14.0 provenance layer exists to stop, and unlike\n  `unresolved` it is *accepted as evidence*.\n\n### Step 3: Lock\n**With `--force`, rewrite `generation-state.json` with every phase pending here — and every\nphase's persisted `filesChanged[]` cleared** (a full regeneration owns all its files; a stale list\nsurviving into a force run that fails early would feed Step 5 paths from the previous\ngeneration) — once the lock\nis held and the Step 2 refusals have passed. Doing it in Step 2 would let a run that is about to\nbe refused destroy the ledger, and would reset the ledger of another session's generation already\nin progress. Without it, a `--force` run that dies in an early phase leaves the later phases still\nmarked done, and the next plain resume skips them.\nAcquire `docs\u002Fmigration\u002F{app}\u002F{page}\u002F.lock` (stale only when its holder is gone — see ${CLAUDE_PLUGIN_ROOT}\u002FCLAUDE.md → Lock file; JSON schema — `holder`\u002F`pid`\u002FISO-8601 `acquiredAt` — in ${CLAUDE_PLUGIN_ROOT}\u002FCLAUDE.md → Lock file).\n\n### Step 4: Run phases (sequential)\nFor each phase in `buildOrder`, launch the right agent (`subagent_type: \"frontend-migration-plugin:\u003Cagent>\"`), passing **the agent's full\nparameter list from its own file** — not only the additions called out below (subagent isolation\nmeans an omitted param is simply missing, and the agent cannot ask). Inspect the result before the\nnext:\n- `foundation` → **foundation-generator** (types + MSW + harness + **style-spec assets copied** +\n  lint\u002Fformat config scaffold; pass `styleSpecPath`, `legacyDir`, `monorepoRoot`, `legacyDirs`\n  (every `apps.*.legacyDir`), `eslintTemplate`, `prettierTemplate`)\n- `api` \u002F `store` \u002F `component` \u002F `page` → **tdd-cycle-runner** (Red-Green per unit; pass\n  `styleSpecPath` — the component\u002Fpage phases build to the legacy style values)\n- `integration` → **integration-generator** (routes + i18n + MSW global + ESLint on generated\n  code; pass `monorepoRoot`, `eslintTemplate`)\n\nPass the config's **`i18n` block** to `foundation-generator` along with its other params. Its task 3b\n(the key-coverage spec) reads `localesDir`, `languages`, `lookupFns`, and `keyPrefix`, and cannot\ninfer any of them: unlike the plan-derived `languages` that `parity-verifier` and `e2e-test-runner`\ncan read, these exist only in config. Omitting them would skip the spec on a project that *has* i18n\nconfigured, and `fm-verify` Step 4a makes an absent spec a hard failure whose only remedy is\nre-running this phase — which would fail the same way.\n\nAfter each phase, update `generation-state.json` (Read-Modify-Write): mark the phase\n`done`\u002F`failed`, record `currentPhase`, and **persist the phase's `filesChanged[]`** on its entry —\nStep 5's `sourcePaths` union reads these recorded lists, and a resumed session cannot recover an\nunpersisted list without re-running the completed phase. On a **retried** phase, union the retry's\nlist with the entry's existing one — files a failed attempt created still exist when the retry\nreuses them unchanged and does not relist them; `--force` (Step 3) is the only reset. On a phase failure, **stop running further phases and\ncontinue to Step 5** — do not return from the skill here. Step 5 is what writes `gen-failed`,\nrecords `sourcePaths` for the files the completed phases did write, clears the stale gate fields,\nand **releases the lock**. Returning from this step instead would leave the page at `planned` over\nmodified code, with the Step 3 lock held for 30 minutes.\n\n### Step 5: Record\n\n**Tracker lock.** Take `docs\u002Fmigration\u002F.tracker.lock` around every `tracker.json` write below —\nafter the lock this step already holds, released right after the write (${CLAUDE_PLUGIN_ROOT}\u002FCLAUDE.md → Lock file). Write it per ${CLAUDE_PLUGIN_ROOT}\u002FCLAUDE.md → Serialization.\n\n1. Set `generatedAt` and, if all phases succeeded, `tracker.json`\n   `apps[app].pages[page].status = \"generated\"`; any skipped\u002Ffailed phase → `gen-failed`.\n2. Record `apps[app].pages[page].sourcePaths` — the repo-relative paths of the files the phases\n   created or modified (under `appDir`, plus any root-level file a phase legitimately owns),\n   collected from **every** phase's recorded `filesChanged[]` in `generation-state.json` —\n   including phases completed by an earlier resumed run: a resume that rewrites the list from only\n   the current run's phases silently drops watched files. Each list is the phase's **output set**\n   (reused-but-unchanged files included — the agents' contracts say so), which is what makes this\n   full rewrite safe **for the generation-owned files**. Entries merged into `sourcePaths` by\n   `fm-e2e`\u002F`fm-fix`\u002F`fm-delta` (specs, page objects, helpers) are not the phases' to drop:\n   re-add every prior entry whose file still exists on disk — only an entry whose file is gone\n   leaves the list. A phase report missing the list, or\n   carrying paths that do not resolve from the repo root, is **incomplete evidence**: do not\n   record `sourcePaths` from it — re-collect from the phase before recording (an unwatched file\n   evades every later freshness hash). This is the page's\n   **axis 1** of its watch paths — axis 2 is the plan's `sharedDeps[]` mapped to\n   `{packagesDir}\u002F\u003Cpackage>` and axis 3 is the page's `migration-plan.json` itself, and every hash is\n   taken over the **union of all three** (${CLAUDE_PLUGIN_ROOT}\u002FCLAUDE.md → \"Gate Result Accounting\" F). `fm-route --flag-on` (Step 1a) and `fm-progress` hash that union to\n   tell a still-fresh PASS from a stale one, and neither can derive axis 1 otherwise\n   — `componentTree` carries component *names*, not paths. Rewrite the list on every run so a\n   removed file leaves it.\n3. Clear `apps[app].pages[page].gateEvidence` **and the legacy `verifiedAt` \u002F `e2ePassedAt` \u002F\n   `parityPassedAt`** — the page's code has been regenerated, so every prior gate PASS now rests on\n   code that no longer exists. Clearing only `gateEvidence` is the wrong half of the job: that is the\n   field Step 1a treats as evidence, while `fm-route` Step 1's **hard** precondition reads\n   `verifiedAt` and the two gate report files, so the authoritative traces would survive this\n   regeneration and re-authorize a flip on code no gate has seen. The report files are not deleted\n   (`fm-fix` reads them) — the tracker's claim about them is what has to go.\n   Delete the page's `cascade-diff*.json` and clear the tracker `cascade` record too — `fm-route`\n   reads that file directly, and a clean pre-regeneration report would silently vouch for markup\n   this regeneration may have changed.\n   **Clear `routePrepared` and `flagKey` too.** `fm-route` Step 1-pre accepts `routePrepared: true`\n   as proof the code PR was prepared; left standing, a regenerated page reaches `--flag-on` without\n   a fresh `--flag-off`, skipping the route-stage Codex audit that step exists to force.\n     **Clear `regenRequiredAt` — but only when every phase succeeded**, the same condition 5.1\n     writes `generated` under. `fm-fix` (a fix that changed no code) and `fm-verify` (an absent i18n\n     key-coverage spec) both write it to mean \"a full regeneration is still owed\", and only a\n     completed run here — or a later passing `fm-verify` — discharges that. `fm-fix` and `fm-delta`\n     deliberately do not clear it: neither performs a full regeneration. The SessionStart hook and `fm-progress` read it; left\n     standing it outlives the run it asked for and hijacks every later `*-failed` state, while\n     clearing it on a `gen-failed` run would drop the record while the debt stands.\n4. Release the lock.\n\n### Step 5b: Codex audit (advisory) — see ${CLAUDE_PLUGIN_ROOT}\u002FCLAUDE.md → \"Codex Independent Audit\"\nIf `codexAudit` is enabled, this stage is in `codexAuditStages` (**absent → all seven**; the key\nnarrows coverage, it never means \"none\"), and generation succeeded, after the\nlock is released spawn `codex-auditor` (`subagent_type: \"frontend-migration-plugin:codex-auditor\"`) for the `gen` stage (params: `app`, `page`, `stage=\"gen\"`,\n`appDir`, `legacyDir`, the generated diff + `planPath`, `outPath = docs\u002Fmigration\u002F{app}\u002F{page}\u002Fcodex-audit.json`,\n`workingLanguage`). Codex checks mapping fidelity, RR v7 idioms, and secret-boundary violations.\nAdvisory — never changes the page status. Surface its verdict in the report.\n\n### Step 6: Report\nIn `workingLanguage`: phases completed, files created, total tests with RED\u002FGREEN evidence from\neach TDD phase, harness status, and any manual integration steps. Next step: on **`generated`** →\n`\u002Ffrontend-migration-plugin:fm-verify {page}`; on **`gen-failed`** → `\u002Ffrontend-migration-plugin:fm-gen\n{page}` again, which resumes from the incomplete phase. `fm-verify` requires at least `generated`\n(its Step 0) and would refuse — the same carve-out the SessionStart hook and `fm-progress` make.\n",{"name":5,"description":7,"allowed-tools":48,"argument-hint":49,"user-invocable":50},"Read, Write, Edit, Glob, Grep, Bash, Agent","\u003Cpage> [--app pc|mobile|hana] [--force]",true,"main","eaef2a5b6092b72972ff2f25ef79803753bc9bbf","frontend-migration-plugin\u002Fskills\u002Ffm-gen",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":59,"prompt":69,"zipAvailable":50},[60,63,66],{"tool":61,"command":62},"npx","npx skills add ohmyhotelco\u002Fhare-cc-plugins --skill fm-gen",{"tool":64,"command":65},"bunx","bunx skills add ohmyhotelco\u002Fhare-cc-plugins --skill fm-gen",{"tool":67,"command":68},"pnpm","pnpm dlx skills add ohmyhotelco\u002Fhare-cc-plugins --skill fm-gen","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fohmyhotelco\u002Fhare-cc-plugins\u002Fblob\u002Fmain\u002Ffrontend-migration-plugin\u002Fskills\u002Ffm-gen\u002FSKILL.md",[],[72,97,123,147,167,187],{"skillNo":73,"name":74,"nameZh":6,"description":75,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":76,"categories":77,"keywords":78,"publishedAt":43},"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",[],[79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96],"shared","hp shared","state","stack","plugin","how","for","report","code conventions","commands","how to","conventions","false","frontmatter","file","code","hand","check",{"skillNo":98,"name":99,"nameZh":6,"description":100,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":101,"categories":102,"keywords":111,"publishedAt":43},"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",[103,108],{"categoryNo":104,"categoryName":105,"taxonomyType":106,"taxonomyTypeDesc":107},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",{"categoryNo":109,"categoryName":110,"taxonomyType":106,"taxonomyTypeDesc":107},"01JBSEEDCAT000000000000015","视觉设计",[79,112,85,113,87,114,115,116,117,91,94,95,118,96,119,81,83,120,121,122],"fe shared","mode","frontend","design","design principles","files","react","router","principles","command","build",{"skillNo":124,"name":125,"nameZh":6,"description":126,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":127,"categories":128,"keywords":135,"publishedAt":43},"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",[129,132],{"categoryNo":130,"categoryName":131,"taxonomyType":106,"taxonomyTypeDesc":107},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":133,"categoryName":134,"taxonomyType":106,"taxonomyTypeDesc":107},"01JBSEEDCAT000000000000023","Skill与智能体开发",[79,136,137,117,91,138,139,95,82,140,92,141,142,143,85,144,145,81,83,146],"spring","be spring shared","commit","architecture","coding standards","coding","standards","tech","Rules","cqrs","naming",{"skillNo":148,"name":149,"nameZh":6,"description":150,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":151,"categories":152,"keywords":153,"publishedAt":43},"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",[],[154,155,156,157,158,159,160,161,162,163,164,40,93,165,29,166],"pp","pp translate","translate","feature","name","editing","read","spec","write","edit","sync","language","changes",{"skillNo":168,"name":169,"nameZh":6,"description":170,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":171,"categories":172,"keywords":174,"publishedAt":43},"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",[173],{"categoryNo":133,"categoryName":134,"taxonomyType":106,"taxonomyTypeDesc":107},[175,154,176,164,177,178,157,179,180,181,182,40,39,183,184,185,186,158,160,29],"pp sync stitch","stitch","projects","get","project","re","local","MCP","id","wireframes","website","screen",{"skillNo":188,"name":189,"nameZh":6,"description":190,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":191,"categories":192,"keywords":194,"publishedAt":43},"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",[193],{"categoryNo":133,"categoryName":134,"taxonomyType":106,"taxonomyTypeDesc":107},[154,195,164,196,157,180,197,198,182,40,199,158,160,162,163,200,29],"notion","pp sync notion","search","manual","specification","grep"]