@cleocode/contracts 2026.5.112 → 2026.5.113

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/dist/__tests__/cli-meta-suggested-next.test.d.ts +21 -0
  2. package/dist/__tests__/cli-meta-suggested-next.test.d.ts.map +1 -0
  3. package/dist/__tests__/cli-meta-suggested-next.test.js +70 -0
  4. package/dist/__tests__/cli-meta-suggested-next.test.js.map +1 -0
  5. package/dist/config/__tests__/manifest.test.d.ts +13 -0
  6. package/dist/config/__tests__/manifest.test.d.ts.map +1 -0
  7. package/dist/config/__tests__/manifest.test.js +143 -0
  8. package/dist/config/__tests__/manifest.test.js.map +1 -0
  9. package/dist/config/manifest.d.ts +158 -0
  10. package/dist/config/manifest.d.ts.map +1 -0
  11. package/dist/config/manifest.js +123 -0
  12. package/dist/config/manifest.js.map +1 -0
  13. package/dist/docs/__tests__/provenance.test.d.ts +14 -0
  14. package/dist/docs/__tests__/provenance.test.d.ts.map +1 -0
  15. package/dist/docs/__tests__/provenance.test.js +579 -0
  16. package/dist/docs/__tests__/provenance.test.js.map +1 -0
  17. package/dist/docs/provenance.d.ts +731 -0
  18. package/dist/docs/provenance.d.ts.map +1 -0
  19. package/dist/docs/provenance.js +258 -0
  20. package/dist/docs/provenance.js.map +1 -0
  21. package/dist/docs-taxonomy.d.ts +24 -0
  22. package/dist/docs-taxonomy.d.ts.map +1 -1
  23. package/dist/docs-taxonomy.js +27 -0
  24. package/dist/docs-taxonomy.js.map +1 -1
  25. package/dist/doctor.d.ts +101 -0
  26. package/dist/doctor.d.ts.map +1 -1
  27. package/dist/errors.d.ts +4 -0
  28. package/dist/errors.d.ts.map +1 -1
  29. package/dist/errors.js +4 -0
  30. package/dist/errors.js.map +1 -1
  31. package/dist/exit-codes.d.ts +37 -0
  32. package/dist/exit-codes.d.ts.map +1 -1
  33. package/dist/exit-codes.js +37 -0
  34. package/dist/exit-codes.js.map +1 -1
  35. package/dist/index.d.ts +10 -3
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +7 -2
  38. package/dist/index.js.map +1 -1
  39. package/dist/invariants/__tests__/adr-056-release.test.d.ts +16 -0
  40. package/dist/invariants/__tests__/adr-056-release.test.d.ts.map +1 -0
  41. package/dist/invariants/__tests__/adr-056-release.test.js +88 -0
  42. package/dist/invariants/__tests__/adr-056-release.test.js.map +1 -0
  43. package/dist/invariants/__tests__/adr-070-orchestration.test.d.ts +22 -0
  44. package/dist/invariants/__tests__/adr-070-orchestration.test.d.ts.map +1 -0
  45. package/dist/invariants/__tests__/adr-070-orchestration.test.js +151 -0
  46. package/dist/invariants/__tests__/adr-070-orchestration.test.js.map +1 -0
  47. package/dist/invariants/adr-056-release.d.ts +42 -0
  48. package/dist/invariants/adr-056-release.d.ts.map +1 -0
  49. package/dist/invariants/adr-056-release.js +143 -0
  50. package/dist/invariants/adr-056-release.js.map +1 -0
  51. package/dist/invariants/adr-070-orchestration.d.ts +73 -0
  52. package/dist/invariants/adr-070-orchestration.d.ts.map +1 -0
  53. package/dist/invariants/adr-070-orchestration.js +290 -0
  54. package/dist/invariants/adr-070-orchestration.js.map +1 -0
  55. package/dist/invariants/index.d.ts +4 -1
  56. package/dist/invariants/index.d.ts.map +1 -1
  57. package/dist/invariants/index.js +11 -2
  58. package/dist/invariants/index.js.map +1 -1
  59. package/dist/operations/__tests__/input-contract.test.d.ts +20 -0
  60. package/dist/operations/__tests__/input-contract.test.d.ts.map +1 -0
  61. package/dist/operations/__tests__/input-contract.test.js +158 -0
  62. package/dist/operations/__tests__/input-contract.test.js.map +1 -0
  63. package/dist/operations/docs.d.ts +16 -0
  64. package/dist/operations/docs.d.ts.map +1 -1
  65. package/dist/operations/input-contract.d.ts +267 -0
  66. package/dist/operations/input-contract.d.ts.map +1 -0
  67. package/dist/operations/input-contract.js +29 -0
  68. package/dist/operations/input-contract.js.map +1 -0
  69. package/dist/templates/__tests__/manifest.test.d.ts +18 -0
  70. package/dist/templates/__tests__/manifest.test.d.ts.map +1 -0
  71. package/dist/templates/__tests__/manifest.test.js +123 -0
  72. package/dist/templates/__tests__/manifest.test.js.map +1 -0
  73. package/dist/templates/manifest.d.ts +186 -0
  74. package/dist/templates/manifest.d.ts.map +1 -0
  75. package/dist/templates/manifest.js +165 -0
  76. package/dist/templates/manifest.js.map +1 -0
  77. package/package.json +2 -2
  78. package/src/__tests__/cli-meta-suggested-next.test.ts +76 -0
  79. package/src/config/__tests__/manifest.test.ts +176 -0
  80. package/src/config/manifest.ts +218 -0
  81. package/src/docs/__tests__/__snapshots__/provenance.test.ts.snap +252 -0
  82. package/src/docs/__tests__/provenance.test.ts +648 -0
  83. package/src/docs/provenance.ts +601 -0
  84. package/src/docs-taxonomy.ts +55 -0
  85. package/src/doctor.ts +111 -0
  86. package/src/errors.ts +4 -0
  87. package/src/exit-codes.ts +39 -0
  88. package/src/index.ts +90 -0
  89. package/src/invariants/__tests__/adr-056-release.test.ts +102 -0
  90. package/src/invariants/__tests__/adr-070-orchestration.test.ts +186 -0
  91. package/src/invariants/adr-056-release.ts +154 -0
  92. package/src/invariants/adr-070-orchestration.ts +317 -0
  93. package/src/invariants/index.ts +11 -2
  94. package/src/operations/__tests__/input-contract.test.ts +188 -0
  95. package/src/operations/docs.ts +16 -0
  96. package/src/operations/input-contract.ts +301 -0
  97. package/src/templates/__tests__/manifest.test.ts +140 -0
  98. package/src/templates/manifest.ts +208 -0
