@prodshape/integration-openspec 0.6.0-alpha.5 → 0.6.1
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.
|
@@ -116,7 +116,6 @@ artifacts:
|
|
|
116
116
|
it must never approve, apply or archive. If no change is identifiable, ask the user to select
|
|
117
117
|
one or create one through the normal OpenSpec workflow; do not create a second lifecycle.
|
|
118
118
|
This is the same OpenSpec change, not a new refinement workflow.
|
|
119
|
-
This is the same OpenSpec change, not a new refinement workflow.
|
|
120
119
|
requires:
|
|
121
120
|
- intent
|
|
122
121
|
|
|
@@ -149,4 +148,7 @@ apply:
|
|
|
149
148
|
document that now cites old meaning and must be re-grounded.
|
|
150
149
|
4. STOP. Do not archive this change as part of apply: archiving is a separate OpenSpec action
|
|
151
150
|
performed after successful Product apply and verification. Do not implement delivery work, do
|
|
152
|
-
not modify implementation code and never edit docs/product/model by hand.
|
|
151
|
+
not modify implementation code and never edit docs/product/model by hand. When `openspec
|
|
152
|
+
archive` runs later, its non-blocking warning that proposal.md lacks a "## What Changes"
|
|
153
|
+
section comes from the spec-driven parser; this schema has no such section and the warning
|
|
154
|
+
needs no action.
|
package/dist/index.d.ts
CHANGED
|
@@ -380,12 +380,14 @@ declare function checkOpenSpecIntegration(root: string): Promise<{
|
|
|
380
380
|
* asset bytes); hand-edited managed files are preserved and reported in `preserved`, and with
|
|
381
381
|
* no recorded product schema nothing under openspec/schemas is touched.
|
|
382
382
|
* - Deletes `.product/integrations/openspec.json`.
|
|
383
|
-
* -
|
|
383
|
+
* - `openspec/config.yaml` stays on disk with the PDaC content stripped and is reported in `restored`.
|
|
384
|
+
* - `--dry-run` reports what would be removed, restored and preserved without writing.
|
|
384
385
|
*/
|
|
385
386
|
declare function removeOpenSpecIntegration(root: string, options?: {
|
|
386
387
|
dryRun?: boolean;
|
|
387
388
|
}): Promise<{
|
|
388
389
|
removed: string[];
|
|
390
|
+
restored: string[];
|
|
389
391
|
preserved: string[];
|
|
390
392
|
}>;
|
|
391
393
|
|
package/dist/index.js
CHANGED
|
@@ -497,7 +497,8 @@ async function applyOpenSpecProductChange(root, changeName, options = {}) {
|
|
|
497
497
|
repo,
|
|
498
498
|
change,
|
|
499
499
|
liveChanges: all,
|
|
500
|
-
dryRun: options.dryRun ?? false
|
|
500
|
+
dryRun: options.dryRun ?? false,
|
|
501
|
+
excludeDocumentsUnder: [`openspec/changes/${changeName}`, "openspec/changes/archive"]
|
|
501
502
|
});
|
|
502
503
|
}
|
|
503
504
|
async function deriveDeliveryContext(root, ids) {
|
|
@@ -572,6 +573,7 @@ var PDAC_RULES = {
|
|
|
572
573
|
"When a design decision depends on canonical product text, cite the artifact it depends on."
|
|
573
574
|
],
|
|
574
575
|
tasks: [
|
|
576
|
+
"A task that depends on a business rule's parameter cites the rule itself, not only the requirement derived from it: staleness follows the cited artifact's own digest, never its relationships.",
|
|
575
577
|
"A task that changes cited behaviour must include a follow-up task to refresh the affected citations.",
|
|
576
578
|
"Never infer implementation, verification, release or deployment from Product Change status; record that evidence in the delivery workflow."
|
|
577
579
|
]
|
|
@@ -1377,6 +1379,7 @@ async function checkOpenSpecIntegration(root) {
|
|
|
1377
1379
|
async function removeOpenSpecIntegration(root, options = {}) {
|
|
1378
1380
|
const { dryRun = false } = options;
|
|
1379
1381
|
const removed = [];
|
|
1382
|
+
const restored = [];
|
|
1380
1383
|
const preserved = [];
|
|
1381
1384
|
const previousMeta = await readMeta(root);
|
|
1382
1385
|
const recorded = previousMeta?.managed;
|
|
@@ -1466,12 +1469,9 @@ async function removeOpenSpecIntegration(root, options = {}) {
|
|
|
1466
1469
|
configChanged = true;
|
|
1467
1470
|
}
|
|
1468
1471
|
}
|
|
1469
|
-
if (configChanged
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
removed.push(CONFIG_RELATIVE);
|
|
1473
|
-
} else if (configChanged && dryRun) {
|
|
1474
|
-
removed.push(CONFIG_RELATIVE);
|
|
1472
|
+
if (configChanged) {
|
|
1473
|
+
if (!dryRun) await writeFile(configPath(root), serializeConfig(config), "utf8");
|
|
1474
|
+
restored.push(CONFIG_RELATIVE);
|
|
1475
1475
|
}
|
|
1476
1476
|
}
|
|
1477
1477
|
if (await pathExists(ciExamplePath(root))) {
|
|
@@ -1573,7 +1573,7 @@ async function removeOpenSpecIntegration(root, options = {}) {
|
|
|
1573
1573
|
}
|
|
1574
1574
|
removed.push(META_RELATIVE);
|
|
1575
1575
|
}
|
|
1576
|
-
return { removed, preserved };
|
|
1576
|
+
return { removed, restored, preserved };
|
|
1577
1577
|
}
|
|
1578
1578
|
export {
|
|
1579
1579
|
MIN_SUPPORTED_OPENSPEC,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/product-change-schema.ts","../src/process.ts","../src/workspace.ts","../src/population.ts","../src/product-workflow.ts"],"sourcesContent":["/**\n * OpenSpec integration for Product Definition as Code, in two lanes.\n *\n * The citation lane configures OpenSpec's spec-driven delivery workflow: PDaC citation rules are\n * merged into `openspec/config.yaml`, metadata is recorded under `.product/integrations/`, and\n * consumer documents cite canonical product artifacts by id + digest for\n * `prodshape citations verify` to check.\n *\n * The product lane hosts the PDaC product workflow inside OpenSpec: the integration installs a\n * project-local `product-change` schema at `openspec/schemas/product-change/`, OpenSpec's official project\n * extension surface, whose changes host a Product Change delta that `product-workflow.ts`\n * validates as an overlay and applies deterministically.\n *\n * Both lanes write only official OpenSpec surfaces plus ProductShape's own integration metadata.\n * The integration never patches OpenSpec-generated commands or skills, never modifies OpenSpec's\n * built-in schemas, and never writes into a native spec-driven change's documents.\n *\n * The merge logic is the heart of the citation lane: it preserves every field the user authored\n * (schema, context, rules, operations, githubCopilot, and anything else) and only appends PDaC\n * guidance, deduplicating identical entries so the operation is idempotent.\n */\nimport { mkdir, readdir, readFile, rm, rmdir, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport {\n contentDigest,\n isNotFound,\n isRepositoryRelativePath,\n resolveInRepository,\n} from '@prodshape/core';\nimport { parse, stringify } from 'yaml';\nimport {\n loadProductChangeSchemaAssets,\n OPENSPEC_PRODUCT_CHANGE_SCHEMA_NAME,\n OPENSPEC_PRODUCT_CHANGE_SCHEMA_RELATIVE,\n PRODUCT_CHANGE_SCHEMA_MIN_OPENSPEC,\n loadProductRecoverySchemaAssets,\n OPENSPEC_PRODUCT_RECOVERY_SCHEMA_NAME,\n OPENSPEC_PRODUCT_RECOVERY_SCHEMA_RELATIVE,\n} from './product-change-schema.js';\nimport { runCommand } from './process.js';\nimport { envWithLocalBin, isOpenSpecWorkspace, pathExists } from './workspace.js';\n\nexport { isOpenSpecWorkspace } from './workspace.js';\nexport {\n enumerateOpenSpecDocuments,\n isOpenSpecCliAvailable,\n openSpecProvider,\n} from './population.js';\nexport {\n loadProductChangeSchemaAssets,\n OPENSPEC_PRODUCT_CHANGE_SCHEMA_NAME,\n OPENSPEC_PRODUCT_CHANGE_SCHEMA_RELATIVE,\n PRODUCT_CHANGE_SCHEMA_MIN_OPENSPEC,\n loadProductRecoverySchemaAssets,\n OPENSPEC_PRODUCT_RECOVERY_SCHEMA_NAME,\n OPENSPEC_PRODUCT_RECOVERY_SCHEMA_RELATIVE,\n} from './product-change-schema.js';\nexport type { ProductChangeSchemaAsset } from './product-change-schema.js';\nexport {\n applyOpenSpecProductChange,\n deriveDeliveryContext,\n inspectProductModel,\n listOpenSpecProductChanges,\n loadOpenSpecProductChange,\n OPENSPEC_PRODUCT_SUBDIR,\n validateOpenSpecProductChange,\n} from './product-workflow.js';\nexport type {\n DeliveryContextBundle,\n DeliveryContextEntry,\n OpenSpecProductApplyOutcome,\n OpenSpecProductApplyResult,\n OpenSpecProductChangeRef,\n OpenSpecProductChangeValidation,\n ProductModelInspection,\n} from './product-workflow.js';\n\n/** The minimum supported OpenSpec CLI version. */\nexport const MIN_SUPPORTED_OPENSPEC = '1.0.0';\n\n/**\n * The npm spec used when bootstrapping OpenSpec through npx. Pinned to the supported major:\n * MIN_SUPPORTED_OPENSPEC guards the floor, the pin guards the ceiling, so a future OpenSpec 2.x\n * cannot silently change what `init --tools none` does for new adopters.\n */\nexport const OPENSPEC_NPX_SPEC = '@fission-ai/openspec@1';\n\n/**\n * Sentinel markers that bracket the PDaC-injected context block. They let `mergeConfig` find\n * and replace exactly the PDaC portion without touching user-authored context lines.\n */\nconst PDAC_CONTEXT_BEGIN = '<!-- pdac:context begin -->';\nconst PDAC_CONTEXT_END = '<!-- pdac:context end -->';\n\n/** The PDaC authority context block injected into openspec/config.yaml `context:`. */\nexport const PDAC_CONTEXT_BLOCK = `${PDAC_CONTEXT_BEGIN}\nThis repository uses Product Definition as Code (PDaC). The canonical product definition lives in docs/product/model (actors, journeys, use cases, business rules, domain terms, bounded contexts, structured behaviours, and functional, quality and constraint requirements). OpenSpec artifacts consume it and are never a second source of truth.\nA citation binds consumer text to a canonical artifact: it records the artifact id, a sha256 content digest and an optional verification scenario anchor. Verification reports one status per citation: current, stale, tampered or unresolved.\nTo cite: run \\`npx prodshape inspect <ID>\\` to read the current digest, then \\`npx prodshape cite --id <ID> --digest <digest>\\` to emit the canonical payload. Wrap it in the document's native comment (\\`<!-- ... -->\\` in Markdown) on its own line directly under the text it grounds.\nTo find which artifacts a change impacts, compare the change's intent with the whole product definition first, then widen the result with \\`npx prodshape impact <ID>\\`. When the intent contradicts or goes beyond the definition, record that drift in the proposal with the marker \\`<!-- pdac-drift ids=\"...\" summary=\"...\" -->\\` (listed by \\`npx prodshape drift\\`) and let humans decide — never fix it quietly.\nEvery current OpenSpec document (proposal, specs, design, tasks and openspec/specs/) carries exactly one explicit scope declaration: \\`pdac-scope: cited\\` for a bound document that carries at least one citation, or \\`pdac-scope: none\\` with a non-empty human-authored reason (\\`pdac-scope-reason: <why>\\` in frontmatter, or \\`<!-- pdac-scope: none reason=\\\"<why>\\\" -->\\`) for an exempt one. Citations alone never bind: a document without a declaration is unclassified and fails verification, a bound document with zero citations fails, and an exemption without a reason or contradicted by citations fails. Binding and exemption are human declarations: never declare an exemption merely because citations are missing. Verify with \\`npx prodshape citations verify --provider openspec\\`: it enumerates the expected current documents, so zero discovered citations is a set of failures, never a pass. Archived changes are excluded by default; \\`--include-archived\\` also verifies the citations history carries, reported as warnings, because archived history cannot be edited.\nThe accepted Product Definition changes only through a Product Change: validate it as an overlay while the baseline stays untouched, obtain human product approval (represented by status: approved; the integration verifies the state but cannot identify or judge the actor), apply explicitly on a working branch, and let a human accept the resulting baseline by merging its pull request. A Product Change is not a pull request; apply is not acceptance; neither apply nor merge attests implementation, verification, release or deployment. A Product Change lives under docs/product/changes/ or hosted inside a valid OpenSpec change created with the product schema (product-change/change.md plus product-change/proposed/**); its deterministically validated, apply-authorised apply is the only write path into docs/product/model. A spec-driven OpenSpec delivery change never edits docs/product/model directly: it may share a pull request with an applied Product Change or follow later, but it never supplies Product Change status.\n${PDAC_CONTEXT_END}`;\n\n/** The PDaC citation rules injected per artifact into openspec/config.yaml `rules:`. */\nexport const PDAC_RULES: Record<string, string[]> = {\n proposal: [\n 'State which PDaC artifacts this change touches, each with a citation payload emitted by `npx prodshape cite --id <ID> --digest <digest>` and wrapped in the document native comment (`<!-- ... -->` in Markdown). Never write a citation record by hand.',\n 'Find the impacted artifacts as a separate step, before writing any proposal content. Read the whole product definition (docs/product/model by default), compare it with what this change wants — the backlog item behind it, if there is one — and list every artifact the change depends on, alters or contradicts. Then widen the list: run `npx prodshape impact <ID>` on each artifact you found and check its direct and indirect neighbours. Put the resulting list in the proposal, cite every impacted artifact from each document that uses it (proposal, specs, design, tasks), and name any neighbour you checked and left out.',\n 'If this change\\'s goals contradict the product definition, or need behaviour it does not describe, that is product-definition drift. Record it in the proposal under a \\'Product definition drift\\' note naming the artifacts involved, with the marker `<!-- pdac-drift ids=\"<ID>[, <ID>...]\" summary=\"<one line>\" -->` on its own line so `npx prodshape drift` can list it. The decision is human: the developer and the product owner agree whether to propose a Product Change or adjust this change. Never fix drift quietly, drop or weaken a citation to hide it, or write around the conflict.',\n 'If the change implements altered product behaviour, name the Product Change (CHG id) whose applied or accepted artifacts it implements. If no overlay-validated, apply-authorised Product Change exists yet, stop and ask for one instead of proceeding. A spec-driven OpenSpec change is not the Product Change; product intent travels through a Product Change, under docs/product/changes or hosted by a product-change OpenSpec change.',\n 'Every document of this change must end up bound or exempt, each with an explicit declaration: declare `pdac-scope: cited` on a line of its own and cite the canonical text the document depends on, or declare `pdac-scope: none` with a non-empty reason (`pdac-scope-reason: <why>` in frontmatter, or `<!-- pdac-scope: none reason=\"<why>\" -->`) when a human judges the document has no product-semantic dependency. Citations alone never bind, and never declare an exemption just because citations are missing.',\n ],\n specs: [\n 'Every requirement derived from canonical product text MUST carry a citation to every PDaC artifact it derives from — not only the closest one — one citation per line, placed after the requirement text and before the first scenario. Never place citations between the requirement heading and the requirement text; OpenSpec reads the first paragraph as the requirement.',\n 'Cite a specific verification scenario with an anchor when the requirement maps to one (anchor=\"<scenario id>\").',\n 'Never paraphrase canonical text as if it were new; either cite it inline or embed it in a marker-block citation (<!-- pdac:cite ... --> canonical text <!-- /pdac:cite -->).',\n 'Never invent artifact ids or digests. Read them with `npx prodshape inspect <ID>`.',\n ],\n design: [\n 'When a design decision depends on canonical product text, cite the artifact it depends on.',\n ],\n tasks: [\n 'A task that changes cited behaviour must include a follow-up task to refresh the affected citations.',\n 'Never infer implementation, verification, release or deployment from Product Change status; record that evidence in the delivery workflow.',\n ],\n};\n\n/** The PDaC operation guidance injected into openspec/config.yaml `operations:`. */\nexport const PDAC_OPERATIONS: {\n apply: { guidance: string[] };\n archive: { guidance: string[] };\n} = {\n apply: {\n guidance: [\n 'Before finishing, run `npx prodshape citations verify --provider openspec` and fix every stale, tampered or unresolved citation, every unclassified document, every bound document without citations and every exemption without a reason. Refresh a stale digest by re-running `npx prodshape inspect <ID>`; never delete a citation or declare `pdac-scope: none` to silence a diagnostic.',\n ],\n },\n archive: {\n guidance: [\n 'Run `npx prodshape citations verify --provider openspec` one final time. Archive only when every document is bound or exempt and every citation is current.',\n ],\n },\n};\n\n/**\n * The exact strings PDaC injected into openspec/config.yaml, recorded so a later update can\n * remove entries whose wording has since changed instead of appending duplicates alongside them.\n * The context block needs no record: its sentinels already identify it in place.\n */\nexport interface ManagedStrings {\n rules: Record<string, string[]>;\n operations: Record<string, string[]>;\n}\n\n/** The managed strings the current PDaC guidance injects. */\nexport function currentManagedStrings(): ManagedStrings {\n return {\n rules: Object.fromEntries(Object.entries(PDAC_RULES).map(([key, list]) => [key, [...list]])),\n operations: Object.fromEntries(\n Object.entries(PDAC_OPERATIONS).map(([key, op]) => [key, [...op.guidance]]),\n ),\n };\n}\n\n/** Metadata recorded under .product/integrations/openspec.json. */\nexport interface OpenSpecIntegrationMeta {\n provider: 'openspec';\n version: string;\n openspecVersion: string;\n /**\n * When the integration was first installed. Preserved across every later add, update and no-op:\n * an install date that moves on each invocation records nothing, and rewriting it made every\n * no-op command dirty the working tree.\n */\n installedAt: string;\n /**\n * When managed content last actually changed. Absent until the first change after installation;\n * written only when the merged configuration or the CI example differed, so a no-op leaves this\n * file byte-identical. It answers the question `installedAt` cannot: whether this installation\n * has been regenerated since a ProductShape upgrade.\n */\n updatedAt?: string;\n configPath: string;\n /** Absent in metadata written before the CI example existed; `integration update` adds it. */\n ciExamplePath?: string;\n /** Absent in metadata written before the field existed; treated as the current guidance. */\n managed?: ManagedStrings;\n /**\n * The installed OpenSpec product schema: its name, the OpenSpec floor the product workflow\n * needs, and every managed file it consists of, each with the content digest it was installed\n * at. The per-file record and digest are the ownership proof (ADR 0008): update replaces and\n * remove deletes a file only when that exact path was recorded and its bytes still match the\n * recorded digest (legacy path-only records additionally require the current asset bytes). A\n * diverged or unrecorded file is preserved and reported, never overwritten or deleted. Absent in metadata written before the\n * schema existed; `integration update` installs the schema and adds the record. Metadata\n * written by earlier spike revisions carried a plain path array; it is read as paths with no\n * recorded digest, so ownership then rests on matching the current assets.\n */\n productSchema?: { name: string; requiresOpenspec: string; files: Record<string, string> };\n recoverySchema?: { name: string; requiresOpenspec: string; files: Record<string, string> };\n}\n\n/** The pre-#227 schema location accepted only for safe, ownership-proven migration. */\nconst LEGACY_OPENSPEC_PRODUCT_SCHEMA_NAME = 'product';\nconst LEGACY_OPENSPEC_PRODUCT_SCHEMA_RELATIVE = 'openspec/schemas/product';\n\n/** The recorded product-schema files as path-to-digest, tolerating the earlier array shape. */\nfunction recordedProductSchemaFiles(\n meta: OpenSpecIntegrationMeta | null,\n): Record<string, string> | undefined {\n const record = meta?.productSchema;\n if (!record) return undefined;\n if (\n typeof record !== 'object' ||\n record === null ||\n (record.name !== OPENSPEC_PRODUCT_CHANGE_SCHEMA_NAME &&\n record.name !== LEGACY_OPENSPEC_PRODUCT_SCHEMA_NAME &&\n record.name !== OPENSPEC_PRODUCT_RECOVERY_SCHEMA_NAME) ||\n typeof record.requiresOpenspec !== 'string'\n ) {\n throw new Error(\n \"'productSchema' must name the product schema and carry a string 'requiresOpenspec'\",\n );\n }\n const files = record.files as unknown;\n const validatePath = (file: unknown): file is string =>\n typeof file === 'string' &&\n (file.startsWith(`${OPENSPEC_PRODUCT_CHANGE_SCHEMA_RELATIVE}/`) ||\n file.startsWith(`${LEGACY_OPENSPEC_PRODUCT_SCHEMA_RELATIVE}/`) ||\n file.startsWith(`${OPENSPEC_PRODUCT_RECOVERY_SCHEMA_RELATIVE}/`)) &&\n isRepositoryRelativePath(file);\n if (Array.isArray(files)) {\n if (!files.every(validatePath)) {\n throw new Error(\n `'productSchema.files' contains a path outside the managed OpenSpec schema locations`,\n );\n }\n return Object.fromEntries(files.map((file) => [file, '']));\n }\n if (files !== null && typeof files === 'object') {\n const entries = Object.entries(files as Record<string, unknown>);\n if (!entries.every(([file]) => validatePath(file))) {\n throw new Error(\n `'productSchema.files' contains a path outside the managed OpenSpec schema locations`,\n );\n }\n if (\n !entries.every(\n ([, digest]) => typeof digest === 'string' && /^sha256:[0-9a-f]{64}$/.test(digest),\n )\n ) {\n throw new Error(\"'productSchema.files' contains an invalid content digest\");\n }\n return Object.fromEntries(entries as [string, string][]);\n }\n throw new Error(\"'productSchema.files' must be a path-to-digest object or legacy path array\");\n}\n\nfunction recordedRecoverySchemaFiles(\n meta: OpenSpecIntegrationMeta | null,\n): Record<string, string> | undefined {\n const record = meta?.recoverySchema;\n if (!record) return undefined;\n if (\n record.name !== OPENSPEC_PRODUCT_RECOVERY_SCHEMA_NAME ||\n typeof record.requiresOpenspec !== 'string'\n ) {\n throw new Error(\n \"'recoverySchema' must name the product-recovery schema and carry a string 'requiresOpenspec'\",\n );\n }\n if (!record.files || typeof record.files !== 'object' || Array.isArray(record.files)) {\n throw new Error(\"'recoverySchema.files' must be a path-to-digest object\");\n }\n return record.files;\n}\n\n/** Repository-relative paths used by this integration. */\nconst CONFIG_RELATIVE = 'openspec/config.yaml';\nconst META_RELATIVE = '.product/integrations/openspec.json';\nconst CI_EXAMPLE_RELATIVE = '.product/integrations/openspec.ci.yml';\n\n/** The repository-relative path of the integration metadata file, for callers that probe it. */\nexport const OPENSPEC_META_RELATIVE = META_RELATIVE;\n\n/** The repository-relative path of the installed CI-ready example workflow. */\nexport const OPENSPEC_CI_EXAMPLE_RELATIVE = CI_EXAMPLE_RELATIVE;\n\n/** The exact provider-aware verification command the integration stands behind. */\nexport const OPENSPEC_VERIFY_COMMAND = 'npx prodshape citations verify --provider openspec';\n\n/**\n * Render the CI-ready example workflow installed at {@link OPENSPEC_CI_EXAMPLE_RELATIVE}.\n *\n * The gate always blocks unresolved citations, tampered embedded projections, unclassified\n * current documents and bound documents with zero citations. Stale citations follow the\n * repository's configured warning policy (`validation.warnings-as-errors` in\n * `.product/config.yaml`); the example never changes that policy, but it states explicitly what\n * the repository's current choice means and how to escalate it, so the blocking behaviour is a\n * deliberate decision rather than a silent default.\n */\nexport function renderCiExample(options: { warningsAsErrors: boolean }): string {\n const stalePolicy = options.warningsAsErrors\n ? [\n '# - stale citations BLOCK this gate: .product/config.yaml sets',\n '# `validation.warnings-as-errors: true`, escalating the PRODUCT061 warning to an error.',\n '# The integration never changes that policy; relax it by setting the key to false.',\n ]\n : [\n '# - stale citations DO NOT block this gate: .product/config.yaml sets',\n '# `validation.warnings-as-errors: false`, so PRODUCT061 stays a warning. To make stale',\n '# citations block the merge, set `validation.warnings-as-errors: true` in',\n '# .product/config.yaml. The integration never changes that policy for you.',\n ];\n return [\n '# PDaC citation gate for OpenSpec consumer documents (CI-ready example).',\n '# Managed by `prodshape integration add openspec`; copy it into your pipeline',\n '# (e.g. .github/workflows/) and adapt the triggers.',\n '#',\n '# The gate runs ProductShape citation and scope verification only. It never invokes',\n \"# `openspec validate`; keep OpenSpec's own verdict a separate step so a native-spec defect\",\n '# and a grounding defect stay distinguishable.',\n '#',\n '# Blocking behaviour:',\n '# - unresolved citations, tampered embedded projections, unclassified current documents',\n '# and bound documents with zero citations always fail the gate;',\n ...stalePolicy,\n '# - citations in archived changes are checked too, but their problems are reported as',\n '# warnings: archived history cannot be edited, so a problem there is information,',\n '# not something this gate can make anyone fix. Pass --include-archived to apply the',\n '# full gate to archived documents as well.',\n 'name: pdac-citations',\n 'on:',\n ' pull_request:',\n 'jobs:',\n ' verify:',\n ' runs-on: ubuntu-latest',\n ' steps:',\n ' - uses: actions/checkout@v4',\n ' - uses: actions/setup-node@v4',\n ' with:',\n \" node-version: '22'\",\n ' # The OpenSpec CLI ships as @fission-ai/openspec; population discovery runs',\n ' # `openspec list`, so the gate needs it installed.',\n ` - run: npm install -g ${OPENSPEC_NPX_SPEC}`,\n ` - run: ${OPENSPEC_VERIFY_COMMAND}`,\n '',\n ].join('\\n');\n}\n\n/**\n * Every path this integration writes is one of its own module-level literals, resolved through the\n * repository-containment resolver rather than joined directly, so the contract is enforced by the\n * same code that enforces it for repository-supplied paths instead of by inspection of this file.\n */\nfunction configPath(root: string): string {\n return resolveInRepository(root, CONFIG_RELATIVE, 'the OpenSpec integration');\n}\n\nfunction metaPath(root: string): string {\n return resolveInRepository(root, META_RELATIVE, 'the OpenSpec integration');\n}\n\nfunction ciExamplePath(root: string): string {\n return resolveInRepository(root, CI_EXAMPLE_RELATIVE, 'the OpenSpec integration');\n}\n\n/**\n * Compare two semver-like version strings (major.minor.patch, ignoring pre-release suffixes).\n * Returns negative if a < b, 0 if equal, positive if a > b.\n */\nfunction compareVersions(a: string, b: string): number {\n const parseParts = (v: string): [number, number, number] => {\n const core = v.split('-')[0]?.split('+')[0] ?? '';\n const segments = core.split('.').map((s) => Number.parseInt(s, 10));\n return [segments[0] ?? 0, segments[1] ?? 0, segments[2] ?? 0];\n };\n const [aMaj, aMin, aPatch] = parseParts(a);\n const [bMaj, bMin, bPatch] = parseParts(b);\n if (aMaj !== bMaj) return aMaj - bMaj;\n if (aMin !== bMin) return aMin - bMin;\n return aPatch - bPatch;\n}\n\n/** Run a command at the repository root, resolving stdout+stderr or rejecting on failure. */\nasync function runAtRoot(root: string, command: string, args: string[]): Promise<string> {\n const { stdout, stderr } = await runCommand(command, args, {\n cwd: root,\n env: envWithLocalBin(root),\n });\n return `${stdout}${stderr}`;\n}\n\n/**\n * Detect the installed OpenSpec CLI version by running `openspec --version`. When a repository\n * root is given, its node_modules/.bin is consulted first, so a devDependency install counts.\n */\nexport async function detectOpenSpecVersion(\n root?: string,\n): Promise<{ version: string } | { error: string }> {\n try {\n const { stdout, stderr } = await runCommand('openspec', ['--version'], {\n ...(root ? { cwd: root, env: envWithLocalBin(root) } : {}),\n });\n const output = `${stdout}${stderr}`.trim();\n // `openspec --version` prints something like \"1.3.0\" or \"openspec/1.3.0 ...\".\n const match = output.match(/(\\d+\\.\\d+\\.\\d+(?:-[^\\s]+)?)/);\n if (!match?.[1]) {\n return { error: `Could not parse OpenSpec version from output: ${output}` };\n }\n return { version: match[1] };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n error: `OpenSpec CLI not found on PATH (run: npm install -g ${OPENSPEC_NPX_SPEC}). ${message}`,\n };\n }\n}\n\n/**\n * Create an OpenSpec workspace at the root. Uses the installed CLI when one is on PATH (or in\n * node_modules/.bin); otherwise runs the pinned OpenSpec major through npx, which downloads\n * nothing permanent and never mutates the global environment. `--tools none` keeps the\n * framework's own initializer non-interactive.\n *\n * Returns the CLI version that ran and whether it came through npx, so callers can pass the\n * version on to `addOpenSpecIntegration` (nothing may be on PATH afterwards) and recommend a\n * permanent install.\n */\nexport async function bootstrapOpenSpecWorkspace(\n root: string,\n): Promise<{ version: string; viaNpx: boolean; command: string }> {\n if (await isOpenSpecWorkspace(root)) {\n throw new Error('An OpenSpec workspace already exists at this root.');\n }\n const detected = await detectOpenSpecVersion(root);\n if (!('error' in detected)) {\n if (compareVersions(detected.version, MIN_SUPPORTED_OPENSPEC) < 0) {\n throw new Error(\n `OpenSpec CLI version ${detected.version} is below the minimum supported ${MIN_SUPPORTED_OPENSPEC}. Please upgrade OpenSpec.`,\n );\n }\n const command = 'openspec init --tools none';\n await runAtRoot(root, 'openspec', ['init', '--tools', 'none']);\n return { version: detected.version, viaNpx: false, command };\n }\n const command = `npx -y ${OPENSPEC_NPX_SPEC} init --tools none`;\n await runAtRoot(root, 'npx', ['-y', OPENSPEC_NPX_SPEC, 'init', '--tools', 'none']);\n // A second npx call resolves from the cache the first one populated.\n const output = await runAtRoot(root, 'npx', ['-y', OPENSPEC_NPX_SPEC, '--version']);\n const match = output.trim().match(/(\\d+\\.\\d+\\.\\d+(?:-[^\\s]+)?)/);\n if (!match?.[1]) {\n throw new Error(`Could not parse OpenSpec version from npx output: ${output.trim()}`);\n }\n return { version: match[1], viaNpx: true, command };\n}\n\n/** Check if the OpenSpec integration is installed (its metadata file is present). */\nexport async function isOpenSpecIntegrationInstalled(root: string): Promise<boolean> {\n return pathExists(metaPath(root));\n}\n\n/** Read and parse the existing openspec/config.yaml, or null if it does not exist. */\nexport async function readOpenSpecConfig(root: string): Promise<Record<string, unknown> | null> {\n const path = configPath(root);\n if (!(await pathExists(path))) return null;\n const text = await readFile(path, 'utf8');\n const parsed = parse(text);\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n // An empty or non-mapping file: treat as an empty config so we can merge into it.\n return {};\n }\n return parsed as Record<string, unknown>;\n}\n\n/**\n * Extract the PDaC context block (between sentinels) from a context string, or null if absent.\n */\nfunction extractPdacContext(context: string | undefined): string | null {\n if (!context) return null;\n const begin = context.indexOf(PDAC_CONTEXT_BEGIN);\n const end = context.indexOf(PDAC_CONTEXT_END);\n if (begin === -1 || end === -1 || end < begin) return null;\n return context.slice(begin, end + PDAC_CONTEXT_END.length);\n}\n\n/**\n * Merge PDaC guidance into the OpenSpec config, preserving existing content.\n *\n * - `schema` is preserved as-is.\n * - `context`: if the existing context already contains the PDaC block, it is replaced in place\n * (idempotent); otherwise the PDaC block is appended after a separator. User-authored context\n * lines are never lost.\n * - `rules`: for each PDaC artifact (proposal, specs, design, tasks), PDaC rules are appended to\n * existing rules, deduplicating identical entries. Entries recorded as previously managed whose\n * wording is no longer current are removed first, so a guidance change replaces the old string\n * instead of accumulating next to it. User-authored entries are never removed.\n * - `operations`: PDaC apply/archive guidance is appended to existing guidance, deduplicating,\n * with the same previously-managed replacement.\n * - Any other fields (githubCopilot, etc.) are preserved untouched.\n *\n * Returns the merged config and a list of human-readable change descriptions.\n */\nexport function mergeConfig(\n existing: Record<string, unknown> | null,\n previouslyManaged?: ManagedStrings,\n): {\n config: Record<string, unknown>;\n changes: string[];\n} {\n const config: Record<string, unknown> = { ...(existing ?? {}) };\n const changes: string[] = [];\n\n // Strings PDaC injected in the past but no longer injects verbatim: remove before appending.\n const staleManaged = (recorded: string[] | undefined, current: string[]): string[] =>\n (recorded ?? []).filter((entry) => !current.includes(entry));\n\n // --- context ---\n const existingContext =\n typeof config['context'] === 'string' ? (config['context'] as string) : undefined;\n const existingPdacBlock = extractPdacContext(existingContext);\n if (existingPdacBlock !== null) {\n // Replace the existing PDaC block in place (keeps it idempotent and up to date).\n if (existingPdacBlock !== PDAC_CONTEXT_BLOCK) {\n const replaced = existingContext!.replace(existingPdacBlock, PDAC_CONTEXT_BLOCK);\n if (replaced !== existingContext) {\n config['context'] = replaced;\n changes.push('Updated PDaC context block in openspec/config.yaml (context:).');\n }\n }\n // If identical, no change recorded — idempotent.\n } else if (existingContext) {\n // User has their own context: append the PDaC block after a blank-line separator.\n const separator = existingContext.endsWith('\\n') ? '\\n' : '\\n\\n';\n config['context'] = `${existingContext}${separator}${PDAC_CONTEXT_BLOCK}`;\n changes.push('Appended PDaC context block to existing openspec/config.yaml context:.');\n } else {\n config['context'] = PDAC_CONTEXT_BLOCK;\n changes.push('Added PDaC context block to openspec/config.yaml (context:).');\n }\n\n // --- rules ---\n const existingRules =\n config['rules'] && typeof config['rules'] === 'object' && !Array.isArray(config['rules'])\n ? { ...(config['rules'] as Record<string, unknown>) }\n : {};\n let rulesChanged = false;\n let staleRulesRemoved = false;\n const ruleArtifacts = new Set([\n ...Object.keys(PDAC_RULES),\n ...Object.keys(previouslyManaged?.rules ?? {}),\n ]);\n for (const artifact of ruleArtifacts) {\n const pdacRules = PDAC_RULES[artifact] ?? [];\n const stale = staleManaged(previouslyManaged?.rules[artifact], pdacRules);\n const existingList = Array.isArray(existingRules[artifact])\n ? [...(existingRules[artifact] as string[])]\n : [];\n const merged = existingList.filter((entry) => !stale.includes(entry));\n if (merged.length !== existingList.length) {\n staleRulesRemoved = true;\n rulesChanged = true;\n }\n for (const rule of pdacRules) {\n if (!merged.includes(rule)) {\n merged.push(rule);\n rulesChanged = true;\n }\n }\n if (merged.length === 0) {\n delete existingRules[artifact];\n } else {\n existingRules[artifact] = merged;\n }\n }\n if (rulesChanged) {\n if (Object.keys(existingRules).length === 0) {\n delete config['rules'];\n } else {\n config['rules'] = existingRules;\n }\n changes.push(\n staleRulesRemoved\n ? 'Merged PDaC citation rules into openspec/config.yaml (rules:), replacing outdated PDaC entries.'\n : 'Merged PDaC citation rules into openspec/config.yaml (rules:).',\n );\n }\n\n // --- operations ---\n const existingOperations =\n config['operations'] &&\n typeof config['operations'] === 'object' &&\n !Array.isArray(config['operations'])\n ? { ...(config['operations'] as Record<string, unknown>) }\n : {};\n let opsChanged = false;\n let staleOpsRemoved = false;\n for (const opKey of ['apply', 'archive'] as const) {\n const pdacGuidance = PDAC_OPERATIONS[opKey].guidance;\n const stale = staleManaged(previouslyManaged?.operations[opKey], pdacGuidance);\n const existingOp =\n existingOperations[opKey] &&\n typeof existingOperations[opKey] === 'object' &&\n !Array.isArray(existingOperations[opKey])\n ? { ...(existingOperations[opKey] as Record<string, unknown>) }\n : {};\n const existingGuidance = Array.isArray(existingOp['guidance'])\n ? [...(existingOp['guidance'] as string[])]\n : [];\n const merged = existingGuidance.filter((entry) => !stale.includes(entry));\n if (merged.length !== existingGuidance.length) {\n staleOpsRemoved = true;\n opsChanged = true;\n }\n for (const guidance of pdacGuidance) {\n if (!merged.includes(guidance)) {\n merged.push(guidance);\n opsChanged = true;\n }\n }\n existingOp['guidance'] = merged;\n existingOperations[opKey] = existingOp;\n }\n if (opsChanged) {\n config['operations'] = existingOperations;\n changes.push(\n staleOpsRemoved\n ? 'Merged PDaC operation guidance into openspec/config.yaml (operations:), replacing outdated PDaC entries.'\n : 'Merged PDaC operation guidance into openspec/config.yaml (operations:).',\n );\n }\n\n return { config, changes };\n}\n\n/** Serialize config back to YAML with a trailing newline. */\nexport function serializeConfig(config: Record<string, unknown>): string {\n return `${stringify(config).trimEnd()}\\n`;\n}\n\n/** Read the ProductShape version from the integration-openspec package.json. */\nasync function productshapeVersion(): Promise<string> {\n const packageJson = JSON.parse(\n await readFile(new URL('../package.json', import.meta.url), 'utf8'),\n ) as { version: string };\n return packageJson.version;\n}\n\n/**\n * Add the OpenSpec integration to a repository.\n *\n * - Detects the OpenSpec CLI version; rejects if not found or below MIN_SUPPORTED_OPENSPEC.\n * A caller that already ran the CLI (the init bootstrap) can pass `cliVersion` to skip the\n * probe: after an npx bootstrap nothing is on PATH, yet the version is known.\n * - Requires an existing `openspec/` workspace (run `openspec init` first).\n * - Merges PDaC guidance into `openspec/config.yaml`, preserving existing user configuration.\n * - Records metadata under `.product/integrations/openspec.json`, including the exact strings\n * injected, so a later update replaces outdated PDaC entries instead of accumulating them.\n * - Byte-idempotent: running twice produces the same result, reports no changes and rewrites\n * nothing at all — not the merged configuration, not the CI example, not the metadata.\n * - `--dry-run` reports what would change without writing.\n */\nexport async function addOpenSpecIntegration(\n root: string,\n options: {\n force?: boolean;\n dryRun?: boolean;\n cliVersion?: string;\n /**\n * The repository's configured `validation.warnings-as-errors` policy, reflected verbatim in\n * the installed CI example so its blocking behaviour is stated, never silently changed.\n */\n warningsAsErrors?: boolean;\n } = {},\n): Promise<{ written: string[]; changes: string[]; meta: OpenSpecIntegrationMeta }> {\n // `force` is accepted for signature compatibility with the other integrations and deliberately\n // ignored: every managed surface here is compared by content, so there is nothing a forced run\n // could regenerate that a normal one does not. It used to mean \"rewrite even when identical\",\n // which is exactly the byte-churn this integration must not produce.\n const { dryRun = false, warningsAsErrors = false } = options;\n\n let openspecVersion = options.cliVersion;\n if (!openspecVersion) {\n const detected = await detectOpenSpecVersion(root);\n if ('error' in detected) {\n throw new Error(detected.error);\n }\n openspecVersion = detected.version;\n }\n if (compareVersions(openspecVersion, MIN_SUPPORTED_OPENSPEC) < 0) {\n throw new Error(\n `OpenSpec CLI version ${openspecVersion} is below the minimum supported ${MIN_SUPPORTED_OPENSPEC}. Please upgrade OpenSpec.`,\n );\n }\n\n if (!(await isOpenSpecWorkspace(root))) {\n throw new Error('No OpenSpec workspace found. Run `openspec init` first.');\n }\n\n const existing = await readOpenSpecConfig(root);\n const previousMeta = await readMeta(root);\n\n const { config, changes: configChanges } = mergeConfig(existing, previousMeta?.managed);\n\n const ciDesired = renderCiExample({ warningsAsErrors });\n const ciAbsolute = ciExamplePath(root);\n const ciExisting = (await pathExists(ciAbsolute)) ? await readFile(ciAbsolute, 'utf8') : null;\n const ciChanged = ciExisting !== ciDesired;\n\n const changes = [...configChanges];\n if (ciChanged) {\n changes.push(\n ciExisting === null\n ? `Installed CI-ready verification example at ${CI_EXAMPLE_RELATIVE}.`\n : `Updated CI-ready verification example at ${CI_EXAMPLE_RELATIVE}.`,\n );\n }\n\n // The OpenSpec product schema: managed files under openspec/schemas/product-change, the framework's\n // official project-local schema surface. The files are version-independent data, so they are\n // installed regardless of the detected CLI version; whether the product workflow is USABLE on\n // this CLI is a separate, capability-specific report (`checkOpenSpecIntegration`), never a\n // different set of bytes.\n //\n // Ownership is proven per file, never inferred from the schema path or byte equality (ADR\n // 0008): a file may be replaced only when the metadata recorded that exact path and its bytes\n // still match the recorded digest. Any pre-existing unrecorded file is a collision, even when\n // byte-identical to the asset; on update a diverged managed file is preserved and reported, and\n // an obsolete still-managed file is removed only while its digest continues to match.\n const schemaAssets = await loadProductChangeSchemaAssets();\n const recordedSchema = recordedProductSchemaFiles(previousMeta);\n const currentSchemaPaths = new Set(\n schemaAssets.map((asset) => `${OPENSPEC_PRODUCT_CHANGE_SCHEMA_RELATIVE}/${asset.relative}`),\n );\n const schemaFileDigests: Record<string, string> = {};\n const schemaWrites: { relative: string; absolute: string; content: string }[] = [];\n const schemaDeletes: { relative: string; absolute: string }[] = [];\n const schemaCollisions: string[] = [];\n const schemaPreserved: string[] = [];\n let schemaInstalledAnything = false;\n for (const asset of schemaAssets) {\n const relative = `${OPENSPEC_PRODUCT_CHANGE_SCHEMA_RELATIVE}/${asset.relative}`;\n const absolute = resolveInRepository(root, relative, 'the OpenSpec integration');\n const existing = (await pathExists(absolute)) ? await readFile(absolute, 'utf8') : null;\n const assetDigest = contentDigest(asset.content);\n if (existing === null) {\n schemaWrites.push({ relative, absolute, content: asset.content });\n schemaFileDigests[relative] = assetDigest;\n schemaInstalledAnything = true;\n continue;\n }\n const recordedDigest = recordedSchema?.[relative];\n if (recordedDigest === undefined) {\n // Ownership is per file. Byte equality proves compatibility, not authorship: adopting an\n // identical user file on a fresh install or when a later release adds a new asset would let\n // a subsequent remove delete content the integration never installed.\n schemaCollisions.push(relative);\n continue;\n }\n if (existing === asset.content) {\n // A recorded managed path is already byte-identical to the current asset: nothing to write.\n schemaFileDigests[relative] = assetDigest;\n continue;\n }\n if (recordedDigest !== '' && contentDigest(existing) === recordedDigest) {\n // Proven managed at an older recorded version: replace with the current assets.\n schemaWrites.push({ relative, absolute, content: asset.content });\n schemaFileDigests[relative] = assetDigest;\n continue;\n }\n // Managed path whose content diverged from both the record and the current assets: a hand\n // edit. Preserve it, keep its recorded digest (restoring the recorded content re-enables\n // management), and report it.\n schemaPreserved.push(relative);\n if (recordedDigest !== '') schemaFileDigests[relative] = recordedDigest;\n }\n for (const [relative, recordedDigest] of Object.entries(recordedSchema ?? {})) {\n if (currentSchemaPaths.has(relative)) continue;\n const absolute = resolveInRepository(root, relative, 'the OpenSpec integration');\n if (!(await pathExists(absolute))) continue;\n const existing = await readFile(absolute, 'utf8');\n if (recordedDigest !== '' && contentDigest(existing) === recordedDigest) {\n schemaDeletes.push({ relative, absolute });\n continue;\n }\n // Ownership extends to absence, but deletion still requires the recorded digest proof. A\n // diverged obsolete file stays user-visible and remains recorded so check/remove can continue\n // reporting it; a digest-less legacy record is preserved but cannot become ownership proof.\n schemaPreserved.push(relative);\n if (recordedDigest !== '') schemaFileDigests[relative] = recordedDigest;\n }\n if (schemaCollisions.length > 0) {\n throw new Error(\n `Refusing to install the OpenSpec product schema: ${OPENSPEC_PRODUCT_CHANGE_SCHEMA_RELATIVE} already contains files this integration does not manage (${schemaCollisions.join(', ')}). Move them aside or delete them, then run: prodshape integration add openspec. The integration never overwrites user-authored files.`,\n );\n }\n if (schemaWrites.length > 0) {\n changes.push(\n recordedSchema !== undefined || !schemaInstalledAnything\n ? `Updated the OpenSpec product schema at ${OPENSPEC_PRODUCT_CHANGE_SCHEMA_RELATIVE} (${schemaWrites.length} file${schemaWrites.length === 1 ? '' : 's'}).`\n : `Installed the OpenSpec product schema at ${OPENSPEC_PRODUCT_CHANGE_SCHEMA_RELATIVE}.`,\n );\n if (compareVersions(openspecVersion, PRODUCT_CHANGE_SCHEMA_MIN_OPENSPEC) < 0) {\n changes.push(\n `Product workflow unavailable until OpenSpec >= ${PRODUCT_CHANGE_SCHEMA_MIN_OPENSPEC} (detected ${openspecVersion}); the citation lane is unaffected.`,\n );\n }\n }\n for (const { relative } of schemaDeletes) {\n changes.push(`Removed obsolete managed file ${relative}.`);\n }\n for (const relative of schemaPreserved) {\n changes.push(\n `Preserved hand-edited managed file ${relative}; restore or delete it, then run: prodshape integration update.`,\n );\n }\n\n // The recovery workload has its own schema and ownership record. It is intentionally kept\n // beside, rather than inside, product-change: recovery starts without an accepted baseline.\n const recoveryAssets = await loadProductRecoverySchemaAssets();\n const previousRecovery = recordedRecoverySchemaFiles(previousMeta);\n const recoveryDigests: Record<string, string> = {};\n const recoveryWrites: { relative: string; absolute: string; content: string }[] = [];\n const recoveryCollisions: string[] = [];\n for (const asset of recoveryAssets) {\n const relative = `${OPENSPEC_PRODUCT_RECOVERY_SCHEMA_RELATIVE}/${asset.relative}`;\n const absolute = resolveInRepository(root, relative, 'the OpenSpec integration');\n const existing = (await pathExists(absolute)) ? await readFile(absolute, 'utf8') : null;\n const digest = contentDigest(asset.content);\n const recorded = previousRecovery?.[relative];\n if (existing === null) {\n recoveryWrites.push({ relative, absolute, content: asset.content });\n recoveryDigests[relative] = digest;\n } else if (recorded === undefined) {\n recoveryCollisions.push(relative);\n } else if (existing === asset.content) {\n recoveryDigests[relative] = digest;\n } else if (recorded !== '' && contentDigest(existing) === recorded) {\n recoveryWrites.push({ relative, absolute, content: asset.content });\n recoveryDigests[relative] = digest;\n } else {\n recoveryDigests[relative] = recorded;\n changes.push(\n `Preserved hand-edited managed file ${relative}; restore or delete it, then run: prodshape integration update.`,\n );\n }\n }\n if (recoveryCollisions.length > 0) {\n throw new Error(\n `Refusing to install the OpenSpec product-recovery schema: unrecorded files exist (${recoveryCollisions.join(', ')}). The integration never overwrites user-authored files.`,\n );\n }\n if (recoveryWrites.length > 0) {\n changes.push(\n previousRecovery === undefined\n ? `Installed the OpenSpec product-recovery schema at ${OPENSPEC_PRODUCT_RECOVERY_SCHEMA_RELATIVE}.`\n : `Updated the OpenSpec product-recovery schema at ${OPENSPEC_PRODUCT_RECOVERY_SCHEMA_RELATIVE} (${recoveryWrites.length} files).`,\n );\n }\n\n // `installedAt` records when this integration was first installed, and is preserved from here\n // on. Stamping a fresh timestamp on every invocation is what made a no-op `add` — and every\n // `integration update` — rewrite this file, so a command that reported \"already up to date\"\n // still left the working tree dirty. `updatedAt` carries the moving fact instead, and moves\n // only when managed content actually changed.\n const now = new Date().toISOString();\n const meta: OpenSpecIntegrationMeta = {\n provider: 'openspec',\n version: await productshapeVersion(),\n openspecVersion,\n installedAt: previousMeta?.installedAt ?? now,\n configPath: CONFIG_RELATIVE,\n ciExamplePath: CI_EXAMPLE_RELATIVE,\n managed: currentManagedStrings(),\n productSchema: {\n name: OPENSPEC_PRODUCT_CHANGE_SCHEMA_NAME,\n requiresOpenspec: PRODUCT_CHANGE_SCHEMA_MIN_OPENSPEC,\n files: schemaFileDigests,\n },\n recoverySchema: {\n name: OPENSPEC_PRODUCT_RECOVERY_SCHEMA_NAME,\n requiresOpenspec: PRODUCT_CHANGE_SCHEMA_MIN_OPENSPEC,\n files: recoveryDigests,\n },\n };\n const updatedAt =\n changes.length > 0 && previousMeta !== null ? now : (previousMeta?.updatedAt ?? undefined);\n if (updatedAt !== undefined) meta.updatedAt = updatedAt;\n\n const written: string[] = [];\n if (!dryRun) {\n // Every write below is conditional on the bytes actually differing. `force` re-derives the\n // content; it does not license rewriting a file that already carries exactly that content,\n // because a byte-identical rewrite is invisible in a diff and visible everywhere else — in\n // Git's stat cache, in file watchers, and in every build that keys off modification times.\n if (configChanges.length > 0) {\n const serialized = serializeConfig(config);\n await mkdir(dirname(configPath(root)), { recursive: true });\n await writeFile(configPath(root), serialized, 'utf8');\n written.push(CONFIG_RELATIVE);\n }\n if (ciChanged) {\n await mkdir(dirname(ciAbsolute), { recursive: true });\n await writeFile(ciAbsolute, ciDesired, 'utf8');\n written.push(CI_EXAMPLE_RELATIVE);\n }\n for (const deletion of schemaDeletes) {\n await rm(deletion.absolute, { force: true });\n }\n for (const write of schemaWrites) {\n await mkdir(dirname(write.absolute), { recursive: true });\n await writeFile(write.absolute, write.content, 'utf8');\n written.push(write.relative);\n }\n for (const write of recoveryWrites) {\n await mkdir(dirname(write.absolute), { recursive: true });\n await writeFile(write.absolute, write.content, 'utf8');\n written.push(write.relative);\n }\n if (await writeMetaIfChanged(root, meta)) {\n written.push(META_RELATIVE);\n }\n }\n\n return { written, changes, meta };\n}\n\n/**\n * Write the integration metadata file, but only when its bytes would change. Reports whether it\n * was written, so a caller never claims a write that did not happen.\n */\nasync function writeMetaIfChanged(root: string, meta: OpenSpecIntegrationMeta): Promise<boolean> {\n const path = metaPath(root);\n const desired = `${JSON.stringify(meta, null, 2)}\\n`;\n let existing: string | undefined;\n try {\n existing = await readFile(path, 'utf8');\n } catch (error) {\n if (!isNotFound(error)) throw error;\n }\n if (existing === desired) return false;\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, desired, 'utf8');\n return true;\n}\n\n/**\n * An integration metadata file that exists but cannot be read or parsed.\n *\n * Absent metadata means the integration is not installed, which is a supported state. A metadata\n * file that is present and unreadable is not: continuing as though it were absent would discard\n * the record of exactly which guidance strings were injected, and the next update would append\n * duplicates alongside the entries it should have replaced.\n */\nexport class OpenSpecMetaError extends Error {\n constructor(detail: string, options?: { cause?: unknown }) {\n super(\n `OpenSpec integration metadata ${META_RELATIVE} cannot be trusted: ${detail}` +\n '\\nReconcile it by hand, or remove it and re-run: prodshape integration add openspec',\n options,\n );\n this.name = 'OpenSpecMetaError';\n }\n}\n\n/** Read the integration metadata file, or null if absent. Unreadable or malformed is an error. */\nasync function readMeta(root: string): Promise<OpenSpecIntegrationMeta | null> {\n const path = metaPath(root);\n let content: string;\n try {\n content = await readFile(path, 'utf8');\n } catch (error) {\n if (isNotFound(error)) return null;\n throw new OpenSpecMetaError(\n `it exists but could not be read (${error instanceof Error ? error.message : String(error)})`,\n { cause: error },\n );\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(content);\n } catch (error) {\n throw new OpenSpecMetaError(\n `it is not valid JSON (${error instanceof Error ? error.message : String(error)})`,\n { cause: error },\n );\n }\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n throw new OpenSpecMetaError('the document is not a JSON object');\n }\n const record = parsed as Record<string, unknown>;\n if (record.provider !== 'openspec' || typeof record.installedAt !== 'string') {\n throw new OpenSpecMetaError(\n \"it does not match the metadata schema ('provider' must be 'openspec' and 'installedAt' a string)\",\n );\n }\n const meta = record as unknown as OpenSpecIntegrationMeta;\n try {\n recordedProductSchemaFiles(meta);\n } catch (error) {\n throw new OpenSpecMetaError(\n `it does not match the product schema ownership record (${error instanceof Error ? error.message : String(error)})`,\n { cause: error },\n );\n }\n return meta;\n}\n\n/**\n * Update (re-merge) the OpenSpec integration. Re-runs the merge so that a PDaC upgrade that\n * changed the context block or rules is reflected. Idempotent.\n */\nexport async function updateOpenSpecIntegration(\n root: string,\n options: { force?: boolean; dryRun?: boolean; warningsAsErrors?: boolean } = {},\n): Promise<{ written: string[]; changes: string[] }> {\n // An existing metadata record is the compatibility verdict established during installation.\n // Managed file reconciliation must remain usable in release jobs and repositories that no\n // longer have OpenSpec on PATH; initial installation and explicit health checks still probe it.\n const previousMeta = await readMeta(root);\n const result = await addOpenSpecIntegration(root, {\n force: true,\n dryRun: options.dryRun,\n warningsAsErrors: options.warningsAsErrors,\n ...(previousMeta?.openspecVersion ? { cliVersion: previousMeta.openspecVersion } : {}),\n });\n return { written: result.written, changes: result.changes };\n}\n\n/**\n * Check the OpenSpec integration health. Verifies:\n * - OpenSpec CLI is installed and the version is supported.\n * - `openspec/config.yaml` exists and contains the PDaC context block.\n * - `.product/integrations/openspec.json` metadata exists.\n * - The config's PDaC block matches the current PDaC guidance (not removed or altered).\n * - The product schema's managed files are installed and intact, with the product workflow's\n * availability reported per capability: an OpenSpec CLI below the product floor leaves the\n * citation lane usable and states the product workflow UNAVAILABLE instead of failing it.\n */\nexport async function checkOpenSpecIntegration(root: string): Promise<{\n ok: boolean;\n checks: { name: string; ok: boolean; detail: string }[];\n}> {\n const checks: { name: string; ok: boolean; detail: string }[] = [];\n\n // 1. OpenSpec CLI installed and supported.\n const detected = await detectOpenSpecVersion(root);\n const detectedVersion = 'error' in detected ? undefined : detected.version;\n if ('error' in detected) {\n checks.push({ name: 'openspec CLI', ok: false, detail: detected.error });\n } else if (compareVersions(detected.version, MIN_SUPPORTED_OPENSPEC) < 0) {\n checks.push({\n name: 'openspec CLI',\n ok: false,\n detail: `OpenSpec ${detected.version} is below minimum supported ${MIN_SUPPORTED_OPENSPEC}.`,\n });\n } else {\n checks.push({\n name: 'openspec CLI',\n ok: true,\n detail: `OpenSpec ${detected.version} detected.`,\n });\n }\n\n // 2. openspec/config.yaml exists and contains PDaC context.\n const config = await readOpenSpecConfig(root);\n if (config === null) {\n checks.push({\n name: 'config.yaml',\n ok: false,\n detail: 'openspec/config.yaml not found. Run: prodshape integration add openspec',\n });\n } else {\n const context =\n typeof config['context'] === 'string' ? (config['context'] as string) : undefined;\n const pdacBlock = extractPdacContext(context);\n if (pdacBlock === null) {\n checks.push({\n name: 'config.yaml',\n ok: false,\n detail:\n 'PDaC context block missing from openspec/config.yaml. Run: prodshape integration add openspec',\n });\n } else if (pdacBlock !== PDAC_CONTEXT_BLOCK) {\n checks.push({\n name: 'config.yaml',\n ok: false,\n detail:\n 'PDaC context block in openspec/config.yaml is outdated. Run: prodshape integration update',\n });\n } else {\n checks.push({\n name: 'config.yaml',\n ok: true,\n detail: 'PDaC context block present and current.',\n });\n }\n }\n\n // 3. Metadata file exists.\n const meta = await readMeta(root);\n if (meta === null) {\n checks.push({\n name: 'metadata',\n ok: false,\n detail:\n '.product/integrations/openspec.json not found. Run: prodshape integration add openspec',\n });\n } else {\n checks.push({\n name: 'metadata',\n ok: true,\n detail:\n `Integration recorded (OpenSpec ${meta.openspecVersion}, installed ${meta.installedAt}` +\n `${meta.updatedAt ? `, updated ${meta.updatedAt}` : ''}).`,\n });\n }\n\n // 3b. CI-ready verification example present.\n if (await pathExists(ciExamplePath(root))) {\n checks.push({\n name: 'ci example',\n ok: true,\n detail: `CI-ready verification example present at ${CI_EXAMPLE_RELATIVE}.`,\n });\n } else {\n checks.push({\n name: 'ci example',\n ok: false,\n detail: `CI-ready verification example missing (${CI_EXAMPLE_RELATIVE}). Run: prodshape integration update`,\n });\n }\n\n // 4. PDaC rules present.\n if (config) {\n const rules =\n config['rules'] && typeof config['rules'] === 'object' && !Array.isArray(config['rules'])\n ? (config['rules'] as Record<string, unknown>)\n : {};\n const missingRules: string[] = [];\n for (const [artifact, pdacRules] of Object.entries(PDAC_RULES)) {\n const list = Array.isArray(rules[artifact]) ? (rules[artifact] as string[]) : [];\n for (const rule of pdacRules) {\n if (!list.includes(rule)) missingRules.push(artifact);\n }\n }\n if (missingRules.length > 0) {\n checks.push({\n name: 'rules',\n ok: false,\n detail: `PDaC rules missing for: ${[...new Set(missingRules)].join(', ')}. Run: prodshape integration update`,\n });\n } else {\n checks.push({ name: 'rules', ok: true, detail: 'All PDaC citation rules present.' });\n }\n }\n\n // 5. Product workflow: managed schema state plus capability-specific availability. The schema\n // files are inert data valid under any CLI, so a floor-violating CLI is not a managed-state\n // defect; it makes the product workflow UNAVAILABLE, stated as the check's verdict rather than\n // buried as a footnote, while the citation lane stays independently usable.\n const schemaAssets = await loadProductChangeSchemaAssets();\n if (meta === null || meta.productSchema === undefined) {\n checks.push({\n name: 'product workflow',\n ok: false,\n detail: `OpenSpec product schema not installed (${OPENSPEC_PRODUCT_CHANGE_SCHEMA_RELATIVE}). Run: prodshape integration update`,\n });\n } else {\n const recordedSchema = recordedProductSchemaFiles(meta) ?? {};\n const currentSchemaPaths = new Set(\n schemaAssets.map((asset) => `${OPENSPEC_PRODUCT_CHANGE_SCHEMA_RELATIVE}/${asset.relative}`),\n );\n const missing: string[] = [];\n const unrecorded: string[] = [];\n const outdated: string[] = [];\n const diverged: string[] = [];\n const obsolete = Object.keys(recordedSchema)\n .filter((relative) => !currentSchemaPaths.has(relative))\n .sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));\n for (const asset of schemaAssets) {\n const relative = `${OPENSPEC_PRODUCT_CHANGE_SCHEMA_RELATIVE}/${asset.relative}`;\n const absolute = resolveInRepository(root, relative, 'the OpenSpec integration');\n const existing = (await pathExists(absolute)) ? await readFile(absolute, 'utf8') : null;\n const recordedDigest = recordedSchema[relative];\n if (recordedDigest === undefined) {\n unrecorded.push(relative);\n } else if (existing === null) {\n missing.push(relative);\n } else if (existing !== asset.content) {\n if (recordedDigest !== '' && contentDigest(existing) === recordedDigest) {\n outdated.push(relative);\n } else {\n diverged.push(relative);\n }\n }\n }\n if (\n missing.length > 0 ||\n unrecorded.length > 0 ||\n outdated.length > 0 ||\n diverged.length > 0 ||\n obsolete.length > 0\n ) {\n const parts: string[] = [];\n if (missing.length > 0) {\n parts.push(`missing: ${missing.join(', ')} (run: prodshape integration update)`);\n }\n if (unrecorded.length > 0) {\n parts.push(\n `unrecorded: ${unrecorded.join(', ')} (move user files aside, then run: prodshape integration update)`,\n );\n }\n if (outdated.length > 0) {\n parts.push(\n `outdated managed content: ${outdated.join(', ')} (run: prodshape integration update)`,\n );\n }\n if (diverged.length > 0) {\n parts.push(\n `hand-edited and preserved: ${diverged.join(', ')} (restore or delete them, then run: prodshape integration update)`,\n );\n }\n if (obsolete.length > 0) {\n parts.push(\n `obsolete managed content: ${obsolete.join(', ')} (run: prodshape integration update)`,\n );\n }\n checks.push({\n name: 'product workflow',\n ok: false,\n detail: `Product schema files need attention. ${parts.join(' ')}`,\n });\n } else if (detectedVersion === undefined) {\n checks.push({\n name: 'product workflow',\n ok: true,\n detail:\n 'Product workflow UNAVAILABLE: the OpenSpec CLI was not detected. The schema is installed and intact; the openspec CLI check carries the remedy.',\n });\n } else if (compareVersions(detectedVersion, PRODUCT_CHANGE_SCHEMA_MIN_OPENSPEC) < 0) {\n checks.push({\n name: 'product workflow',\n ok: true,\n detail: `Product workflow UNAVAILABLE: requires OpenSpec >= ${PRODUCT_CHANGE_SCHEMA_MIN_OPENSPEC}, detected ${detectedVersion}. The citation lane is unaffected; upgrade OpenSpec to use the product schema.`,\n });\n } else {\n checks.push({\n name: 'product workflow',\n ok: true,\n detail: `Product workflow available: schema installed and OpenSpec ${detectedVersion} meets the ${PRODUCT_CHANGE_SCHEMA_MIN_OPENSPEC} floor.`,\n });\n }\n }\n\n // 6. Hosted recovery schema ownership is checked independently from product-change because\n // the two workloads have different preconditions and may evolve on different cadences.\n const recoveryAssets = await loadProductRecoverySchemaAssets();\n const recoveryRecord = recordedRecoverySchemaFiles(meta);\n if (recoveryRecord === undefined) {\n checks.push({\n name: 'product recovery workflow',\n // Existing installations predate #229. Keep the citation and existing product-change\n // lanes healthy while advertising the opt-in migration; a fresh add/update records the\n // recovery schema and is checked strictly below.\n ok: true,\n detail: `Product recovery workflow UNAVAILABLE: schema not installed (${OPENSPEC_PRODUCT_RECOVERY_SCHEMA_RELATIVE}). Run: prodshape integration update to enable it.`,\n });\n } else {\n const defects: string[] = [];\n for (const asset of recoveryAssets) {\n const relative = `${OPENSPEC_PRODUCT_RECOVERY_SCHEMA_RELATIVE}/${asset.relative}`;\n const absolute = resolveInRepository(root, relative, 'the OpenSpec integration');\n const expected = recoveryRecord[relative];\n if (expected === undefined) defects.push(`unrecorded: ${relative}`);\n else if (!(await pathExists(absolute))) defects.push(`missing: ${relative}`);\n else {\n const actual = await readFile(absolute, 'utf8');\n if (actual !== asset.content && contentDigest(actual) !== expected) {\n defects.push(`hand-edited and preserved: ${relative}`);\n } else if (actual !== asset.content) defects.push(`outdated managed content: ${relative}`);\n }\n }\n checks.push({\n name: 'product recovery workflow',\n ok: defects.length === 0,\n detail:\n defects.length === 0\n ? `Product recovery workflow available: schema installed at ${OPENSPEC_PRODUCT_RECOVERY_SCHEMA_RELATIVE}.`\n : `Product-recovery schema files need attention. ${defects.join(' ')}`,\n });\n }\n\n const ok = checks.every((c) => c.ok);\n return { ok, checks };\n}\n\n/**\n * Remove the PDaC-injected content from openspec/config.yaml, preserving user-authored content,\n * and delete the metadata file.\n *\n * - Removes the PDaC context block (between sentinels) from `context:`.\n * - Removes PDaC rules from each artifact (keeps user-authored rules).\n * - Removes PDaC operation guidance (keeps user-authored guidance).\n * - Deletes the installed product schema files still proven managed (recorded digest or current\n * asset bytes); hand-edited managed files are preserved and reported in `preserved`, and with\n * no recorded product schema nothing under openspec/schemas is touched.\n * - Deletes `.product/integrations/openspec.json`.\n * - `--dry-run` reports what would be removed and preserved without writing.\n */\nexport async function removeOpenSpecIntegration(\n root: string,\n options: { dryRun?: boolean } = {},\n): Promise<{ removed: string[]; preserved: string[] }> {\n const { dryRun = false } = options;\n const removed: string[] = [];\n const preserved: string[] = [];\n\n // Remove both what the metadata recorded as injected and what the current guidance would\n // inject: the union covers metadata written before the record existed as well as entries left\n // behind by an older ProductShape.\n const previousMeta = await readMeta(root);\n const recorded = previousMeta?.managed;\n const current = currentManagedStrings();\n const managedRules: Record<string, string[]> = {};\n for (const key of new Set([\n ...Object.keys(current.rules),\n ...Object.keys(recorded?.rules ?? {}),\n ])) {\n managedRules[key] = [\n ...new Set([...(current.rules[key] ?? []), ...(recorded?.rules[key] ?? [])]),\n ];\n }\n const managedOperations: Record<string, string[]> = {};\n for (const key of new Set([\n ...Object.keys(current.operations),\n ...Object.keys(recorded?.operations ?? {}),\n ])) {\n managedOperations[key] = [\n ...new Set([...(current.operations[key] ?? []), ...(recorded?.operations[key] ?? [])]),\n ];\n }\n\n const config = await readOpenSpecConfig(root);\n if (config !== null) {\n let configChanged = false;\n\n // Remove PDaC context block.\n const context =\n typeof config['context'] === 'string' ? (config['context'] as string) : undefined;\n if (context) {\n const pdacBlock = extractPdacContext(context);\n if (pdacBlock !== null) {\n const cleaned = context\n .replace(pdacBlock, '')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trimEnd();\n if (cleaned === '') {\n delete config['context'];\n } else {\n config['context'] = cleaned;\n }\n configChanged = true;\n }\n }\n\n // Remove PDaC rules from each artifact.\n const rules =\n config['rules'] && typeof config['rules'] === 'object' && !Array.isArray(config['rules'])\n ? (config['rules'] as Record<string, unknown>)\n : null;\n if (rules) {\n let rulesChanged = false;\n for (const [artifact, pdacRules] of Object.entries(managedRules)) {\n if (!Array.isArray(rules[artifact])) continue;\n const list = rules[artifact] as string[];\n const filtered = list.filter((r) => !pdacRules.includes(r));\n if (filtered.length !== list.length) {\n if (filtered.length === 0) {\n delete rules[artifact];\n } else {\n rules[artifact] = filtered;\n }\n rulesChanged = true;\n }\n }\n if (rulesChanged) {\n if (Object.keys(rules).length === 0) {\n delete config['rules'];\n }\n configChanged = true;\n }\n }\n\n // Remove PDaC operation guidance.\n const operations =\n config['operations'] &&\n typeof config['operations'] === 'object' &&\n !Array.isArray(config['operations'])\n ? (config['operations'] as Record<string, unknown>)\n : null;\n if (operations) {\n let opsChanged = false;\n for (const opKey of ['apply', 'archive'] as const) {\n const pdacGuidance = managedOperations[opKey] ?? [];\n const op =\n operations[opKey] &&\n typeof operations[opKey] === 'object' &&\n !Array.isArray(operations[opKey])\n ? (operations[opKey] as Record<string, unknown>)\n : null;\n if (!op || !Array.isArray(op['guidance'])) continue;\n const list = op['guidance'] as string[];\n const filtered = list.filter((g) => !pdacGuidance.includes(g));\n if (filtered.length !== list.length) {\n if (filtered.length === 0) {\n delete op['guidance'];\n } else {\n op['guidance'] = filtered;\n }\n opsChanged = true;\n }\n if (Object.keys(op).length === 0) {\n delete operations[opKey];\n }\n }\n if (opsChanged) {\n if (Object.keys(operations).length === 0) {\n delete config['operations'];\n }\n configChanged = true;\n }\n }\n\n if (configChanged && !dryRun) {\n const serialized = serializeConfig(config);\n await writeFile(configPath(root), serialized, 'utf8');\n removed.push(CONFIG_RELATIVE);\n } else if (configChanged && dryRun) {\n removed.push(CONFIG_RELATIVE);\n }\n }\n\n // Remove the installed CI example.\n if (await pathExists(ciExamplePath(root))) {\n if (!dryRun) {\n await rm(ciExamplePath(root), { force: true });\n }\n removed.push(CI_EXAMPLE_RELATIVE);\n }\n\n // Remove the installed product schema, deleting only paths with a per-file ownership record and\n // bytes still proven managed by the recorded digest (or, for a legacy path-only record, the\n // current asset). A diverged or unrecorded file is preserved and reported, and with no recorded\n // product schema at all nothing under openspec/schemas is touched. Directories are pruned only\n // when the removal emptied them, so adjacent schemas and user files inside ours survive.\n const recordedSchema = recordedProductSchemaFiles(previousMeta);\n if (recordedSchema !== undefined) {\n const assetContentByRelative = new Map(\n (await loadProductChangeSchemaAssets()).map((asset) => [\n `${OPENSPEC_PRODUCT_CHANGE_SCHEMA_RELATIVE}/${asset.relative}`,\n asset.content,\n ]),\n );\n const schemaFileSet = Object.keys(recordedSchema).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));\n const pruneCandidates = new Set<string>();\n for (const relative of schemaFileSet) {\n const absolute = resolveInRepository(root, relative, 'the OpenSpec integration');\n if (!(await pathExists(absolute))) continue;\n const existing = await readFile(absolute, 'utf8');\n const recordedDigest = recordedSchema[relative];\n const proven =\n existing === assetContentByRelative.get(relative) ||\n (recordedDigest !== undefined &&\n recordedDigest !== '' &&\n contentDigest(existing) === recordedDigest);\n if (!proven) {\n preserved.push(relative);\n continue;\n }\n if (!dryRun) {\n await rm(absolute, { force: true });\n let parent = dirname(relative);\n while (\n parent.startsWith('openspec/schemas') &&\n parent !== 'openspec/schemas' &&\n parent !== 'openspec'\n ) {\n pruneCandidates.add(parent);\n parent = parent.split('/').slice(0, -1).join('/');\n }\n pruneCandidates.add('openspec/schemas');\n }\n removed.push(relative);\n }\n // A schema-level record is not ownership of every current asset path. If a path is absent\n // from the per-file record, preserve and report it even when its bytes equal today's asset.\n for (const relative of [...assetContentByRelative.keys()].sort((a, b) =>\n a < b ? -1 : a > b ? 1 : 0,\n )) {\n if (recordedSchema[relative] !== undefined) continue;\n const absolute = resolveInRepository(root, relative, 'the OpenSpec integration');\n if (await pathExists(absolute)) preserved.push(relative);\n }\n if (!dryRun) {\n // Deepest first, so a directory is only considered after its children were.\n const ordered = [...pruneCandidates].sort((a, b) => b.length - a.length);\n for (const relative of ordered) {\n const absolute = resolveInRepository(root, relative, 'the OpenSpec integration');\n try {\n const entries = await readdir(absolute);\n if (entries.length === 0) await rmdir(absolute);\n } catch (error) {\n if (!isNotFound(error)) throw error;\n }\n }\n }\n }\n\n const recordedRecovery = recordedRecoverySchemaFiles(previousMeta);\n if (recordedRecovery !== undefined) {\n const recoveryAssets = new Map(\n (await loadProductRecoverySchemaAssets()).map((asset) => [\n `${OPENSPEC_PRODUCT_RECOVERY_SCHEMA_RELATIVE}/${asset.relative}`,\n asset.content,\n ]),\n );\n for (const relative of Object.keys(recordedRecovery).sort()) {\n const absolute = resolveInRepository(root, relative, 'the OpenSpec integration');\n if (!(await pathExists(absolute))) continue;\n const existing = await readFile(absolute, 'utf8');\n const recordedDigest = recordedRecovery[relative];\n const proven =\n existing === recoveryAssets.get(relative) ||\n (recordedDigest !== '' && contentDigest(existing) === recordedDigest);\n if (!proven) {\n preserved.push(relative);\n } else {\n if (!dryRun) await rm(absolute, { force: true });\n removed.push(relative);\n }\n }\n if (!dryRun) {\n for (const relative of [\n `${OPENSPEC_PRODUCT_RECOVERY_SCHEMA_RELATIVE}/templates`,\n OPENSPEC_PRODUCT_RECOVERY_SCHEMA_RELATIVE,\n 'openspec/schemas',\n ]) {\n const absolute = resolveInRepository(root, relative, 'the OpenSpec integration');\n try {\n if ((await readdir(absolute)).length === 0) await rmdir(absolute);\n } catch (error) {\n if (!isNotFound(error)) throw error;\n }\n }\n }\n }\n\n // Remove metadata file.\n if (await pathExists(metaPath(root))) {\n if (!dryRun) {\n await rm(metaPath(root), { force: true });\n }\n removed.push(META_RELATIVE);\n }\n\n return { removed, preserved };\n}\n","/**\n * The OpenSpec `product-change` schema shipped by this integration.\n *\n * The schema is a set of static assets (schema.yaml, templates, bridge scripts) bundled with the\n * package and installed into a consumer repository at `openspec/schemas/product-change/`. OpenSpec reads\n * project-local schemas from that directory natively; installing files there uses the framework's\n * official extension surface, exactly as merging guidance into `openspec/config.yaml` does.\n *\n * The assets are data. Loading them performs no repository writes; installation, verification and\n * removal live in `index.ts` beside the rest of the managed-surface lifecycle.\n */\nimport { readdir, readFile } from 'node:fs/promises';\nimport { fileURLToPath } from 'node:url';\nimport { join } from 'node:path';\n\n/** The OpenSpec schema name (the directory key OpenSpec resolves). */\nexport const OPENSPEC_PRODUCT_CHANGE_SCHEMA_NAME = 'product-change';\n\n/** The repository-relative directory the product schema is installed into. */\nexport const OPENSPEC_PRODUCT_CHANGE_SCHEMA_RELATIVE = 'openspec/schemas/product-change';\n\n/**\n * The minimum OpenSpec CLI version the product workflow needs. Project-local schemas are far\n * older, but three 1.7.0 behaviours are load-bearing: `openspec new change` records\n * `skip_specs: true` for schemas with no specs artifact (so `openspec validate` stops demanding\n * delta specs), artifacts follow schema declaration order, and the generated skills stop\n * overriding custom-schema instructions with spec-driven patterns. Below this floor the schema\n * files are inert data: the citation lane keeps working and the product workflow is reported\n * unavailable, never broken.\n */\nexport const PRODUCT_CHANGE_SCHEMA_MIN_OPENSPEC = '1.7.0';\n\n/** The separate hosted brownfield recovery workload. */\nexport const OPENSPEC_PRODUCT_RECOVERY_SCHEMA_NAME = 'product-recovery';\nexport const OPENSPEC_PRODUCT_RECOVERY_SCHEMA_RELATIVE = 'openspec/schemas/product-recovery';\n\n/** One bundled product-schema file: its path relative to the installed schema directory. */\nexport interface ProductChangeSchemaAsset {\n relative: string;\n content: string;\n}\n\n/** The bundled asset directory, resolved from this module (both src/ and dist/ sit one level below the package root). */\nfunction assetsDir(): string {\n return fileURLToPath(new URL('../assets/openspec-product-change-schema/', import.meta.url));\n}\n\nasync function collectFiles(dir: string, prefix: string): Promise<string[]> {\n const entries = await readdir(dir, { withFileTypes: true });\n const files: string[] = [];\n for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {\n const relative = prefix === '' ? entry.name : `${prefix}/${entry.name}`;\n if (entry.isDirectory()) {\n files.push(...(await collectFiles(join(dir, entry.name), relative)));\n } else if (entry.isFile()) {\n files.push(relative);\n }\n }\n return files;\n}\n\n/**\n * Load the bundled product-schema assets, sorted by relative path so installation, metadata and\n * comparison all see one deterministic order.\n */\nexport async function loadProductChangeSchemaAssets(): Promise<ProductChangeSchemaAsset[]> {\n const dir = assetsDir();\n const relatives = await collectFiles(dir, '');\n const assets: ProductChangeSchemaAsset[] = [];\n for (const relative of relatives.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))) {\n assets.push({\n relative,\n content: await readFile(join(dir, ...relative.split('/')), 'utf8'),\n });\n }\n return assets;\n}\n\n/** Load the bundled recovery schema assets using the same deterministic asset contract. */\nexport async function loadProductRecoverySchemaAssets(): Promise<ProductChangeSchemaAsset[]> {\n const dir = fileURLToPath(\n new URL('../assets/openspec-product-recovery-schema/', import.meta.url),\n );\n const relatives = await collectFiles(dir, '');\n return Promise.all(\n relatives\n .sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))\n .map(async (relative) => ({\n relative,\n content: await readFile(join(dir, ...relative.split('/')), 'utf8'),\n })),\n );\n}\n","import crossSpawn from 'cross-spawn';\n\nexport interface CommandResult {\n stdout: string;\n stderr: string;\n}\n\nexport interface RunCommandOptions {\n cwd?: string;\n env?: NodeJS.ProcessEnv;\n maxBuffer?: number;\n}\n\nfunction renderCommand(command: string, args: string[]): string {\n return [command, ...args]\n .map((value) => (/^[A-Za-z0-9_./:@=+-]+$/.test(value) ? value : JSON.stringify(value)))\n .join(' ');\n}\n\n/**\n * Run one executable with an argument array and no shell interpolation.\n *\n * cross-spawn resolves Windows command shims and escapes their arguments while preserving native\n * executable spawning on POSIX. This keeps repository-local node_modules/.bin commands portable\n * without passing an argument array through `shell: true`.\n */\nexport function runCommand(\n command: string,\n args: string[],\n options: RunCommandOptions = {},\n): Promise<CommandResult> {\n return new Promise((resolve, reject) => {\n const maxBuffer = options.maxBuffer ?? 10 * 1024 * 1024;\n const child = crossSpawn(command, args, {\n cwd: options.cwd,\n env: options.env,\n stdio: ['ignore', 'pipe', 'pipe'],\n windowsHide: true,\n });\n let stdout = '';\n let stderr = '';\n let settled = false;\n\n const fail = (error: Error): void => {\n if (settled) return;\n settled = true;\n reject(error);\n };\n const append = (stream: 'stdout' | 'stderr', chunk: string): void => {\n if (settled) return;\n if (stream === 'stdout') stdout += chunk;\n else stderr += chunk;\n if (Buffer.byteLength(stdout) + Buffer.byteLength(stderr) > maxBuffer) {\n child.kill();\n fail(\n new Error(`${renderCommand(command, args)} exceeded its ${maxBuffer}-byte output limit`),\n );\n }\n };\n\n child.stdout?.setEncoding('utf8');\n child.stderr?.setEncoding('utf8');\n child.stdout?.on('data', (chunk: string) => append('stdout', chunk));\n child.stderr?.on('data', (chunk: string) => append('stderr', chunk));\n child.once('error', (error) => {\n fail(new Error(`Could not start ${renderCommand(command, args)}: ${error.message}`));\n });\n child.once('close', (code, signal) => {\n if (settled) return;\n if (code === 0) {\n settled = true;\n resolve({ stdout, stderr });\n return;\n }\n const status = code === null ? `signal ${signal ?? 'unknown'}` : `exit ${code}`;\n const detail = stderr.trim() || stdout.trim();\n fail(\n new Error(\n `${renderCommand(command, args)} failed with ${status}${detail ? `: ${detail}` : ''}`,\n ),\n );\n });\n });\n}\n","/**\n * Shared workspace helpers for the OpenSpec integration: workspace detection and the\n * child-process environment that lets a devDependency-installed OpenSpec CLI count as installed.\n */\nimport { access } from 'node:fs/promises';\nimport { join } from 'node:path';\n\n/** Check whether a filesystem path exists. */\nexport async function pathExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Check if an OpenSpec workspace exists at the given root (openspec/ directory present). */\nexport async function isOpenSpecWorkspace(root: string): Promise<boolean> {\n return pathExists(join(root, 'openspec'));\n}\n\n/**\n * Build a child-process environment whose PATH starts with the repository's node_modules/.bin,\n * so an OpenSpec installed as a devDependency counts as installed. Windows spells the variable\n * `Path` (and env objects lose the case-insensitive lookup once spread), so every casing is\n * collapsed into a single `PATH` entry.\n */\nexport function envWithLocalBin(root: string): NodeJS.ProcessEnv {\n const env: NodeJS.ProcessEnv = { ...process.env };\n const separator = process.platform === 'win32' ? ';' : ':';\n let existing = '';\n for (const key of Object.keys(env)) {\n if (key.toUpperCase() === 'PATH') {\n existing = env[key] ?? '';\n delete env[key];\n }\n }\n const localBin = join(root, 'node_modules', '.bin');\n env['PATH'] = existing ? `${localBin}${separator}${existing}` : localBin;\n return env;\n}\n","/**\n * OpenSpec consumer-document enumeration: the OpenSpec implementation of the framework-neutral\n * SDD integration-provider contract defined in @prodshape/core.\n *\n * Everything OpenSpec-specific about verification lives here — the `openspec/` layout, the\n * changes/archive lifecycle split, the artifact-file conventions, and the `openspec` CLI — so\n * the core package stays SDD-framework blind. The provider only enumerates the expected current\n * consumer-document population; scope classification and citation verification are core\n * responsibilities that operate on the enumeration.\n */\nimport { readFileSync } from 'node:fs';\nimport { readdir, readFile } from 'node:fs/promises';\nimport { join, relative, sep } from 'node:path';\nimport fg from 'fast-glob';\nimport { codes } from '@prodshape/core';\nimport type {\n ConsumerDocument,\n ConsumerDocumentEnumeration,\n Diagnostic,\n EnumerateConsumerDocumentsOptions,\n SddIntegrationProvider,\n} from '@prodshape/core';\nimport { runCommand } from './process.js';\nimport { envWithLocalBin, isOpenSpecWorkspace, pathExists } from './workspace.js';\n\n/**\n * The artifact files that make up an OpenSpec change, relative to the change directory.\n * `specs/` is a directory containing one or more `<capability>/spec.md` files.\n */\nconst CHANGE_ARTIFACT_FILES: Array<{ file: string; kind: string }> = [\n { file: 'proposal.md', kind: 'proposal' },\n { file: 'design.md', kind: 'design' },\n { file: 'tasks.md', kind: 'tasks' },\n];\n\n/** Run `openspec` with the given args at the repository root, returning stdout. */\nasync function runOpenSpec(\n repoRoot: string,\n args: string[],\n): Promise<{ stdout: string; stderr: string }> {\n return runCommand('openspec', args, {\n cwd: repoRoot,\n env: envWithLocalBin(repoRoot),\n maxBuffer: 10 * 1024 * 1024,\n });\n}\n\n/** Parsed entry from `openspec list --json`. */\ninterface OpenSpecListEntry {\n name: string;\n}\n\n/**\n * Call `openspec list --json` and return the change names. Returns `null` when the CLI is\n * unavailable or the output is unparseable, so the caller can fall back to filesystem scanning.\n */\nasync function openspecListChanges(repoRoot: string): Promise<string[] | null> {\n try {\n const { stdout } = await runOpenSpec(repoRoot, ['list', '--json']);\n const parsed = JSON.parse(stdout) as { changes?: OpenSpecListEntry[] };\n if (!Array.isArray(parsed.changes)) return null;\n return parsed.changes.map((c) => c.name).filter((n): n is string => typeof n === 'string');\n } catch {\n return null;\n }\n}\n\n/** Check whether `openspec` is runnable (PATH or node_modules/.bin) with a parseable version. */\nexport async function isOpenSpecCliAvailable(repoRoot?: string): Promise<boolean> {\n try {\n const { stdout } = await runCommand('openspec', ['--version'], {\n ...(repoRoot ? { cwd: repoRoot, env: envWithLocalBin(repoRoot) } : {}),\n });\n return /\\d+\\.\\d+\\.\\d+/.test(stdout);\n } catch {\n return false;\n }\n}\n\n/** Discover all `.md` files under a directory, returning absolute paths sorted. */\nasync function discoverMarkdownFiles(dir: string): Promise<string[]> {\n const entries = await fg('**/*.md', {\n cwd: dir,\n absolute: true,\n dot: false,\n ignore: ['**/node_modules/**'],\n });\n return entries.map((e) => e.split('/').join(sep)).sort();\n}\n\n/** Build a {@link ConsumerDocument} from an absolute path. */\nfunction buildConsumerDocument(\n absolutePath: string,\n repoRoot: string,\n opts: { change?: string; archived: boolean; artifactKind?: string },\n): ConsumerDocument {\n return {\n path: relative(repoRoot, absolutePath).split(sep).join('/'),\n absolutePath,\n change: opts.change,\n archived: opts.archived,\n artifactKind: opts.artifactKind,\n };\n}\n\n/** Discover artifact files for a single change directory. */\nasync function discoverChangeDocuments(\n changeDir: string,\n repoRoot: string,\n change: string,\n archived: boolean,\n): Promise<ConsumerDocument[]> {\n const docs: ConsumerDocument[] = [];\n\n for (const { file, kind } of CHANGE_ARTIFACT_FILES) {\n const absolutePath = join(changeDir, file);\n try {\n await readFile(absolutePath);\n docs.push(\n buildConsumerDocument(absolutePath, repoRoot, { change, archived, artifactKind: kind }),\n );\n } catch {\n // File doesn't exist; skip.\n }\n }\n\n const specsDir = join(changeDir, 'specs');\n try {\n const specFiles = await discoverMarkdownFiles(specsDir);\n for (const absolutePath of specFiles) {\n docs.push(\n buildConsumerDocument(absolutePath, repoRoot, { change, archived, artifactKind: 'specs' }),\n );\n }\n } catch {\n // No specs/ directory; skip.\n }\n\n return docs;\n}\n\n/** Discover all `.md` files under `openspec/specs/` (the current specs, not changes). */\nasync function discoverSpecDocuments(\n specsDir: string,\n repoRoot: string,\n): Promise<ConsumerDocument[]> {\n const docs: ConsumerDocument[] = [];\n try {\n const specFiles = await discoverMarkdownFiles(specsDir);\n for (const absolutePath of specFiles) {\n docs.push(\n buildConsumerDocument(absolutePath, repoRoot, { archived: false, artifactKind: 'spec' }),\n );\n }\n } catch {\n // No specs/ directory; skip.\n }\n return docs;\n}\n\n/** Discover archived change directories under `openspec/changes/archive/`. */\nasync function discoverArchivedChanges(archiveDir: string): Promise<string[]> {\n try {\n const entries = await readdir(archiveDir, { withFileTypes: true });\n return entries\n .filter((e) => e.isDirectory())\n .map((e) => e.name)\n .sort();\n } catch {\n return [];\n }\n}\n\n/**\n * Enumerate the consumer-document population of an OpenSpec workspace.\n *\n * Uses `openspec list --json` to determine the current (non-archived) changes. The OpenSpec CLI\n * excludes `openspec/changes/archive/` from `list`, so the default population is current-only.\n * When `includeArchived` is true, archived changes under `openspec/changes/archive/` are also\n * scanned via the filesystem (the CLI cannot address archived changes by name).\n *\n * For each change, the artifact files are resolved by filesystem convention: `proposal.md`,\n * `specs/` (one or more spec files), `design.md`, `tasks.md`. The `openspec show --json` output\n * does not include artifact paths, so convention-based resolution is the stable contract.\n *\n * For `openspec/specs/` (the current specs, not changes), all `.md` files are discovered.\n *\n * If the OpenSpec CLI is not runnable, enumeration falls back to filesystem scanning of\n * `openspec/changes/` (excluding `archive/` unless `includeArchived`) and reports PRODUCT069:\n * the population then rests on layout convention alone, which the gate must surface rather than\n * silently trust.\n */\nexport async function enumerateOpenSpecDocuments(\n repoRoot: string,\n options?: EnumerateConsumerDocumentsOptions,\n): Promise<ConsumerDocumentEnumeration> {\n const includeArchived = options?.includeArchived ?? false;\n const root = join(repoRoot, 'openspec');\n const changesDir = join(root, 'changes');\n const archiveDir = join(changesDir, 'archive');\n const specsDir = join(root, 'specs');\n\n const documents: ConsumerDocument[] = [];\n const diagnostics: Diagnostic[] = [];\n\n // A workspace with no `openspec/changes/` directory at all (specs only) has zero changes by\n // construction: nothing to discover, no CLI needed, and no PRODUCT069 — older OpenSpec CLIs\n // (1.3.x) even reject `list` outright in such a workspace. Otherwise prefer\n // `openspec list --json` and fall back to filesystem scanning.\n let currentChangeNames: string[] | null = (await pathExists(changesDir))\n ? await openspecListChanges(repoRoot)\n : [];\n\n if (currentChangeNames === null) {\n diagnostics.push({\n severity: 'error',\n code: codes.openSpecCliMissing,\n message: `OpenSpec CLI not available; the document population was enumerated by filesystem convention instead of 'openspec list'. Install with 'npm install -g @fission-ai/openspec@1' for authoritative population discovery.`,\n file: 'openspec/',\n });\n // archive/ is excluded here: archived changes are enumerated separately below.\n try {\n const entries = await readdir(changesDir, { withFileTypes: true });\n currentChangeNames = entries\n .filter((e) => e.isDirectory() && e.name !== 'archive')\n .map((e) => e.name)\n .sort();\n } catch {\n currentChangeNames = [];\n }\n }\n\n if (options?.change) {\n const wanted = options.change;\n if (currentChangeNames.includes(wanted)) {\n currentChangeNames = [wanted];\n } else {\n // The requested change isn't in the current list. Check the archive below if asked.\n currentChangeNames = [];\n }\n }\n\n for (const name of currentChangeNames) {\n const changeDir = join(changesDir, name);\n documents.push(...(await discoverChangeDocuments(changeDir, repoRoot, name, false)));\n }\n\n if (includeArchived) {\n const archivedNames = await discoverArchivedChanges(archiveDir);\n for (const name of archivedNames) {\n // If a specific change was requested and it wasn't current, check the archive.\n if (options?.change && name !== options.change) continue;\n const changeDir = join(archiveDir, name);\n documents.push(...(await discoverChangeDocuments(changeDir, repoRoot, name, true)));\n }\n }\n\n documents.push(...(await discoverSpecDocuments(specsDir, repoRoot)));\n\n return {\n documents,\n root: 'openspec',\n includeArchived,\n diagnostics,\n };\n}\n\n/** The integration version, read once from this package's own manifest. */\nconst integrationVersion = (\n JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')) as {\n version: string;\n }\n).version;\n\n/**\n * The OpenSpec implementation of the SDD integration-provider contract.\n * `prodshape citations verify --provider openspec` verifies the population this enumerates.\n */\nexport const openSpecProvider: SddIntegrationProvider = {\n name: 'openspec',\n version: integrationVersion,\n detectWorkspace: isOpenSpecWorkspace,\n enumerateDocuments: enumerateOpenSpecDocuments,\n};\n","/**\n * The deterministic rails of the OpenSpec-hosted PDaC product workflow.\n *\n * An OpenSpec change created with the `product-change` schema hosts a PDaC Product Change at\n * `openspec/changes/<name>/product-change/` (change.md plus proposed/**, the normative delta format).\n * OpenSpec owns the workflow orchestration (`/opsx:new`, `/opsx:continue`, `/opsx:apply`,\n * `/opsx:archive`); this module owns what OpenSpec cannot provide: reading the accepted model,\n * compiling the semantic graph in memory, validating the hosted delta as an overlay on the\n * untouched baseline, applying a valid delta deterministically, and deriving fresh context for a\n * later delivery workflow.\n *\n * Two boundaries are deliberate:\n * - Validation is mandatory and happens AT APPLY TIME: `applyOpenSpecProductChange` performs its\n * own full validation immediately before planning, so a stale earlier validate call can never\n * authorise a write (no time-of-check to time-of-use gap). `validateOpenSpecProductChange`\n * exists for explicit preflight, wrappers, diagnostics and tests; it is never a precondition\n * token.\n * - Authorisation policy stays outside this deterministic mechanism: `status: approved` in the\n * hosted change.md is the apply-authorised protocol state (PRODUCT028 refuses anything else).\n * ProductShape's accepted policy keeps product approval human; this rail verifies the recorded\n * state but cannot identify or judge the actor, and nothing here performs the transition.\n *\n * Everything deterministic comes from `@prodshape/core`. The one lifecycle divergence from the\n * native Product Change flow is the container: the hosted change directory belongs to OpenSpec,\n * so the plan's `move-change` action (ProductShape's `changes/active -> changes/completed`\n * archive move) is filtered out and `openspec archive` moves the container instead, as a separate\n * action after apply. `planApply`'s contract sanctions callers filtering the returned action set.\n */\nimport { readdir, readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { parse } from 'yaml';\nimport {\n analyzeImpact,\n applyHostedProductChange,\n assessHostedProductChange,\n compareCodePoints,\n isNotFound,\n loadChange,\n loadLiveChanges,\n openRepository,\n sortDiagnostics,\n validateBaseline,\n} from '@prodshape/core';\nimport type {\n BaselineValidation,\n Diagnostic,\n HostedProductApplyOutcome,\n HostedProductApplyResult,\n ImpactReport,\n LoadedArtifact,\n LoadedChange,\n ProductGraph,\n ProductRepository,\n} from '@prodshape/core';\nimport { OPENSPEC_PRODUCT_CHANGE_SCHEMA_NAME } from './product-change-schema.js';\nimport { pathExists } from './workspace.js';\n\n/** The subdirectory of an OpenSpec change that hosts the PDaC Product Change delta. */\nexport const OPENSPEC_PRODUCT_SUBDIR = 'product-change';\n\n/** OpenSpec 1.11's change-id grammar (`isKebabId`): one folder-safe path segment. */\nconst OPENSPEC_CHANGE_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;\n\nfunction assertOpenSpecChangeName(changeName: string): void {\n if (changeName.length > 200 || !OPENSPEC_CHANGE_NAME.test(changeName)) {\n throw new Error(\n `OpenSpec change '${changeName}' is not a valid OpenSpec change name; use kebab-case with lowercase letters, numbers and single hyphen separators.`,\n );\n }\n}\n\n/** The accepted product model, read directly from disk and validated, with its in-memory graph. */\nexport interface ProductModelInspection {\n repo: ProductRepository;\n artifacts: LoadedArtifact[];\n graph: ProductGraph;\n /** Configuration diagnostics plus full baseline validation, sorted. */\n diagnostics: Diagnostic[];\n}\n\n/**\n * Read the accepted model at the repository's product root (default docs/product), compile the\n * semantic graph in memory and validate. Needs no `.product/generated` output and no\n * configuration file: kernel defaults apply when `.product/config.yaml` is absent.\n */\nexport async function inspectProductModel(root: string): Promise<ProductModelInspection> {\n const repo = await openRepository(root);\n const baseline = await validateBaseline(repo);\n return {\n repo,\n artifacts: baseline.artifacts,\n graph: baseline.graph,\n diagnostics: sortDiagnostics([...repo.configDiagnostics, ...baseline.diagnostics]),\n };\n}\n\n/** One OpenSpec change hosting a PDaC Product Change delta. */\nexport interface OpenSpecProductChangeRef {\n /** The OpenSpec change name (the directory under openspec/changes/). */\n name: string;\n /** Absolute path of the hosted PDaC change directory (…/<name>/product-change). */\n dir: string;\n /** Repository-relative POSIX path of the hosted change.md. */\n file: string;\n}\n\n/** The result of reading a change's `.openspec.yaml` schema pin. */\ntype HostedSchemaPin =\n { schema: string } | { problem: 'missing' } | { problem: 'malformed'; detail: string };\n\nconst OPEN_SPEC_KEBAB_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;\n\n/** Validate the OpenSpec 1.11 ChangeMetadataSchema fields without taking a runtime dependency. */\nfunction openSpecChangeMetadataProblem(parsed: unknown): string | undefined {\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return 'the document must be an object';\n }\n const record = parsed as Record<string, unknown>;\n if (typeof record['schema'] !== 'string' || record['schema'].length === 0) {\n return 'schema is required';\n }\n const created = record['created'];\n if (\n created !== undefined &&\n (typeof created !== 'string' || !/^\\d{4}-\\d{2}-\\d{2}$/u.test(created))\n ) {\n return 'created must be YYYY-MM-DD format';\n }\n const goal = record['goal'];\n if (goal !== undefined && (typeof goal !== 'string' || goal.length === 0)) {\n return 'goal must be a non-empty string';\n }\n const affectedAreas = record['affected_areas'];\n if (\n affectedAreas !== undefined &&\n (!Array.isArray(affectedAreas) ||\n !affectedAreas.every((area) => typeof area === 'string' && area.length > 0))\n ) {\n return 'affected_areas must be an array of non-empty strings';\n }\n const initiative = record['initiative'];\n if (initiative !== undefined) {\n if (initiative === null || typeof initiative !== 'object' || Array.isArray(initiative)) {\n return 'initiative must be an object';\n }\n const link = initiative as Record<string, unknown>;\n if (\n Object.keys(link).some((key) => key !== 'store' && key !== 'id') ||\n typeof link['store'] !== 'string' ||\n !OPEN_SPEC_KEBAB_ID.test(link['store']) ||\n typeof link['id'] !== 'string' ||\n !OPEN_SPEC_KEBAB_ID.test(link['id'])\n ) {\n return 'initiative must contain only kebab-case store and id fields';\n }\n }\n if (record['skip_specs'] !== undefined && typeof record['skip_specs'] !== 'boolean') {\n return 'skip_specs must be a boolean';\n }\n if (\n record['retire_capabilities'] !== undefined &&\n typeof record['retire_capabilities'] !== 'boolean'\n ) {\n return 'retire_capabilities must be a boolean';\n }\n return undefined;\n}\n\n/**\n * Read the schema a change is pinned to. OpenSpec records it in the change's `.openspec.yaml`\n * (`openspec new change <name> --schema product-change` writes it), and the hosted product rail treats\n * that pin as load-bearing: OpenSpec selects the workflow, so a change is a product change\n * because its container says so, never because a `product/` directory happens to exist.\n */\nasync function readHostedSchemaPin(changeDir: string): Promise<HostedSchemaPin> {\n const metadataPath = join(changeDir, '.openspec.yaml');\n if (!(await pathExists(metadataPath))) return { problem: 'missing' };\n let parsed: unknown;\n try {\n parsed = parse(await readFile(metadataPath, 'utf8'));\n } catch (error) {\n return {\n problem: 'malformed',\n detail: error instanceof Error ? error.message : String(error),\n };\n }\n const problem = openSpecChangeMetadataProblem(parsed);\n if (problem !== undefined) return { problem: 'malformed', detail: problem };\n return { schema: (parsed as Record<string, string>)['schema']! };\n}\n\nfunction hostedSchemaPinError(\n changeName: string,\n pin: Exclude<HostedSchemaPin, { schema: string }>,\n): Error {\n if (pin.problem === 'missing') {\n return new Error(\n `OpenSpec change '${changeName}' has no .openspec.yaml, so its schema pin is unknown; the hosted product rail refuses it. Create product changes with: openspec new change ${changeName} --schema ${OPENSPEC_PRODUCT_CHANGE_SCHEMA_NAME}.`,\n );\n }\n return new Error(\n `OpenSpec change '${changeName}' has an unreadable .openspec.yaml (${pin.detail}); the hosted product rail refuses it until the metadata is valid under OpenSpec 1.11.`,\n );\n}\n\n/**\n * List the OpenSpec changes that host a PDaC Product Change: pinned to `schema: product-change` in\n * `.openspec.yaml` AND carrying `product-change/change.md`. The OpenSpec archive is history and is never\n * listed; a change without a product delta (for example one whose honest intent verdict was \"no\n * product delta\") is not a product change; and a change pinned to another schema is not one\n * either, whatever directories it contains, so it never enters the product rail or its\n * concurrency input.\n */\nexport async function listOpenSpecProductChanges(\n root: string,\n): Promise<OpenSpecProductChangeRef[]> {\n const changesDir = join(root, 'openspec', 'changes');\n let entries;\n try {\n entries = await readdir(changesDir, { withFileTypes: true });\n } catch (error) {\n if (isNotFound(error)) return [];\n throw error;\n }\n const names = entries\n .filter((entry) => entry.isDirectory() && entry.name !== 'archive')\n .map((entry) => entry.name)\n .sort(compareCodePoints);\n const refs: OpenSpecProductChangeRef[] = [];\n for (const name of names) {\n const changeDir = join(changesDir, name);\n const dir = join(changeDir, OPENSPEC_PRODUCT_SUBDIR);\n if (!(await pathExists(join(dir, 'change.md')))) continue;\n assertOpenSpecChangeName(name);\n const pin = await readHostedSchemaPin(changeDir);\n if ('problem' in pin) throw hostedSchemaPinError(name, pin);\n if (pin.schema !== OPENSPEC_PRODUCT_CHANGE_SCHEMA_NAME) continue;\n refs.push({\n name,\n dir,\n file: `openspec/changes/${name}/${OPENSPEC_PRODUCT_SUBDIR}/change.md`,\n });\n }\n return refs;\n}\n\n/** Load one hosted Product Change through core's loader (change.md plus proposed/**). */\nexport async function loadOpenSpecProductChange(\n root: string,\n changeName: string,\n): Promise<LoadedChange> {\n const repo = await openRepository(root);\n return loadHostedChange(root, repo, changeName);\n}\n\nasync function loadHostedChange(\n root: string,\n repo: ProductRepository,\n changeName: string,\n): Promise<LoadedChange> {\n assertOpenSpecChangeName(changeName);\n const changesDir = join(root, 'openspec', 'changes');\n const changeDir = join(changesDir, changeName);\n const deltaDir = join(changeDir, OPENSPEC_PRODUCT_SUBDIR);\n const hasDelta =\n changeName !== 'archive' &&\n (await pathExists(changeDir)) &&\n (await pathExists(join(deltaDir, 'change.md')));\n if (!hasDelta) {\n const refs = await listOpenSpecProductChanges(root);\n const known = refs.map((candidate) => candidate.name).join(', ') || 'none';\n throw new Error(\n `No OpenSpec product change named '${changeName}' under openspec/changes/ (a product change hosts product-change/change.md; found: ${known}).`,\n );\n }\n // The schema pin is load-bearing and fails closed: without a readable pin naming the product\n // schema, this change never enters the product rail, however product-shaped its contents look.\n const pin = await readHostedSchemaPin(changeDir);\n if ('problem' in pin) {\n throw hostedSchemaPinError(changeName, pin);\n }\n if (pin.schema !== OPENSPEC_PRODUCT_CHANGE_SCHEMA_NAME) {\n throw new Error(\n `OpenSpec change '${changeName}' is pinned to schema '${pin.schema}', not '${OPENSPEC_PRODUCT_CHANGE_SCHEMA_NAME}'; the hosted product rail refuses it. Product intent travels through a change created with: openspec new change <name> --schema ${OPENSPEC_PRODUCT_CHANGE_SCHEMA_NAME}.`,\n );\n }\n return loadChange(deltaDir, repo.root, repo.registry);\n}\n\nasync function loadAllLiveChanges(root: string, repo: ProductRepository): Promise<LoadedChange[]> {\n const refs = await listOpenSpecProductChanges(root);\n return loadLiveChanges(\n repo,\n refs.map((ref) => ref.dir),\n );\n}\n\n/** The result of validating a hosted Product Change as an overlay on the untouched baseline. */\nexport interface OpenSpecProductChangeValidation {\n change: LoadedChange;\n baseline: BaselineValidation;\n overlayArtifacts: LoadedArtifact[];\n overlayGraph: ProductGraph;\n /** Configuration, baseline and change diagnostics, deduplicated and sorted. */\n diagnostics: Diagnostic[];\n /** The diagnostics that block, under the repository's warnings-as-errors policy. */\n blocking: Diagnostic[];\n}\n\n/**\n * Validate one hosted Product Change: the accepted baseline stays untouched, the delta is\n * compiled over it into an overlay, and the overlay is validated end to end (operation checks,\n * cross-container concurrency, open-questions state, full structural validation with the\n * PRODUCT023/PRODUCT024 overlay recodes). Useful as explicit preflight and for wrappers and\n * tests; apply never trusts it and revalidates on its own.\n */\nexport async function validateOpenSpecProductChange(\n root: string,\n changeName: string,\n): Promise<OpenSpecProductChangeValidation> {\n const repo = await openRepository(root);\n const all = await loadAllLiveChanges(root, repo);\n // An unknown name resolves through the loader, whose error names the known product changes.\n const change =\n all.find((candidate) => hostedName(root, candidate) === changeName) ??\n (await loadHostedChange(root, repo, changeName));\n const assessed = await assessHostedProductChange(repo, change, all);\n return {\n change,\n baseline: assessed.baseline,\n overlayArtifacts: assessed.overlayArtifacts,\n overlayGraph: assessed.overlayGraph,\n diagnostics: assessed.diagnostics,\n blocking: assessed.blocking,\n };\n}\n\n/** The hosted OpenSpec change name of a loaded change, or undefined for a native change. */\nfunction hostedName(root: string, change: LoadedChange): string | undefined {\n const normalizedRoot = root.replaceAll('\\\\', '/');\n const normalizedDir = change.dir.replaceAll('\\\\', '/');\n const relative = normalizedDir.startsWith(normalizedRoot)\n ? normalizedDir.slice(normalizedRoot.length).replace(/^\\//, '')\n : normalizedDir;\n const match = relative.match(/^openspec\\/changes\\/([^/]+)\\/product-change$/);\n return match?.[1];\n}\n\n/** How an apply invocation ended. */\nexport type OpenSpecProductApplyOutcome = HostedProductApplyOutcome;\n\n/** The container move is `openspec archive`, never part of apply. */\nexport type OpenSpecProductApplyResult = HostedProductApplyResult;\n\nexport async function applyOpenSpecProductChange(\n root: string,\n changeName: string,\n options: { dryRun?: boolean } = {},\n): Promise<OpenSpecProductApplyResult> {\n const repo = await openRepository(root);\n const all = await loadAllLiveChanges(root, repo);\n const change =\n all.find((candidate) => hostedName(root, candidate) === changeName) ??\n (await loadHostedChange(root, repo, changeName));\n return applyHostedProductChange({\n repo,\n change,\n liveChanges: all,\n dryRun: options.dryRun ?? false,\n });\n}\n\n/** One artifact carried into delivery context, with the digest of its current accepted content. */\nexport interface DeliveryContextEntry {\n id: string;\n type?: string;\n title?: string;\n status?: string;\n file: string;\n digest: string;\n}\n\n/** Context derived from a fresh read of the accepted model, for a later delivery workflow. */\nexport interface DeliveryContextBundle {\n ids: string[];\n /** The requested artifacts, from the freshly read accepted model. */\n artifacts: DeliveryContextEntry[];\n /** Impact around each requested artifact (direction both, depth 2). */\n impacts: Record<string, ImpactReport>;\n /** Direct neighbours of the requested artifacts, deduplicated, requested ids excluded. */\n neighbours: DeliveryContextEntry[];\n /** Whether the freshly read accepted model validates with zero errors. */\n modelErrorFree: boolean;\n}\n\n/**\n * Derive delivery context from the accepted model as it exists on disk NOW: a fresh read and a\n * fresh graph, never state carried over from a product workflow. This is the handoff boundary a\n * future delivery workflow starts from.\n */\nexport async function deriveDeliveryContext(\n root: string,\n ids: string[],\n): Promise<DeliveryContextBundle> {\n const inspection = await inspectProductModel(root);\n const byId = new Map(\n inspection.artifacts\n .filter((artifact) => artifact.id !== undefined)\n .map((artifact) => [artifact.id as string, artifact]),\n );\n const entryOf = (artifact: LoadedArtifact): DeliveryContextEntry => {\n const entry: DeliveryContextEntry = {\n id: artifact.id as string,\n file: artifact.file,\n digest: artifact.digest,\n };\n if (artifact.type !== undefined) entry.type = artifact.type;\n if (artifact.title !== undefined) entry.title = artifact.title;\n if (artifact.status !== undefined) entry.status = artifact.status;\n return entry;\n };\n\n const sortedIds = [...ids].sort(compareCodePoints);\n const artifacts: DeliveryContextEntry[] = [];\n const impacts: Record<string, ImpactReport> = {};\n const neighbourIds = new Set<string>();\n for (const id of sortedIds) {\n const artifact = byId.get(id);\n if (!artifact) {\n throw new Error(`Unknown artifact id '${id}' in the accepted model at ${root}.`);\n }\n artifacts.push(entryOf(artifact));\n const report = analyzeImpact(inspection.graph, id, { direction: 'both', depth: 2 });\n impacts[id] = report;\n for (const item of report.direct) neighbourIds.add(item.id);\n }\n for (const id of sortedIds) neighbourIds.delete(id);\n const neighbours = [...neighbourIds]\n .sort(compareCodePoints)\n .map((id) => byId.get(id))\n .filter((artifact): artifact is LoadedArtifact => artifact !== undefined)\n .map(entryOf);\n\n return {\n ids: sortedIds,\n artifacts,\n impacts,\n neighbours,\n modelErrorFree: inspection.diagnostics.every((diagnostic) => diagnostic.severity !== 'error'),\n };\n}\n"],"mappings":"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","names":["readdir","readFile","isNotFound","parse","relative","join","readdir","readFile","join","join","readFile","readdir","readdir","readFile","join","join","readFile","readdir","relative","command","readFile","parse","relative","existing","isNotFound","readdir"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/product-change-schema.ts","../src/process.ts","../src/workspace.ts","../src/population.ts","../src/product-workflow.ts"],"sourcesContent":["/**\n * OpenSpec integration for Product Definition as Code, in two lanes.\n *\n * The citation lane configures OpenSpec's spec-driven delivery workflow: PDaC citation rules are\n * merged into `openspec/config.yaml`, metadata is recorded under `.product/integrations/`, and\n * consumer documents cite canonical product artifacts by id + digest for\n * `prodshape citations verify` to check.\n *\n * The product lane hosts the PDaC product workflow inside OpenSpec: the integration installs a\n * project-local `product-change` schema at `openspec/schemas/product-change/`, OpenSpec's official project\n * extension surface, whose changes host a Product Change delta that `product-workflow.ts`\n * validates as an overlay and applies deterministically.\n *\n * Both lanes write only official OpenSpec surfaces plus ProductShape's own integration metadata.\n * The integration never patches OpenSpec-generated commands or skills, never modifies OpenSpec's\n * built-in schemas, and never writes into a native spec-driven change's documents.\n *\n * The merge logic is the heart of the citation lane: it preserves every field the user authored\n * (schema, context, rules, operations, githubCopilot, and anything else) and only appends PDaC\n * guidance, deduplicating identical entries so the operation is idempotent.\n */\nimport { mkdir, readdir, readFile, rm, rmdir, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport {\n contentDigest,\n isNotFound,\n isRepositoryRelativePath,\n resolveInRepository,\n} from '@prodshape/core';\nimport { parse, stringify } from 'yaml';\nimport {\n loadProductChangeSchemaAssets,\n OPENSPEC_PRODUCT_CHANGE_SCHEMA_NAME,\n OPENSPEC_PRODUCT_CHANGE_SCHEMA_RELATIVE,\n PRODUCT_CHANGE_SCHEMA_MIN_OPENSPEC,\n loadProductRecoverySchemaAssets,\n OPENSPEC_PRODUCT_RECOVERY_SCHEMA_NAME,\n OPENSPEC_PRODUCT_RECOVERY_SCHEMA_RELATIVE,\n} from './product-change-schema.js';\nimport { runCommand } from './process.js';\nimport { envWithLocalBin, isOpenSpecWorkspace, pathExists } from './workspace.js';\n\nexport { isOpenSpecWorkspace } from './workspace.js';\nexport {\n enumerateOpenSpecDocuments,\n isOpenSpecCliAvailable,\n openSpecProvider,\n} from './population.js';\nexport {\n loadProductChangeSchemaAssets,\n OPENSPEC_PRODUCT_CHANGE_SCHEMA_NAME,\n OPENSPEC_PRODUCT_CHANGE_SCHEMA_RELATIVE,\n PRODUCT_CHANGE_SCHEMA_MIN_OPENSPEC,\n loadProductRecoverySchemaAssets,\n OPENSPEC_PRODUCT_RECOVERY_SCHEMA_NAME,\n OPENSPEC_PRODUCT_RECOVERY_SCHEMA_RELATIVE,\n} from './product-change-schema.js';\nexport type { ProductChangeSchemaAsset } from './product-change-schema.js';\nexport {\n applyOpenSpecProductChange,\n deriveDeliveryContext,\n inspectProductModel,\n listOpenSpecProductChanges,\n loadOpenSpecProductChange,\n OPENSPEC_PRODUCT_SUBDIR,\n validateOpenSpecProductChange,\n} from './product-workflow.js';\nexport type {\n DeliveryContextBundle,\n DeliveryContextEntry,\n OpenSpecProductApplyOutcome,\n OpenSpecProductApplyResult,\n OpenSpecProductChangeRef,\n OpenSpecProductChangeValidation,\n ProductModelInspection,\n} from './product-workflow.js';\n\n/** The minimum supported OpenSpec CLI version. */\nexport const MIN_SUPPORTED_OPENSPEC = '1.0.0';\n\n/**\n * The npm spec used when bootstrapping OpenSpec through npx. Pinned to the supported major:\n * MIN_SUPPORTED_OPENSPEC guards the floor, the pin guards the ceiling, so a future OpenSpec 2.x\n * cannot silently change what `init --tools none` does for new adopters.\n */\nexport const OPENSPEC_NPX_SPEC = '@fission-ai/openspec@1';\n\n/**\n * Sentinel markers that bracket the PDaC-injected context block. They let `mergeConfig` find\n * and replace exactly the PDaC portion without touching user-authored context lines.\n */\nconst PDAC_CONTEXT_BEGIN = '<!-- pdac:context begin -->';\nconst PDAC_CONTEXT_END = '<!-- pdac:context end -->';\n\n/** The PDaC authority context block injected into openspec/config.yaml `context:`. */\nexport const PDAC_CONTEXT_BLOCK = `${PDAC_CONTEXT_BEGIN}\nThis repository uses Product Definition as Code (PDaC). The canonical product definition lives in docs/product/model (actors, journeys, use cases, business rules, domain terms, bounded contexts, structured behaviours, and functional, quality and constraint requirements). OpenSpec artifacts consume it and are never a second source of truth.\nA citation binds consumer text to a canonical artifact: it records the artifact id, a sha256 content digest and an optional verification scenario anchor. Verification reports one status per citation: current, stale, tampered or unresolved.\nTo cite: run \\`npx prodshape inspect <ID>\\` to read the current digest, then \\`npx prodshape cite --id <ID> --digest <digest>\\` to emit the canonical payload. Wrap it in the document's native comment (\\`<!-- ... -->\\` in Markdown) on its own line directly under the text it grounds.\nTo find which artifacts a change impacts, compare the change's intent with the whole product definition first, then widen the result with \\`npx prodshape impact <ID>\\`. When the intent contradicts or goes beyond the definition, record that drift in the proposal with the marker \\`<!-- pdac-drift ids=\"...\" summary=\"...\" -->\\` (listed by \\`npx prodshape drift\\`) and let humans decide — never fix it quietly.\nEvery current OpenSpec document (proposal, specs, design, tasks and openspec/specs/) carries exactly one explicit scope declaration: \\`pdac-scope: cited\\` for a bound document that carries at least one citation, or \\`pdac-scope: none\\` with a non-empty human-authored reason (\\`pdac-scope-reason: <why>\\` in frontmatter, or \\`<!-- pdac-scope: none reason=\\\"<why>\\\" -->\\`) for an exempt one. Citations alone never bind: a document without a declaration is unclassified and fails verification, a bound document with zero citations fails, and an exemption without a reason or contradicted by citations fails. Binding and exemption are human declarations: never declare an exemption merely because citations are missing. Verify with \\`npx prodshape citations verify --provider openspec\\`: it enumerates the expected current documents, so zero discovered citations is a set of failures, never a pass. Archived changes are excluded by default; \\`--include-archived\\` also verifies the citations history carries, reported as warnings, because archived history cannot be edited.\nThe accepted Product Definition changes only through a Product Change: validate it as an overlay while the baseline stays untouched, obtain human product approval (represented by status: approved; the integration verifies the state but cannot identify or judge the actor), apply explicitly on a working branch, and let a human accept the resulting baseline by merging its pull request. A Product Change is not a pull request; apply is not acceptance; neither apply nor merge attests implementation, verification, release or deployment. A Product Change lives under docs/product/changes/ or hosted inside a valid OpenSpec change created with the product schema (product-change/change.md plus product-change/proposed/**); its deterministically validated, apply-authorised apply is the only write path into docs/product/model. A spec-driven OpenSpec delivery change never edits docs/product/model directly: it may share a pull request with an applied Product Change or follow later, but it never supplies Product Change status.\n${PDAC_CONTEXT_END}`;\n\n/** The PDaC citation rules injected per artifact into openspec/config.yaml `rules:`. */\nexport const PDAC_RULES: Record<string, string[]> = {\n proposal: [\n 'State which PDaC artifacts this change touches, each with a citation payload emitted by `npx prodshape cite --id <ID> --digest <digest>` and wrapped in the document native comment (`<!-- ... -->` in Markdown). Never write a citation record by hand.',\n 'Find the impacted artifacts as a separate step, before writing any proposal content. Read the whole product definition (docs/product/model by default), compare it with what this change wants — the backlog item behind it, if there is one — and list every artifact the change depends on, alters or contradicts. Then widen the list: run `npx prodshape impact <ID>` on each artifact you found and check its direct and indirect neighbours. Put the resulting list in the proposal, cite every impacted artifact from each document that uses it (proposal, specs, design, tasks), and name any neighbour you checked and left out.',\n 'If this change\\'s goals contradict the product definition, or need behaviour it does not describe, that is product-definition drift. Record it in the proposal under a \\'Product definition drift\\' note naming the artifacts involved, with the marker `<!-- pdac-drift ids=\"<ID>[, <ID>...]\" summary=\"<one line>\" -->` on its own line so `npx prodshape drift` can list it. The decision is human: the developer and the product owner agree whether to propose a Product Change or adjust this change. Never fix drift quietly, drop or weaken a citation to hide it, or write around the conflict.',\n 'If the change implements altered product behaviour, name the Product Change (CHG id) whose applied or accepted artifacts it implements. If no overlay-validated, apply-authorised Product Change exists yet, stop and ask for one instead of proceeding. A spec-driven OpenSpec change is not the Product Change; product intent travels through a Product Change, under docs/product/changes or hosted by a product-change OpenSpec change.',\n 'Every document of this change must end up bound or exempt, each with an explicit declaration: declare `pdac-scope: cited` on a line of its own and cite the canonical text the document depends on, or declare `pdac-scope: none` with a non-empty reason (`pdac-scope-reason: <why>` in frontmatter, or `<!-- pdac-scope: none reason=\"<why>\" -->`) when a human judges the document has no product-semantic dependency. Citations alone never bind, and never declare an exemption just because citations are missing.',\n ],\n specs: [\n 'Every requirement derived from canonical product text MUST carry a citation to every PDaC artifact it derives from — not only the closest one — one citation per line, placed after the requirement text and before the first scenario. Never place citations between the requirement heading and the requirement text; OpenSpec reads the first paragraph as the requirement.',\n 'Cite a specific verification scenario with an anchor when the requirement maps to one (anchor=\"<scenario id>\").',\n 'Never paraphrase canonical text as if it were new; either cite it inline or embed it in a marker-block citation (<!-- pdac:cite ... --> canonical text <!-- /pdac:cite -->).',\n 'Never invent artifact ids or digests. Read them with `npx prodshape inspect <ID>`.',\n ],\n design: [\n 'When a design decision depends on canonical product text, cite the artifact it depends on.',\n ],\n tasks: [\n \"A task that depends on a business rule's parameter cites the rule itself, not only the requirement derived from it: staleness follows the cited artifact's own digest, never its relationships.\",\n 'A task that changes cited behaviour must include a follow-up task to refresh the affected citations.',\n 'Never infer implementation, verification, release or deployment from Product Change status; record that evidence in the delivery workflow.',\n ],\n};\n\n/** The PDaC operation guidance injected into openspec/config.yaml `operations:`. */\nexport const PDAC_OPERATIONS: {\n apply: { guidance: string[] };\n archive: { guidance: string[] };\n} = {\n apply: {\n guidance: [\n 'Before finishing, run `npx prodshape citations verify --provider openspec` and fix every stale, tampered or unresolved citation, every unclassified document, every bound document without citations and every exemption without a reason. Refresh a stale digest by re-running `npx prodshape inspect <ID>`; never delete a citation or declare `pdac-scope: none` to silence a diagnostic.',\n ],\n },\n archive: {\n guidance: [\n 'Run `npx prodshape citations verify --provider openspec` one final time. Archive only when every document is bound or exempt and every citation is current.',\n ],\n },\n};\n\n/**\n * The exact strings PDaC injected into openspec/config.yaml, recorded so a later update can\n * remove entries whose wording has since changed instead of appending duplicates alongside them.\n * The context block needs no record: its sentinels already identify it in place.\n */\nexport interface ManagedStrings {\n rules: Record<string, string[]>;\n operations: Record<string, string[]>;\n}\n\n/** The managed strings the current PDaC guidance injects. */\nexport function currentManagedStrings(): ManagedStrings {\n return {\n rules: Object.fromEntries(Object.entries(PDAC_RULES).map(([key, list]) => [key, [...list]])),\n operations: Object.fromEntries(\n Object.entries(PDAC_OPERATIONS).map(([key, op]) => [key, [...op.guidance]]),\n ),\n };\n}\n\n/** Metadata recorded under .product/integrations/openspec.json. */\nexport interface OpenSpecIntegrationMeta {\n provider: 'openspec';\n version: string;\n openspecVersion: string;\n /**\n * When the integration was first installed. Preserved across every later add, update and no-op:\n * an install date that moves on each invocation records nothing, and rewriting it made every\n * no-op command dirty the working tree.\n */\n installedAt: string;\n /**\n * When managed content last actually changed. Absent until the first change after installation;\n * written only when the merged configuration or the CI example differed, so a no-op leaves this\n * file byte-identical. It answers the question `installedAt` cannot: whether this installation\n * has been regenerated since a ProductShape upgrade.\n */\n updatedAt?: string;\n configPath: string;\n /** Absent in metadata written before the CI example existed; `integration update` adds it. */\n ciExamplePath?: string;\n /** Absent in metadata written before the field existed; treated as the current guidance. */\n managed?: ManagedStrings;\n /**\n * The installed OpenSpec product schema: its name, the OpenSpec floor the product workflow\n * needs, and every managed file it consists of, each with the content digest it was installed\n * at. The per-file record and digest are the ownership proof (ADR 0008): update replaces and\n * remove deletes a file only when that exact path was recorded and its bytes still match the\n * recorded digest (legacy path-only records additionally require the current asset bytes). A\n * diverged or unrecorded file is preserved and reported, never overwritten or deleted. Absent in metadata written before the\n * schema existed; `integration update` installs the schema and adds the record. Metadata\n * written by earlier spike revisions carried a plain path array; it is read as paths with no\n * recorded digest, so ownership then rests on matching the current assets.\n */\n productSchema?: { name: string; requiresOpenspec: string; files: Record<string, string> };\n recoverySchema?: { name: string; requiresOpenspec: string; files: Record<string, string> };\n}\n\n/** The pre-#227 schema location accepted only for safe, ownership-proven migration. */\nconst LEGACY_OPENSPEC_PRODUCT_SCHEMA_NAME = 'product';\nconst LEGACY_OPENSPEC_PRODUCT_SCHEMA_RELATIVE = 'openspec/schemas/product';\n\n/** The recorded product-schema files as path-to-digest, tolerating the earlier array shape. */\nfunction recordedProductSchemaFiles(\n meta: OpenSpecIntegrationMeta | null,\n): Record<string, string> | undefined {\n const record = meta?.productSchema;\n if (!record) return undefined;\n if (\n typeof record !== 'object' ||\n record === null ||\n (record.name !== OPENSPEC_PRODUCT_CHANGE_SCHEMA_NAME &&\n record.name !== LEGACY_OPENSPEC_PRODUCT_SCHEMA_NAME &&\n record.name !== OPENSPEC_PRODUCT_RECOVERY_SCHEMA_NAME) ||\n typeof record.requiresOpenspec !== 'string'\n ) {\n throw new Error(\n \"'productSchema' must name the product schema and carry a string 'requiresOpenspec'\",\n );\n }\n const files = record.files as unknown;\n const validatePath = (file: unknown): file is string =>\n typeof file === 'string' &&\n (file.startsWith(`${OPENSPEC_PRODUCT_CHANGE_SCHEMA_RELATIVE}/`) ||\n file.startsWith(`${LEGACY_OPENSPEC_PRODUCT_SCHEMA_RELATIVE}/`) ||\n file.startsWith(`${OPENSPEC_PRODUCT_RECOVERY_SCHEMA_RELATIVE}/`)) &&\n isRepositoryRelativePath(file);\n if (Array.isArray(files)) {\n if (!files.every(validatePath)) {\n throw new Error(\n `'productSchema.files' contains a path outside the managed OpenSpec schema locations`,\n );\n }\n return Object.fromEntries(files.map((file) => [file, '']));\n }\n if (files !== null && typeof files === 'object') {\n const entries = Object.entries(files as Record<string, unknown>);\n if (!entries.every(([file]) => validatePath(file))) {\n throw new Error(\n `'productSchema.files' contains a path outside the managed OpenSpec schema locations`,\n );\n }\n if (\n !entries.every(\n ([, digest]) => typeof digest === 'string' && /^sha256:[0-9a-f]{64}$/.test(digest),\n )\n ) {\n throw new Error(\"'productSchema.files' contains an invalid content digest\");\n }\n return Object.fromEntries(entries as [string, string][]);\n }\n throw new Error(\"'productSchema.files' must be a path-to-digest object or legacy path array\");\n}\n\nfunction recordedRecoverySchemaFiles(\n meta: OpenSpecIntegrationMeta | null,\n): Record<string, string> | undefined {\n const record = meta?.recoverySchema;\n if (!record) return undefined;\n if (\n record.name !== OPENSPEC_PRODUCT_RECOVERY_SCHEMA_NAME ||\n typeof record.requiresOpenspec !== 'string'\n ) {\n throw new Error(\n \"'recoverySchema' must name the product-recovery schema and carry a string 'requiresOpenspec'\",\n );\n }\n if (!record.files || typeof record.files !== 'object' || Array.isArray(record.files)) {\n throw new Error(\"'recoverySchema.files' must be a path-to-digest object\");\n }\n return record.files;\n}\n\n/** Repository-relative paths used by this integration. */\nconst CONFIG_RELATIVE = 'openspec/config.yaml';\nconst META_RELATIVE = '.product/integrations/openspec.json';\nconst CI_EXAMPLE_RELATIVE = '.product/integrations/openspec.ci.yml';\n\n/** The repository-relative path of the integration metadata file, for callers that probe it. */\nexport const OPENSPEC_META_RELATIVE = META_RELATIVE;\n\n/** The repository-relative path of the installed CI-ready example workflow. */\nexport const OPENSPEC_CI_EXAMPLE_RELATIVE = CI_EXAMPLE_RELATIVE;\n\n/** The exact provider-aware verification command the integration stands behind. */\nexport const OPENSPEC_VERIFY_COMMAND = 'npx prodshape citations verify --provider openspec';\n\n/**\n * Render the CI-ready example workflow installed at {@link OPENSPEC_CI_EXAMPLE_RELATIVE}.\n *\n * The gate always blocks unresolved citations, tampered embedded projections, unclassified\n * current documents and bound documents with zero citations. Stale citations follow the\n * repository's configured warning policy (`validation.warnings-as-errors` in\n * `.product/config.yaml`); the example never changes that policy, but it states explicitly what\n * the repository's current choice means and how to escalate it, so the blocking behaviour is a\n * deliberate decision rather than a silent default.\n */\nexport function renderCiExample(options: { warningsAsErrors: boolean }): string {\n const stalePolicy = options.warningsAsErrors\n ? [\n '# - stale citations BLOCK this gate: .product/config.yaml sets',\n '# `validation.warnings-as-errors: true`, escalating the PRODUCT061 warning to an error.',\n '# The integration never changes that policy; relax it by setting the key to false.',\n ]\n : [\n '# - stale citations DO NOT block this gate: .product/config.yaml sets',\n '# `validation.warnings-as-errors: false`, so PRODUCT061 stays a warning. To make stale',\n '# citations block the merge, set `validation.warnings-as-errors: true` in',\n '# .product/config.yaml. The integration never changes that policy for you.',\n ];\n return [\n '# PDaC citation gate for OpenSpec consumer documents (CI-ready example).',\n '# Managed by `prodshape integration add openspec`; copy it into your pipeline',\n '# (e.g. .github/workflows/) and adapt the triggers.',\n '#',\n '# The gate runs ProductShape citation and scope verification only. It never invokes',\n \"# `openspec validate`; keep OpenSpec's own verdict a separate step so a native-spec defect\",\n '# and a grounding defect stay distinguishable.',\n '#',\n '# Blocking behaviour:',\n '# - unresolved citations, tampered embedded projections, unclassified current documents',\n '# and bound documents with zero citations always fail the gate;',\n ...stalePolicy,\n '# - citations in archived changes are checked too, but their problems are reported as',\n '# warnings: archived history cannot be edited, so a problem there is information,',\n '# not something this gate can make anyone fix. Pass --include-archived to apply the',\n '# full gate to archived documents as well.',\n 'name: pdac-citations',\n 'on:',\n ' pull_request:',\n 'jobs:',\n ' verify:',\n ' runs-on: ubuntu-latest',\n ' steps:',\n ' - uses: actions/checkout@v4',\n ' - uses: actions/setup-node@v4',\n ' with:',\n \" node-version: '22'\",\n ' # The OpenSpec CLI ships as @fission-ai/openspec; population discovery runs',\n ' # `openspec list`, so the gate needs it installed.',\n ` - run: npm install -g ${OPENSPEC_NPX_SPEC}`,\n ` - run: ${OPENSPEC_VERIFY_COMMAND}`,\n '',\n ].join('\\n');\n}\n\n/**\n * Every path this integration writes is one of its own module-level literals, resolved through the\n * repository-containment resolver rather than joined directly, so the contract is enforced by the\n * same code that enforces it for repository-supplied paths instead of by inspection of this file.\n */\nfunction configPath(root: string): string {\n return resolveInRepository(root, CONFIG_RELATIVE, 'the OpenSpec integration');\n}\n\nfunction metaPath(root: string): string {\n return resolveInRepository(root, META_RELATIVE, 'the OpenSpec integration');\n}\n\nfunction ciExamplePath(root: string): string {\n return resolveInRepository(root, CI_EXAMPLE_RELATIVE, 'the OpenSpec integration');\n}\n\n/**\n * Compare two semver-like version strings (major.minor.patch, ignoring pre-release suffixes).\n * Returns negative if a < b, 0 if equal, positive if a > b.\n */\nfunction compareVersions(a: string, b: string): number {\n const parseParts = (v: string): [number, number, number] => {\n const core = v.split('-')[0]?.split('+')[0] ?? '';\n const segments = core.split('.').map((s) => Number.parseInt(s, 10));\n return [segments[0] ?? 0, segments[1] ?? 0, segments[2] ?? 0];\n };\n const [aMaj, aMin, aPatch] = parseParts(a);\n const [bMaj, bMin, bPatch] = parseParts(b);\n if (aMaj !== bMaj) return aMaj - bMaj;\n if (aMin !== bMin) return aMin - bMin;\n return aPatch - bPatch;\n}\n\n/** Run a command at the repository root, resolving stdout+stderr or rejecting on failure. */\nasync function runAtRoot(root: string, command: string, args: string[]): Promise<string> {\n const { stdout, stderr } = await runCommand(command, args, {\n cwd: root,\n env: envWithLocalBin(root),\n });\n return `${stdout}${stderr}`;\n}\n\n/**\n * Detect the installed OpenSpec CLI version by running `openspec --version`. When a repository\n * root is given, its node_modules/.bin is consulted first, so a devDependency install counts.\n */\nexport async function detectOpenSpecVersion(\n root?: string,\n): Promise<{ version: string } | { error: string }> {\n try {\n const { stdout, stderr } = await runCommand('openspec', ['--version'], {\n ...(root ? { cwd: root, env: envWithLocalBin(root) } : {}),\n });\n const output = `${stdout}${stderr}`.trim();\n // `openspec --version` prints something like \"1.3.0\" or \"openspec/1.3.0 ...\".\n const match = output.match(/(\\d+\\.\\d+\\.\\d+(?:-[^\\s]+)?)/);\n if (!match?.[1]) {\n return { error: `Could not parse OpenSpec version from output: ${output}` };\n }\n return { version: match[1] };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n error: `OpenSpec CLI not found on PATH (run: npm install -g ${OPENSPEC_NPX_SPEC}). ${message}`,\n };\n }\n}\n\n/**\n * Create an OpenSpec workspace at the root. Uses the installed CLI when one is on PATH (or in\n * node_modules/.bin); otherwise runs the pinned OpenSpec major through npx, which downloads\n * nothing permanent and never mutates the global environment. `--tools none` keeps the\n * framework's own initializer non-interactive.\n *\n * Returns the CLI version that ran and whether it came through npx, so callers can pass the\n * version on to `addOpenSpecIntegration` (nothing may be on PATH afterwards) and recommend a\n * permanent install.\n */\nexport async function bootstrapOpenSpecWorkspace(\n root: string,\n): Promise<{ version: string; viaNpx: boolean; command: string }> {\n if (await isOpenSpecWorkspace(root)) {\n throw new Error('An OpenSpec workspace already exists at this root.');\n }\n const detected = await detectOpenSpecVersion(root);\n if (!('error' in detected)) {\n if (compareVersions(detected.version, MIN_SUPPORTED_OPENSPEC) < 0) {\n throw new Error(\n `OpenSpec CLI version ${detected.version} is below the minimum supported ${MIN_SUPPORTED_OPENSPEC}. Please upgrade OpenSpec.`,\n );\n }\n const command = 'openspec init --tools none';\n await runAtRoot(root, 'openspec', ['init', '--tools', 'none']);\n return { version: detected.version, viaNpx: false, command };\n }\n const command = `npx -y ${OPENSPEC_NPX_SPEC} init --tools none`;\n await runAtRoot(root, 'npx', ['-y', OPENSPEC_NPX_SPEC, 'init', '--tools', 'none']);\n // A second npx call resolves from the cache the first one populated.\n const output = await runAtRoot(root, 'npx', ['-y', OPENSPEC_NPX_SPEC, '--version']);\n const match = output.trim().match(/(\\d+\\.\\d+\\.\\d+(?:-[^\\s]+)?)/);\n if (!match?.[1]) {\n throw new Error(`Could not parse OpenSpec version from npx output: ${output.trim()}`);\n }\n return { version: match[1], viaNpx: true, command };\n}\n\n/** Check if the OpenSpec integration is installed (its metadata file is present). */\nexport async function isOpenSpecIntegrationInstalled(root: string): Promise<boolean> {\n return pathExists(metaPath(root));\n}\n\n/** Read and parse the existing openspec/config.yaml, or null if it does not exist. */\nexport async function readOpenSpecConfig(root: string): Promise<Record<string, unknown> | null> {\n const path = configPath(root);\n if (!(await pathExists(path))) return null;\n const text = await readFile(path, 'utf8');\n const parsed = parse(text);\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n // An empty or non-mapping file: treat as an empty config so we can merge into it.\n return {};\n }\n return parsed as Record<string, unknown>;\n}\n\n/**\n * Extract the PDaC context block (between sentinels) from a context string, or null if absent.\n */\nfunction extractPdacContext(context: string | undefined): string | null {\n if (!context) return null;\n const begin = context.indexOf(PDAC_CONTEXT_BEGIN);\n const end = context.indexOf(PDAC_CONTEXT_END);\n if (begin === -1 || end === -1 || end < begin) return null;\n return context.slice(begin, end + PDAC_CONTEXT_END.length);\n}\n\n/**\n * Merge PDaC guidance into the OpenSpec config, preserving existing content.\n *\n * - `schema` is preserved as-is.\n * - `context`: if the existing context already contains the PDaC block, it is replaced in place\n * (idempotent); otherwise the PDaC block is appended after a separator. User-authored context\n * lines are never lost.\n * - `rules`: for each PDaC artifact (proposal, specs, design, tasks), PDaC rules are appended to\n * existing rules, deduplicating identical entries. Entries recorded as previously managed whose\n * wording is no longer current are removed first, so a guidance change replaces the old string\n * instead of accumulating next to it. User-authored entries are never removed.\n * - `operations`: PDaC apply/archive guidance is appended to existing guidance, deduplicating,\n * with the same previously-managed replacement.\n * - Any other fields (githubCopilot, etc.) are preserved untouched.\n *\n * Returns the merged config and a list of human-readable change descriptions.\n */\nexport function mergeConfig(\n existing: Record<string, unknown> | null,\n previouslyManaged?: ManagedStrings,\n): {\n config: Record<string, unknown>;\n changes: string[];\n} {\n const config: Record<string, unknown> = { ...(existing ?? {}) };\n const changes: string[] = [];\n\n // Strings PDaC injected in the past but no longer injects verbatim: remove before appending.\n const staleManaged = (recorded: string[] | undefined, current: string[]): string[] =>\n (recorded ?? []).filter((entry) => !current.includes(entry));\n\n // --- context ---\n const existingContext =\n typeof config['context'] === 'string' ? (config['context'] as string) : undefined;\n const existingPdacBlock = extractPdacContext(existingContext);\n if (existingPdacBlock !== null) {\n // Replace the existing PDaC block in place (keeps it idempotent and up to date).\n if (existingPdacBlock !== PDAC_CONTEXT_BLOCK) {\n const replaced = existingContext!.replace(existingPdacBlock, PDAC_CONTEXT_BLOCK);\n if (replaced !== existingContext) {\n config['context'] = replaced;\n changes.push('Updated PDaC context block in openspec/config.yaml (context:).');\n }\n }\n // If identical, no change recorded — idempotent.\n } else if (existingContext) {\n // User has their own context: append the PDaC block after a blank-line separator.\n const separator = existingContext.endsWith('\\n') ? '\\n' : '\\n\\n';\n config['context'] = `${existingContext}${separator}${PDAC_CONTEXT_BLOCK}`;\n changes.push('Appended PDaC context block to existing openspec/config.yaml context:.');\n } else {\n config['context'] = PDAC_CONTEXT_BLOCK;\n changes.push('Added PDaC context block to openspec/config.yaml (context:).');\n }\n\n // --- rules ---\n const existingRules =\n config['rules'] && typeof config['rules'] === 'object' && !Array.isArray(config['rules'])\n ? { ...(config['rules'] as Record<string, unknown>) }\n : {};\n let rulesChanged = false;\n let staleRulesRemoved = false;\n const ruleArtifacts = new Set([\n ...Object.keys(PDAC_RULES),\n ...Object.keys(previouslyManaged?.rules ?? {}),\n ]);\n for (const artifact of ruleArtifacts) {\n const pdacRules = PDAC_RULES[artifact] ?? [];\n const stale = staleManaged(previouslyManaged?.rules[artifact], pdacRules);\n const existingList = Array.isArray(existingRules[artifact])\n ? [...(existingRules[artifact] as string[])]\n : [];\n const merged = existingList.filter((entry) => !stale.includes(entry));\n if (merged.length !== existingList.length) {\n staleRulesRemoved = true;\n rulesChanged = true;\n }\n for (const rule of pdacRules) {\n if (!merged.includes(rule)) {\n merged.push(rule);\n rulesChanged = true;\n }\n }\n if (merged.length === 0) {\n delete existingRules[artifact];\n } else {\n existingRules[artifact] = merged;\n }\n }\n if (rulesChanged) {\n if (Object.keys(existingRules).length === 0) {\n delete config['rules'];\n } else {\n config['rules'] = existingRules;\n }\n changes.push(\n staleRulesRemoved\n ? 'Merged PDaC citation rules into openspec/config.yaml (rules:), replacing outdated PDaC entries.'\n : 'Merged PDaC citation rules into openspec/config.yaml (rules:).',\n );\n }\n\n // --- operations ---\n const existingOperations =\n config['operations'] &&\n typeof config['operations'] === 'object' &&\n !Array.isArray(config['operations'])\n ? { ...(config['operations'] as Record<string, unknown>) }\n : {};\n let opsChanged = false;\n let staleOpsRemoved = false;\n for (const opKey of ['apply', 'archive'] as const) {\n const pdacGuidance = PDAC_OPERATIONS[opKey].guidance;\n const stale = staleManaged(previouslyManaged?.operations[opKey], pdacGuidance);\n const existingOp =\n existingOperations[opKey] &&\n typeof existingOperations[opKey] === 'object' &&\n !Array.isArray(existingOperations[opKey])\n ? { ...(existingOperations[opKey] as Record<string, unknown>) }\n : {};\n const existingGuidance = Array.isArray(existingOp['guidance'])\n ? [...(existingOp['guidance'] as string[])]\n : [];\n const merged = existingGuidance.filter((entry) => !stale.includes(entry));\n if (merged.length !== existingGuidance.length) {\n staleOpsRemoved = true;\n opsChanged = true;\n }\n for (const guidance of pdacGuidance) {\n if (!merged.includes(guidance)) {\n merged.push(guidance);\n opsChanged = true;\n }\n }\n existingOp['guidance'] = merged;\n existingOperations[opKey] = existingOp;\n }\n if (opsChanged) {\n config['operations'] = existingOperations;\n changes.push(\n staleOpsRemoved\n ? 'Merged PDaC operation guidance into openspec/config.yaml (operations:), replacing outdated PDaC entries.'\n : 'Merged PDaC operation guidance into openspec/config.yaml (operations:).',\n );\n }\n\n return { config, changes };\n}\n\n/** Serialize config back to YAML with a trailing newline. */\nexport function serializeConfig(config: Record<string, unknown>): string {\n return `${stringify(config).trimEnd()}\\n`;\n}\n\n/** Read the ProductShape version from the integration-openspec package.json. */\nasync function productshapeVersion(): Promise<string> {\n const packageJson = JSON.parse(\n await readFile(new URL('../package.json', import.meta.url), 'utf8'),\n ) as { version: string };\n return packageJson.version;\n}\n\n/**\n * Add the OpenSpec integration to a repository.\n *\n * - Detects the OpenSpec CLI version; rejects if not found or below MIN_SUPPORTED_OPENSPEC.\n * A caller that already ran the CLI (the init bootstrap) can pass `cliVersion` to skip the\n * probe: after an npx bootstrap nothing is on PATH, yet the version is known.\n * - Requires an existing `openspec/` workspace (run `openspec init` first).\n * - Merges PDaC guidance into `openspec/config.yaml`, preserving existing user configuration.\n * - Records metadata under `.product/integrations/openspec.json`, including the exact strings\n * injected, so a later update replaces outdated PDaC entries instead of accumulating them.\n * - Byte-idempotent: running twice produces the same result, reports no changes and rewrites\n * nothing at all — not the merged configuration, not the CI example, not the metadata.\n * - `--dry-run` reports what would change without writing.\n */\nexport async function addOpenSpecIntegration(\n root: string,\n options: {\n force?: boolean;\n dryRun?: boolean;\n cliVersion?: string;\n /**\n * The repository's configured `validation.warnings-as-errors` policy, reflected verbatim in\n * the installed CI example so its blocking behaviour is stated, never silently changed.\n */\n warningsAsErrors?: boolean;\n } = {},\n): Promise<{ written: string[]; changes: string[]; meta: OpenSpecIntegrationMeta }> {\n // `force` is accepted for signature compatibility with the other integrations and deliberately\n // ignored: every managed surface here is compared by content, so there is nothing a forced run\n // could regenerate that a normal one does not. It used to mean \"rewrite even when identical\",\n // which is exactly the byte-churn this integration must not produce.\n const { dryRun = false, warningsAsErrors = false } = options;\n\n let openspecVersion = options.cliVersion;\n if (!openspecVersion) {\n const detected = await detectOpenSpecVersion(root);\n if ('error' in detected) {\n throw new Error(detected.error);\n }\n openspecVersion = detected.version;\n }\n if (compareVersions(openspecVersion, MIN_SUPPORTED_OPENSPEC) < 0) {\n throw new Error(\n `OpenSpec CLI version ${openspecVersion} is below the minimum supported ${MIN_SUPPORTED_OPENSPEC}. Please upgrade OpenSpec.`,\n );\n }\n\n if (!(await isOpenSpecWorkspace(root))) {\n throw new Error('No OpenSpec workspace found. Run `openspec init` first.');\n }\n\n const existing = await readOpenSpecConfig(root);\n const previousMeta = await readMeta(root);\n\n const { config, changes: configChanges } = mergeConfig(existing, previousMeta?.managed);\n\n const ciDesired = renderCiExample({ warningsAsErrors });\n const ciAbsolute = ciExamplePath(root);\n const ciExisting = (await pathExists(ciAbsolute)) ? await readFile(ciAbsolute, 'utf8') : null;\n const ciChanged = ciExisting !== ciDesired;\n\n const changes = [...configChanges];\n if (ciChanged) {\n changes.push(\n ciExisting === null\n ? `Installed CI-ready verification example at ${CI_EXAMPLE_RELATIVE}.`\n : `Updated CI-ready verification example at ${CI_EXAMPLE_RELATIVE}.`,\n );\n }\n\n // The OpenSpec product schema: managed files under openspec/schemas/product-change, the framework's\n // official project-local schema surface. The files are version-independent data, so they are\n // installed regardless of the detected CLI version; whether the product workflow is USABLE on\n // this CLI is a separate, capability-specific report (`checkOpenSpecIntegration`), never a\n // different set of bytes.\n //\n // Ownership is proven per file, never inferred from the schema path or byte equality (ADR\n // 0008): a file may be replaced only when the metadata recorded that exact path and its bytes\n // still match the recorded digest. Any pre-existing unrecorded file is a collision, even when\n // byte-identical to the asset; on update a diverged managed file is preserved and reported, and\n // an obsolete still-managed file is removed only while its digest continues to match.\n const schemaAssets = await loadProductChangeSchemaAssets();\n const recordedSchema = recordedProductSchemaFiles(previousMeta);\n const currentSchemaPaths = new Set(\n schemaAssets.map((asset) => `${OPENSPEC_PRODUCT_CHANGE_SCHEMA_RELATIVE}/${asset.relative}`),\n );\n const schemaFileDigests: Record<string, string> = {};\n const schemaWrites: { relative: string; absolute: string; content: string }[] = [];\n const schemaDeletes: { relative: string; absolute: string }[] = [];\n const schemaCollisions: string[] = [];\n const schemaPreserved: string[] = [];\n let schemaInstalledAnything = false;\n for (const asset of schemaAssets) {\n const relative = `${OPENSPEC_PRODUCT_CHANGE_SCHEMA_RELATIVE}/${asset.relative}`;\n const absolute = resolveInRepository(root, relative, 'the OpenSpec integration');\n const existing = (await pathExists(absolute)) ? await readFile(absolute, 'utf8') : null;\n const assetDigest = contentDigest(asset.content);\n if (existing === null) {\n schemaWrites.push({ relative, absolute, content: asset.content });\n schemaFileDigests[relative] = assetDigest;\n schemaInstalledAnything = true;\n continue;\n }\n const recordedDigest = recordedSchema?.[relative];\n if (recordedDigest === undefined) {\n // Ownership is per file. Byte equality proves compatibility, not authorship: adopting an\n // identical user file on a fresh install or when a later release adds a new asset would let\n // a subsequent remove delete content the integration never installed.\n schemaCollisions.push(relative);\n continue;\n }\n if (existing === asset.content) {\n // A recorded managed path is already byte-identical to the current asset: nothing to write.\n schemaFileDigests[relative] = assetDigest;\n continue;\n }\n if (recordedDigest !== '' && contentDigest(existing) === recordedDigest) {\n // Proven managed at an older recorded version: replace with the current assets.\n schemaWrites.push({ relative, absolute, content: asset.content });\n schemaFileDigests[relative] = assetDigest;\n continue;\n }\n // Managed path whose content diverged from both the record and the current assets: a hand\n // edit. Preserve it, keep its recorded digest (restoring the recorded content re-enables\n // management), and report it.\n schemaPreserved.push(relative);\n if (recordedDigest !== '') schemaFileDigests[relative] = recordedDigest;\n }\n for (const [relative, recordedDigest] of Object.entries(recordedSchema ?? {})) {\n if (currentSchemaPaths.has(relative)) continue;\n const absolute = resolveInRepository(root, relative, 'the OpenSpec integration');\n if (!(await pathExists(absolute))) continue;\n const existing = await readFile(absolute, 'utf8');\n if (recordedDigest !== '' && contentDigest(existing) === recordedDigest) {\n schemaDeletes.push({ relative, absolute });\n continue;\n }\n // Ownership extends to absence, but deletion still requires the recorded digest proof. A\n // diverged obsolete file stays user-visible and remains recorded so check/remove can continue\n // reporting it; a digest-less legacy record is preserved but cannot become ownership proof.\n schemaPreserved.push(relative);\n if (recordedDigest !== '') schemaFileDigests[relative] = recordedDigest;\n }\n if (schemaCollisions.length > 0) {\n throw new Error(\n `Refusing to install the OpenSpec product schema: ${OPENSPEC_PRODUCT_CHANGE_SCHEMA_RELATIVE} already contains files this integration does not manage (${schemaCollisions.join(', ')}). Move them aside or delete them, then run: prodshape integration add openspec. The integration never overwrites user-authored files.`,\n );\n }\n if (schemaWrites.length > 0) {\n changes.push(\n recordedSchema !== undefined || !schemaInstalledAnything\n ? `Updated the OpenSpec product schema at ${OPENSPEC_PRODUCT_CHANGE_SCHEMA_RELATIVE} (${schemaWrites.length} file${schemaWrites.length === 1 ? '' : 's'}).`\n : `Installed the OpenSpec product schema at ${OPENSPEC_PRODUCT_CHANGE_SCHEMA_RELATIVE}.`,\n );\n if (compareVersions(openspecVersion, PRODUCT_CHANGE_SCHEMA_MIN_OPENSPEC) < 0) {\n changes.push(\n `Product workflow unavailable until OpenSpec >= ${PRODUCT_CHANGE_SCHEMA_MIN_OPENSPEC} (detected ${openspecVersion}); the citation lane is unaffected.`,\n );\n }\n }\n for (const { relative } of schemaDeletes) {\n changes.push(`Removed obsolete managed file ${relative}.`);\n }\n for (const relative of schemaPreserved) {\n changes.push(\n `Preserved hand-edited managed file ${relative}; restore or delete it, then run: prodshape integration update.`,\n );\n }\n\n // The recovery workload has its own schema and ownership record. It is intentionally kept\n // beside, rather than inside, product-change: recovery starts without an accepted baseline.\n const recoveryAssets = await loadProductRecoverySchemaAssets();\n const previousRecovery = recordedRecoverySchemaFiles(previousMeta);\n const recoveryDigests: Record<string, string> = {};\n const recoveryWrites: { relative: string; absolute: string; content: string }[] = [];\n const recoveryCollisions: string[] = [];\n for (const asset of recoveryAssets) {\n const relative = `${OPENSPEC_PRODUCT_RECOVERY_SCHEMA_RELATIVE}/${asset.relative}`;\n const absolute = resolveInRepository(root, relative, 'the OpenSpec integration');\n const existing = (await pathExists(absolute)) ? await readFile(absolute, 'utf8') : null;\n const digest = contentDigest(asset.content);\n const recorded = previousRecovery?.[relative];\n if (existing === null) {\n recoveryWrites.push({ relative, absolute, content: asset.content });\n recoveryDigests[relative] = digest;\n } else if (recorded === undefined) {\n recoveryCollisions.push(relative);\n } else if (existing === asset.content) {\n recoveryDigests[relative] = digest;\n } else if (recorded !== '' && contentDigest(existing) === recorded) {\n recoveryWrites.push({ relative, absolute, content: asset.content });\n recoveryDigests[relative] = digest;\n } else {\n recoveryDigests[relative] = recorded;\n changes.push(\n `Preserved hand-edited managed file ${relative}; restore or delete it, then run: prodshape integration update.`,\n );\n }\n }\n if (recoveryCollisions.length > 0) {\n throw new Error(\n `Refusing to install the OpenSpec product-recovery schema: unrecorded files exist (${recoveryCollisions.join(', ')}). The integration never overwrites user-authored files.`,\n );\n }\n if (recoveryWrites.length > 0) {\n changes.push(\n previousRecovery === undefined\n ? `Installed the OpenSpec product-recovery schema at ${OPENSPEC_PRODUCT_RECOVERY_SCHEMA_RELATIVE}.`\n : `Updated the OpenSpec product-recovery schema at ${OPENSPEC_PRODUCT_RECOVERY_SCHEMA_RELATIVE} (${recoveryWrites.length} files).`,\n );\n }\n\n // `installedAt` records when this integration was first installed, and is preserved from here\n // on. Stamping a fresh timestamp on every invocation is what made a no-op `add` — and every\n // `integration update` — rewrite this file, so a command that reported \"already up to date\"\n // still left the working tree dirty. `updatedAt` carries the moving fact instead, and moves\n // only when managed content actually changed.\n const now = new Date().toISOString();\n const meta: OpenSpecIntegrationMeta = {\n provider: 'openspec',\n version: await productshapeVersion(),\n openspecVersion,\n installedAt: previousMeta?.installedAt ?? now,\n configPath: CONFIG_RELATIVE,\n ciExamplePath: CI_EXAMPLE_RELATIVE,\n managed: currentManagedStrings(),\n productSchema: {\n name: OPENSPEC_PRODUCT_CHANGE_SCHEMA_NAME,\n requiresOpenspec: PRODUCT_CHANGE_SCHEMA_MIN_OPENSPEC,\n files: schemaFileDigests,\n },\n recoverySchema: {\n name: OPENSPEC_PRODUCT_RECOVERY_SCHEMA_NAME,\n requiresOpenspec: PRODUCT_CHANGE_SCHEMA_MIN_OPENSPEC,\n files: recoveryDigests,\n },\n };\n const updatedAt =\n changes.length > 0 && previousMeta !== null ? now : (previousMeta?.updatedAt ?? undefined);\n if (updatedAt !== undefined) meta.updatedAt = updatedAt;\n\n const written: string[] = [];\n if (!dryRun) {\n // Every write below is conditional on the bytes actually differing. `force` re-derives the\n // content; it does not license rewriting a file that already carries exactly that content,\n // because a byte-identical rewrite is invisible in a diff and visible everywhere else — in\n // Git's stat cache, in file watchers, and in every build that keys off modification times.\n if (configChanges.length > 0) {\n const serialized = serializeConfig(config);\n await mkdir(dirname(configPath(root)), { recursive: true });\n await writeFile(configPath(root), serialized, 'utf8');\n written.push(CONFIG_RELATIVE);\n }\n if (ciChanged) {\n await mkdir(dirname(ciAbsolute), { recursive: true });\n await writeFile(ciAbsolute, ciDesired, 'utf8');\n written.push(CI_EXAMPLE_RELATIVE);\n }\n for (const deletion of schemaDeletes) {\n await rm(deletion.absolute, { force: true });\n }\n for (const write of schemaWrites) {\n await mkdir(dirname(write.absolute), { recursive: true });\n await writeFile(write.absolute, write.content, 'utf8');\n written.push(write.relative);\n }\n for (const write of recoveryWrites) {\n await mkdir(dirname(write.absolute), { recursive: true });\n await writeFile(write.absolute, write.content, 'utf8');\n written.push(write.relative);\n }\n if (await writeMetaIfChanged(root, meta)) {\n written.push(META_RELATIVE);\n }\n }\n\n return { written, changes, meta };\n}\n\n/**\n * Write the integration metadata file, but only when its bytes would change. Reports whether it\n * was written, so a caller never claims a write that did not happen.\n */\nasync function writeMetaIfChanged(root: string, meta: OpenSpecIntegrationMeta): Promise<boolean> {\n const path = metaPath(root);\n const desired = `${JSON.stringify(meta, null, 2)}\\n`;\n let existing: string | undefined;\n try {\n existing = await readFile(path, 'utf8');\n } catch (error) {\n if (!isNotFound(error)) throw error;\n }\n if (existing === desired) return false;\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, desired, 'utf8');\n return true;\n}\n\n/**\n * An integration metadata file that exists but cannot be read or parsed.\n *\n * Absent metadata means the integration is not installed, which is a supported state. A metadata\n * file that is present and unreadable is not: continuing as though it were absent would discard\n * the record of exactly which guidance strings were injected, and the next update would append\n * duplicates alongside the entries it should have replaced.\n */\nexport class OpenSpecMetaError extends Error {\n constructor(detail: string, options?: { cause?: unknown }) {\n super(\n `OpenSpec integration metadata ${META_RELATIVE} cannot be trusted: ${detail}` +\n '\\nReconcile it by hand, or remove it and re-run: prodshape integration add openspec',\n options,\n );\n this.name = 'OpenSpecMetaError';\n }\n}\n\n/** Read the integration metadata file, or null if absent. Unreadable or malformed is an error. */\nasync function readMeta(root: string): Promise<OpenSpecIntegrationMeta | null> {\n const path = metaPath(root);\n let content: string;\n try {\n content = await readFile(path, 'utf8');\n } catch (error) {\n if (isNotFound(error)) return null;\n throw new OpenSpecMetaError(\n `it exists but could not be read (${error instanceof Error ? error.message : String(error)})`,\n { cause: error },\n );\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(content);\n } catch (error) {\n throw new OpenSpecMetaError(\n `it is not valid JSON (${error instanceof Error ? error.message : String(error)})`,\n { cause: error },\n );\n }\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n throw new OpenSpecMetaError('the document is not a JSON object');\n }\n const record = parsed as Record<string, unknown>;\n if (record.provider !== 'openspec' || typeof record.installedAt !== 'string') {\n throw new OpenSpecMetaError(\n \"it does not match the metadata schema ('provider' must be 'openspec' and 'installedAt' a string)\",\n );\n }\n const meta = record as unknown as OpenSpecIntegrationMeta;\n try {\n recordedProductSchemaFiles(meta);\n } catch (error) {\n throw new OpenSpecMetaError(\n `it does not match the product schema ownership record (${error instanceof Error ? error.message : String(error)})`,\n { cause: error },\n );\n }\n return meta;\n}\n\n/**\n * Update (re-merge) the OpenSpec integration. Re-runs the merge so that a PDaC upgrade that\n * changed the context block or rules is reflected. Idempotent.\n */\nexport async function updateOpenSpecIntegration(\n root: string,\n options: { force?: boolean; dryRun?: boolean; warningsAsErrors?: boolean } = {},\n): Promise<{ written: string[]; changes: string[] }> {\n // An existing metadata record is the compatibility verdict established during installation.\n // Managed file reconciliation must remain usable in release jobs and repositories that no\n // longer have OpenSpec on PATH; initial installation and explicit health checks still probe it.\n const previousMeta = await readMeta(root);\n const result = await addOpenSpecIntegration(root, {\n force: true,\n dryRun: options.dryRun,\n warningsAsErrors: options.warningsAsErrors,\n ...(previousMeta?.openspecVersion ? { cliVersion: previousMeta.openspecVersion } : {}),\n });\n return { written: result.written, changes: result.changes };\n}\n\n/**\n * Check the OpenSpec integration health. Verifies:\n * - OpenSpec CLI is installed and the version is supported.\n * - `openspec/config.yaml` exists and contains the PDaC context block.\n * - `.product/integrations/openspec.json` metadata exists.\n * - The config's PDaC block matches the current PDaC guidance (not removed or altered).\n * - The product schema's managed files are installed and intact, with the product workflow's\n * availability reported per capability: an OpenSpec CLI below the product floor leaves the\n * citation lane usable and states the product workflow UNAVAILABLE instead of failing it.\n */\nexport async function checkOpenSpecIntegration(root: string): Promise<{\n ok: boolean;\n checks: { name: string; ok: boolean; detail: string }[];\n}> {\n const checks: { name: string; ok: boolean; detail: string }[] = [];\n\n // 1. OpenSpec CLI installed and supported.\n const detected = await detectOpenSpecVersion(root);\n const detectedVersion = 'error' in detected ? undefined : detected.version;\n if ('error' in detected) {\n checks.push({ name: 'openspec CLI', ok: false, detail: detected.error });\n } else if (compareVersions(detected.version, MIN_SUPPORTED_OPENSPEC) < 0) {\n checks.push({\n name: 'openspec CLI',\n ok: false,\n detail: `OpenSpec ${detected.version} is below minimum supported ${MIN_SUPPORTED_OPENSPEC}.`,\n });\n } else {\n checks.push({\n name: 'openspec CLI',\n ok: true,\n detail: `OpenSpec ${detected.version} detected.`,\n });\n }\n\n // 2. openspec/config.yaml exists and contains PDaC context.\n const config = await readOpenSpecConfig(root);\n if (config === null) {\n checks.push({\n name: 'config.yaml',\n ok: false,\n detail: 'openspec/config.yaml not found. Run: prodshape integration add openspec',\n });\n } else {\n const context =\n typeof config['context'] === 'string' ? (config['context'] as string) : undefined;\n const pdacBlock = extractPdacContext(context);\n if (pdacBlock === null) {\n checks.push({\n name: 'config.yaml',\n ok: false,\n detail:\n 'PDaC context block missing from openspec/config.yaml. Run: prodshape integration add openspec',\n });\n } else if (pdacBlock !== PDAC_CONTEXT_BLOCK) {\n checks.push({\n name: 'config.yaml',\n ok: false,\n detail:\n 'PDaC context block in openspec/config.yaml is outdated. Run: prodshape integration update',\n });\n } else {\n checks.push({\n name: 'config.yaml',\n ok: true,\n detail: 'PDaC context block present and current.',\n });\n }\n }\n\n // 3. Metadata file exists.\n const meta = await readMeta(root);\n if (meta === null) {\n checks.push({\n name: 'metadata',\n ok: false,\n detail:\n '.product/integrations/openspec.json not found. Run: prodshape integration add openspec',\n });\n } else {\n checks.push({\n name: 'metadata',\n ok: true,\n detail:\n `Integration recorded (OpenSpec ${meta.openspecVersion}, installed ${meta.installedAt}` +\n `${meta.updatedAt ? `, updated ${meta.updatedAt}` : ''}).`,\n });\n }\n\n // 3b. CI-ready verification example present.\n if (await pathExists(ciExamplePath(root))) {\n checks.push({\n name: 'ci example',\n ok: true,\n detail: `CI-ready verification example present at ${CI_EXAMPLE_RELATIVE}.`,\n });\n } else {\n checks.push({\n name: 'ci example',\n ok: false,\n detail: `CI-ready verification example missing (${CI_EXAMPLE_RELATIVE}). Run: prodshape integration update`,\n });\n }\n\n // 4. PDaC rules present.\n if (config) {\n const rules =\n config['rules'] && typeof config['rules'] === 'object' && !Array.isArray(config['rules'])\n ? (config['rules'] as Record<string, unknown>)\n : {};\n const missingRules: string[] = [];\n for (const [artifact, pdacRules] of Object.entries(PDAC_RULES)) {\n const list = Array.isArray(rules[artifact]) ? (rules[artifact] as string[]) : [];\n for (const rule of pdacRules) {\n if (!list.includes(rule)) missingRules.push(artifact);\n }\n }\n if (missingRules.length > 0) {\n checks.push({\n name: 'rules',\n ok: false,\n detail: `PDaC rules missing for: ${[...new Set(missingRules)].join(', ')}. Run: prodshape integration update`,\n });\n } else {\n checks.push({ name: 'rules', ok: true, detail: 'All PDaC citation rules present.' });\n }\n }\n\n // 5. Product workflow: managed schema state plus capability-specific availability. The schema\n // files are inert data valid under any CLI, so a floor-violating CLI is not a managed-state\n // defect; it makes the product workflow UNAVAILABLE, stated as the check's verdict rather than\n // buried as a footnote, while the citation lane stays independently usable.\n const schemaAssets = await loadProductChangeSchemaAssets();\n if (meta === null || meta.productSchema === undefined) {\n checks.push({\n name: 'product workflow',\n ok: false,\n detail: `OpenSpec product schema not installed (${OPENSPEC_PRODUCT_CHANGE_SCHEMA_RELATIVE}). Run: prodshape integration update`,\n });\n } else {\n const recordedSchema = recordedProductSchemaFiles(meta) ?? {};\n const currentSchemaPaths = new Set(\n schemaAssets.map((asset) => `${OPENSPEC_PRODUCT_CHANGE_SCHEMA_RELATIVE}/${asset.relative}`),\n );\n const missing: string[] = [];\n const unrecorded: string[] = [];\n const outdated: string[] = [];\n const diverged: string[] = [];\n const obsolete = Object.keys(recordedSchema)\n .filter((relative) => !currentSchemaPaths.has(relative))\n .sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));\n for (const asset of schemaAssets) {\n const relative = `${OPENSPEC_PRODUCT_CHANGE_SCHEMA_RELATIVE}/${asset.relative}`;\n const absolute = resolveInRepository(root, relative, 'the OpenSpec integration');\n const existing = (await pathExists(absolute)) ? await readFile(absolute, 'utf8') : null;\n const recordedDigest = recordedSchema[relative];\n if (recordedDigest === undefined) {\n unrecorded.push(relative);\n } else if (existing === null) {\n missing.push(relative);\n } else if (existing !== asset.content) {\n if (recordedDigest !== '' && contentDigest(existing) === recordedDigest) {\n outdated.push(relative);\n } else {\n diverged.push(relative);\n }\n }\n }\n if (\n missing.length > 0 ||\n unrecorded.length > 0 ||\n outdated.length > 0 ||\n diverged.length > 0 ||\n obsolete.length > 0\n ) {\n const parts: string[] = [];\n if (missing.length > 0) {\n parts.push(`missing: ${missing.join(', ')} (run: prodshape integration update)`);\n }\n if (unrecorded.length > 0) {\n parts.push(\n `unrecorded: ${unrecorded.join(', ')} (move user files aside, then run: prodshape integration update)`,\n );\n }\n if (outdated.length > 0) {\n parts.push(\n `outdated managed content: ${outdated.join(', ')} (run: prodshape integration update)`,\n );\n }\n if (diverged.length > 0) {\n parts.push(\n `hand-edited and preserved: ${diverged.join(', ')} (restore or delete them, then run: prodshape integration update)`,\n );\n }\n if (obsolete.length > 0) {\n parts.push(\n `obsolete managed content: ${obsolete.join(', ')} (run: prodshape integration update)`,\n );\n }\n checks.push({\n name: 'product workflow',\n ok: false,\n detail: `Product schema files need attention. ${parts.join(' ')}`,\n });\n } else if (detectedVersion === undefined) {\n checks.push({\n name: 'product workflow',\n ok: true,\n detail:\n 'Product workflow UNAVAILABLE: the OpenSpec CLI was not detected. The schema is installed and intact; the openspec CLI check carries the remedy.',\n });\n } else if (compareVersions(detectedVersion, PRODUCT_CHANGE_SCHEMA_MIN_OPENSPEC) < 0) {\n checks.push({\n name: 'product workflow',\n ok: true,\n detail: `Product workflow UNAVAILABLE: requires OpenSpec >= ${PRODUCT_CHANGE_SCHEMA_MIN_OPENSPEC}, detected ${detectedVersion}. The citation lane is unaffected; upgrade OpenSpec to use the product schema.`,\n });\n } else {\n checks.push({\n name: 'product workflow',\n ok: true,\n detail: `Product workflow available: schema installed and OpenSpec ${detectedVersion} meets the ${PRODUCT_CHANGE_SCHEMA_MIN_OPENSPEC} floor.`,\n });\n }\n }\n\n // 6. Hosted recovery schema ownership is checked independently from product-change because\n // the two workloads have different preconditions and may evolve on different cadences.\n const recoveryAssets = await loadProductRecoverySchemaAssets();\n const recoveryRecord = recordedRecoverySchemaFiles(meta);\n if (recoveryRecord === undefined) {\n checks.push({\n name: 'product recovery workflow',\n // Existing installations predate #229. Keep the citation and existing product-change\n // lanes healthy while advertising the opt-in migration; a fresh add/update records the\n // recovery schema and is checked strictly below.\n ok: true,\n detail: `Product recovery workflow UNAVAILABLE: schema not installed (${OPENSPEC_PRODUCT_RECOVERY_SCHEMA_RELATIVE}). Run: prodshape integration update to enable it.`,\n });\n } else {\n const defects: string[] = [];\n for (const asset of recoveryAssets) {\n const relative = `${OPENSPEC_PRODUCT_RECOVERY_SCHEMA_RELATIVE}/${asset.relative}`;\n const absolute = resolveInRepository(root, relative, 'the OpenSpec integration');\n const expected = recoveryRecord[relative];\n if (expected === undefined) defects.push(`unrecorded: ${relative}`);\n else if (!(await pathExists(absolute))) defects.push(`missing: ${relative}`);\n else {\n const actual = await readFile(absolute, 'utf8');\n if (actual !== asset.content && contentDigest(actual) !== expected) {\n defects.push(`hand-edited and preserved: ${relative}`);\n } else if (actual !== asset.content) defects.push(`outdated managed content: ${relative}`);\n }\n }\n checks.push({\n name: 'product recovery workflow',\n ok: defects.length === 0,\n detail:\n defects.length === 0\n ? `Product recovery workflow available: schema installed at ${OPENSPEC_PRODUCT_RECOVERY_SCHEMA_RELATIVE}.`\n : `Product-recovery schema files need attention. ${defects.join(' ')}`,\n });\n }\n\n const ok = checks.every((c) => c.ok);\n return { ok, checks };\n}\n\n/**\n * Remove the PDaC-injected content from openspec/config.yaml, preserving user-authored content,\n * and delete the metadata file.\n *\n * - Removes the PDaC context block (between sentinels) from `context:`.\n * - Removes PDaC rules from each artifact (keeps user-authored rules).\n * - Removes PDaC operation guidance (keeps user-authored guidance).\n * - Deletes the installed product schema files still proven managed (recorded digest or current\n * asset bytes); hand-edited managed files are preserved and reported in `preserved`, and with\n * no recorded product schema nothing under openspec/schemas is touched.\n * - Deletes `.product/integrations/openspec.json`.\n * - `openspec/config.yaml` stays on disk with the PDaC content stripped and is reported in `restored`.\n * - `--dry-run` reports what would be removed, restored and preserved without writing.\n */\nexport async function removeOpenSpecIntegration(\n root: string,\n options: { dryRun?: boolean } = {},\n): Promise<{ removed: string[]; restored: string[]; preserved: string[] }> {\n const { dryRun = false } = options;\n const removed: string[] = [];\n const restored: string[] = [];\n const preserved: string[] = [];\n\n // Remove both what the metadata recorded as injected and what the current guidance would\n // inject: the union covers metadata written before the record existed as well as entries left\n // behind by an older ProductShape.\n const previousMeta = await readMeta(root);\n const recorded = previousMeta?.managed;\n const current = currentManagedStrings();\n const managedRules: Record<string, string[]> = {};\n for (const key of new Set([\n ...Object.keys(current.rules),\n ...Object.keys(recorded?.rules ?? {}),\n ])) {\n managedRules[key] = [\n ...new Set([...(current.rules[key] ?? []), ...(recorded?.rules[key] ?? [])]),\n ];\n }\n const managedOperations: Record<string, string[]> = {};\n for (const key of new Set([\n ...Object.keys(current.operations),\n ...Object.keys(recorded?.operations ?? {}),\n ])) {\n managedOperations[key] = [\n ...new Set([...(current.operations[key] ?? []), ...(recorded?.operations[key] ?? [])]),\n ];\n }\n\n const config = await readOpenSpecConfig(root);\n if (config !== null) {\n let configChanged = false;\n\n // Remove PDaC context block.\n const context =\n typeof config['context'] === 'string' ? (config['context'] as string) : undefined;\n if (context) {\n const pdacBlock = extractPdacContext(context);\n if (pdacBlock !== null) {\n const cleaned = context\n .replace(pdacBlock, '')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trimEnd();\n if (cleaned === '') {\n delete config['context'];\n } else {\n config['context'] = cleaned;\n }\n configChanged = true;\n }\n }\n\n // Remove PDaC rules from each artifact.\n const rules =\n config['rules'] && typeof config['rules'] === 'object' && !Array.isArray(config['rules'])\n ? (config['rules'] as Record<string, unknown>)\n : null;\n if (rules) {\n let rulesChanged = false;\n for (const [artifact, pdacRules] of Object.entries(managedRules)) {\n if (!Array.isArray(rules[artifact])) continue;\n const list = rules[artifact] as string[];\n const filtered = list.filter((r) => !pdacRules.includes(r));\n if (filtered.length !== list.length) {\n if (filtered.length === 0) {\n delete rules[artifact];\n } else {\n rules[artifact] = filtered;\n }\n rulesChanged = true;\n }\n }\n if (rulesChanged) {\n if (Object.keys(rules).length === 0) {\n delete config['rules'];\n }\n configChanged = true;\n }\n }\n\n // Remove PDaC operation guidance.\n const operations =\n config['operations'] &&\n typeof config['operations'] === 'object' &&\n !Array.isArray(config['operations'])\n ? (config['operations'] as Record<string, unknown>)\n : null;\n if (operations) {\n let opsChanged = false;\n for (const opKey of ['apply', 'archive'] as const) {\n const pdacGuidance = managedOperations[opKey] ?? [];\n const op =\n operations[opKey] &&\n typeof operations[opKey] === 'object' &&\n !Array.isArray(operations[opKey])\n ? (operations[opKey] as Record<string, unknown>)\n : null;\n if (!op || !Array.isArray(op['guidance'])) continue;\n const list = op['guidance'] as string[];\n const filtered = list.filter((g) => !pdacGuidance.includes(g));\n if (filtered.length !== list.length) {\n if (filtered.length === 0) {\n delete op['guidance'];\n } else {\n op['guidance'] = filtered;\n }\n opsChanged = true;\n }\n if (Object.keys(op).length === 0) {\n delete operations[opKey];\n }\n }\n if (opsChanged) {\n if (Object.keys(operations).length === 0) {\n delete config['operations'];\n }\n configChanged = true;\n }\n }\n\n if (configChanged) {\n if (!dryRun) await writeFile(configPath(root), serializeConfig(config), 'utf8');\n restored.push(CONFIG_RELATIVE);\n }\n }\n\n // Remove the installed CI example.\n if (await pathExists(ciExamplePath(root))) {\n if (!dryRun) {\n await rm(ciExamplePath(root), { force: true });\n }\n removed.push(CI_EXAMPLE_RELATIVE);\n }\n\n // Remove the installed product schema, deleting only paths with a per-file ownership record and\n // bytes still proven managed by the recorded digest (or, for a legacy path-only record, the\n // current asset). A diverged or unrecorded file is preserved and reported, and with no recorded\n // product schema at all nothing under openspec/schemas is touched. Directories are pruned only\n // when the removal emptied them, so adjacent schemas and user files inside ours survive.\n const recordedSchema = recordedProductSchemaFiles(previousMeta);\n if (recordedSchema !== undefined) {\n const assetContentByRelative = new Map(\n (await loadProductChangeSchemaAssets()).map((asset) => [\n `${OPENSPEC_PRODUCT_CHANGE_SCHEMA_RELATIVE}/${asset.relative}`,\n asset.content,\n ]),\n );\n const schemaFileSet = Object.keys(recordedSchema).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));\n const pruneCandidates = new Set<string>();\n for (const relative of schemaFileSet) {\n const absolute = resolveInRepository(root, relative, 'the OpenSpec integration');\n if (!(await pathExists(absolute))) continue;\n const existing = await readFile(absolute, 'utf8');\n const recordedDigest = recordedSchema[relative];\n const proven =\n existing === assetContentByRelative.get(relative) ||\n (recordedDigest !== undefined &&\n recordedDigest !== '' &&\n contentDigest(existing) === recordedDigest);\n if (!proven) {\n preserved.push(relative);\n continue;\n }\n if (!dryRun) {\n await rm(absolute, { force: true });\n let parent = dirname(relative);\n while (\n parent.startsWith('openspec/schemas') &&\n parent !== 'openspec/schemas' &&\n parent !== 'openspec'\n ) {\n pruneCandidates.add(parent);\n parent = parent.split('/').slice(0, -1).join('/');\n }\n pruneCandidates.add('openspec/schemas');\n }\n removed.push(relative);\n }\n // A schema-level record is not ownership of every current asset path. If a path is absent\n // from the per-file record, preserve and report it even when its bytes equal today's asset.\n for (const relative of [...assetContentByRelative.keys()].sort((a, b) =>\n a < b ? -1 : a > b ? 1 : 0,\n )) {\n if (recordedSchema[relative] !== undefined) continue;\n const absolute = resolveInRepository(root, relative, 'the OpenSpec integration');\n if (await pathExists(absolute)) preserved.push(relative);\n }\n if (!dryRun) {\n // Deepest first, so a directory is only considered after its children were.\n const ordered = [...pruneCandidates].sort((a, b) => b.length - a.length);\n for (const relative of ordered) {\n const absolute = resolveInRepository(root, relative, 'the OpenSpec integration');\n try {\n const entries = await readdir(absolute);\n if (entries.length === 0) await rmdir(absolute);\n } catch (error) {\n if (!isNotFound(error)) throw error;\n }\n }\n }\n }\n\n const recordedRecovery = recordedRecoverySchemaFiles(previousMeta);\n if (recordedRecovery !== undefined) {\n const recoveryAssets = new Map(\n (await loadProductRecoverySchemaAssets()).map((asset) => [\n `${OPENSPEC_PRODUCT_RECOVERY_SCHEMA_RELATIVE}/${asset.relative}`,\n asset.content,\n ]),\n );\n for (const relative of Object.keys(recordedRecovery).sort()) {\n const absolute = resolveInRepository(root, relative, 'the OpenSpec integration');\n if (!(await pathExists(absolute))) continue;\n const existing = await readFile(absolute, 'utf8');\n const recordedDigest = recordedRecovery[relative];\n const proven =\n existing === recoveryAssets.get(relative) ||\n (recordedDigest !== '' && contentDigest(existing) === recordedDigest);\n if (!proven) {\n preserved.push(relative);\n } else {\n if (!dryRun) await rm(absolute, { force: true });\n removed.push(relative);\n }\n }\n if (!dryRun) {\n for (const relative of [\n `${OPENSPEC_PRODUCT_RECOVERY_SCHEMA_RELATIVE}/templates`,\n OPENSPEC_PRODUCT_RECOVERY_SCHEMA_RELATIVE,\n 'openspec/schemas',\n ]) {\n const absolute = resolveInRepository(root, relative, 'the OpenSpec integration');\n try {\n if ((await readdir(absolute)).length === 0) await rmdir(absolute);\n } catch (error) {\n if (!isNotFound(error)) throw error;\n }\n }\n }\n }\n\n // Remove metadata file.\n if (await pathExists(metaPath(root))) {\n if (!dryRun) {\n await rm(metaPath(root), { force: true });\n }\n removed.push(META_RELATIVE);\n }\n\n return { removed, restored, preserved };\n}\n","/**\n * The OpenSpec `product-change` schema shipped by this integration.\n *\n * The schema is a set of static assets (schema.yaml, templates, bridge scripts) bundled with the\n * package and installed into a consumer repository at `openspec/schemas/product-change/`. OpenSpec reads\n * project-local schemas from that directory natively; installing files there uses the framework's\n * official extension surface, exactly as merging guidance into `openspec/config.yaml` does.\n *\n * The assets are data. Loading them performs no repository writes; installation, verification and\n * removal live in `index.ts` beside the rest of the managed-surface lifecycle.\n */\nimport { readdir, readFile } from 'node:fs/promises';\nimport { fileURLToPath } from 'node:url';\nimport { join } from 'node:path';\n\n/** The OpenSpec schema name (the directory key OpenSpec resolves). */\nexport const OPENSPEC_PRODUCT_CHANGE_SCHEMA_NAME = 'product-change';\n\n/** The repository-relative directory the product schema is installed into. */\nexport const OPENSPEC_PRODUCT_CHANGE_SCHEMA_RELATIVE = 'openspec/schemas/product-change';\n\n/**\n * The minimum OpenSpec CLI version the product workflow needs. Project-local schemas are far\n * older, but three 1.7.0 behaviours are load-bearing: `openspec new change` records\n * `skip_specs: true` for schemas with no specs artifact (so `openspec validate` stops demanding\n * delta specs), artifacts follow schema declaration order, and the generated skills stop\n * overriding custom-schema instructions with spec-driven patterns. Below this floor the schema\n * files are inert data: the citation lane keeps working and the product workflow is reported\n * unavailable, never broken.\n */\nexport const PRODUCT_CHANGE_SCHEMA_MIN_OPENSPEC = '1.7.0';\n\n/** The separate hosted brownfield recovery workload. */\nexport const OPENSPEC_PRODUCT_RECOVERY_SCHEMA_NAME = 'product-recovery';\nexport const OPENSPEC_PRODUCT_RECOVERY_SCHEMA_RELATIVE = 'openspec/schemas/product-recovery';\n\n/** One bundled product-schema file: its path relative to the installed schema directory. */\nexport interface ProductChangeSchemaAsset {\n relative: string;\n content: string;\n}\n\n/** The bundled asset directory, resolved from this module (both src/ and dist/ sit one level below the package root). */\nfunction assetsDir(): string {\n return fileURLToPath(new URL('../assets/openspec-product-change-schema/', import.meta.url));\n}\n\nasync function collectFiles(dir: string, prefix: string): Promise<string[]> {\n const entries = await readdir(dir, { withFileTypes: true });\n const files: string[] = [];\n for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {\n const relative = prefix === '' ? entry.name : `${prefix}/${entry.name}`;\n if (entry.isDirectory()) {\n files.push(...(await collectFiles(join(dir, entry.name), relative)));\n } else if (entry.isFile()) {\n files.push(relative);\n }\n }\n return files;\n}\n\n/**\n * Load the bundled product-schema assets, sorted by relative path so installation, metadata and\n * comparison all see one deterministic order.\n */\nexport async function loadProductChangeSchemaAssets(): Promise<ProductChangeSchemaAsset[]> {\n const dir = assetsDir();\n const relatives = await collectFiles(dir, '');\n const assets: ProductChangeSchemaAsset[] = [];\n for (const relative of relatives.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))) {\n assets.push({\n relative,\n content: await readFile(join(dir, ...relative.split('/')), 'utf8'),\n });\n }\n return assets;\n}\n\n/** Load the bundled recovery schema assets using the same deterministic asset contract. */\nexport async function loadProductRecoverySchemaAssets(): Promise<ProductChangeSchemaAsset[]> {\n const dir = fileURLToPath(\n new URL('../assets/openspec-product-recovery-schema/', import.meta.url),\n );\n const relatives = await collectFiles(dir, '');\n return Promise.all(\n relatives\n .sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))\n .map(async (relative) => ({\n relative,\n content: await readFile(join(dir, ...relative.split('/')), 'utf8'),\n })),\n );\n}\n","import crossSpawn from 'cross-spawn';\n\nexport interface CommandResult {\n stdout: string;\n stderr: string;\n}\n\nexport interface RunCommandOptions {\n cwd?: string;\n env?: NodeJS.ProcessEnv;\n maxBuffer?: number;\n}\n\nfunction renderCommand(command: string, args: string[]): string {\n return [command, ...args]\n .map((value) => (/^[A-Za-z0-9_./:@=+-]+$/.test(value) ? value : JSON.stringify(value)))\n .join(' ');\n}\n\n/**\n * Run one executable with an argument array and no shell interpolation.\n *\n * cross-spawn resolves Windows command shims and escapes their arguments while preserving native\n * executable spawning on POSIX. This keeps repository-local node_modules/.bin commands portable\n * without passing an argument array through `shell: true`.\n */\nexport function runCommand(\n command: string,\n args: string[],\n options: RunCommandOptions = {},\n): Promise<CommandResult> {\n return new Promise((resolve, reject) => {\n const maxBuffer = options.maxBuffer ?? 10 * 1024 * 1024;\n const child = crossSpawn(command, args, {\n cwd: options.cwd,\n env: options.env,\n stdio: ['ignore', 'pipe', 'pipe'],\n windowsHide: true,\n });\n let stdout = '';\n let stderr = '';\n let settled = false;\n\n const fail = (error: Error): void => {\n if (settled) return;\n settled = true;\n reject(error);\n };\n const append = (stream: 'stdout' | 'stderr', chunk: string): void => {\n if (settled) return;\n if (stream === 'stdout') stdout += chunk;\n else stderr += chunk;\n if (Buffer.byteLength(stdout) + Buffer.byteLength(stderr) > maxBuffer) {\n child.kill();\n fail(\n new Error(`${renderCommand(command, args)} exceeded its ${maxBuffer}-byte output limit`),\n );\n }\n };\n\n child.stdout?.setEncoding('utf8');\n child.stderr?.setEncoding('utf8');\n child.stdout?.on('data', (chunk: string) => append('stdout', chunk));\n child.stderr?.on('data', (chunk: string) => append('stderr', chunk));\n child.once('error', (error) => {\n fail(new Error(`Could not start ${renderCommand(command, args)}: ${error.message}`));\n });\n child.once('close', (code, signal) => {\n if (settled) return;\n if (code === 0) {\n settled = true;\n resolve({ stdout, stderr });\n return;\n }\n const status = code === null ? `signal ${signal ?? 'unknown'}` : `exit ${code}`;\n const detail = stderr.trim() || stdout.trim();\n fail(\n new Error(\n `${renderCommand(command, args)} failed with ${status}${detail ? `: ${detail}` : ''}`,\n ),\n );\n });\n });\n}\n","/**\n * Shared workspace helpers for the OpenSpec integration: workspace detection and the\n * child-process environment that lets a devDependency-installed OpenSpec CLI count as installed.\n */\nimport { access } from 'node:fs/promises';\nimport { join } from 'node:path';\n\n/** Check whether a filesystem path exists. */\nexport async function pathExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Check if an OpenSpec workspace exists at the given root (openspec/ directory present). */\nexport async function isOpenSpecWorkspace(root: string): Promise<boolean> {\n return pathExists(join(root, 'openspec'));\n}\n\n/**\n * Build a child-process environment whose PATH starts with the repository's node_modules/.bin,\n * so an OpenSpec installed as a devDependency counts as installed. Windows spells the variable\n * `Path` (and env objects lose the case-insensitive lookup once spread), so every casing is\n * collapsed into a single `PATH` entry.\n */\nexport function envWithLocalBin(root: string): NodeJS.ProcessEnv {\n const env: NodeJS.ProcessEnv = { ...process.env };\n const separator = process.platform === 'win32' ? ';' : ':';\n let existing = '';\n for (const key of Object.keys(env)) {\n if (key.toUpperCase() === 'PATH') {\n existing = env[key] ?? '';\n delete env[key];\n }\n }\n const localBin = join(root, 'node_modules', '.bin');\n env['PATH'] = existing ? `${localBin}${separator}${existing}` : localBin;\n return env;\n}\n","/**\n * OpenSpec consumer-document enumeration: the OpenSpec implementation of the framework-neutral\n * SDD integration-provider contract defined in @prodshape/core.\n *\n * Everything OpenSpec-specific about verification lives here — the `openspec/` layout, the\n * changes/archive lifecycle split, the artifact-file conventions, and the `openspec` CLI — so\n * the core package stays SDD-framework blind. The provider only enumerates the expected current\n * consumer-document population; scope classification and citation verification are core\n * responsibilities that operate on the enumeration.\n */\nimport { readFileSync } from 'node:fs';\nimport { readdir, readFile } from 'node:fs/promises';\nimport { join, relative, sep } from 'node:path';\nimport fg from 'fast-glob';\nimport { codes } from '@prodshape/core';\nimport type {\n ConsumerDocument,\n ConsumerDocumentEnumeration,\n Diagnostic,\n EnumerateConsumerDocumentsOptions,\n SddIntegrationProvider,\n} from '@prodshape/core';\nimport { runCommand } from './process.js';\nimport { envWithLocalBin, isOpenSpecWorkspace, pathExists } from './workspace.js';\n\n/**\n * The artifact files that make up an OpenSpec change, relative to the change directory.\n * `specs/` is a directory containing one or more `<capability>/spec.md` files.\n */\nconst CHANGE_ARTIFACT_FILES: Array<{ file: string; kind: string }> = [\n { file: 'proposal.md', kind: 'proposal' },\n { file: 'design.md', kind: 'design' },\n { file: 'tasks.md', kind: 'tasks' },\n];\n\n/** Run `openspec` with the given args at the repository root, returning stdout. */\nasync function runOpenSpec(\n repoRoot: string,\n args: string[],\n): Promise<{ stdout: string; stderr: string }> {\n return runCommand('openspec', args, {\n cwd: repoRoot,\n env: envWithLocalBin(repoRoot),\n maxBuffer: 10 * 1024 * 1024,\n });\n}\n\n/** Parsed entry from `openspec list --json`. */\ninterface OpenSpecListEntry {\n name: string;\n}\n\n/**\n * Call `openspec list --json` and return the change names. Returns `null` when the CLI is\n * unavailable or the output is unparseable, so the caller can fall back to filesystem scanning.\n */\nasync function openspecListChanges(repoRoot: string): Promise<string[] | null> {\n try {\n const { stdout } = await runOpenSpec(repoRoot, ['list', '--json']);\n const parsed = JSON.parse(stdout) as { changes?: OpenSpecListEntry[] };\n if (!Array.isArray(parsed.changes)) return null;\n return parsed.changes.map((c) => c.name).filter((n): n is string => typeof n === 'string');\n } catch {\n return null;\n }\n}\n\n/** Check whether `openspec` is runnable (PATH or node_modules/.bin) with a parseable version. */\nexport async function isOpenSpecCliAvailable(repoRoot?: string): Promise<boolean> {\n try {\n const { stdout } = await runCommand('openspec', ['--version'], {\n ...(repoRoot ? { cwd: repoRoot, env: envWithLocalBin(repoRoot) } : {}),\n });\n return /\\d+\\.\\d+\\.\\d+/.test(stdout);\n } catch {\n return false;\n }\n}\n\n/** Discover all `.md` files under a directory, returning absolute paths sorted. */\nasync function discoverMarkdownFiles(dir: string): Promise<string[]> {\n const entries = await fg('**/*.md', {\n cwd: dir,\n absolute: true,\n dot: false,\n ignore: ['**/node_modules/**'],\n });\n return entries.map((e) => e.split('/').join(sep)).sort();\n}\n\n/** Build a {@link ConsumerDocument} from an absolute path. */\nfunction buildConsumerDocument(\n absolutePath: string,\n repoRoot: string,\n opts: { change?: string; archived: boolean; artifactKind?: string },\n): ConsumerDocument {\n return {\n path: relative(repoRoot, absolutePath).split(sep).join('/'),\n absolutePath,\n change: opts.change,\n archived: opts.archived,\n artifactKind: opts.artifactKind,\n };\n}\n\n/** Discover artifact files for a single change directory. */\nasync function discoverChangeDocuments(\n changeDir: string,\n repoRoot: string,\n change: string,\n archived: boolean,\n): Promise<ConsumerDocument[]> {\n const docs: ConsumerDocument[] = [];\n\n for (const { file, kind } of CHANGE_ARTIFACT_FILES) {\n const absolutePath = join(changeDir, file);\n try {\n await readFile(absolutePath);\n docs.push(\n buildConsumerDocument(absolutePath, repoRoot, { change, archived, artifactKind: kind }),\n );\n } catch {\n // File doesn't exist; skip.\n }\n }\n\n const specsDir = join(changeDir, 'specs');\n try {\n const specFiles = await discoverMarkdownFiles(specsDir);\n for (const absolutePath of specFiles) {\n docs.push(\n buildConsumerDocument(absolutePath, repoRoot, { change, archived, artifactKind: 'specs' }),\n );\n }\n } catch {\n // No specs/ directory; skip.\n }\n\n return docs;\n}\n\n/** Discover all `.md` files under `openspec/specs/` (the current specs, not changes). */\nasync function discoverSpecDocuments(\n specsDir: string,\n repoRoot: string,\n): Promise<ConsumerDocument[]> {\n const docs: ConsumerDocument[] = [];\n try {\n const specFiles = await discoverMarkdownFiles(specsDir);\n for (const absolutePath of specFiles) {\n docs.push(\n buildConsumerDocument(absolutePath, repoRoot, { archived: false, artifactKind: 'spec' }),\n );\n }\n } catch {\n // No specs/ directory; skip.\n }\n return docs;\n}\n\n/** Discover archived change directories under `openspec/changes/archive/`. */\nasync function discoverArchivedChanges(archiveDir: string): Promise<string[]> {\n try {\n const entries = await readdir(archiveDir, { withFileTypes: true });\n return entries\n .filter((e) => e.isDirectory())\n .map((e) => e.name)\n .sort();\n } catch {\n return [];\n }\n}\n\n/**\n * Enumerate the consumer-document population of an OpenSpec workspace.\n *\n * Uses `openspec list --json` to determine the current (non-archived) changes. The OpenSpec CLI\n * excludes `openspec/changes/archive/` from `list`, so the default population is current-only.\n * When `includeArchived` is true, archived changes under `openspec/changes/archive/` are also\n * scanned via the filesystem (the CLI cannot address archived changes by name).\n *\n * For each change, the artifact files are resolved by filesystem convention: `proposal.md`,\n * `specs/` (one or more spec files), `design.md`, `tasks.md`. The `openspec show --json` output\n * does not include artifact paths, so convention-based resolution is the stable contract.\n *\n * For `openspec/specs/` (the current specs, not changes), all `.md` files are discovered.\n *\n * If the OpenSpec CLI is not runnable, enumeration falls back to filesystem scanning of\n * `openspec/changes/` (excluding `archive/` unless `includeArchived`) and reports PRODUCT069:\n * the population then rests on layout convention alone, which the gate must surface rather than\n * silently trust.\n */\nexport async function enumerateOpenSpecDocuments(\n repoRoot: string,\n options?: EnumerateConsumerDocumentsOptions,\n): Promise<ConsumerDocumentEnumeration> {\n const includeArchived = options?.includeArchived ?? false;\n const root = join(repoRoot, 'openspec');\n const changesDir = join(root, 'changes');\n const archiveDir = join(changesDir, 'archive');\n const specsDir = join(root, 'specs');\n\n const documents: ConsumerDocument[] = [];\n const diagnostics: Diagnostic[] = [];\n\n // A workspace with no `openspec/changes/` directory at all (specs only) has zero changes by\n // construction: nothing to discover, no CLI needed, and no PRODUCT069 — older OpenSpec CLIs\n // (1.3.x) even reject `list` outright in such a workspace. Otherwise prefer\n // `openspec list --json` and fall back to filesystem scanning.\n let currentChangeNames: string[] | null = (await pathExists(changesDir))\n ? await openspecListChanges(repoRoot)\n : [];\n\n if (currentChangeNames === null) {\n diagnostics.push({\n severity: 'error',\n code: codes.openSpecCliMissing,\n message: `OpenSpec CLI not available; the document population was enumerated by filesystem convention instead of 'openspec list'. Install with 'npm install -g @fission-ai/openspec@1' for authoritative population discovery.`,\n file: 'openspec/',\n });\n // archive/ is excluded here: archived changes are enumerated separately below.\n try {\n const entries = await readdir(changesDir, { withFileTypes: true });\n currentChangeNames = entries\n .filter((e) => e.isDirectory() && e.name !== 'archive')\n .map((e) => e.name)\n .sort();\n } catch {\n currentChangeNames = [];\n }\n }\n\n if (options?.change) {\n const wanted = options.change;\n if (currentChangeNames.includes(wanted)) {\n currentChangeNames = [wanted];\n } else {\n // The requested change isn't in the current list. Check the archive below if asked.\n currentChangeNames = [];\n }\n }\n\n for (const name of currentChangeNames) {\n const changeDir = join(changesDir, name);\n documents.push(...(await discoverChangeDocuments(changeDir, repoRoot, name, false)));\n }\n\n if (includeArchived) {\n const archivedNames = await discoverArchivedChanges(archiveDir);\n for (const name of archivedNames) {\n // If a specific change was requested and it wasn't current, check the archive.\n if (options?.change && name !== options.change) continue;\n const changeDir = join(archiveDir, name);\n documents.push(...(await discoverChangeDocuments(changeDir, repoRoot, name, true)));\n }\n }\n\n documents.push(...(await discoverSpecDocuments(specsDir, repoRoot)));\n\n return {\n documents,\n root: 'openspec',\n includeArchived,\n diagnostics,\n };\n}\n\n/** The integration version, read once from this package's own manifest. */\nconst integrationVersion = (\n JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')) as {\n version: string;\n }\n).version;\n\n/**\n * The OpenSpec implementation of the SDD integration-provider contract.\n * `prodshape citations verify --provider openspec` verifies the population this enumerates.\n */\nexport const openSpecProvider: SddIntegrationProvider = {\n name: 'openspec',\n version: integrationVersion,\n detectWorkspace: isOpenSpecWorkspace,\n enumerateDocuments: enumerateOpenSpecDocuments,\n};\n","/**\n * The deterministic rails of the OpenSpec-hosted PDaC product workflow.\n *\n * An OpenSpec change created with the `product-change` schema hosts a PDaC Product Change at\n * `openspec/changes/<name>/product-change/` (change.md plus proposed/**, the normative delta format).\n * OpenSpec owns the workflow orchestration (`/opsx:new`, `/opsx:continue`, `/opsx:apply`,\n * `/opsx:archive`); this module owns what OpenSpec cannot provide: reading the accepted model,\n * compiling the semantic graph in memory, validating the hosted delta as an overlay on the\n * untouched baseline, applying a valid delta deterministically, and deriving fresh context for a\n * later delivery workflow.\n *\n * Two boundaries are deliberate:\n * - Validation is mandatory and happens AT APPLY TIME: `applyOpenSpecProductChange` performs its\n * own full validation immediately before planning, so a stale earlier validate call can never\n * authorise a write (no time-of-check to time-of-use gap). `validateOpenSpecProductChange`\n * exists for explicit preflight, wrappers, diagnostics and tests; it is never a precondition\n * token.\n * - Authorisation policy stays outside this deterministic mechanism: `status: approved` in the\n * hosted change.md is the apply-authorised protocol state (PRODUCT028 refuses anything else).\n * ProductShape's accepted policy keeps product approval human; this rail verifies the recorded\n * state but cannot identify or judge the actor, and nothing here performs the transition.\n *\n * Everything deterministic comes from `@prodshape/core`. The one lifecycle divergence from the\n * native Product Change flow is the container: the hosted change directory belongs to OpenSpec,\n * so the plan's `move-change` action (ProductShape's `changes/active -> changes/completed`\n * archive move) is filtered out and `openspec archive` moves the container instead, as a separate\n * action after apply. `planApply`'s contract sanctions callers filtering the returned action set.\n */\nimport { readdir, readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { parse } from 'yaml';\nimport {\n analyzeImpact,\n applyHostedProductChange,\n assessHostedProductChange,\n compareCodePoints,\n isNotFound,\n loadChange,\n loadLiveChanges,\n openRepository,\n sortDiagnostics,\n validateBaseline,\n} from '@prodshape/core';\nimport type {\n BaselineValidation,\n Diagnostic,\n HostedProductApplyOutcome,\n HostedProductApplyResult,\n ImpactReport,\n LoadedArtifact,\n LoadedChange,\n ProductGraph,\n ProductRepository,\n} from '@prodshape/core';\nimport { OPENSPEC_PRODUCT_CHANGE_SCHEMA_NAME } from './product-change-schema.js';\nimport { pathExists } from './workspace.js';\n\n/** The subdirectory of an OpenSpec change that hosts the PDaC Product Change delta. */\nexport const OPENSPEC_PRODUCT_SUBDIR = 'product-change';\n\n/** OpenSpec 1.11's change-id grammar (`isKebabId`): one folder-safe path segment. */\nconst OPENSPEC_CHANGE_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;\n\nfunction assertOpenSpecChangeName(changeName: string): void {\n if (changeName.length > 200 || !OPENSPEC_CHANGE_NAME.test(changeName)) {\n throw new Error(\n `OpenSpec change '${changeName}' is not a valid OpenSpec change name; use kebab-case with lowercase letters, numbers and single hyphen separators.`,\n );\n }\n}\n\n/** The accepted product model, read directly from disk and validated, with its in-memory graph. */\nexport interface ProductModelInspection {\n repo: ProductRepository;\n artifacts: LoadedArtifact[];\n graph: ProductGraph;\n /** Configuration diagnostics plus full baseline validation, sorted. */\n diagnostics: Diagnostic[];\n}\n\n/**\n * Read the accepted model at the repository's product root (default docs/product), compile the\n * semantic graph in memory and validate. Needs no `.product/generated` output and no\n * configuration file: kernel defaults apply when `.product/config.yaml` is absent.\n */\nexport async function inspectProductModel(root: string): Promise<ProductModelInspection> {\n const repo = await openRepository(root);\n const baseline = await validateBaseline(repo);\n return {\n repo,\n artifacts: baseline.artifacts,\n graph: baseline.graph,\n diagnostics: sortDiagnostics([...repo.configDiagnostics, ...baseline.diagnostics]),\n };\n}\n\n/** One OpenSpec change hosting a PDaC Product Change delta. */\nexport interface OpenSpecProductChangeRef {\n /** The OpenSpec change name (the directory under openspec/changes/). */\n name: string;\n /** Absolute path of the hosted PDaC change directory (…/<name>/product-change). */\n dir: string;\n /** Repository-relative POSIX path of the hosted change.md. */\n file: string;\n}\n\n/** The result of reading a change's `.openspec.yaml` schema pin. */\ntype HostedSchemaPin =\n { schema: string } | { problem: 'missing' } | { problem: 'malformed'; detail: string };\n\nconst OPEN_SPEC_KEBAB_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;\n\n/** Validate the OpenSpec 1.11 ChangeMetadataSchema fields without taking a runtime dependency. */\nfunction openSpecChangeMetadataProblem(parsed: unknown): string | undefined {\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return 'the document must be an object';\n }\n const record = parsed as Record<string, unknown>;\n if (typeof record['schema'] !== 'string' || record['schema'].length === 0) {\n return 'schema is required';\n }\n const created = record['created'];\n if (\n created !== undefined &&\n (typeof created !== 'string' || !/^\\d{4}-\\d{2}-\\d{2}$/u.test(created))\n ) {\n return 'created must be YYYY-MM-DD format';\n }\n const goal = record['goal'];\n if (goal !== undefined && (typeof goal !== 'string' || goal.length === 0)) {\n return 'goal must be a non-empty string';\n }\n const affectedAreas = record['affected_areas'];\n if (\n affectedAreas !== undefined &&\n (!Array.isArray(affectedAreas) ||\n !affectedAreas.every((area) => typeof area === 'string' && area.length > 0))\n ) {\n return 'affected_areas must be an array of non-empty strings';\n }\n const initiative = record['initiative'];\n if (initiative !== undefined) {\n if (initiative === null || typeof initiative !== 'object' || Array.isArray(initiative)) {\n return 'initiative must be an object';\n }\n const link = initiative as Record<string, unknown>;\n if (\n Object.keys(link).some((key) => key !== 'store' && key !== 'id') ||\n typeof link['store'] !== 'string' ||\n !OPEN_SPEC_KEBAB_ID.test(link['store']) ||\n typeof link['id'] !== 'string' ||\n !OPEN_SPEC_KEBAB_ID.test(link['id'])\n ) {\n return 'initiative must contain only kebab-case store and id fields';\n }\n }\n if (record['skip_specs'] !== undefined && typeof record['skip_specs'] !== 'boolean') {\n return 'skip_specs must be a boolean';\n }\n if (\n record['retire_capabilities'] !== undefined &&\n typeof record['retire_capabilities'] !== 'boolean'\n ) {\n return 'retire_capabilities must be a boolean';\n }\n return undefined;\n}\n\n/**\n * Read the schema a change is pinned to. OpenSpec records it in the change's `.openspec.yaml`\n * (`openspec new change <name> --schema product-change` writes it), and the hosted product rail treats\n * that pin as load-bearing: OpenSpec selects the workflow, so a change is a product change\n * because its container says so, never because a `product/` directory happens to exist.\n */\nasync function readHostedSchemaPin(changeDir: string): Promise<HostedSchemaPin> {\n const metadataPath = join(changeDir, '.openspec.yaml');\n if (!(await pathExists(metadataPath))) return { problem: 'missing' };\n let parsed: unknown;\n try {\n parsed = parse(await readFile(metadataPath, 'utf8'));\n } catch (error) {\n return {\n problem: 'malformed',\n detail: error instanceof Error ? error.message : String(error),\n };\n }\n const problem = openSpecChangeMetadataProblem(parsed);\n if (problem !== undefined) return { problem: 'malformed', detail: problem };\n return { schema: (parsed as Record<string, string>)['schema']! };\n}\n\nfunction hostedSchemaPinError(\n changeName: string,\n pin: Exclude<HostedSchemaPin, { schema: string }>,\n): Error {\n if (pin.problem === 'missing') {\n return new Error(\n `OpenSpec change '${changeName}' has no .openspec.yaml, so its schema pin is unknown; the hosted product rail refuses it. Create product changes with: openspec new change ${changeName} --schema ${OPENSPEC_PRODUCT_CHANGE_SCHEMA_NAME}.`,\n );\n }\n return new Error(\n `OpenSpec change '${changeName}' has an unreadable .openspec.yaml (${pin.detail}); the hosted product rail refuses it until the metadata is valid under OpenSpec 1.11.`,\n );\n}\n\n/**\n * List the OpenSpec changes that host a PDaC Product Change: pinned to `schema: product-change` in\n * `.openspec.yaml` AND carrying `product-change/change.md`. The OpenSpec archive is history and is never\n * listed; a change without a product delta (for example one whose honest intent verdict was \"no\n * product delta\") is not a product change; and a change pinned to another schema is not one\n * either, whatever directories it contains, so it never enters the product rail or its\n * concurrency input.\n */\nexport async function listOpenSpecProductChanges(\n root: string,\n): Promise<OpenSpecProductChangeRef[]> {\n const changesDir = join(root, 'openspec', 'changes');\n let entries;\n try {\n entries = await readdir(changesDir, { withFileTypes: true });\n } catch (error) {\n if (isNotFound(error)) return [];\n throw error;\n }\n const names = entries\n .filter((entry) => entry.isDirectory() && entry.name !== 'archive')\n .map((entry) => entry.name)\n .sort(compareCodePoints);\n const refs: OpenSpecProductChangeRef[] = [];\n for (const name of names) {\n const changeDir = join(changesDir, name);\n const dir = join(changeDir, OPENSPEC_PRODUCT_SUBDIR);\n if (!(await pathExists(join(dir, 'change.md')))) continue;\n assertOpenSpecChangeName(name);\n const pin = await readHostedSchemaPin(changeDir);\n if ('problem' in pin) throw hostedSchemaPinError(name, pin);\n if (pin.schema !== OPENSPEC_PRODUCT_CHANGE_SCHEMA_NAME) continue;\n refs.push({\n name,\n dir,\n file: `openspec/changes/${name}/${OPENSPEC_PRODUCT_SUBDIR}/change.md`,\n });\n }\n return refs;\n}\n\n/** Load one hosted Product Change through core's loader (change.md plus proposed/**). */\nexport async function loadOpenSpecProductChange(\n root: string,\n changeName: string,\n): Promise<LoadedChange> {\n const repo = await openRepository(root);\n return loadHostedChange(root, repo, changeName);\n}\n\nasync function loadHostedChange(\n root: string,\n repo: ProductRepository,\n changeName: string,\n): Promise<LoadedChange> {\n assertOpenSpecChangeName(changeName);\n const changesDir = join(root, 'openspec', 'changes');\n const changeDir = join(changesDir, changeName);\n const deltaDir = join(changeDir, OPENSPEC_PRODUCT_SUBDIR);\n const hasDelta =\n changeName !== 'archive' &&\n (await pathExists(changeDir)) &&\n (await pathExists(join(deltaDir, 'change.md')));\n if (!hasDelta) {\n const refs = await listOpenSpecProductChanges(root);\n const known = refs.map((candidate) => candidate.name).join(', ') || 'none';\n throw new Error(\n `No OpenSpec product change named '${changeName}' under openspec/changes/ (a product change hosts product-change/change.md; found: ${known}).`,\n );\n }\n // The schema pin is load-bearing and fails closed: without a readable pin naming the product\n // schema, this change never enters the product rail, however product-shaped its contents look.\n const pin = await readHostedSchemaPin(changeDir);\n if ('problem' in pin) {\n throw hostedSchemaPinError(changeName, pin);\n }\n if (pin.schema !== OPENSPEC_PRODUCT_CHANGE_SCHEMA_NAME) {\n throw new Error(\n `OpenSpec change '${changeName}' is pinned to schema '${pin.schema}', not '${OPENSPEC_PRODUCT_CHANGE_SCHEMA_NAME}'; the hosted product rail refuses it. Product intent travels through a change created with: openspec new change <name> --schema ${OPENSPEC_PRODUCT_CHANGE_SCHEMA_NAME}.`,\n );\n }\n return loadChange(deltaDir, repo.root, repo.registry);\n}\n\nasync function loadAllLiveChanges(root: string, repo: ProductRepository): Promise<LoadedChange[]> {\n const refs = await listOpenSpecProductChanges(root);\n return loadLiveChanges(\n repo,\n refs.map((ref) => ref.dir),\n );\n}\n\n/** The result of validating a hosted Product Change as an overlay on the untouched baseline. */\nexport interface OpenSpecProductChangeValidation {\n change: LoadedChange;\n baseline: BaselineValidation;\n overlayArtifacts: LoadedArtifact[];\n overlayGraph: ProductGraph;\n /** Configuration, baseline and change diagnostics, deduplicated and sorted. */\n diagnostics: Diagnostic[];\n /** The diagnostics that block, under the repository's warnings-as-errors policy. */\n blocking: Diagnostic[];\n}\n\n/**\n * Validate one hosted Product Change: the accepted baseline stays untouched, the delta is\n * compiled over it into an overlay, and the overlay is validated end to end (operation checks,\n * cross-container concurrency, open-questions state, full structural validation with the\n * PRODUCT023/PRODUCT024 overlay recodes). Useful as explicit preflight and for wrappers and\n * tests; apply never trusts it and revalidates on its own.\n */\nexport async function validateOpenSpecProductChange(\n root: string,\n changeName: string,\n): Promise<OpenSpecProductChangeValidation> {\n const repo = await openRepository(root);\n const all = await loadAllLiveChanges(root, repo);\n // An unknown name resolves through the loader, whose error names the known product changes.\n const change =\n all.find((candidate) => hostedName(root, candidate) === changeName) ??\n (await loadHostedChange(root, repo, changeName));\n const assessed = await assessHostedProductChange(repo, change, all);\n return {\n change,\n baseline: assessed.baseline,\n overlayArtifacts: assessed.overlayArtifacts,\n overlayGraph: assessed.overlayGraph,\n diagnostics: assessed.diagnostics,\n blocking: assessed.blocking,\n };\n}\n\n/** The hosted OpenSpec change name of a loaded change, or undefined for a native change. */\nfunction hostedName(root: string, change: LoadedChange): string | undefined {\n const normalizedRoot = root.replaceAll('\\\\', '/');\n const normalizedDir = change.dir.replaceAll('\\\\', '/');\n const relative = normalizedDir.startsWith(normalizedRoot)\n ? normalizedDir.slice(normalizedRoot.length).replace(/^\\//, '')\n : normalizedDir;\n const match = relative.match(/^openspec\\/changes\\/([^/]+)\\/product-change$/);\n return match?.[1];\n}\n\n/** How an apply invocation ended. */\nexport type OpenSpecProductApplyOutcome = HostedProductApplyOutcome;\n\n/** The container move is `openspec archive`, never part of apply. */\nexport type OpenSpecProductApplyResult = HostedProductApplyResult;\n\nexport async function applyOpenSpecProductChange(\n root: string,\n changeName: string,\n options: { dryRun?: boolean } = {},\n): Promise<OpenSpecProductApplyResult> {\n const repo = await openRepository(root);\n const all = await loadAllLiveChanges(root, repo);\n const change =\n all.find((candidate) => hostedName(root, candidate) === changeName) ??\n (await loadHostedChange(root, repo, changeName));\n return applyHostedProductChange({\n repo,\n change,\n liveChanges: all,\n dryRun: options.dryRun ?? false,\n excludeDocumentsUnder: [`openspec/changes/${changeName}`, 'openspec/changes/archive'],\n });\n}\n\n/** One artifact carried into delivery context, with the digest of its current accepted content. */\nexport interface DeliveryContextEntry {\n id: string;\n type?: string;\n title?: string;\n status?: string;\n file: string;\n digest: string;\n}\n\n/** Context derived from a fresh read of the accepted model, for a later delivery workflow. */\nexport interface DeliveryContextBundle {\n ids: string[];\n /** The requested artifacts, from the freshly read accepted model. */\n artifacts: DeliveryContextEntry[];\n /** Impact around each requested artifact (direction both, depth 2). */\n impacts: Record<string, ImpactReport>;\n /** Direct neighbours of the requested artifacts, deduplicated, requested ids excluded. */\n neighbours: DeliveryContextEntry[];\n /** Whether the freshly read accepted model validates with zero errors. */\n modelErrorFree: boolean;\n}\n\n/**\n * Derive delivery context from the accepted model as it exists on disk NOW: a fresh read and a\n * fresh graph, never state carried over from a product workflow. This is the handoff boundary a\n * future delivery workflow starts from.\n */\nexport async function deriveDeliveryContext(\n root: string,\n ids: string[],\n): Promise<DeliveryContextBundle> {\n const inspection = await inspectProductModel(root);\n const byId = new Map(\n inspection.artifacts\n .filter((artifact) => artifact.id !== undefined)\n .map((artifact) => [artifact.id as string, artifact]),\n );\n const entryOf = (artifact: LoadedArtifact): DeliveryContextEntry => {\n const entry: DeliveryContextEntry = {\n id: artifact.id as string,\n file: artifact.file,\n digest: artifact.digest,\n };\n if (artifact.type !== undefined) entry.type = artifact.type;\n if (artifact.title !== undefined) entry.title = artifact.title;\n if (artifact.status !== undefined) entry.status = artifact.status;\n return entry;\n };\n\n const sortedIds = [...ids].sort(compareCodePoints);\n const artifacts: DeliveryContextEntry[] = [];\n const impacts: Record<string, ImpactReport> = {};\n const neighbourIds = new Set<string>();\n for (const id of sortedIds) {\n const artifact = byId.get(id);\n if (!artifact) {\n throw new Error(`Unknown artifact id '${id}' in the accepted model at ${root}.`);\n }\n artifacts.push(entryOf(artifact));\n const report = analyzeImpact(inspection.graph, id, { direction: 'both', depth: 2 });\n impacts[id] = report;\n for (const item of report.direct) neighbourIds.add(item.id);\n }\n for (const id of sortedIds) neighbourIds.delete(id);\n const neighbours = [...neighbourIds]\n .sort(compareCodePoints)\n .map((id) => byId.get(id))\n .filter((artifact): artifact is LoadedArtifact => artifact !== undefined)\n .map(entryOf);\n\n return {\n ids: sortedIds,\n artifacts,\n impacts,\n neighbours,\n modelErrorFree: inspection.diagnostics.every((diagnostic) => diagnostic.severity !== 'error'),\n };\n}\n"],"mappings":"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","names":["readdir","readFile","isNotFound","parse","relative","join","readdir","readFile","join","join","readFile","readdir","readdir","readFile","join","join","readFile","readdir","relative","command","readFile","parse","relative","existing","isNotFound","readdir"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prodshape/integration-openspec",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "OpenSpec integration for Product Definition as Code: configures OpenSpec workspaces with PDaC citation rules and verifies citations against the OpenSpec population.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"cross-spawn": "^7.0.6",
|
|
29
29
|
"fast-glob": "^3.3.0",
|
|
30
30
|
"yaml": "^2.5.0",
|
|
31
|
-
"@prodshape/core": "0.21.
|
|
31
|
+
"@prodshape/core": "0.21.1"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@types/cross-spawn": "^6.0.6",
|
package/src/index.ts
CHANGED
|
@@ -121,6 +121,7 @@ export const PDAC_RULES: Record<string, string[]> = {
|
|
|
121
121
|
'When a design decision depends on canonical product text, cite the artifact it depends on.',
|
|
122
122
|
],
|
|
123
123
|
tasks: [
|
|
124
|
+
"A task that depends on a business rule's parameter cites the rule itself, not only the requirement derived from it: staleness follows the cited artifact's own digest, never its relationships.",
|
|
124
125
|
'A task that changes cited behaviour must include a follow-up task to refresh the affected citations.',
|
|
125
126
|
'Never infer implementation, verification, release or deployment from Product Change status; record that evidence in the delivery workflow.',
|
|
126
127
|
],
|
|
@@ -1307,14 +1308,16 @@ export async function checkOpenSpecIntegration(root: string): Promise<{
|
|
|
1307
1308
|
* asset bytes); hand-edited managed files are preserved and reported in `preserved`, and with
|
|
1308
1309
|
* no recorded product schema nothing under openspec/schemas is touched.
|
|
1309
1310
|
* - Deletes `.product/integrations/openspec.json`.
|
|
1310
|
-
* -
|
|
1311
|
+
* - `openspec/config.yaml` stays on disk with the PDaC content stripped and is reported in `restored`.
|
|
1312
|
+
* - `--dry-run` reports what would be removed, restored and preserved without writing.
|
|
1311
1313
|
*/
|
|
1312
1314
|
export async function removeOpenSpecIntegration(
|
|
1313
1315
|
root: string,
|
|
1314
1316
|
options: { dryRun?: boolean } = {},
|
|
1315
|
-
): Promise<{ removed: string[]; preserved: string[] }> {
|
|
1317
|
+
): Promise<{ removed: string[]; restored: string[]; preserved: string[] }> {
|
|
1316
1318
|
const { dryRun = false } = options;
|
|
1317
1319
|
const removed: string[] = [];
|
|
1320
|
+
const restored: string[] = [];
|
|
1318
1321
|
const preserved: string[] = [];
|
|
1319
1322
|
|
|
1320
1323
|
// Remove both what the metadata recorded as injected and what the current guidance would
|
|
@@ -1433,12 +1436,9 @@ export async function removeOpenSpecIntegration(
|
|
|
1433
1436
|
}
|
|
1434
1437
|
}
|
|
1435
1438
|
|
|
1436
|
-
if (configChanged
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
removed.push(CONFIG_RELATIVE);
|
|
1440
|
-
} else if (configChanged && dryRun) {
|
|
1441
|
-
removed.push(CONFIG_RELATIVE);
|
|
1439
|
+
if (configChanged) {
|
|
1440
|
+
if (!dryRun) await writeFile(configPath(root), serializeConfig(config), 'utf8');
|
|
1441
|
+
restored.push(CONFIG_RELATIVE);
|
|
1442
1442
|
}
|
|
1443
1443
|
}
|
|
1444
1444
|
|
|
@@ -1565,5 +1565,5 @@ export async function removeOpenSpecIntegration(
|
|
|
1565
1565
|
removed.push(META_RELATIVE);
|
|
1566
1566
|
}
|
|
1567
1567
|
|
|
1568
|
-
return { removed, preserved };
|
|
1568
|
+
return { removed, restored, preserved };
|
|
1569
1569
|
}
|
package/src/product-workflow.ts
CHANGED