@cleocode/contracts 2026.5.111 → 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 (118) 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/__tests__/evidence-atom-schema.test.d.ts +16 -0
  6. package/dist/__tests__/evidence-atom-schema.test.d.ts.map +1 -0
  7. package/dist/__tests__/evidence-atom-schema.test.js +394 -0
  8. package/dist/__tests__/evidence-atom-schema.test.js.map +1 -0
  9. package/dist/config/__tests__/manifest.test.d.ts +13 -0
  10. package/dist/config/__tests__/manifest.test.d.ts.map +1 -0
  11. package/dist/config/__tests__/manifest.test.js +143 -0
  12. package/dist/config/__tests__/manifest.test.js.map +1 -0
  13. package/dist/config/manifest.d.ts +158 -0
  14. package/dist/config/manifest.d.ts.map +1 -0
  15. package/dist/config/manifest.js +123 -0
  16. package/dist/config/manifest.js.map +1 -0
  17. package/dist/docs/__tests__/provenance.test.d.ts +14 -0
  18. package/dist/docs/__tests__/provenance.test.d.ts.map +1 -0
  19. package/dist/docs/__tests__/provenance.test.js +579 -0
  20. package/dist/docs/__tests__/provenance.test.js.map +1 -0
  21. package/dist/docs/provenance.d.ts +731 -0
  22. package/dist/docs/provenance.d.ts.map +1 -0
  23. package/dist/docs/provenance.js +258 -0
  24. package/dist/docs/provenance.js.map +1 -0
  25. package/dist/docs-taxonomy.d.ts +24 -0
  26. package/dist/docs-taxonomy.d.ts.map +1 -1
  27. package/dist/docs-taxonomy.js +27 -0
  28. package/dist/docs-taxonomy.js.map +1 -1
  29. package/dist/doctor.d.ts +101 -0
  30. package/dist/doctor.d.ts.map +1 -1
  31. package/dist/errors.d.ts +4 -0
  32. package/dist/errors.d.ts.map +1 -1
  33. package/dist/errors.js +4 -0
  34. package/dist/errors.js.map +1 -1
  35. package/dist/evidence-atom-schema.d.ts +407 -0
  36. package/dist/evidence-atom-schema.d.ts.map +1 -0
  37. package/dist/evidence-atom-schema.js +505 -0
  38. package/dist/evidence-atom-schema.js.map +1 -0
  39. package/dist/exit-codes.d.ts +37 -0
  40. package/dist/exit-codes.d.ts.map +1 -1
  41. package/dist/exit-codes.js +37 -0
  42. package/dist/exit-codes.js.map +1 -1
  43. package/dist/index.d.ts +13 -2
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +8 -1
  46. package/dist/index.js.map +1 -1
  47. package/dist/invariants/__tests__/adr-056-release.test.d.ts +16 -0
  48. package/dist/invariants/__tests__/adr-056-release.test.d.ts.map +1 -0
  49. package/dist/invariants/__tests__/adr-056-release.test.js +88 -0
  50. package/dist/invariants/__tests__/adr-056-release.test.js.map +1 -0
  51. package/dist/invariants/__tests__/adr-070-orchestration.test.d.ts +22 -0
  52. package/dist/invariants/__tests__/adr-070-orchestration.test.d.ts.map +1 -0
  53. package/dist/invariants/__tests__/adr-070-orchestration.test.js +151 -0
  54. package/dist/invariants/__tests__/adr-070-orchestration.test.js.map +1 -0
  55. package/dist/invariants/__tests__/registry.test.d.ts +14 -0
  56. package/dist/invariants/__tests__/registry.test.d.ts.map +1 -0
  57. package/dist/invariants/__tests__/registry.test.js +113 -0
  58. package/dist/invariants/__tests__/registry.test.js.map +1 -0
  59. package/dist/invariants/adr-056-release.d.ts +42 -0
  60. package/dist/invariants/adr-056-release.d.ts.map +1 -0
  61. package/dist/invariants/adr-056-release.js +143 -0
  62. package/dist/invariants/adr-056-release.js.map +1 -0
  63. package/dist/invariants/adr-070-orchestration.d.ts +73 -0
  64. package/dist/invariants/adr-070-orchestration.d.ts.map +1 -0
  65. package/dist/invariants/adr-070-orchestration.js +290 -0
  66. package/dist/invariants/adr-070-orchestration.js.map +1 -0
  67. package/dist/invariants/adr-073-saga.d.ts +32 -0
  68. package/dist/invariants/adr-073-saga.d.ts.map +1 -0
  69. package/dist/invariants/adr-073-saga.js +155 -0
  70. package/dist/invariants/adr-073-saga.js.map +1 -0
  71. package/dist/invariants/index.d.ts +162 -0
  72. package/dist/invariants/index.d.ts.map +1 -0
  73. package/dist/invariants/index.js +118 -0
  74. package/dist/invariants/index.js.map +1 -0
  75. package/dist/operations/__tests__/input-contract.test.d.ts +20 -0
  76. package/dist/operations/__tests__/input-contract.test.d.ts.map +1 -0
  77. package/dist/operations/__tests__/input-contract.test.js +158 -0
  78. package/dist/operations/__tests__/input-contract.test.js.map +1 -0
  79. package/dist/operations/docs.d.ts +16 -0
  80. package/dist/operations/docs.d.ts.map +1 -1
  81. package/dist/operations/input-contract.d.ts +267 -0
  82. package/dist/operations/input-contract.d.ts.map +1 -0
  83. package/dist/operations/input-contract.js +29 -0
  84. package/dist/operations/input-contract.js.map +1 -0
  85. package/dist/templates/__tests__/manifest.test.d.ts +18 -0
  86. package/dist/templates/__tests__/manifest.test.d.ts.map +1 -0
  87. package/dist/templates/__tests__/manifest.test.js +123 -0
  88. package/dist/templates/__tests__/manifest.test.js.map +1 -0
  89. package/dist/templates/manifest.d.ts +186 -0
  90. package/dist/templates/manifest.d.ts.map +1 -0
  91. package/dist/templates/manifest.js +165 -0
  92. package/dist/templates/manifest.js.map +1 -0
  93. package/package.json +2 -2
  94. package/src/__tests__/cli-meta-suggested-next.test.ts +76 -0
  95. package/src/__tests__/evidence-atom-schema.test.ts +499 -0
  96. package/src/config/__tests__/manifest.test.ts +176 -0
  97. package/src/config/manifest.ts +218 -0
  98. package/src/docs/__tests__/__snapshots__/provenance.test.ts.snap +252 -0
  99. package/src/docs/__tests__/provenance.test.ts +648 -0
  100. package/src/docs/provenance.ts +601 -0
  101. package/src/docs-taxonomy.ts +55 -0
  102. package/src/doctor.ts +111 -0
  103. package/src/errors.ts +4 -0
  104. package/src/evidence-atom-schema.ts +628 -0
  105. package/src/exit-codes.ts +39 -0
  106. package/src/index.ts +129 -0
  107. package/src/invariants/__tests__/adr-056-release.test.ts +102 -0
  108. package/src/invariants/__tests__/adr-070-orchestration.test.ts +186 -0
  109. package/src/invariants/__tests__/registry.test.ts +136 -0
  110. package/src/invariants/adr-056-release.ts +154 -0
  111. package/src/invariants/adr-070-orchestration.ts +317 -0
  112. package/src/invariants/adr-073-saga.ts +167 -0
  113. package/src/invariants/index.ts +222 -0
  114. package/src/operations/__tests__/input-contract.test.ts +188 -0
  115. package/src/operations/docs.ts +16 -0
  116. package/src/operations/input-contract.ts +301 -0
  117. package/src/templates/__tests__/manifest.test.ts +140 -0
  118. 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. */