@@ -98,6 +98,20 @@ export interface DocKindMetadata {
98
98
  * Built-in kinds leave this unset; extensions set it to `true`.
99
99
  */
100
100
  readonly isExtension?: boolean;
101
+ /**
102
+ * Optional list of H2 section titles that MUST appear in the document
103
+ * body. Validated by `validateDocBody(kind, body)` (T10160).
104
+ *
105
+ * Match is case-insensitive and tolerant of hyphen-vs-space variants
106
+ * (e.g. `## Next Steps` and `## next-steps` both satisfy a required
107
+ * section of `Next Steps`).
108
+ *
109
+ * Empty or omitted → no body schema requirements for this kind.
110
+ *
111
+ * @task T10160 (E12.C3 · absorbs T10154)
112
+ * @epic T10157
113
+ */
114
+ readonly requiredSections?: ReadonlyArray<string>;
101
115
  }
102
116
 
103
117
  // ---------------------------------------------------------------------------
@@ -125,6 +139,7 @@ export const BUILTIN_DOC_KINDS: ReadonlyArray<DocKindMetadata> = [
125
139
  publishDir: 'docs/adr',
126
140
  requiresEntityId: true,
127
141
  entityIdPattern: /^adr-\d{3,4}-[a-z0-9-]+$/,
142
+ requiredSections: ['Status', 'Date', 'Context', 'Decision', 'Consequences'],
128
143
  },
