@icebreakers/monorepo-templates 3.0.1 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/assets/.agents/skills/repoctl/references/commands.md +36 -0
  2. package/assets/.agents/skills/repoctl/references/config.md +10 -1
  3. package/assets/.agents/skills/repoctl/references/dependencies.md +2 -0
  4. package/assets/.agents/skills/repoctl/references/templates.md +6 -0
  5. package/dist/cli.mjs +1 -1
  6. package/dist/index.mjs +1 -1
  7. package/dist/{prepare-CiHv-JJL.mjs → prepare-awxaJLdj.mjs} +19 -0
  8. package/package.json +1 -1
  9. package/templates/nimbus/AGENTS.md +6 -1
  10. package/templates/nimbus/README.md +14 -1
  11. package/templates/nimbus/UPSTREAM.md +6 -0
  12. package/templates/nimbus/astro.config.ts +5 -3
  13. package/templates/nimbus/package.json +1 -0
  14. package/templates/nimbus/src/components/head.astro +4 -2
  15. package/templates/nimbus/src/content/docs/get-started.mdx +1 -1
  16. package/templates/nimbus/src/content/docs/zh/get-started.mdx +1 -1
  17. package/templates/nimbus/src/layouts/docs.astro +15 -2
  18. package/templates/nimbus/src/lib/deployment/integration.ts +28 -0
  19. package/templates/nimbus/src/lib/deployment.ts +10 -0
  20. package/templates/nimbus/src/lib/locale.ts +6 -0
  21. package/templates/nimbus/src/pages/robots.txt.ts +3 -8
  22. package/templates/nimbus/src/styles/site.css +8 -0
  23. package/templates/nimbus/turbo.json +48 -0
  24. package/templates/react-lib/package.json +11 -3
  25. package/templates/vitepress/.vitepress/config.ts +1 -1
  26. package/templates/vitepress/.vitepress/deployment/index.ts +7 -21
  27. package/templates/vitepress/.vitepress/deployment/output.ts +11 -3
  28. package/templates/vitepress/package.json +1 -0
  29. package/templates/vitepress/reference/commands.md +39 -2
  30. package/templates/vitepress/reference/config.md +9 -1
  31. package/templates/vitepress/reference/templates.md +2 -0
  32. package/templates/vitepress/tasks/docs.md +140 -16
  33. package/templates/vitepress/tasks/release.md +2 -0
  34. package/templates/vitepress/test/deployment.test.ts +6 -2
  35. package/templates/vitepress/turbo.json +48 -0
  36. package/templates/vitepress/zh/reference/commands.md +39 -2
  37. package/templates/vitepress/zh/reference/config.md +9 -1
  38. package/templates/vitepress/zh/reference/templates.md +2 -0
  39. package/templates/vitepress/zh/tasks/docs.md +133 -9
  40. package/templates/vitepress/zh/tasks/release.md +2 -0
@@ -1,12 +1,20 @@
1
1
  import type { DocsDeployment } from './index'
2
- import { writeFile } from 'node:fs/promises'
2
+ import { readFile, writeFile } from 'node:fs/promises'
3
3
  import path from 'node:path'
4
+ import { mergeDocsHeaders } from 'repoctl/docs-context'
4
5
  import { crawlerFiles } from './index'
5
6
 
6
7
  export async function writeDeploymentFiles(outDir: string, deployment: DocsDeployment) {
7
- const { robots, headers } = crawlerFiles(deployment)
8
+ const { robots } = crawlerFiles(deployment)
9
+ const filename = path.join(outDir, '_headers')
10
+ const existing = await readFile(filename, 'utf8').catch((error: NodeJS.ErrnoException) => {
11
+ if (error.code !== 'ENOENT') {
12
+ throw error
13
+ }
14
+ return ''
15
+ })
8
16
  await Promise.all([
9
17
  writeFile(path.join(outDir, 'robots.txt'), robots),
10
- writeFile(path.join(outDir, '_headers'), headers),
18
+ writeFile(filename, mergeDocsHeaders(existing, deployment)),
11
19
  ])
12
20
  }
