@prodshape/integration-openspec 0.5.3 → 0.6.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -0
- package/assets/openspec-product-schema/schema.yaml +120 -0
- package/assets/openspec-product-schema/scripts/product-apply.mjs +105 -0
- package/assets/openspec-product-schema/scripts/product-validate.mjs +77 -0
- package/assets/openspec-product-schema/templates/change.md +49 -0
- package/assets/openspec-product-schema/templates/proposal.md +29 -0
- package/dist/index.d.ts +165 -4
- package/dist/index.js +647 -34
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/src/index.ts +416 -17
- package/src/product-schema.ts +73 -0
- package/src/product-workflow.ts +557 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EnumerateConsumerDocumentsOptions, ConsumerDocumentEnumeration, SddIntegrationProvider } from '@prodshape/core';
|
|
1
|
+
import { EnumerateConsumerDocumentsOptions, ConsumerDocumentEnumeration, SddIntegrationProvider, ImpactReport, ApplyPlan, LoadedChange, BaselineValidation, LoadedArtifact, ProductGraph, Diagnostic, ProductRepository } from '@prodshape/core';
|
|
2
2
|
|
|
3
3
|
/** Check if an OpenSpec workspace exists at the given root (openspec/ directory present). */
|
|
4
4
|
declare function isOpenSpecWorkspace(root: string): Promise<boolean>;
|
|
@@ -31,6 +31,144 @@ declare function enumerateOpenSpecDocuments(repoRoot: string, options?: Enumerat
|
|
|
31
31
|
*/
|
|
32
32
|
declare const openSpecProvider: SddIntegrationProvider;
|
|
33
33
|
|
|
34
|
+
/** The OpenSpec schema name (the directory key OpenSpec resolves). */
|
|
35
|
+
declare const OPENSPEC_PRODUCT_SCHEMA_NAME = "product";
|
|
36
|
+
/** The repository-relative directory the product schema is installed into. */
|
|
37
|
+
declare const OPENSPEC_PRODUCT_SCHEMA_RELATIVE = "openspec/schemas/product";
|
|
38
|
+
/**
|
|
39
|
+
* The minimum OpenSpec CLI version the product workflow needs. Project-local schemas are far
|
|
40
|
+
* older, but three 1.7.0 behaviours are load-bearing: `openspec new change` records
|
|
41
|
+
* `skip_specs: true` for schemas with no specs artifact (so `openspec validate` stops demanding
|
|
42
|
+
* delta specs), artifacts follow schema declaration order, and the generated skills stop
|
|
43
|
+
* overriding custom-schema instructions with spec-driven patterns. Below this floor the schema
|
|
44
|
+
* files are inert data: the citation lane keeps working and the product workflow is reported
|
|
45
|
+
* unavailable, never broken.
|
|
46
|
+
*/
|
|
47
|
+
declare const PRODUCT_SCHEMA_MIN_OPENSPEC = "1.7.0";
|
|
48
|
+
/** One bundled product-schema file: its path relative to the installed schema directory. */
|
|
49
|
+
interface ProductSchemaAsset {
|
|
50
|
+
relative: string;
|
|
51
|
+
content: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Load the bundled product-schema assets, sorted by relative path so installation, metadata and
|
|
55
|
+
* comparison all see one deterministic order.
|
|
56
|
+
*/
|
|
57
|
+
declare function loadProductSchemaAssets(): Promise<ProductSchemaAsset[]>;
|
|
58
|
+
|
|
59
|
+
/** The subdirectory of an OpenSpec change that hosts the PDaC Product Change delta. */
|
|
60
|
+
declare const OPENSPEC_PRODUCT_SUBDIR = "product";
|
|
61
|
+
/** The accepted product model, read directly from disk and validated, with its in-memory graph. */
|
|
62
|
+
interface ProductModelInspection {
|
|
63
|
+
repo: ProductRepository;
|
|
64
|
+
artifacts: LoadedArtifact[];
|
|
65
|
+
graph: ProductGraph;
|
|
66
|
+
/** Configuration diagnostics plus full baseline validation, sorted. */
|
|
67
|
+
diagnostics: Diagnostic[];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Read the accepted model at the repository's product root (default docs/product), compile the
|
|
71
|
+
* semantic graph in memory and validate. Needs no `.product/generated` output and no
|
|
72
|
+
* configuration file: kernel defaults apply when `.product/config.yaml` is absent.
|
|
73
|
+
*/
|
|
74
|
+
declare function inspectProductModel(root: string): Promise<ProductModelInspection>;
|
|
75
|
+
/** One OpenSpec change hosting a PDaC Product Change delta. */
|
|
76
|
+
interface OpenSpecProductChangeRef {
|
|
77
|
+
/** The OpenSpec change name (the directory under openspec/changes/). */
|
|
78
|
+
name: string;
|
|
79
|
+
/** Absolute path of the hosted PDaC change directory (…/<name>/product). */
|
|
80
|
+
dir: string;
|
|
81
|
+
/** Repository-relative POSIX path of the hosted change.md. */
|
|
82
|
+
file: string;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* List the OpenSpec changes that host a PDaC Product Change: pinned to `schema: product` in
|
|
86
|
+
* `.openspec.yaml` AND carrying `product/change.md`. The OpenSpec archive is history and is never
|
|
87
|
+
* listed; a change without a product delta (for example one whose honest intent verdict was "no
|
|
88
|
+
* product delta") is not a product change; and a change pinned to another schema is not one
|
|
89
|
+
* either, whatever directories it contains, so it never enters the product rail or its
|
|
90
|
+
* concurrency input.
|
|
91
|
+
*/
|
|
92
|
+
declare function listOpenSpecProductChanges(root: string): Promise<OpenSpecProductChangeRef[]>;
|
|
93
|
+
/** Load one hosted Product Change through core's loader (change.md plus proposed/**). */
|
|
94
|
+
declare function loadOpenSpecProductChange(root: string, changeName: string): Promise<LoadedChange>;
|
|
95
|
+
/** The result of validating a hosted Product Change as an overlay on the untouched baseline. */
|
|
96
|
+
interface OpenSpecProductChangeValidation {
|
|
97
|
+
change: LoadedChange;
|
|
98
|
+
baseline: BaselineValidation;
|
|
99
|
+
overlayArtifacts: LoadedArtifact[];
|
|
100
|
+
overlayGraph: ProductGraph;
|
|
101
|
+
/** Configuration, baseline and change diagnostics, deduplicated and sorted. */
|
|
102
|
+
diagnostics: Diagnostic[];
|
|
103
|
+
/** The diagnostics that block, under the repository's warnings-as-errors policy. */
|
|
104
|
+
blocking: Diagnostic[];
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Validate one hosted Product Change: the accepted baseline stays untouched, the delta is
|
|
108
|
+
* compiled over it into an overlay, and the overlay is validated end to end (operation checks,
|
|
109
|
+
* cross-container concurrency, open-questions state, full structural validation with the
|
|
110
|
+
* PRODUCT023/PRODUCT024 overlay recodes). Useful as explicit preflight and for wrappers and
|
|
111
|
+
* tests; apply never trusts it and revalidates on its own.
|
|
112
|
+
*/
|
|
113
|
+
declare function validateOpenSpecProductChange(root: string, changeName: string): Promise<OpenSpecProductChangeValidation>;
|
|
114
|
+
/** How an apply invocation ended. */
|
|
115
|
+
type OpenSpecProductApplyOutcome = 'applied' | 'dry-run' | 'refused';
|
|
116
|
+
interface OpenSpecProductApplyResult {
|
|
117
|
+
outcome: OpenSpecProductApplyOutcome;
|
|
118
|
+
/**
|
|
119
|
+
* The executed (or refused) plan. The ProductShape lifecycle's `move-change` action is already
|
|
120
|
+
* filtered out: the hosted change directory belongs to OpenSpec and only `openspec archive`
|
|
121
|
+
* moves it, as a separate action after apply.
|
|
122
|
+
*/
|
|
123
|
+
plan: ApplyPlan;
|
|
124
|
+
change: LoadedChange;
|
|
125
|
+
/** Fresh read and validation of the accepted model after a real apply. */
|
|
126
|
+
resultingModel?: BaselineValidation;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Apply one hosted Product Change to the accepted model, deterministically and fail closed.
|
|
130
|
+
*
|
|
131
|
+
* The sequence mirrors the native apply and is fail closed end to end: full revalidation at apply
|
|
132
|
+
* time of the configuration, the baseline (including its per-document load diagnostics) and the
|
|
133
|
+
* overlay, the apply-authorised state gate (PRODUCT028; the integration never performs the
|
|
134
|
+
* authorising transition), base-revision drift by content digest (PRODUCT027), write and delete
|
|
135
|
+
* actions named by lowercase id, the product diff computed from the result, and the hosted
|
|
136
|
+
* change.md status flipped to applied in place. Any blocking diagnostic refuses BEFORE any
|
|
137
|
+
* mutation, with `docs/product/model` and the change container byte-identical. A dry run still
|
|
138
|
+
* preflights every action. Nothing is committed and nothing is archived: verification and the
|
|
139
|
+
* container move remain separate, later actions.
|
|
140
|
+
*/
|
|
141
|
+
declare function applyOpenSpecProductChange(root: string, changeName: string, options?: {
|
|
142
|
+
dryRun?: boolean;
|
|
143
|
+
}): Promise<OpenSpecProductApplyResult>;
|
|
144
|
+
/** One artifact carried into delivery context, with the digest of its current accepted content. */
|
|
145
|
+
interface DeliveryContextEntry {
|
|
146
|
+
id: string;
|
|
147
|
+
type?: string;
|
|
148
|
+
title?: string;
|
|
149
|
+
status?: string;
|
|
150
|
+
file: string;
|
|
151
|
+
digest: string;
|
|
152
|
+
}
|
|
153
|
+
/** Context derived from a fresh read of the accepted model, for a later delivery workflow. */
|
|
154
|
+
interface DeliveryContextBundle {
|
|
155
|
+
ids: string[];
|
|
156
|
+
/** The requested artifacts, from the freshly read accepted model. */
|
|
157
|
+
artifacts: DeliveryContextEntry[];
|
|
158
|
+
/** Impact around each requested artifact (direction both, depth 2). */
|
|
159
|
+
impacts: Record<string, ImpactReport>;
|
|
160
|
+
/** Direct neighbours of the requested artifacts, deduplicated, requested ids excluded. */
|
|
161
|
+
neighbours: DeliveryContextEntry[];
|
|
162
|
+
/** Whether the freshly read accepted model validates with zero errors. */
|
|
163
|
+
modelErrorFree: boolean;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Derive delivery context from the accepted model as it exists on disk NOW: a fresh read and a
|
|
167
|
+
* fresh graph, never state carried over from a product workflow. This is the handoff boundary a
|
|
168
|
+
* future delivery workflow starts from.
|
|
169
|
+
*/
|
|
170
|
+
declare function deriveDeliveryContext(root: string, ids: string[]): Promise<DeliveryContextBundle>;
|
|
171
|
+
|
|
34
172
|
/** The minimum supported OpenSpec CLI version. */
|
|
35
173
|
declare const MIN_SUPPORTED_OPENSPEC = "1.0.0";
|
|
36
174
|
/**
|
|
@@ -40,7 +178,7 @@ declare const MIN_SUPPORTED_OPENSPEC = "1.0.0";
|
|
|
40
178
|
*/
|
|
41
179
|
declare const OPENSPEC_NPX_SPEC = "@fission-ai/openspec@1";
|
|
42
180
|
/** The PDaC authority context block injected into openspec/config.yaml `context:`. */
|
|
43
|
-
declare 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 \u2014 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
|
|
181
|
+
declare 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 \u2014 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.md plus product/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 -->";
|
|
44
182
|
/** The PDaC citation rules injected per artifact into openspec/config.yaml `rules:`. */
|
|
45
183
|
declare const PDAC_RULES: Record<string, string[]>;
|
|
46
184
|
/** The PDaC operation guidance injected into openspec/config.yaml `operations:`. */
|
|
@@ -86,6 +224,22 @@ interface OpenSpecIntegrationMeta {
|
|
|
86
224
|
ciExamplePath?: string;
|
|
87
225
|
/** Absent in metadata written before the field existed; treated as the current guidance. */
|
|
88
226
|
managed?: ManagedStrings;
|
|
227
|
+
/**
|
|
228
|
+
* The installed OpenSpec product schema: its name, the OpenSpec floor the product workflow
|
|
229
|
+
* needs, and every managed file it consists of, each with the content digest it was installed
|
|
230
|
+
* at. The per-file record and digest are the ownership proof (ADR 0008): update replaces and
|
|
231
|
+
* remove deletes a file only when that exact path was recorded and its bytes still match the
|
|
232
|
+
* recorded digest (legacy path-only records additionally require the current asset bytes). A
|
|
233
|
+
* diverged or unrecorded file is preserved and reported, never overwritten or deleted. Absent in metadata written before the
|
|
234
|
+
* schema existed; `integration update` installs the schema and adds the record. Metadata
|
|
235
|
+
* written by earlier spike revisions carried a plain path array; it is read as paths with no
|
|
236
|
+
* recorded digest, so ownership then rests on matching the current assets.
|
|
237
|
+
*/
|
|
238
|
+
productSchema?: {
|
|
239
|
+
name: string;
|
|
240
|
+
requiresOpenspec: string;
|
|
241
|
+
files: Record<string, string>;
|
|
242
|
+
};
|
|
89
243
|
}
|
|
90
244
|
/** The repository-relative path of the integration metadata file, for callers that probe it. */
|
|
91
245
|
declare const OPENSPEC_META_RELATIVE = ".product/integrations/openspec.json";
|
|
@@ -216,6 +370,9 @@ declare function updateOpenSpecIntegration(root: string, options?: {
|
|
|
216
370
|
* - `openspec/config.yaml` exists and contains the PDaC context block.
|
|
217
371
|
* - `.product/integrations/openspec.json` metadata exists.
|
|
218
372
|
* - The config's PDaC block matches the current PDaC guidance (not removed or altered).
|
|
373
|
+
* - The product schema's managed files are installed and intact, with the product workflow's
|
|
374
|
+
* availability reported per capability: an OpenSpec CLI below the product floor leaves the
|
|
375
|
+
* citation lane usable and states the product workflow UNAVAILABLE instead of failing it.
|
|
219
376
|
*/
|
|
220
377
|
declare function checkOpenSpecIntegration(root: string): Promise<{
|
|
221
378
|
ok: boolean;
|
|
@@ -232,13 +389,17 @@ declare function checkOpenSpecIntegration(root: string): Promise<{
|
|
|
232
389
|
* - Removes the PDaC context block (between sentinels) from `context:`.
|
|
233
390
|
* - Removes PDaC rules from each artifact (keeps user-authored rules).
|
|
234
391
|
* - Removes PDaC operation guidance (keeps user-authored guidance).
|
|
392
|
+
* - Deletes the installed product schema files still proven managed (recorded digest or current
|
|
393
|
+
* asset bytes); hand-edited managed files are preserved and reported in `preserved`, and with
|
|
394
|
+
* no recorded product schema nothing under openspec/schemas is touched.
|
|
235
395
|
* - Deletes `.product/integrations/openspec.json`.
|
|
236
|
-
* - `--dry-run` reports what would be removed without writing.
|
|
396
|
+
* - `--dry-run` reports what would be removed and preserved without writing.
|
|
237
397
|
*/
|
|
238
398
|
declare function removeOpenSpecIntegration(root: string, options?: {
|
|
239
399
|
dryRun?: boolean;
|
|
240
400
|
}): Promise<{
|
|
241
401
|
removed: string[];
|
|
402
|
+
preserved: string[];
|
|
242
403
|
}>;
|
|
243
404
|
|
|
244
|
-
export { MIN_SUPPORTED_OPENSPEC, type ManagedStrings, OPENSPEC_CI_EXAMPLE_RELATIVE, OPENSPEC_META_RELATIVE, OPENSPEC_NPX_SPEC, OPENSPEC_VERIFY_COMMAND, type OpenSpecIntegrationMeta, OpenSpecMetaError, PDAC_CONTEXT_BLOCK, PDAC_OPERATIONS, PDAC_RULES, addOpenSpecIntegration, bootstrapOpenSpecWorkspace, checkOpenSpecIntegration, currentManagedStrings, detectOpenSpecVersion, enumerateOpenSpecDocuments, isOpenSpecCliAvailable, isOpenSpecIntegrationInstalled, isOpenSpecWorkspace, mergeConfig, openSpecProvider, readOpenSpecConfig, removeOpenSpecIntegration, renderCiExample, serializeConfig, updateOpenSpecIntegration };
|
|
405
|
+
export { type DeliveryContextBundle, type DeliveryContextEntry, MIN_SUPPORTED_OPENSPEC, type ManagedStrings, OPENSPEC_CI_EXAMPLE_RELATIVE, OPENSPEC_META_RELATIVE, OPENSPEC_NPX_SPEC, OPENSPEC_PRODUCT_SCHEMA_NAME, OPENSPEC_PRODUCT_SCHEMA_RELATIVE, OPENSPEC_PRODUCT_SUBDIR, OPENSPEC_VERIFY_COMMAND, type OpenSpecIntegrationMeta, OpenSpecMetaError, type OpenSpecProductApplyOutcome, type OpenSpecProductApplyResult, type OpenSpecProductChangeRef, type OpenSpecProductChangeValidation, PDAC_CONTEXT_BLOCK, PDAC_OPERATIONS, PDAC_RULES, PRODUCT_SCHEMA_MIN_OPENSPEC, type ProductModelInspection, type ProductSchemaAsset, addOpenSpecIntegration, applyOpenSpecProductChange, bootstrapOpenSpecWorkspace, checkOpenSpecIntegration, currentManagedStrings, deriveDeliveryContext, detectOpenSpecVersion, enumerateOpenSpecDocuments, inspectProductModel, isOpenSpecCliAvailable, isOpenSpecIntegrationInstalled, isOpenSpecWorkspace, listOpenSpecProductChanges, loadOpenSpecProductChange, loadProductSchemaAssets, mergeConfig, openSpecProvider, readOpenSpecConfig, removeOpenSpecIntegration, renderCiExample, serializeConfig, updateOpenSpecIntegration, validateOpenSpecProductChange };
|