129
144
  {
130
145
  kind: 'spec',
@@ -133,6 +148,7 @@ export const BUILTIN_DOC_KINDS: ReadonlyArray<DocKindMetadata> = [
133
148
  defaultOwnerKind: 'task',
134
149
  publishDir: 'docs/spec',
135
150
  requiresEntityId: false,
151
+ requiredSections: ['Goal', 'Non-Goals', 'Requirements', 'Out-of-Scope'],
136
152
  },
137
153
  {
138
154
  kind: 'research',
@@ -141,6 +157,7 @@ export const BUILTIN_DOC_KINDS: ReadonlyArray<DocKindMetadata> = [
141
157
  defaultOwnerKind: 'task',
142
158
  publishDir: 'docs/research',
143
159
  requiresEntityId: false,
160
+ requiredSections: ['Question', 'Findings', 'Sources'],
144
161
  },
145
162
  {
146
163
  kind: 'handoff',
@@ -149,6 +166,7 @@ export const BUILTIN_DOC_KINDS: ReadonlyArray<DocKindMetadata> = [
149
166
  defaultOwnerKind: 'session',
150
167
  publishDir: 'docs/handoff',
151
168
  requiresEntityId: false,
169
+ requiredSections: ['Context', 'State', 'Next-Steps'],
152
170
  },
153
171
  {
154
172
  kind: 'note',
@@ -157,6 +175,7 @@ export const BUILTIN_DOC_KINDS: ReadonlyArray<DocKindMetadata> = [
157
175
  defaultOwnerKind: 'observation',
158
176
  publishDir: 'docs/note',
159
177
  requiresEntityId: false,
178
+ requiredSections: [],
160
179
  },
161
180
  {
162
181
  kind: 'llm-readme',
@@ -165,6 +184,7 @@ export const BUILTIN_DOC_KINDS: ReadonlyArray<DocKindMetadata> = [
165
184
  defaultOwnerKind: 'project',
166
185
  publishDir: '.',
167
186
  requiresEntityId: false,
187
+ requiredSections: [],
168
188
  },
169
189
  {
170
190
  kind: 'changeset',
@@ -174,6 +194,7 @@ export const BUILTIN_DOC_KINDS: ReadonlyArray<DocKindMetadata> = [
174
194
  publishDir: '.changeset',
175
195
  requiresEntityId: true,
176
196
  entityIdPattern: /^t\d+-[a-z0-9-]+$/,
197
+ requiredSections: [],
177
198
  },
178
199
  {
179
200
  kind: 'release-note',
@@ -183,6 +204,7 @@ export const BUILTIN_DOC_KINDS: ReadonlyArray<DocKindMetadata> = [
183
204
  publishDir: 'docs/release',
184
205
  requiresEntityId: true,
185
206
  entityIdPattern: /^v\d{4}\.\d+\.\d+(-[a-z0-9-]+)?$/,
207
+ requiredSections: ['Changes', 'Migration'],
186
208
  },
187
209
  {
188
210
  kind: 'plan',
@@ -191,6 +213,7 @@ export const BUILTIN_DOC_KINDS: ReadonlyArray<DocKindMetadata> = [
191
213
  defaultOwnerKind: 'task',
192
214
  publishDir: 'docs/plan',
193
215
  requiresEntityId: false,
216
+ requiredSections: ['Goal', 'Steps', 'Owners'],
194
217
  },
195
218
  {
196
219
  kind: 'rcasd',
@@ -200,6 +223,7 @@ export const BUILTIN_DOC_KINDS: ReadonlyArray<DocKindMetadata> = [
200
223
  publishDir: '.cleo/rcasd',
201
224
  requiresEntityId: true,
202
225
  entityIdPattern: /^t\d+(-.+)?$/,
226
+ requiredSections: ['Root-Cause', 'Action', 'Schedule', 'Detection'],
203
227
  },
204
228
  ];
205
229
 
@@ -253,6 +277,16 @@ export interface DocKindExtensionConfig {
253
277
  * a malformed config can't trigger pathological backtracking.
254
278
  */
255
279
  readonly entityIdPattern?: string;
280
+ /**
281
+ * Optional list of required H2 section titles for this extension kind.
282
+ *
283
+ * Same semantics as {@link DocKindMetadata.requiredSections} — matched
284
+ * case-insensitively with hyphen/space normalisation. Omit or pass
285
+ * `[]` for kinds that have no body schema.
286
+ *
287
+ * @task T10160
288
+ */
289
+ readonly requiredSections?: ReadonlyArray<string>;
256
290
  }
257
291
 
258
292
  /**
@@ -596,6 +630,25 @@ function validateExtensionEntry(
596
630
  );
597
631
  }
598
632
 
633
+ let requiredSections: ReadonlyArray<string> | undefined;
634
+ if (obj.requiredSections !== undefined) {
635
+ if (!Array.isArray(obj.requiredSections)) {
636
+ throw new DocKindConfigError(`${where}: 'requiredSections' must be an array`, source);
637
+ }
638
+ const sections: string[] = [];
639
+ for (let i = 0; i < obj.requiredSections.length; i++) {
640
+ const item = obj.requiredSections[i];
641
+ if (typeof item !== 'string' || item.length === 0) {
642
+ throw new DocKindConfigError(
643
+ `${where}: 'requiredSections[${i}]' must be a non-empty string`,
644
+ source,
645
+ );
646
+ }
647
+ sections.push(item);
648
+ }
649
+ requiredSections = sections;
650
+ }
651
+
599
652
  return {
600
653
  kind,
601
654
  label,
@@ -604,6 +657,7 @@ function validateExtensionEntry(
604
657
  publishDir,
605
658
  requiresEntityId,
606
659
  ...(entityIdPattern !== undefined ? { entityIdPattern } : {}),
660
+ ...(requiredSections !== undefined ? { requiredSections } : {}),
607
661
  };
608
662
  }
609
663
 
@@ -633,6 +687,7 @@ function compileExtension(ext: DocKindExtensionConfig, source: string): DocKindM
633
687
  publishDir: ext.publishDir,
634
688
  requiresEntityId: ext.requiresEntityId,
635
689
  ...(entityIdPattern !== undefined ? { entityIdPattern } : {}),
690
+ ...(ext.requiredSections !== undefined ? { requiredSections: ext.requiredSections } : {}),
636
691
  isExtension: true,
637
692
  };
638
693
  }
package/src/doctor.ts CHANGED
@@ -20,6 +20,8 @@
20
20
  * @epic T9808
21
21
  */
22
22
 
23
+ import type { InvariantSeverity } from './invariants/index.js';
24
+
23
25
  /**
24
26
  * One orphan `.cleo/` directory discovered under
25
27
  * `<projectRoot>/.claude/worktrees/`.
@@ -286,6 +288,115 @@ export interface SagaAuditResult {
286
288
  driftCount: number;
287
289
  }
288
290
 
291
+ // ============================================================================
292
+ // Invariant Registry Audit (T10340 — Saga T10326 SG-SUBSTRATE-RECONCILIATION /
293
+ // Epic T10327 E-INVARIANT-REGISTRY-SSOT / R6)
294
+ // ============================================================================
295
+
296
+ /**
297
+ * Per-entry status reported by {@link auditInvariantRegistry}.
298
+ *
299
+ * - `'pass'` — the entry has an adapter that executed against the current
300
+ * `.cleo/tasks.db` state and observed zero violations.
301
+ * - `'fail'` — the entry has an adapter that executed and observed one or
302
+ * more violations (forwarded into {@link InvariantAuditEntry.violations}).
303
+ * - `'not-applicable'` — the entry's `runtimeGate` is spawn-bound,
304
+ * session-bound, or release-tag-bound, so there is no "scan current DB"
305
+ * interpretation. Reported for completeness so the registry walk is
306
+ * visible end-to-end.
307
+ * - `'documented'` — the entry has `runtimeGate === null` by design (the
308
+ * invariant is a display/storage/process concern enforced elsewhere).
309
+ * No check performed; surfaced so operators can audit gap coverage.
310
+ *
311
+ * @task T10340
312
+ */
313
+ export type InvariantAuditStatus = 'pass' | 'fail' | 'not-applicable' | 'documented';
314
+
315
+ /**
316
+ * One violation surfaced by {@link auditInvariantRegistry}.
317
+ *
318
+ * The shape intentionally mirrors {@link SagaAuditViolation} (so the saga
319
+ * audit pipeline can adapt its findings into this envelope without an
320
+ * intermediate transform), but the `invariantKey` field is the registry
321
+ * key (`${adr}.${code}`, e.g. `'ADR-073.I5'`) instead of a saga-only
322
+ * `kind` discriminator.
323
+ *
324
+ * @task T10340
325
+ */
326
+ export interface InvariantAuditViolation {
327
+ /** Registry key — `${adr}.${code}`. */
328
+ invariantKey: string;
329
+ /** The offending task / row / resource identifier. */
330
+ offendingId: string;
331
+ /** Human-readable message naming the offender + repair command. */
332
+ message: string;
333
+ /** Canonical `cleo` command an operator should run to resolve. */
334
+ repairCommand: string;
335
+ }
336
+
337
+ /**
338
+ * Per-invariant audit entry produced by {@link auditInvariantRegistry}.
339
+ *
340
+ * One entry per registered invariant. `status` summarises the outcome of
341
+ * the adapter (or the lack thereof). `violations` is always present —
342
+ * empty when `status !== 'fail'`.
343
+ *
344
+ * @task T10340
345
+ */
346
+ export interface InvariantAuditEntry {
347
+ /** Registry key — `${adr}.${code}`. */
348
+ invariantKey: string;
349
+ /** Source ADR identifier, e.g. `'ADR-073'`. */
350
+ adr: string;
351
+ /** Invariant code within the ADR, e.g. `'I3'` or `'ORC-001'`. */
352
+ code: string;
353
+ /** Short human-readable name. */
354
+ name: string;
355
+ /** Severity tier — drives exit code in the CLI surface. */
356
+ severity: InvariantSeverity;
357
+ /** Adapter outcome. See {@link InvariantAuditStatus}. */
358
+ status: InvariantAuditStatus;
359
+ /** Free-form note for `'not-applicable'` / `'documented'` entries. */
360
+ note: string;
361
+ /** Runtime-gate function name, when one is registered. `null` otherwise. */
362
+ runtimeGate: string | null;
363
+ /** Violations observed by the adapter. Empty when `status !== 'fail'`. */
364
+ violations: InvariantAuditViolation[];
365
+ }
366
+
367
+ /**
368
+ * Aggregated audit result returned by {@link auditInvariantRegistry}.
369
+ *
370
+ * `entries` carries one entry per registered invariant, sorted first by
371
+ * ADR (alphabetical), then by code. `errorCount` is the count of
372
+ * `severity: 'error'` entries whose status is `'fail'` — the only
373
+ * dimension that should drive a non-zero exit code in the CLI surface.
374
+ *
375
+ * Aggregates `warningCount` + `infoCount` cover the corresponding
376
+ * severities for `'fail'` entries. `notApplicableCount` + `documentedCount`
377
+ * surface the gap analysis (how many invariants are registry-only).
378
+ *
379
+ * @task T10340
380
+ */
381
+ export interface InvariantAuditResult {
382
+ /** Per-invariant audit entries. Sorted by ADR then code. */
383
+ entries: InvariantAuditEntry[];
384
+ /** Total registered invariants walked. */
385
+ totalCount: number;
386
+ /** Entries with status='fail' AND severity='error'. */
387
+ errorCount: number;
388
+ /** Entries with status='fail' AND severity='warning'. */
389
+ warningCount: number;
390
+ /** Entries with status='fail' AND severity='info'. */
391
+ infoCount: number;
392
+ /** Entries with status='not-applicable' (spawn/session/release-bound). */
393
+ notApplicableCount: number;
394
+ /** Entries with status='documented' (runtimeGate:null by design). */
395
+ documentedCount: number;
396
+ /** ADR filter applied (e.g. `'ADR-073'`), or `null` when the walk was unfiltered. */
397
+ filteredByAdr: string | null;
398
+ }
399
+
289
400
  // ============================================================================
290
401
  // DB-Substrate Survey (T10307 — Saga T10281 SG-BRAIN-DB-RESILIENCE / Epic T10282)
291
402
  // ============================================================================
package/src/errors.ts CHANGED
@@ -29,6 +29,8 @@ import { ExitCode } from './exit-codes.js';
29
29
  *
30
30
  * @task T889 Orchestration Coherence v3
31
31
  * @task T907 Thin-agent enforcement
32
+ * @see INVARIANTS_REGISTRY['ADR-070.ORC-012'] — central-registry entry
33
+ * for this invariant. Source: `./invariants/adr-070-orchestration.ts`.
32
34
  */
33
35
  export class ThinAgentViolationError extends Error {
34
36
  /** Stable LAFS error code string for envelope emission. */
@@ -209,6 +211,8 @@ export class DecisionValidatorFailedError extends Error {
209
211
  * @codeName E_LEAD_BYPASS_DETECTED
210
212
  * @task T9230
211
213
  * @adr ADR-070
214
+ * @see INVARIANTS_REGISTRY['ADR-070.ORC-014'] — central-registry entry
215
+ * for this invariant. Source: `./invariants/adr-070-orchestration.ts`.
212
216
  */
213
217
  export class LeadBypassDetectedError extends Error {
214
218
  /** Stable LAFS error code string for envelope emission. */
package/src/exit-codes.ts CHANGED
@@ -323,6 +323,45 @@ export const E_WORKFLOW_NOT_FOUND = 'E_WORKFLOW_NOT_FOUND' as const;
323
323
  */
324
324
  export const E_WRITE_CONTENTION = 'E_WRITE_CONTENTION' as const;
325
325
 
326
+ /**
327
+ * E_INVALID_SEVERITY_VALUE — `--severity` was passed a value that is not one
328
+ * of the canonical {@link TASK_SEVERITIES} members (`P0|P1|P2|P3`).
329
+ *
330
+ * Returned by the `cleo add` and `cleo update` CLI commands at the dispatch
331
+ * boundary BEFORE the request reaches the database. Replaces the previous
332
+ * late-stage failure mode where SQLite surfaced a cryptic
333
+ * `CHECK constraint failed: severity` error after the write attempt.
334
+ *
335
+ * Maps to {@link ExitCode.VALIDATION_ERROR} (6) at the CLI boundary.
336
+ *
337
+ * @task T10341
338
+ * @epic T10327
339
+ * @saga T10326
340
+ */
341
+ export const E_INVALID_SEVERITY_VALUE = 'E_INVALID_SEVERITY_VALUE' as const;
342
+
343
+ /**
344
+ * E_INVALID_PIPELINE_STAGE — `--pipeline-stage` was passed a value that
345
+ * either (a) is not a recognised stage name in the canonical RCASD-IVTR+C
346
+ * pipeline (`research|consensus|architecture_decision|specification|`
347
+ * `decomposition|implementation|validation|testing|release|contribution|`
348
+ * `cancelled`), or (b) would move the task BACKWARD relative to its
349
+ * current stage. Pipeline stages are forward-only.
350
+ *
351
+ * Returned by the `cleo update` CLI command at the dispatch boundary BEFORE
352
+ * the request reaches the database. Replaces the previous late-stage
353
+ * failure mode where SQLite surfaced a cryptic
354
+ * `CHECK constraint failed: pipeline_stage` error or the core engine threw
355
+ * an opaque {@link CleoError} after a partial round-trip.
356
+ *
357
+ * Maps to {@link ExitCode.VALIDATION_ERROR} (6) at the CLI boundary.
358
+ *
359
+ * @task T10341
360
+ * @epic T10327
361
+ * @saga T10326
362
+ */
363
+ export const E_INVALID_PIPELINE_STAGE = 'E_INVALID_PIPELINE_STAGE' as const;
364
+
326
365
  /** Check if an exit code represents an error (1-99). */
327
366
  export function isErrorCode(code: ExitCode): boolean {
328
367
  return code >= 1 && code < 100;
package/src/index.ts CHANGED
@@ -224,6 +224,21 @@ export type {
224
224
  ConduitTopicSubscribeOptions,
225
225
  ConduitUnsubscribe,
226
226
  } from './conduit.js';
227
+ // === Config Manifest (T9876 / Saga T9855) ===
228
+ export type {
229
+ ConfigManifestEntry,
230
+ ConfigManifestScope,
231
+ ConfigScope,
232
+ DriftDetection,
233
+ } from './config/manifest.js';
234
+ export {
235
+ CLEO_CONFIG_MANIFEST,
236
+ CONFIG_MANIFEST_ENTRIES,
237
+ configManifestEntrySchema,
238
+ GLOBAL_CLEO_CONFIG_MANIFEST,
239
+ PROJECT_CONTEXT_MANIFEST,
240
+ PROJECT_INFO_MANIFEST,
241
+ } from './config/manifest.js';
227
242
  // === Configuration Types ===
228
243
  export type {
229
244
  BackupConfig,
@@ -312,6 +327,42 @@ export { CANONICAL_DOMAINS } from './dispatch/identity.js';
312
327
  export type { OperationDef, Resolution } from './dispatch/operation-def.js';
313
328
  // === Dispatch OPERATIONS data + builder helpers (T10061 — T9833b / E-CLI-BOUNDARY / SG-ARCH-SOLID) ===
314
329
  export { defineDomain, defineOp, OPERATIONS } from './dispatch/operations-registry.js';
330
+ // === Docs Provenance Graph (T10166 / Saga T9855 / E12) ===
331
+ export type {
332
+ DocLifecycleStatus,
333
+ DocProvenanceResponse,
334
+ ProvenanceDecisionNode,
335
+ ProvenanceDocNode,
336
+ ProvenanceEdge,
337
+ ProvenanceEdgeRelation,
338
+ ProvenanceMemoryNode,
339
+ ProvenanceNode,
340
+ ProvenanceNodeKind,
341
+ ProvenanceSessionNode,
342
+ ProvenanceTaskNode,
343
+ } from './docs/provenance.js';
344
+ export {
345
+ DOC_LIFECYCLE_STATUSES,
346
+ docLifecycleStatusSchema,
347
+ docProvenanceResponseSchema,
348
+ isDocProvenanceResponse,
349
+ isProvenanceDecisionNode,
350
+ isProvenanceDocNode,
351
+ isProvenanceMemoryNode,
352
+ isProvenanceSessionNode,
353
+ isProvenanceTaskNode,
354
+ PROVENANCE_EDGE_RELATIONS,
355
+ PROVENANCE_NODE_KINDS,
356
+ provenanceDecisionNodeSchema,
357
+ provenanceDocNodeSchema,
358
+ provenanceEdgeRelationSchema,
359
+ provenanceEdgeSchema,
360
+ provenanceMemoryNodeSchema,
361
+ provenanceNodeKindSchema,
362
+ provenanceNodeSchema,
363
+ provenanceSessionNodeSchema,
364
+ provenanceTaskNodeSchema,
365
+ } from './docs/provenance.js';
315
366
  // === DocsAccessor Contracts (T9063) ===
316
367
  export type {
317
368
  DocExportFormat,
@@ -342,6 +393,7 @@ export {
342
393
  // === Doctor: DB-Substrate Survey (T10307 — Saga T10281 / Epic T10282) ===
343
394
  // === Doctor: Legacy-Backup Walker (T10309 — Saga T10281 / Epic T10282) ===
344
395
  // === Doctor: Cross-DB Invariants (T10323 — Saga T10281 / Epic T10285) ===
396
+ // === Doctor: Invariant Registry Audit (T10340 — Saga T10326 / Epic T10327) ===
345
397
  export type {
346
398
  ComprehensiveAuditResult,
347
399
  DbCrossDbInvariantId,
@@ -356,6 +408,10 @@ export type {
356
408
  DbSubstrateSurveyOptions,
357
409
  DbSubstrateWarning,
358
410
  DbSubstrateWarningKind,
411
+ InvariantAuditEntry,
412
+ InvariantAuditResult,
413
+ InvariantAuditStatus,
414
+ InvariantAuditViolation,
359
415
  LegacyBackupEntry,
360
416
  LegacyBackupOriginHint,
361
417
  LegacyBackupRecommendation,
@@ -480,6 +536,9 @@ export {
480
536
  E_EVIDENCE_INSUFFICIENT,
481
537
  // SPEC-T9345 release pipeline v2 error code names (T9530)
482
538
  E_GH_NOT_AUTHENTICATED,
539
+ // T10341 — typed CLI-layer validation for --severity + --pipeline-stage
540
+ E_INVALID_PIPELINE_STAGE,
541
+ E_INVALID_SEVERITY_VALUE,
483
542
  E_INVALID_STATE,
484
543
  E_PLAN_NOT_FOUND,
485
544
  E_RELEASE_PLAN_INVALID,
@@ -563,6 +622,7 @@ export type {
563
622
  RegisteredInvariant,
564
623
  } from './invariants/index.js';
565
624
  export {
625
+ ADR_056_INVARIANTS,
566
626
  ADR_073_INVARIANTS,
567
627
  getInvariant,
568
628
  getInvariantsByAdr,
@@ -855,6 +915,17 @@ export type {
855
915
  } from './operations/dialectic.js';
856
916
  // === Operations Types (API wire format, namespaced to avoid collision with domain types) ===
857
917
  export * as ops from './operations/index.js';
918
+ // === Operation Input Contracts (T9914 / Saga T9855 / E7) ===
919
+ // Re-exported at top level so SDK consumers (validator T9915, mutate DX
920
+ // surface, generated docs) can import without the `ops.` namespace hop.
921
+ export type {
922
+ JsonSchema,
923
+ OperationInputContract,
924
+ OperationInputContractRegistry,
925
+ OperationInputExample,
926
+ ValidationError,
927
+ ValidationResult,
928
+ } from './operations/input-contract.js';
858
929
  // === Lifecycle Operation Types (T1455 — ADR-057 D1 Core normalization) ===
859
930
  // Re-exported at top level so @cleocode/core/lifecycle can import without the `ops.` namespace hop.
860
931
  // Note: Gate is included here — GateStatus conflict resolved (T1694): lifecycle.ts now re-exports
@@ -1844,6 +1915,25 @@ export type {
1844
1915
  TaskViewNextAction,
1845
1916
  TaskViewPipelineStage,
1846
1917
  } from './tasks.js';
1918
+ // === Template Manifest (T9875 / Saga T9855) ===
1919
+ export type {
1920
+ PlaceholderSource,
1921
+ PlaceholderSpec,
1922
+ PlaceholderSpecInput,
1923
+ TemplateKind,
1924
+ TemplateManifestEntry,
1925
+ TemplateManifestEntryInput,
1926
+ TemplateSubstitution,
1927
+ UpdateStrategy,
1928
+ } from './templates/manifest.js';
1929
+ export {
1930
+ PLACEHOLDER_SOURCES,
1931
+ PlaceholderSpecSchema,
1932
+ TEMPLATE_KINDS,
1933
+ TEMPLATE_SUBSTITUTIONS,
1934
+ TEMPLATE_UPDATE_STRATEGIES,
1935
+ TemplateManifestEntrySchema,
1936
+ } from './templates/manifest.js';
1847
1937
  // === Tessera Types ===
1848
1938
  export type {
1849
1939
  TesseraInstantiationInput,
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Unit tests for the ADR-056 D1-D6 entries in the central invariants
3
+ * registry (T10339 — R5).
4
+ *
5
+ * The release-side executable subsystem at
6
+ * `packages/core/src/release/invariants/registry.ts` consumes these
7
+ * metadata entries via `getInvariantsByAdr('ADR-056')`. The tests below
8
+ * assert the metadata shape downstream consumers (R4 CI gate, R6 doctor
9
+ * audit, R8 docs renderer) rely on.
10
+ *
11
+ * @epic T10327 — E-INVARIANT-REGISTRY-SSOT
12
+ * @saga T10326 — SG-SUBSTRATE-RECONCILIATION
13
+ * @task T10339 — R5: release-registry consumes central substrate
14
+ */
15
+
16
+ import { describe, expect, it } from 'vitest';
17
+ import {
18
+ ADR_056_INVARIANTS,
19
+ getInvariant,
20
+ getInvariantsByAdr,
21
+ INVARIANTS_REGISTRY,
22
+ } from '../index.js';
23
+
24
+ describe('ADR-056 invariants — central registry entries', () => {
25
+ it('registers all six ADR-056 decisions D1-D6 under predictable keys', () => {
26
+ for (let i = 1; i <= 6; i++) {
27
+ const key = `ADR-056.D${i}`;
28
+ expect(INVARIANTS_REGISTRY[key]).toBeDefined();
29
+ expect(INVARIANTS_REGISTRY[key]?.adr).toBe('ADR-056');
30
+ expect(INVARIANTS_REGISTRY[key]?.code).toBe(`D${i}`);
31
+ }
32
+ });
33
+
34
+ it('getInvariantsByAdr("ADR-056") returns D1..D6 in declaration order', () => {
35
+ const adr056 = getInvariantsByAdr('ADR-056');
36
+ expect(adr056).toHaveLength(6);
37
+ expect(adr056.map((e) => e.code)).toEqual(['D1', 'D2', 'D3', 'D4', 'D5', 'D6']);
38
+ });
39
+
40
+ it('ADR_056_INVARIANTS convenience binding matches getInvariantsByAdr output', () => {
41
+ expect(ADR_056_INVARIANTS).toHaveLength(6);
42
+ const enumerated = getInvariantsByAdr('ADR-056');
43
+ expect(enumerated).toHaveLength(ADR_056_INVARIANTS.length);
44
+ for (let i = 0; i < ADR_056_INVARIANTS.length; i++) {
45
+ expect(enumerated[i]?.code).toBe(ADR_056_INVARIANTS[i]?.code);
46
+ }
47
+ });
48
+
49
+ it('every entry carries a non-empty name and description', () => {
50
+ for (const entry of ADR_056_INVARIANTS) {
51
+ expect(entry.name.length).toBeGreaterThan(0);
52
+ expect(entry.description.length).toBeGreaterThan(0);
53
+ }
54
+ });
55
+
56
+ it('D4 is the archive-reason runtime gate (severity:error)', () => {
57
+ const d4 = getInvariant('ADR-056.D4');
58
+ expect(d4).toBeDefined();
59
+ expect(d4?.severity).toBe('error');
60
+ expect(d4?.runtimeGate).not.toBeNull();
61
+ expect(d4?.runtimeGate?.functionName).toBe('assertArchiveReason');
62
+ expect(d4?.runtimeGate?.module).toBe('packages/contracts/src/tasks/archive.ts');
63
+ });
64
+
65
+ it('D5 points at the release-invariants registry runtime gate', () => {
66
+ const d5 = getInvariant('ADR-056.D5');
67
+ expect(d5).toBeDefined();
68
+ expect(d5?.severity).toBe('warning');
69
+ expect(d5?.runtimeGate).not.toBeNull();
70
+ expect(d5?.runtimeGate?.functionName).toBe('runInvariants');
71
+ expect(d5?.runtimeGate?.module).toBe('packages/core/src/release/invariants/registry.ts');
72
+ });
73
+
74
+ it('D6 carries a lintRule pointer to the commit-msg hook (info severity)', () => {
75
+ const d6 = getInvariant('ADR-056.D6');
76
+ expect(d6).toBeDefined();
77
+ expect(d6?.severity).toBe('info');
78
+ expect(d6?.runtimeGate).toBeNull();
79
+ expect(d6?.lintRule?.lintScript).toBe('scripts/hooks/commit-msg-release-lint.mjs');
80
+ });
81
+
82
+ it('D1, D2, D3 are info-tier topology/convention decisions with no runtime gate', () => {
83
+ for (const code of ['D1', 'D2', 'D3'] as const) {
84
+ const entry = getInvariant(`ADR-056.${code}`);
85
+ expect(entry).toBeDefined();
86
+ expect(entry?.severity).toBe('info');
87
+ expect(entry?.runtimeGate).toBeNull();
88
+ }
89
+ });
90
+
91
+ it('every severity:"error" ADR-056 entry has a non-null runtimeGate', () => {
92
+ const errorTier = ADR_056_INVARIANTS.filter((e) => e.severity === 'error');
93
+ expect(errorTier.length).toBeGreaterThan(0);
94
+ for (const entry of errorTier) {
95
+ expect(entry.runtimeGate).not.toBeNull();
96
+ if (entry.runtimeGate !== null) {
97
+ expect(entry.runtimeGate.module.length).toBeGreaterThan(0);
98
+ expect(entry.runtimeGate.functionName.length).toBeGreaterThan(0);
99
+ }
100
+ }
101
+ });
102
+ });