Skip to main content

migrate-service

Migrate one existing Ghost Core service to the boot-owned defineService pattern. Use when asked to migrate or prepare an individual service, including its dependency wiring, consumers, inventory and enforcement. Establish coverage first and keep the initializer migration to one service and one commit.

来源信息

仓库
TryGhost/Ghost
最近来源活动
2026年10月25日 15:31
检测到的 SKILL.md 语言
英语
星标
55,603
Forks
12,016

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
migrate-service
description
Migrate one existing Ghost Core service to the boot-owned defineService pattern. Use when asked to migrate or prepare an individual service, including its dependency wiring, consumers, inventory and enforcement. Establish coverage first and keep the initializer migration to one service and one commit.
# Migrate one service Prepare a small, behavior-preserving migration of one existing service. This is an execution workflow, not the final service-authoring guide or permission to redesign a service's lifecycle. Read current repository guidance rather than copying an earlier migration blindly. ## 1. Establish the starting point - Read the root and nearest `AGENTS.md`, the [services guide](../../../ghost/core/server/services/README.md), [kernel contract](../../../ghost/core/kernel/README.md#service-initialization), [testing guide](../../../docs/contributing/testing.md) and [contribution workflow](../../../docs/contributing/workflow.md). - Inspect status, existing branches/worktrees and related PRs. Preserve unrelated work. Fetch `origin` and start from current `origin/main`; refresh dependencies with `pnpm bootstrap` in a fresh checkout or after dependency changes. - Read this service's entry in `ghost/core/server/services/service-inventory.yaml` and any existing migration plan. If a plan-registry skill is available, use it to find/update that plan; do not create a competing plan in the repository. - Verify prerequisites are merged. An open tests or enforcement PR is not main. Preparation can proceed against a proposed policy, but record its exact head and repeat validation against the policy that actually merges. ## 2. Audit the whole service and its callers Use `rg` across production and tests. Include deep imports, nested helpers, separate actions, job handlers, CLI use and CommonJS consumers. Also inspect shared boot harnesses and tests that construct routers or renderers indirectly; a search for the service name alone misses those fixtures. Record: - Existing runtime/type exports and the intended instance and extras surfaces. - Who constructs each instance and whether consumers capture it during import. - Dependencies acquired at import, boot, request, dispatch and job execution. - Configuration captured once versus read again for each operation. - Shared identity, caches, pending/rejected promises and invalidation semantics. - Resource ownership, partial-startup cleanup, normal shutdown and failure policy. Confirm the capability belongs in services. A reusable constructor or helper without an application-owned instance belongs elsewhere; do not add an empty initializer merely to keep its directory. Read `.dependency-cruiser.cjs` and `.oxlintrc.json` on the actual base. Where the service DI ratchet is present, its scope includes every implementation file in the directory, not just the main class. Only the top-level wiring `index.ts` is exempt. Implementations must not retrieve collaborators through their own root. Wire application instances, models, logging and configured adapters in the root. Keep approved libraries imported where used and use explicit type-only imports. Do not inject stateless utilities to evade the policy or duplicate shared helpers. Inspect the source before adding a library allowance. Justify why the dependency is a library rather than an application-owned instance, and allow only its exact entry or file. Include the reason and a boundary regression in the migration PR that needs the allowance. Assert each rejected target as well as its rule and source in boundary regressions. Keep allowances out of standalone skill/tooling PRs. Seek review when that distinction is unclear or a broader policy change is needed; do not widen the rule merely to make lint pass. Normal inventory and root-shape opt-ins still belong in each migration. Preserve late acquisition with getters/factories when dependencies are unavailable at boot. If audit scripts exist, inspect their detailed output: findings may not make the audit command fail, and a summary may omit unresolved/own-root imports. ## 3. Establish coverage before changing production Map each proposed change to an existing behavioral assertion. Run the relevant unchanged root/implementation tests and real consumer checks on the base. Use the testing guide's focused Nx commands: they prepare workspace builds and Core assets before running the selected files. Direct Vitest or `test:single` commands skip those prerequisites; `pnpm bootstrap` alone does not supply them. Treat missing package outputs or card manifests as setup failures. Do not compile Core merely to supply workspace outputs: adjacent emitted JavaScript can shadow TypeScript edits in CommonJS consumers. Initialization complexity, an audited inventory entry or an old green result is not proof of coverage. If a test reloads CommonJS modules, restore the loaded consumer chain too: cached consumers can retain a discarded root or collaborator. Run those suites with their neighboring consumers in one worker to check isolation. Cover the real risk: retained consumers, configuration timing, transaction or cache behavior, boot ordering and resource cleanup. Use existing cold-boot HTTP/job coverage where it already proves readiness. Do not copy a boot harness for every service or duplicate generic kernel concurrency/retry tests. For async ordering tests, verify both a missing required await and a harmless extra await: the first must fail and the second must pass. Avoid checks that count microtask turns; follow the testing guide's async-ordering advice. If a needed assertion is absent, prepare passing characterization against the old implementation in a tests-only PR and merge it first. Keep one commit per service in preparatory batches. If a test exposes a production bug, isolate the fix ahead of initializer adoption; do not change expectations to hide it. Update each prepared service's inventory evidence in that tests-only change, citing the new tests and narrowing only the blockers they cover. Preserve historical audit revisions; do not defer this evidence to initializer adoption. Substantial dependency refactors, fixture rewrites and business changes are also preparation work, separate from the minimal initializer PR. Preserve the old public API during preparation. Small dependency wiring can accompany migration when existing tests protect it. ## 4. Make the smallest initializer change - Create a TypeScript top-level `index.ts` default-exporting `defineService` from the kernel. Return one fully ready instance from its factory. - Expose normal operations on `.service`. Keep classes and implementation contracts in implementation files; root named exports are type-only. - Use optional `extras` only for justified transitional concerns. Catalogue each helper's purpose, callers, timing, dependencies, failure policy and destination. Extras are not automatic lifecycle hooks or a substitute for readiness. - Boot calls and **awaits** `init()` in the appropriate phase, even for a synchronous factory. Initialize before eager consumers read `.service`. Importing a root early is fine; evaluating its guarded getter early is not. - Adapt every production consumer. Retain CommonJS `.default` at existing boundaries; do not convert boot wholesale or add a wrapper solely for it. - Preserve receivers when forwarding methods. Keep validation at the same public boundary and preserve synchronous throws versus promise rejections where callers depend on that distinction. - Keep config reads, external operations, jobs and per-operation resources at their established times. Do not make the first request initialize the service. - If startup acquires resources, clean up that attempt before rejecting. Do not dispose borrowed shared resources or change normal shutdown ownership. If the service requires new kernel features, a restart/replacement policy, an API redesign or broad boot orchestration, record the concrete mismatch and separate that decision from the small migration. Do not silently expand its scope. Migration tests may receive mechanical import/type/stub and initializer-await adaptations. New behavioral assertions or substantial harness changes return to preparation. The only new cases permitted in the migration commit are boundary regressions for new exact dependency allowances. Never weaken assertions, coverage gates, lint or types to make the refactor pass. ## 5. Update metadata and verify Update this service's inventory entry: `migrated`, entry points, evidence, contracts, extras and remaining gaps. Repair affected file references and startup-order descriptions in other entries mechanically, without changing their unrelated assessments. Preserve historical audit provenance; record the inspected base and distinguish old evidence from new validation. Do not restore manual totals or erase unrelated backlog items. Opt the root into `ghost/service-root-shape` in `.oxlintrc.json` as required by the current configuration; the tooling scope check must agree with the inventory. Once the DI ratchet is present, `migrated: true` also opts implementations into its normal boundary checks. Do not claim enforcement passed if that policy was only checked in a temporary integration or is still awaiting merge. Run focused tests before/after, relevant real consumer/DB checks, root-shape and inventory validation, lint/typechecks and the default `pnpm check`. Run one full local validation at a time. Investigate failures from fresh output; a passing retry or an unchanged file alone does not establish that a failure belongs to main. Keep unrelated fixes out of the migration. Review the final diff: one service, unchanged operation behavior, no hidden composition, and only justified metadata/enforcement changes. New test cases are limited to boundary regressions for new exact dependency allowances. Use the repository commit skill and make **one commit for this service**. Keep unrelated tooling/skill changes on their own branch. Report the branch, commit, validation and unresolved prerequisites. Push/open a PR only within the user's authorization, rebase on fetched main before opening, and report current CI/review state accurately. Do not merge or enable auto-merge implicitly.
在 GitHub 查看