@@ -41,6 +41,7 @@
41
41
  "devDependencies": {
42
42
  "@tailwindcss/vite": "^4.3.3",
43
43
  "mermaid": "^12.2.0",
44
+ "repoctl": "latest",
44
45
  "tailwindcss": "^4.3.3",
45
46
  "vitepress": "2.0.0-alpha.20",
46
47
  "vitepress-plugin-llms": "^1.14.0",
@@ -14,7 +14,9 @@ repo docs plan guide --channel stable --release <completion-record-id> --json
14
14
  repo docs plan guide --channel stable --commits <correction-sha> --json
15
15
  ```
16
16
 
17
- `plan` reads publication evidence and checks the frozen source. `deploy` accepts the same selection and runs the configured build/deployment scripts; stable execution belongs in the serialized managed GitHub workflow. Configure `commands.docs.sites` explicitly. See [documentation publication](/tasks/docs) for workflow setup, independent corrections, adapter outputs and recovery.
17
+ `plan` reads publication evidence and checks the frozen source. `deploy` accepts the same selection, runs the configured build, and uses either the built-in `cloudflare-workers` provider or a `deployScript` adapter. Native Cloudflare preflight checks static assets-only configuration, identity and read access before the frozen checkout's install/build; it does not certify future write permission or public HTTP readiness. Stable execution belongs in the serialized managed GitHub workflow. Configure `commands.docs.sites` explicitly. See [documentation publication](/tasks/docs) for target fields, workflow setup, independent corrections, adapter outputs and recovery with the same deployment ID.
18
+
19
+ `deploy --json` keeps stdout to the result or safe failure report, streams child logs live to stderr and exits nonzero on failure. Reports include ordered stages, elapsed times, upload/reconcile and resumable phase; `GITHUB_STEP_SUMMARY` gets the same escaped report. Optional target `readiness` verifies the exact public manifest before current advances. A saved `verifying` retry skips frozen-source install/build/upload; the workflow still performs its bootstrap install. See [documentation publication](/tasks/docs) for configuration, receipt recovery, `DocsDeploymentError.report` and summary-write warnings.
18
20
 
19
21
  ## Main Entry
20
22
 
@@ -60,6 +62,8 @@ Use it to check root workspace files, Node compatibility, CLI dependency presenc
60
62
 
61
63
  `--strict` treats warnings as failures. `--redact` removes local absolute paths from shareable reports.
62
64
 
65
+ Text and Markdown include the stable rule ID, affected paths and manifest field when available. Identical findings share one explanation and fix, with every path and the original check count retained. For example, `[template-instance-drift]` with `path: apps/docs/astro.config.ts` can be inspected with `repo doctor --rules template-instance-drift`. Different reasons, fixes and suppressions remain separate. Grouping does not change warning/failure totals or suppression policy; JSON always contains the complete individual checks.
66
+
63
67
  ```bash
64
68
  repo doctor --list-rules
65
69
  repo doctor --rules root-scripts,package-manager --strict
@@ -130,7 +134,21 @@ repo package check --filter my-library --keep-temp --json
130
134
 
131
135
  Build the selected workspace packages and their dependencies in dependency order, then validate real `pnpm pack` tarballs with publint 0.3.25 and ATTW 0.18.5. Build failure prevents all packing. Local development prerequisites are included in the build; private prerequisites can build without being packed or checked. Private packages are reported as skipped unless `--include-private` is supplied. Repeat `--filter` to combine pnpm selectors; `--build-script` changes the default `build` script. Packages without that script are treated as already authored artifacts.
132
136
 
133
- Independent temporary consumers install the tarballs and their local runtime dependency tarballs, then exercise declared Node ESM/CJS entrypoints and TypeScript NodeNext consumption. TypeScript consumption uses ATTW's pinned TypeScript 5.6.1-rc. Undeclared module formats are not required. Browser-only and non-JavaScript asset exports receive manifest/type analysis rather than Node execution; JavaScript bins receive syntax checks, without invoking application commands. Installation can contact the registry, while dependency install scripts are disabled. Workspace build and pack lifecycle scripts run normally.
137
+ Independent temporary consumers install the tarballs and their local runtime dependency tarballs, then exercise declared Node ESM/CJS entrypoints and TypeScript NodeNext consumption. Typed consumers install the source workspace's exact installed TypeScript version and run that isolated compiler with `skipLibCheck: false`; when the source has no TypeScript installation, they use ATTW's pinned compiler version as the default. The compiler and repoctl's controlled Node types use dedicated npm aliases that avoid all dependency names in the runtime closure, including required peers. A separate type root exposes the controlled Node declarations for verification, preserving the package's runtime TypeScript and Node peer requirements. JavaScript-only consumers do not install this type toolchain. Optional peer integrations declared in the tested runtime dependency closure are installed with the intersection of their peer ranges, including required peer constraints and npm aliases. Original range expressions are retained when they express the complete intersection. For complex npm alias intersections unsupported by pnpm 12, the consumer queries its configured registry through `pnpm view` and installs the highest published version satisfying the intersection as an exact alias. Queries use the consumer's package-manager configuration and appear in its command log; missing matching versions fail the check. Development dependencies and unrelated workspace integrations are not copied.
138
+
139
+ Undeclared module formats are not required. Browser-only and non-JavaScript asset exports receive manifest/type analysis rather than Node execution; JavaScript bins receive syntax checks, without invoking application commands. A JavaScript resource such as a Service Worker can explicitly declare an exact export subpath in its published `package.json`:
140
+
141
+ ```json
142
+ {
143
+ "repoctl": {
144
+ "packageCheck": {
145
+ "runtimeAssets": ["./worker.js"]
146
+ }
147
+ }
148
+ }
149
+ ```
150
+
151
+ Each declared resource must be a packed JavaScript export. Consumers verify its export resolution, existence, package boundary and JavaScript syntax without executing browser globals. Missing paths, wildcard declarations, escaping links and invalid syntax fail. A resource does not need types merely because other exports have them; explicit or adjacent declarations attached to the resource are still checked. Other runtime exports and declaration checks remain enabled. Snapshot and published nightly consumers use the same contract; nightly derives optional integrations from checked tarballs after validating integrity and snapshot identity. Installation can contact the registry, while dependency install scripts are disabled. Workspace build and pack lifecycle scripts run normally.
134
152
 
135
153
  JSON contains each package's file list, stable diagnostic source/code, original upstream details, and subprocess argument arrays. `--strict` also fails on warnings. `--keep-temp` preserves the tarballs, consumer manifests and command working directories for reproduction; otherwise temporary files are removed even after failure. The command never publishes, changes package versions, or writes release state.
136
154
 
@@ -184,6 +202,25 @@ Baselines live in `.repoctl/baselines/root/<path-sha256>.json`; commit this dire
184
202
 
185
203
  Locally deleted tracked files remain deleted, even with `--overwrite`; restore them manually to resume upgrades. Upstream deletion removes only an unchanged owned file, while customized files remain conflicts. A selected file and its reviewed baseline update share one transaction, including rollback and retry checks. JSON file entries include optional `baseline` operations with before/after hashes and exact content; partial selection never advances other baselines. Older saved plans without this field still apply without creating unreviewed records. Conflict files are never applied, and CLI preview/apply returns exit code 1 while any remain; API apply results list unresolved paths in `conflicts` even when other selected files were successfully applied.
186
204
 
205
+ New `pnpm-workspace.yaml` overrides are checked against dependency declarations in the **proposed** workspace membership, including `commands.deps.groups`, npm aliases and catalogs. A global override that expands an existing selector or escapes a matching declaration becomes a `workspace-override-policy` conflict; `--yes` and `--overwrite` cannot bypass it. JSON `workspacePolicy.findings` names the affected manifests, declarations and groups. Disjoint version selectors remain independent. This is an offline declaration check, not proof of every transitive resolution. Newly added parent-qualified selectors or non-semver replacements are `unverified`: the ordinary command requires explicit file selection, and automatic maintenance rejects them. A reviewed saved JSON plan may be applied explicitly. Existing identical override keys are not reported repeatedly.
206
+
207
+ During a recognized default Husky-to-Lefthook migration, unrelated unverified override additions are marked `deferred` and remain absent from the applied workspace. The automatic hook migration can finish without accepting them. Preview again afterwards to review those overrides as `unverified`; explicit selection or saved-plan apply can then accept them. A hard conflict or protected skip of a required file blocks the entire migration group, including explicit partial selection. This includes preset-owned targets and locally deleted tracked files. Automatic maintenance preparation and its independent publisher reject both `unverified` and `deferred`; complete the hook migration and review the overrides before retrying maintenance.
208
+
209
+ To retain one local override or its absence, add a reviewed `commands.upgrade.keep` entry, then **regenerate** the plan:
210
+
211
+ ```ts
212
+ keep: [{
213
+ file: 'pnpm-workspace.yaml',
214
+ path: ['overrides', 'vite'],
215
+ reason: 'Keep Taro Vite 4 and uni-app Vite 5 isolated.',
216
+ upstreamHash: '<copy upstreamHash from the finding>',
217
+ localHash: '<copy localHash from the finding>',
218
+ expires: '2030-12-31', // Optional inclusive UTC date.
219
+ }]
220
+ ```
221
+
222
+ Only exact `['overrides', selector]` paths in `pnpm-workspace.yaml` are supported; package names containing dots or slashes remain one array element. Commit the decisions in configuration. The SHA-256 fingerprints bind both upstream and local values, distinguishing an absent key from YAML `null`. Changed values, invalid dates, duplicate decisions and expired reviews require renewed review; an upstream upgrade cannot silently reuse an old decision. Other nonconflicting settings still merge. Saved-plan application verifies configuration, all inspected manifests and the proposed package set again, and rejects elapsed decision expiry. This complements existing file selection, root baselines and template-instance `excludedPaths`; it does not unmanage a whole file or suppress dependency diagnostics.
223
+
187
224
  Versioned migrations appear in the plan's `migrations` report with stable IDs, version boundaries, selection reasons and affected files. The built-in `changesets-to-pnpm-versioning` migration crosses template version `1.1.0`, the release that introduced pnpm native versioning. The target is the exact installed template package version. If a repository has no history, `--from-version 1.0.15` can attest its exact previous template version; dependency ranges such as `^1.0.0` never establish that version. Unknown versions only adopt recognized legacy formats and report `adopt-detected-legacy-format`; malformed metadata and downgrades are blocked. Fresh projects without legacy metadata do not run or initialize old migrations.
188
225
 
189
226
  Commit `.repoctl/migrations/ledger.json` once created. Its `evaluatedVersion` is a migration cursor, not a claim that every managed asset uses that template version. Dry runs write nothing. The completed ledger is reviewed as a normal file diff, while JSON includes the exact intermediate pending/failed contents. Migration files form an indivisible selection group; unselected or failed steps never advance the cursor. The pending record is saved before changes, and completed is written last in the migration transaction. A failure to write completed rolls back that transaction and records failed; if recording failure also fails, the pending journal remains for recovery. Independent selected assets apply after the migration group, so a later independent failure does not erase a completed migration.
@@ -8,7 +8,9 @@ repoctl.config.ts
8
8
 
9
9
  `monorepo.config.ts` is no longer loaded at runtime. Rename it to `repoctl.config.ts` before running repoctl.
10
10
 
11
- `commands.docs.sites` opts individual sites into release-aligned documentation. Each site specifies product packages, root build/deploy scripts, preview/stable targets and allowed correction paths. See [documentation publication](/tasks/docs) for the complete configuration and adapter contract. Omitting this map leaves documentation deployment disabled.
11
+ `commands.docs.sites` opts individual sites into release-aligned documentation. Each site specifies product packages, a root build script, preview/stable targets and allowed correction paths. Each target is either `{ deployScript, url? }` or `{ provider: 'cloudflare-workers', config, workerName, accountId, environment?, url? }`; do not mix the alternatives. Native targets use static assets-only JSON/JSONC Wrangler configuration, while existing script targets remain valid without a provider field. See [documentation publication](/tasks/docs) for identity checks, credentials, supported configuration and recovery. Omitting this map leaves documentation deployment disabled.
12
+
13
+ Both target variants accept optional `readiness: { timeoutMs?, requestTimeoutMs? }`. Omission makes no public request; `{}` enables exact public identity verification at the configured HTTPS origin's `/.well-known/repoctl-deployment.json`. The URL cannot include credentials, query or fragment. Positive integer defaults/ranges are 120000/1–600000 ms total and 10000/1–30000 ms per request. Defaults are computed at runtime without changing frozen configuration identity. See [documentation publication](/tasks/docs#verify-public-delivery-before-advancing-current) for script manifest responsibilities and saved `verifying` recovery.
12
14
 
13
15
  ## Minimal Config
14
16
 
@@ -163,3 +165,9 @@ The root aggregates selected projects. Only explicit `relations` add compilation
163
165
  Existing references remain manually owned. Only newly added entries are recorded in `.repoctl/typescript-references.json`; commit that registry alongside the tsconfigs. Removing or excluding a project removes only entries recorded there. Editing/removing an owned entry blocks synchronization: restore it, or deliberately remove its registry entry to return ownership to the user. JSONC content outside the `references` value, BOM and line endings are preserved. Root aggregation and explicit edges produce stable relative config-file paths.
164
166
 
165
167
  Saved plans include input fingerprints and exact diffs. Applying a stale or edited plan fails before writes; reapplying an already completed plan is a no-op. A process lock at `.repoctl/typescript-references.lock` covers revalidation, writing, verification, rollback and cleanup. Changes stage together with backups and roll back on failure. If concurrent edits prevent safe rollback, the error names retained recovery files: preserve them, reconcile the business edit with the original backup, restore the ownership registry consistently and generate a new plan. A killed process can also leave `.repoctl-references-*.bak`/`.tmp` files; verify no writer remains, reconcile matching backups and the registry, then remove the stale lock. No recovery action overwrites a concurrent edit automatically.
168
+
169
+ `commands.upgrade.keep` records reviewed decisions for exact `pnpm-workspace.yaml` override keys. Each entry requires `file`, `path: ['overrides', selector]`, `reason`, `upstreamHash` and `localHash` from the upgrade finding, with optional inclusive UTC `expires`. Changes to either value or expiry require renewed review; see [upgrade](./commands.md#repo-upgrade).
170
+
171
+ ## Documentation build metadata
172
+
173
+ The pure `repoctl/docs-context` subpath exposes `resolveDocsContext`, `DocsContextOptions`, `DocsContext`, `DocsChannel`, `docsPreviewRobots`, `docsRobots` and `mergeDocsHeaders`. It is independent of provider configuration and credentials. See [build channel context](/tasks/docs#build-channel-context) for origin validation, local stable builds, crawler-header ownership and Turbo hashes.
@@ -19,6 +19,8 @@ repoctl templates are maintained by `@icebreakers/monorepo-templates`. The CLI,
19
19
 
20
20
  Nimbus is the default for the interactive **Docs Site** goal. It includes English at `/`, Chinese at `/zh/`, search and AI documentation endpoints. VitePress remains an explicit alternative. Their default targets are `apps/docs` and `apps/website`, so both can coexist. General creation still defaults to `tsdown`; naming a project `docs` does not infer its template. In noninteractive workflows, pass `--template nimbus` explicitly.
21
21
 
22
+ Both documentation templates include [preview/stable build context](/tasks/docs#build-channel-context), local search, bilingual unpublished notices and ownership-aware crawler headers. Configure the origins before deployment; a local stable build needs no deployment credentials. Keep the template-local Turbo env/input declarations and repoctl dependency.
23
+
22
24
  ## Discover Templates
23
25
 
24
26
  ```bash
@@ -50,6 +50,85 @@ const releaseInputs = {
50
50
 
51
51
  An explicit input list replaces that package's defaults. Covered packages must have publication evidence, matching versions and source ancestry, no unconsumed release intent, and no changed release inputs. Missing evidence blocks that site's deployment. Unrelated packages do not have to publish merely to update this site.
52
52
 
53
+ ## Use the built-in Cloudflare Workers provider
54
+
55
+ For an assets-only Worker, replace the site's `preview` and `stable` script targets with native targets:
56
+
57
+ ```ts
58
+ const cloudflare = {
59
+ provider: 'cloudflare-workers' as const,
60
+ config: 'website/wrangler.jsonc',
61
+ accountId: '0123456789abcdef0123456789abcdef',
62
+ }
63
+
64
+ const targets = {
65
+ preview: {
66
+ ...cloudflare,
67
+ workerName: 'docs-site-preview',
68
+ environment: 'preview',
69
+ url: 'https://next.example.com',
70
+ },
71
+ stable: {
72
+ ...cloudflare,
73
+ workerName: 'docs-site',
74
+ url: 'https://docs.example.com',
75
+ },
76
+ }
77
+ ```
78
+
79
+ Keep `buildScript`, `packages`, `releaseInputs` and `patchPaths` on the site as before. A target chooses either `deployScript` or `provider: 'cloudflare-workers'`; combining them is invalid. Existing script targets require no new discriminator or defaults, and remain supported for other providers and custom Cloudflare deployments.
80
+
81
+ Use a committed, repository-relative JSON or JSONC Wrangler configuration, for example `website/wrangler.jsonc`:
82
+
83
+ ```jsonc
84
+ {
85
+ "name": "docs-site",
86
+ "account_id": "0123456789abcdef0123456789abcdef",
87
+ "compatibility_date": "2026-10-01",
88
+ "workers_dev": false,
89
+ "preview_urls": false,
90
+ "assets": { "directory": "dist" },
91
+ "routes": [{ "pattern": "docs.example.com", "custom_domain": true }],
92
+ "env": {
93
+ "preview": {
94
+ "name": "docs-site-preview",
95
+ "routes": [{ "pattern": "next.example.com", "custom_domain": true }]
96
+ }
97
+ }
98
+ }
99
+ ```
100
+
101
+ `workerName` and `accountId` assert the resolved deployment identity; they do not silently rewrite Wrangler configuration. Omit `environment` for the top-level configuration. A named environment must exist; its explicit `name` wins, otherwise Wrangler appends the environment name to the top-level Worker name. An environment's `assets` object replaces the whole top-level object, so a partial override must still include `directory`. Output paths are relative to the Wrangler file and must remain within the frozen checkout, including through symlinks.
102
+
103
+ Named environments must explicitly configure their own routes when the top level has routes. Preview and stable cannot share a physical Worker in the same account, even with different environment names, or claim each other's custom domains. A configured `url` must agree with the selected target. The example uses custom-domain routes; ambiguous routing is rejected.
104
+
105
+ The native provider accepts a deliberately limited static configuration: JSON/JSONC, assets-only Workers and supported custom-domain routing. TOML, generated or plugin-selected configuration, Worker code (`main`), custom build commands, bindings, service environments, provisioning and unsupported options require a `deployScript` adapter. Install Wrangler as a development dependency in the relevant workspace and commit its lockfile; repoctl uses the installed CLI without downloading one at deployment time.
106
+
107
+ During `deploy`, native preflight runs against the frozen configuration before that checkout's dependency installation and build. It checks credentials, account/Worker/configuration identity, paths and read-only Cloudflare deployment/version responses. Supply `CLOUDFLARE_API_TOKEN` at runtime. The nonsecret `accountId` is frozen in the site target; any `CLOUDFLARE_ACCOUNT_ID` environment value or effective Wrangler `account_id` must match it. A successful read proves access to the queried resource, not permission for a future upload or domain update: the token still needs the write permissions required by the chosen Worker and domains. Tokens are never part of the saved deployment identity.
108
+
109
+ Only an explicit Worker-missing response, HTTP 404 with Cloudflare error code `10007`, permits a first upload. Authentication errors, generic 404s, rate limits, malformed responses and empty deployment lists stop deployment. `repo docs plan` remains a source/publication inspection; it does not perform this credential preflight or certify provider access.
110
+
111
+ After the build, repoctl rechecks configuration and output containment, then writes `/.well-known/repoctl-deployment.json` into the assets output with the site, channel, deployment ID, source/tree SHAs and optional release ID. Existing content at that path must be a valid manifest for the same full identity; conflicting manifests or unrelated user files are preserved by failing the deployment. Cloudflare confirmation requires the latest active deployment to serve exactly one version at 100% and carry `repoctl-docs:<deploymentId>`. This confirms provider state. Enable the optional readiness gate below to verify public HTTP delivery.
112
+
113
+ ## Verify public delivery before advancing current
114
+
115
+ Add `readiness: {}` to either target to require public verification after provider confirmation. Omitting `readiness` preserves the existing behavior and makes no public HTTP request. An opted-in target requires an explicit HTTPS `url` without credentials, query or fragment. A path is allowed, but the manifest is always requested at the origin root: `/.well-known/repoctl-deployment.json`. When using the shared Nimbus or VitePress channel helper, configure the target `url` as an origin without a path and set any site prefix through the framework's `base`; repoctl passes this URL unchanged as `REPO_DOCS_URL`.
116
+
117
+ ```ts
118
+ const readiness = {
119
+ timeoutMs: 120000,
120
+ requestTimeoutMs: 10000,
121
+ }
122
+ ```
123
+
124
+ Both options are positive integers. `timeoutMs` defaults to 120000 and accepts 1–600000 ms; `requestTimeoutMs` defaults to 10000 and accepts 1–30000 ms. Each attempt gets a new finite budget. Requests and body reads are bounded, redirects are rejected, caching is bypassed, and provider credentials are never sent to the public URL. A successful HTTP response must contain the exact schema, site, channel, deployment ID, source/tree SHAs and stable release ID; an old page or HTTP 200 alone is insufficient.
125
+
126
+ The native provider stamps the identity manifest after building. Script targets opting in must produce the same JSON manifest in their final static output, using `schemaVersion: 1`, `REPO_DOCS_SITE`, `REPO_DOCS_CHANNEL`, `REPO_DOCS_DEPLOYMENT_ID`, `REPO_DOCS_SOURCE_SHA` and `REPO_DOCS_TREE_SHA` as `site`, `channel`, `deploymentId`, `sourceSha` and `treeSha`. Add `releaseId` from `REPO_DOCS_RELEASE_ID` only for stable. Use the supplied tree SHA, which includes selected corrections, and preserve unrelated generated files such as Nimbus `_headers`. The manifest must be publicly accessible without authentication.
127
+
128
+ Once the provider result is saved, state moves from `deploying` to `verifying` before the first public request. A timeout or finalization failure keeps that receipt and the previous fully verified `current` pointer. Public traffic may already serve the new version even though `current` has not advanced.
129
+
130
+ Retry the same deployment to resume verification without installing, building or uploading the frozen source again. Native targets recheck that the saved version is still active before polling and again before finalization; script targets reuse the receipt without rerunning `deployScript`. A different active version, mixed traffic or changed identity fails instead of silently uploading. The reusable workflow still installs its root dependencies to bootstrap repoctl. A stable retry retains its saved readiness policy and URL even when main changes or removes them; another stable deployment cannot replace an unresolved `verifying` operation.
131
+
53
132
  ## Install the managed workflows
54
133
 
55
134
  Upgrade repoctl to a version containing this capability, then review and apply the managed asset update:
@@ -64,7 +143,7 @@ pnpm exec repo upgrade --yes
64
143
 
65
144
  Disable the old `main` → production deployment and any hosting-provider Git integration that also publishes production. Keep one deployment owner per channel. The reusable workflow uses `docs-<site>-<channel>` environments and a matching concurrency group with cancellation disabled. Environment approvals follow the repository's existing settings; repoctl does not add an approval policy.
66
145
 
67
- For Cloudflare deployments, provide `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` through the existing repository or environment secrets; the account ID may also be a variable. The core command is provider-independent. Other providers can supply credentials through a customized workflow while retaining its source selection, deployment commands and concurrency contract.
146
+ For Cloudflare deployments, provide `CLOUDFLARE_API_TOKEN` through existing repository or environment secrets. The managed workflow also passes `CLOUDFLARE_ACCOUNT_ID` from a secret or variable; for native targets it must match the configured `accountId`. Other providers use deployment scripts and can supply credentials through a customized workflow while retaining its source selection, deployment commands and concurrency contract.
68
147
 
69
148
  ## Stable publication and the first baseline
70
149
 
@@ -76,7 +155,7 @@ The Release run's `repoctl-ci-progress.json` includes `releaseRecord` for manual
76
155
 
77
156
  The first production deployment waits for the next complete stable release after enabling this configuration. It cannot import arbitrary current `main` content as an initial baseline. Keep the existing production deployment serving until that release passes the documentation gate.
78
157
 
79
- Each deployment installs the frozen lockfile and builds the complete original source checkout. Later `main` commits do not enter the stable build. Preview uses an exact `main` SHA and rechecks the remote branch before deploying. An older source cannot replace a newer deployed source.
158
+ A fresh deployment installs the frozen lockfile and builds the complete original source checkout; a saved verification retry reuses its provider receipt. Later `main` commits do not enter the stable build. Preview uses an exact `main` SHA and rechecks the remote branch before deploying. An older source cannot replace a newer deployed source.
80
159
 
81
160
  Inspect either channel without installing dependencies, building or deploying:
82
161
 
@@ -103,33 +182,78 @@ Every correction must be a single-parent commit already in `origin/main` history
103
182
 
104
183
  repoctl applies only those selected patches to the stable source. It does not copy a current directory from `main`. A conflict stops deployment. Existing corrections remain applied across retries; when a later stable source already contains a correction, it is no longer reapplied.
105
184
 
106
- ## Deployment script contract and recovery
185
+ ## Deployment adapters and recovery
107
186
 
108
- repoctl invokes `pnpm install --frozen-lockfile`, the configured build script, then the selected deployment script in an isolated source checkout. Both scripts receive:
187
+ repoctl invokes `pnpm install --frozen-lockfile` and the configured build script in an isolated source checkout, then calls the selected native provider or deployment script. Native targets perform the preflight described above first. Installation retains lifecycle scripts and ordinary CI/package-manager environment variables, but removes all inherited `REPO_DOCS_*` variables and sets `LEFTHOOK=0`. A workspace-wide postinstall can therefore build unrelated sites with their own default channel and origin. Only the selected site build and provider/deployment phase receive the frozen deployment context; the caller's environment is not mutated. The build and any deployment script receive:
109
188
 
110
- | Variable | Meaning |
111
- | -------------------------------------- | -------------------------------------------------------------------------- |
112
- | `REPO_DOCS_SITE` / `REPO_DOCS_CHANNEL` | Selected site and `preview` or `stable` |
113
- | `REPO_DOCS_SOURCE_SHA` | Exact source commit |
114
- | `REPO_DOCS_RELEASE_ID` | Stable completion record ID; empty for preview |
115
- | `REPO_DOCS_DEPLOYMENT_ID` | Deterministic deployment identity, including corrections |
116
- | `REPO_DOCS_URL` | Optional configured target URL |
117
- | `REPO_DOCS_RESULT_FILE` | File the deployment adapter must write after verifying the provider result |
189
+ | Variable | Meaning |
190
+ | -------------------------------------- | -------------------------------------------------------------------- |
191
+ | `REPO_DOCS_SITE` / `REPO_DOCS_CHANNEL` | Selected site and `preview` or `stable` |
192
+ | `REPO_DOCS_SOURCE_SHA` | Exact source commit |
193
+ | `REPO_DOCS_TREE_SHA` | Verified tree including selected documentation corrections |
194
+ | `REPO_DOCS_RELEASE_ID` | Stable completion record ID; empty for preview |
195
+ | `REPO_DOCS_DEPLOYMENT_ID` | Deterministic deployment identity, including corrections |
196
+ | `REPO_DOCS_URL` | Optional configured target URL |
197
+ | `REPO_DOCS_RESULT_FILE` | File a script adapter must write after verifying the provider result |
118
198
 
119
- The adapter must return a nonempty provider version ID and may return an HTTP(S) URL:
199
+ A script adapter must write a nonempty provider version ID and may return an HTTP(S) URL. The native provider returns this result directly; no deployment script or result-file writer is needed:
120
200
 
121
201
  ```json
122
202
  { "versionId": "provider-deployment-version", "url": "https://docs.example.com" }
123
203
  ```
124
204
 
125
- Use `REPO_DOCS_DEPLOYMENT_ID` to reconcile an interrupted deployment before uploading again. Write the result file only after the provider confirms the outcome. A zero process exit without a valid result file is not a completed deployment.
205
+ Use `REPO_DOCS_DEPLOYMENT_ID` to reconcile an interrupted deployment before uploading again. Write the result file only after the provider confirms the outcome. A zero process exit without a valid result file is not a completed deployment. With readiness enabled, a saved receipt accepts only `versionId` and optional `url`; its version must be nonempty, at most 512 characters and free of control characters, and its HTTP(S) URL must not contain credentials, query or fragment.
206
+
207
+ The native Cloudflare provider performs this reconciliation itself. An active version with the same annotation and 100% of traffic is reused without another upload; a historical matching version is insufficient, and mixed traffic fails. A valid active version belonging to another deployment permits the new authorized upload. Wrangler's reported Worker, environment and version must match the target, and the reported version must be confirmed active before the current pointer advances.
126
208
 
127
209
  The workflow runs `repo docs plan` followed by `repo docs deploy` with the same inputs. Stable deployment requires GitHub Actions, a run ID and `REPO_DOCS_CONCURRENCY_GROUP` matching the real serialized workflow group; use `plan` for local review. Revision-checked state is claimed before building or deploying, and the deployed pointer advances only after a verified provider result.
128
210
 
129
- If a build fails, fix the cause and rerun the documentation workflow against the same release record. npm is not republished. If a stable runner disappears during deployment, rerun the same deployment so the adapter can reconcile its result; a different stable deployment cannot replace an unresolved `deploying` operation. For preview, the serialized managed workflow may replace an interrupted older deployment with the latest main source. Already completed deployments and unrelated release batches skip. Retain `repoctl-release-state`; short-lived Actions artifacts are diagnostic copies, not the deployment baseline.
211
+ If a build fails, fix the cause and rerun the documentation workflow against the same release record. npm is not republished. If a stable runner disappears during deployment, rerun the same deployment so the adapter can reconcile its result; a different stable deployment cannot replace an unresolved `deploying` or `verifying` operation. For preview, the serialized managed workflow may replace an interrupted older deployment with the latest main source. Already completed deployments and unrelated release batches skip. Retain `repoctl-release-state`; short-lived Actions artifacts are diagnostic copies, not the deployment baseline.
130
212
 
131
213
  An unfinished stable deployment saves its site configuration, release record, requested corrections and verified source tree. Retry with the same release and correction inputs. The planner rechecks those saved inputs and deployment identity, and the build uses the saved configuration even if `main` has changed or removed that site. Omitting `release` selects the current stable baseline; when the first deployment has no current baseline, it selects the pending release. If a newer release is pending while an older baseline is current, specify the newer release explicitly. Invalid or inconsistent saved state stops recovery before building or uploading.
132
214
 
215
+ If an upload succeeds but its result has not been durably saved as a verification receipt, or readiness is disabled and finalization fails, the `deploying` claim remains. A retry still installs and rebuilds the same frozen source, then checks the active remote version before deciding to upload. A failed preflight or rebuild during that retry must not erase the unknown upload outcome. Old pending script deployments continue to use their saved script target and original source after the live configuration switches to the native provider; migrate future deployments without rewriting historical snapshots.
216
+
133
217
  A fix that changes product source or build scripts needs a new stable baseline; the same release record continues to build its original source.
134
218
 
135
- The programmatic APIs are `planDocsDeployment(options)` and `deployDocs(options)`. `DocsOptions` accepts the site, channel, source/release/correction selection, optional config and environment, and a `DocsStateStore` implementing `readAutomationState`, `writeAutomationState` with revision checks, and `readBranchHead`. Query errors must fail closed.
219
+ Known repoctl failures retain fixed reason codes and guidance, including missing credentials, conflicting provider identity, unsupported configuration, an inactive saved version, a changed claim and an advanced main branch. Unknown exceptions retain only their stage classification; their messages and causes are not copied.
220
+
221
+ The programmatic APIs are `planDocsDeployment(options)` and `deployDocs(options)`. `DocsOptions` accepts the site, channel, source/release/correction selection, optional config and environment, and a `DocsStateStore` implementing `readAutomationState`, `writeAutomationState` with revision checks, and `readBranchHead`. Optional `fetch` supplies the native provider and public readiness HTTP transport; `spawn` supplies the process transport. Query errors must fail closed. Public target types are `DocsScriptDeploymentTarget`, `DocsCloudflareWorkersTarget` and their union `DocsDeploymentTarget`.
222
+
223
+ ## Deployment reports
224
+
225
+ `deployDocs()` includes an optional `report` on deployed and skipped results. Failures reject with exported `DocsDeploymentError`; its `report` is the safe structured failure result. `DocsDeploymentReport` contains attempt timestamps and monotonic elapsed milliseconds, ordered `plan`, `preflight`, `install`, `build`, `provider`, `readiness` and `finalize` stages, explicit completed/skipped/failed statuses, safe reason codes, provider `upload`/`reconcile`, and available deployment/version IDs. Failures identify `failedStage` and an optional `resumablePhase` of `deploying` or `verifying`. Resumed installation/build stages are marked skipped, not completed in zero time.
226
+
227
+ `repo docs deploy ... --json` writes only the result or safe failure report to stdout and exits nonzero on failure. Child install/build/script output still streams live, redirected to stderr in JSON mode; programmatic callers select the same transport with `DocsOptions.json`. Normal CLI mode adds safe failure guidance on stderr. repoctl-generated reports and errors omit raw exceptions, HTTP response bodies and credentials. Deployment scripts remain responsible for their own terminal output; repoctl does not promise to redact arbitrary script logs.
228
+
229
+ `GITHUB_STEP_SUMMARY` receives the same stage report on success, skip and failure, with escaped text and retry guidance. A summary-write failure adds `warnings: ['summary-write-failed']` without changing the deployment outcome, reverting current or hiding the primary failure. Reports describe this invocation only, not an invented total across previous attempts.
230
+
231
+ ## Build channel context
232
+
233
+ Nimbus and VitePress use the lightweight `repoctl/docs-context` entrypoint. Configure distinct preview and stable origins in the site's deployment module (`src/lib/deployment.ts` for Nimbus, `.vitepress/deployment/index.ts` for VitePress). Read only public build inputs at the framework boundary:
234
+
235
+ ```ts
236
+ import { resolveDocsContext } from 'repoctl/docs-context'
237
+
238
+ const context = resolveDocsContext({
239
+ channel: process.env.REPO_DOCS_CHANNEL,
240
+ url: process.env.REPO_DOCS_URL,
241
+ previewOrigin: 'https://next.example.com',
242
+ stableOrigin: 'https://docs.example.com',
243
+ })
244
+ ```
245
+
246
+ Undefined or empty channel selects `preview`; unknown channels fail. Origins must be distinct HTTP(S) origins without credentials, paths, queries or fragments; a trailing root slash is normalized. Set a framework `base` separately for subpath hosting. If `REPO_DOCS_URL` is nonempty, it must match the configured channel origin. The helper never reads credentials, GitHub state or release records. Both local builds work without deployment authorization:
247
+
248
+ ```sh
249
+ REPO_DOCS_CHANNEL=preview pnpm build
250
+ REPO_DOCS_CHANNEL=stable pnpm build
251
+ ```
252
+
253
+ `resolveDocsContext(options)` returns `DocsContext` (`channel`, `preview`, `origin`, `stableOrigin`); `DocsChannel` and `DocsContextOptions` are also exported. `docsPreviewRobots`, `docsRobots(context, sitemapUrl?)` and `mergeDocsHeaders(existing, context)` provide shared crawler policy. The default stable sitemap is `<origin>/sitemap.xml`; Nimbus passes its own `sitemap-index.xml` URL. The core package provides the same pure API at `@icebreakers/monorepo/docs-context`, without loading the CLI, filesystem or deployment providers.
254
+
255
+ The preview origin drives canonical, hreflang, social/structured URLs and Nimbus Markdown, LLM and discovery exports. Preview shows an unpublished notice linking to stable, emits `noindex, nofollow, noarchive`, disallows crawlers in robots.txt and adds an equivalent marked X-Robots-Tag rule. Local search stays indexed. A page's authored `noindex` remains effective on stable; Nimbus honors an explicit `searchable: true` independently. Crawler directives are not access control.
256
+
257
+ `mergeDocsHeaders` preserves user and Nimbus rules, inserts marked preview fields into an existing wildcard rule where possible, and removes only its own fields on stable. Malformed ownership markers fail the build. Nimbus runs this merge after its discovery integration; VitePress uses its buildEnd hook. Framework output cleanup removes stale preview/stable artifacts on repeated builds.
258
+
259
+ The templates declare `REPO_DOCS_CHANNEL` and `REPO_DOCS_URL` in task-local Turbo `env` for build, lint, typecheck and test. Their authored Markdown, framework configuration and validation scripts participate in cache inputs. Keep these values hashed, not passthrough-only; verify with `repo env check build lint typecheck test --json`. Declare `repoctl` in the docs workspace's devDependencies so the helper is available in a frozen build. Existing customized layouts are preserved: adopt these pieces manually or through a reviewed `repo templates upgrade` plan, rather than assuming a root `repo upgrade` rewrites them.
@@ -24,6 +24,8 @@ After review, use the separate preparation entry `repo release ci --mode=prepare
24
24
 
25
25
  ## Expected result
26
26
 
27
+ The plan also validates internal dependencies of public packages: `dependencies`, `devDependencies`, and `optionalDependencies` must use `workspace:*`; `peerDependencies` may use `workspace:*`, `workspace:^`, or `workspace:~`. For a target at `2.0.1`, pnpm pack produces peer ranges of `2.0.1`, `^2.0.1`, and `~2.0.1`, respectively. Choose the declaration that expresses your compatibility contract; replacing a caret or tilde peer with an exact version changes that contract. External ranges and private consumers are outside this publishing protocol policy. Invalid protocols produce a `blocked` plan without rewriting manifests or running release hooks.
28
+
27
29
  JSON and Markdown describe the same native version decisions. Notes use the existing release renderer and exclude already consumed intent entries. Private packages may change version but are marked as non-publish candidates. The public `createReleasePlan({ cwd })` API returns the same report without writing it to disk.
28
30
 
29
31
  Planning, automatic triggers, and version preparation recognize the same root and private-package intents. Native pnpm controls version propagation across the complete workspace. Release PR notes include only publishable child packages and require their version-specific changelogs; missing root/private changelogs do not block preparation. Root packages are not publication candidates, even when they are not marked private. Configure pnpm's `versioning.ignore` when a workspace package should not participate in versioning, rather than changing workspace discovery only for the apply step.
@@ -1,5 +1,5 @@
1
1
  import type { TransformContext } from 'vitepress'
2
- import { mkdtemp, readFile, rm } from 'node:fs/promises'
2
+ import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
3
3
  import os from 'node:os'
4
4
  import path from 'node:path'
5
5
  import { fileURLToPath } from 'node:url'
@@ -24,6 +24,8 @@ describe('documentation channels', () => {
24
24
  expect(getDocsDeployment(undefined).preview).toBe(true)
25
25
  expect(getDocsDeployment('').preview).toBe(true)
26
26
  expect(() => getDocsDeployment('production')).toThrow('Unsupported documentation channel')
27
+ expect(() => getDocsDeployment('preview', stableOrigin)).toThrow('does not match')
28
+ expect(getDocsDeployment('stable', `${stableOrigin}/`).origin).toBe(stableOrigin)
27
29
  })
28
30
 
29
31
  it.each(['preview', 'stable'] as const)('keeps canonical, social and crawler policy consistent for %s', async (channel) => {
@@ -57,10 +59,12 @@ describe('documentation channels', () => {
57
59
 
58
60
  it('removes preview crawler headers when rebuilding the same output for stable', async () => {
59
61
  const output = await mkdtemp(path.join(os.tmpdir(), 'repoctl-docs-channel-'))
62
+ const ownerHeaders = '/*\n X-Frame-Options: DENY\n/assets/*\n Cache-Control: immutable\n'
60
63
  try {
64
+ await writeFile(path.join(output, '_headers'), ownerHeaders)
61
65
  await writeDeploymentFiles(output, getDocsDeployment('preview'))
62
66
  await writeDeploymentFiles(output, getDocsDeployment('stable'))
63
- expect(await readFile(path.join(output, '_headers'), 'utf8')).toBe('')
67
+ expect(await readFile(path.join(output, '_headers'), 'utf8')).toBe(ownerHeaders)
64
68
  expect(await readFile(path.join(output, 'robots.txt'), 'utf8')).not.toContain('Disallow: /\n')
65
69
  }
66
70
  finally {
@@ -0,0 +1,48 @@
1
+ {
2
+ "$schema": "https://turborepo.com/schema.json",
3
+ "extends": [
4
+ "//"
5
+ ],
6
+ "tasks": {
7
+ "build": {
8
+ "env": [
9
+ "$TURBO_EXTENDS$",
10
+ "REPO_DOCS_CHANNEL",
11
+ "REPO_DOCS_URL"
12
+ ],
13
+ "inputs": [
14
+ "$TURBO_DEFAULT$"
15
+ ]
16
+ },
17
+ "lint": {
18
+ "env": [
19
+ "$TURBO_EXTENDS$",
20
+ "REPO_DOCS_CHANNEL",
21
+ "REPO_DOCS_URL"
22
+ ],
23
+ "inputs": [
24
+ "$TURBO_DEFAULT$"
25
+ ]
26
+ },
27
+ "typecheck": {
28
+ "env": [
29
+ "$TURBO_EXTENDS$",
30
+ "REPO_DOCS_CHANNEL",
31
+ "REPO_DOCS_URL"
32
+ ],
33
+ "inputs": [
34
+ "$TURBO_DEFAULT$"
35
+ ]
36
+ },
37
+ "test": {
38
+ "env": [
39
+ "$TURBO_EXTENDS$",
40
+ "REPO_DOCS_CHANNEL",
41
+ "REPO_DOCS_URL"
42
+ ],
43
+ "inputs": [
44
+ "$TURBO_DEFAULT$"
45
+ ]
46
+ }
47
+ }
48
+ }
@@ -16,7 +16,9 @@ repo docs plan guide --channel stable --release <发布完成记录-ID> --json
16
16
  repo docs plan guide --channel stable --commits <修正提交-SHA> --json
17
17
  ```
18
18
 
19
- `plan` 读取发布证据并检查冻结源码。`deploy` 接受相同选择,运行配置的构建与部署脚本;正式执行必须进入串行的受管 GitHub 工作流。需显式配置 `commands.docs.sites`。工作流接入、独立修正、适配器输出和恢复方式见[文档发布](/zh/tasks/docs)。
19
+ `plan` 读取发布证据并检查冻结源码。`deploy` 接受相同选择,运行配置的构建,再使用内置 `cloudflare-workers` provider 或 `deployScript` 适配器。原生 Cloudflare 预检在冻结源码安装/构建前核对纯静态资源配置、身份及读取权限,不证明后续写权限或公网 HTTP 已就绪。正式执行必须进入串行的受管 GitHub 工作流。需显式配置 `commands.docs.sites`。目标字段、工作流接入、独立修正、适配器输出和相同部署 ID 的恢复方式见[文档发布](/zh/tasks/docs)。
20
+
21
+ `deploy --json` 的 stdout 仅保留结果或安全失败报告,子进程日志实时输出到 stderr,失败退出码非零。报告包含有序阶段、耗时、upload/reconcile 与可恢复阶段;`GITHUB_STEP_SUMMARY` 写入同一份已转义报告。可选目标 `readiness` 在推进 current 前核验准确的公网 manifest。已保存 `verifying` 的重试跳过冻结源码的安装/构建/上传,工作流本身仍执行启动依赖安装。配置、receipt 恢复、`DocsDeploymentError.report` 及摘要写入警告见[文档发布](/zh/tasks/docs)。
20
22
 
21
23
  ## 最推荐入口
22
24
 
@@ -73,6 +75,8 @@ repo doctor --markdown --redact --out reports/doctor.md
73
75
  - `--json` 和 `--markdown` 适合自动化、PR、issue 和外部协作。
74
76
  - `--redact` 会脱敏 workspace、cwd、home 等绝对路径。
75
77
 
78
+ 文本和 Markdown 会展示稳定的规则 ID、受影响路径,以及适用时的清单字段。相同诊断合并展示说明和修复建议,保留每个路径与原始检查数量。例如,看到 `[template-instance-drift]` 与 `path: apps/docs/astro.config.ts` 后,可运行 `repo doctor --rules template-instance-drift` 继续定位。原因、建议或抑制不同的诊断分别展示。分组不会改变警告、失败统计或抑制策略;JSON 始终保留完整的逐项检查数据。
79
+
76
80
  ```bash
77
81
  repo doctor --list-rules
78
82
  repo doctor --rules root-scripts,package-manager --strict
@@ -152,7 +156,21 @@ repo package check --filter my-library --keep-temp --json
152
156
 
153
157
  按依赖顺序构建选中的 workspace 包及其依赖,再使用 publint 0.3.25 和 ATTW 0.18.5 校验实际 `pnpm pack` tarball。构建失败后不再打包。本地开发依赖也纳入构建,private 构建依赖可以只构建、不打包或校验。private 包默认明确标记为跳过,传入 `--include-private` 可包含它们。可重复 `--filter` 合并 pnpm 选择器;`--build-script` 可替换默认 `build` 脚本,没有该脚本的包按已经准备好的发布文件处理。
154
158
 
155
- 临时消费者独立安装 tarball 和本地运行时依赖的 tarball,执行包声明支持的 Node ESM/CJS 入口及 TypeScript NodeNext 消费检查。类型消费使用 ATTW 固定的 TypeScript 5.6.1-rc。未声明支持的模块格式不强制通过;仅供浏览器使用的入口及非 JavaScript 资源接受清单/类型分析,不执行 Node import;JavaScript bin 做语法检查,不调用应用命令。安装可能访问 registry,依赖安装脚本默认禁用;workspace 构建与 pack 生命周期脚本正常执行。
159
+ 临时消费者独立安装 tarball 和本地运行时依赖的 tarball,执行包声明支持的 Node ESM/CJS 入口及 TypeScript NodeNext 消费检查。有类型声明时,消费者安装源 workspace 已安装的准确 TypeScript 版本,并在隔离目录执行编译器,保持 `skipLibCheck: false`;源项目没有安装 TypeScript 时,默认使用 ATTW 固定的编译器版本。编译器与 repoctl 受控的 Node 类型使用专用 npm alias,自动避开运行依赖闭包内的全部依赖名,包括 required peer;验证通过独立 type root 加载受控 Node 声明,保留包运行时自身的 TypeScript 和 Node peer 要求。纯 JavaScript 包不安装这套类型工具。被测运行依赖闭包中声明的 optional peer 集成会安装到消费者,保留 peer 范围交集、required peer 约束和 npm alias。若原范围能表达完整交集则保留其写法;pnpm 12 不支持的复杂 npm alias 交集通过消费者的 `pnpm view` 查询所配置 registry,选取满足交集的最高已发布版本并写成精确 alias。查询沿用消费者包管理器配置并记录到命令日志,没有匹配的已发布版本则检查失败。不复制开发依赖或无关 workspace 的集成依赖。
160
+
161
+ 未声明支持的模块格式不强制通过;仅供浏览器使用的入口及非 JavaScript 资源接受清单/类型分析,不执行 Node import;JavaScript bin 做语法检查,不调用应用命令。Service Worker 等 JavaScript 资源可在发布的 `package.json` 中显式声明精确导出子路径:
162
+
163
+ ```json
164
+ {
165
+ "repoctl": {
166
+ "packageCheck": {
167
+ "runtimeAssets": ["./worker.js"]
168
+ }
169
+ }
170
+ }
171
+ ```
172
+
173
+ 每个资源必须对应实际打包的 JavaScript 导出。消费者验证导出解析、文件存在、包目录边界和 JavaScript 语法,不执行浏览器全局变量;缺失路径、通配符、越界链接和错误语法仍失败。包的其他入口有类型,不会强制复制资源也提供类型;资源显式声明的类型或相邻声明文件仍会校验,其他运行入口和类型声明也继续检查。Snapshot 与发布后的 nightly 消费者共用该契约;nightly 复核 tarball 完整性及 snapshot 身份后,从已校验产物读取可选集成依赖。安装可能访问 registry,依赖安装脚本默认禁用;workspace 构建与 pack 生命周期脚本正常执行。
156
174
 
157
175
  JSON 包含每个包的文件清单、稳定的诊断来源/代码、上游原始细节及子进程参数数组。`--strict` 将 warning 视为失败。`--keep-temp` 保留 tarball、消费者清单和命令工作目录,方便复现;未启用时,失败后也会清理临时文件。命令不会发布包、修改版本或写入发布状态。
158
176
 
@@ -215,6 +233,25 @@ repo upgrade --apply upgrade-plan.json
215
233
 
216
234
  基线保存在 `.repoctl/baselines/root/<路径 SHA-256>.json`;应将此目录提交到仓库,让不同克隆共享升级历史。每条记录包含资产路径、模板包和版本、源文件 SHA-256,以及上游原文和哈希,不把定制后的合并结果当作上游。哈希用于内容完整性和本地来源追溯,并非发布者签名。保存的计划本身就是待审核的写入内容;修改内容并重算哈希后,需要重新审核,哈希不能证明其作者身份。没有历史基线且内容不同的旧文件标为 `baseline-missing` 冲突;审核后可使用 `--overwrite` 显式采用新模板,`--yes` 本身不能消除冲突。与上游完全相同的文件可以只登记基线,不重写资产。删除基线目录会丢失历史信息,下一次升级重新采用保守的首次处理规则。
217
235
 
236
+ 新增 `pnpm-workspace.yaml` override 会依据**合并后**的工作区范围检查依赖声明,复用 `commands.deps.groups`,并解析 npm alias 与 catalog。全局 override 扩大现有限定选择器、或越过匹配依赖的声明范围时,该文件成为 `workspace-override-policy` 冲突,`--yes` 和 `--overwrite` 也不能绕过。JSON 的 `workspacePolicy.findings` 定位受影响的清单、声明和分组;完全不相交的版本选择器互不影响。这是离线声明检查,不能证明全部传递依赖的实际解析。新增父级限定选择器或非 semver 替换标为 `unverified`:普通升级必须显式选择文件,自动维护会拒绝;审核后的保存 JSON 计划可以显式应用。已有相同键不会反复提示。
237
+
238
+ 识别到默认 Husky 到 Lefthook 的迁移时,无关且无法自动判定的新增 override 标为 `deferred`,应用后仍不写入工作区,自动 hook 迁移可以继续完成。迁移后再次预览,这些 override 会重新标为 `unverified`,可显式选择文件或应用审核后的保存计划来接受。必需文件的明确冲突或受保护跳过会阻止整个迁移组,显式选择部分文件也无法绕过,包括由 preset 管理的目标和本地已删除的受管文件。自动维护准备阶段及其独立发布器同时拒绝 `unverified` 和 `deferred`;先完成 hook 迁移并审查 override,再重试维护。
239
+
240
+ 需要保留单个本地 override 或保持该键不存在时,在 `commands.upgrade.keep` 中记录决定,再**重新生成**计划:
241
+
242
+ ```ts
243
+ keep: [{
244
+ file: 'pnpm-workspace.yaml',
245
+ path: ['overrides', 'vite'],
246
+ reason: '保留 Taro Vite 4 与 uni-app Vite 5 的框架隔离。',
247
+ upstreamHash: '<从 finding 复制 upstreamHash>',
248
+ localHash: '<从 finding 复制 localHash>',
249
+ expires: '2030-12-31', // 可选,到期当天仍有效,使用 UTC 日期。
250
+ }]
251
+ ```
252
+
253
+ 仅支持 `pnpm-workspace.yaml` 中精确的 `['overrides', selector]` 路径;含点号、斜线的包名仍放在一个数组元素中。决定随配置提交。SHA-256 指纹同时绑定上游值和本地值,并区分键缺失与 YAML `null`。值变化、日期无效、重复决定或到期均要求重新审查,新上游不能静默沿用旧决定;其他无冲突设置仍正常合并。应用保存计划时会复核配置、全部已检查的清单、合并后的包集合以及决定有效期。本能力补充现有文件选择、根资产基线与模板实例 `excludedPaths`,不将整个文件脱离管理,也不抑制依赖诊断。
254
+
218
255
  版本化迁移显示在计划的 `migrations` 中,包含稳定 ID、版本边界、选择原因和受影响文件。首个内置迁移 `changesets-to-pnpm-versioning` 的边界为模板 `1.1.0`,对应引入 pnpm 原生版本管理的发行版。目标版本取已安装模板包的精确版本;没有历史记录时,可用 `--from-version 1.0.15` 明确提供旧模板版本,不会从 `^1.0.0` 等依赖范围推断。未知来源只收养可识别的旧格式,原因标为 `adopt-detected-legacy-format`;格式不明和降级会阻止执行。没有旧元数据的新项目不触发历史迁移,也不初始化账本。
219
256
 
220
257
  账本创建后,应提交 `.repoctl/migrations/ledger.json`。其中 `evaluatedVersion` 仅表示迁移评估到的版本,不代表每个受管资产都已使用该模板版本。预览不写文件;完成状态以普通文件 diff 审阅,JSON 同时包含 pending/failed 中间状态的精确内容。迁移文件组成不可拆分的选择组,未选择或失败的步骤不会推进游标。执行先保存 pending,迁移文件事务最后才写 completed;完成状态写入失败会回滚并记录 failed,如果失败状态也写不进去,则保留 pending 日志供恢复。其他选中资产在迁移组之后执行,其失败不会撤销已成功完成的迁移。
@@ -8,10 +8,12 @@ repoctl.config.ts
8
8
 
9
9
  `monorepo.config.ts` 已不再作为运行时配置入口。迁移旧仓库时请改名为 `repoctl.config.ts`。
10
10
 
11
- `commands.docs.sites` 用于逐站点启用随版本发布的文档。每站配置产品包、根构建/部署脚本、预览与正式目标,以及允许独立修正的路径。完整配置和适配器约定见[文档发布](/zh/tasks/docs)。省略该映射时不会启用文档部署。
11
+ `commands.docs.sites` 用于逐站点启用随版本发布的文档。每站配置产品包、根构建脚本、预览与正式目标,以及允许独立修正的路径。每个目标选择 `{ deployScript, url? }` 或 `{ provider: 'cloudflare-workers', config, workerName, accountId, environment?, url? }`,不得混用。原生目标使用纯静态资源的 JSON/JSONC Wrangler 配置;旧脚本目标无需新增 provider 字段。身份核对、凭据、支持范围与恢复方式见[文档发布](/zh/tasks/docs)。省略该映射时不会启用文档部署。
12
12
 
13
13
  repoctl 支持 `ts`、`mts`、`cts`、`js`、`mjs`、`cjs` 等配置文件后缀。新项目优先使用 `repoctl.config.ts`,这样能保留类型提示和更清晰的迁移路径。
14
14
 
15
+ 两种目标均支持可选 `readiness: { timeoutMs?, requestTimeoutMs? }`。省略时不发起公网请求;`{}` 启用配置 HTTPS origin 下 `/.well-known/repoctl-deployment.json` 的准确身份验证。URL 不得包含凭据、query 或 fragment。正整数默认值/范围分别为总预算 120000/1–600000 ms、每请求 10000/1–30000 ms。默认值仅在运行时计算,不改变冻结配置身份。脚本 manifest 责任与已保存 `verifying` 的恢复方式见[文档发布](/zh/tasks/docs)。
16
+
15
17
  ## 最小配置
16
18
 
17
19
  ```ts
@@ -182,3 +184,9 @@ export default defineMonorepoConfig({
182
184
  已有引用归用户维护。只有本功能新增的条目会记入 `.repoctl/typescript-references.json`,请将该文件与 tsconfig 一起提交。删除或排除项目只移除登记过的引用。用户编辑或移除受管条目会阻止同步:可以恢复条目,或明确删除登记记录,将该条目重新交给用户管理。保留 `references` 以外的 JSONC 字节、BOM 和换行风格,根引用与显式关系使用稳定的相对配置文件路径。
183
185
 
184
186
  保存的计划包含输入指纹和精确 diff。过期或被修改的计划在写入前失败;重复应用已完成的计划不产生变化。`.repoctl/typescript-references.lock` 进程锁覆盖重新校验、写入、验证、回滚和清理。多文件变更先准备备份,再统一替换并验证,失败会回滚。若并发编辑阻止安全回滚,错误会列出保留的恢复文件:保留这些文件,合并业务修改与原始备份,一致恢复归属登记后重新生成计划。进程被终止也可能留下 `.repoctl-references-*.bak`/`.tmp`;确认没有写入进程,恢复对应备份与归属登记后,再移除遗留锁;不会自动覆盖并发编辑。
187
+
188
+ `commands.upgrade.keep` 记录对 `pnpm-workspace.yaml` 单个 override 的保留决定。每项必须包含 `file`、`path: ['overrides', selector]`、`reason`,以及升级 finding 中的 `upstreamHash`、`localHash`;可选 `expires` 使用 UTC 日期,到期当天仍有效。任一值变化或到期均需重新审查,见 [upgrade](./commands.md#repo-upgrade)。
189
+
190
+ ## 文档构建元数据
191
+
192
+ 纯入口 `repoctl/docs-context` 导出 `resolveDocsContext`、`DocsContextOptions`、`DocsContext`、`DocsChannel`、`docsPreviewRobots`、`docsRobots` 和 `mergeDocsHeaders`,独立于 provider 配置和凭据。origin 校验、本地正式构建、爬虫响应头归属和 Turbo hash 详见[构建渠道上下文](/zh/tasks/docs#构建渠道上下文)。
@@ -512,3 +512,5 @@ repo package create api --template team --data ./answers.json
512
512
  公开 API 为 `resolveCreateNewProjectPlan({ parameters, parameterPrompt? })` 和 `applyCreateNewProjectPlan(plan)`。执行接受同一进程中未修改的原始计划,JSON 报告不能直接执行,须用原数据重新规划。来源变化、已存在目标或计划修改会拒绝写入。参数化创建统一提交暂存输出、workspace 清单和实例记录;失败恢复本次文件,遇到并发编辑或文件替换则保留并报告恢复位置。重复创建不会覆盖项目。
513
513
 
514
514
  移动参数化模板实例会保留原 ID、参数与敏感文件排除。在腾出的原路径再次创建同一模板时,新实例获得独立 ID,两份实例可以分别检查漂移和升级;登记失败只回滚本次新建输出。
515
+
516
+ 两种文档模板均包含[预览/正式构建上下文](/zh/tasks/docs#构建渠道上下文)、本地搜索、双语未发布提示与带归属的爬虫响应头。部署前配置 origin,本地正式构建不需要部署凭据。保留模板自身的 Turbo env/input 声明和 repoctl 依赖。