@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.
- package/dist/__tests__/cli-meta-suggested-next.test.d.ts +21 -0
- package/dist/__tests__/cli-meta-suggested-next.test.d.ts.map +1 -0
- package/dist/__tests__/cli-meta-suggested-next.test.js +70 -0
- package/dist/__tests__/cli-meta-suggested-next.test.js.map +1 -0
- package/dist/config/__tests__/manifest.test.d.ts +13 -0
- package/dist/config/__tests__/manifest.test.d.ts.map +1 -0
- package/dist/config/__tests__/manifest.test.js +143 -0
- package/dist/config/__tests__/manifest.test.js.map +1 -0
- package/dist/config/manifest.d.ts +158 -0
- package/dist/config/manifest.d.ts.map +1 -0
- package/dist/config/manifest.js +123 -0
- package/dist/config/manifest.js.map +1 -0
- package/dist/docs/__tests__/provenance.test.d.ts +14 -0
- package/dist/docs/__tests__/provenance.test.d.ts.map +1 -0
- package/dist/docs/__tests__/provenance.test.js +579 -0
- package/dist/docs/__tests__/provenance.test.js.map +1 -0
- package/dist/docs/provenance.d.ts +731 -0
- package/dist/docs/provenance.d.ts.map +1 -0
- package/dist/docs/provenance.js +258 -0
- package/dist/docs/provenance.js.map +1 -0
- package/dist/docs-taxonomy.d.ts +24 -0
- package/dist/docs-taxonomy.d.ts.map +1 -1
- package/dist/docs-taxonomy.js +27 -0
- package/dist/docs-taxonomy.js.map +1 -1
- package/dist/doctor.d.ts +101 -0
- package/dist/doctor.d.ts.map +1 -1
- package/dist/errors.d.ts +4 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +4 -0
- package/dist/errors.js.map +1 -1
- package/dist/exit-codes.d.ts +37 -0
- package/dist/exit-codes.d.ts.map +1 -1
- package/dist/exit-codes.js +37 -0
- package/dist/exit-codes.js.map +1 -1
- package/dist/index.d.ts +10 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -2
- package/dist/index.js.map +1 -1
- package/dist/invariants/__tests__/adr-056-release.test.d.ts +16 -0
- package/dist/invariants/__tests__/adr-056-release.test.d.ts.map +1 -0
- package/dist/invariants/__tests__/adr-056-release.test.js +88 -0
- package/dist/invariants/__tests__/adr-056-release.test.js.map +1 -0
- package/dist/invariants/__tests__/adr-070-orchestration.test.d.ts +22 -0
- package/dist/invariants/__tests__/adr-070-orchestration.test.d.ts.map +1 -0
- package/dist/invariants/__tests__/adr-070-orchestration.test.js +151 -0
- package/dist/invariants/__tests__/adr-070-orchestration.test.js.map +1 -0
- package/dist/invariants/adr-056-release.d.ts +42 -0
- package/dist/invariants/adr-056-release.d.ts.map +1 -0
- package/dist/invariants/adr-056-release.js +143 -0
- package/dist/invariants/adr-056-release.js.map +1 -0
- package/dist/invariants/adr-070-orchestration.d.ts +73 -0
- package/dist/invariants/adr-070-orchestration.d.ts.map +1 -0
- package/dist/invariants/adr-070-orchestration.js +290 -0
- package/dist/invariants/adr-070-orchestration.js.map +1 -0
- package/dist/invariants/index.d.ts +4 -1
- package/dist/invariants/index.d.ts.map +1 -1
- package/dist/invariants/index.js +11 -2
- package/dist/invariants/index.js.map +1 -1
- package/dist/operations/__tests__/input-contract.test.d.ts +20 -0
- package/dist/operations/__tests__/input-contract.test.d.ts.map +1 -0
- package/dist/operations/__tests__/input-contract.test.js +158 -0
- package/dist/operations/__tests__/input-contract.test.js.map +1 -0
- package/dist/operations/docs.d.ts +16 -0
- package/dist/operations/docs.d.ts.map +1 -1
- package/dist/operations/input-contract.d.ts +267 -0
- package/dist/operations/input-contract.d.ts.map +1 -0
- package/dist/operations/input-contract.js +29 -0
- package/dist/operations/input-contract.js.map +1 -0
- package/dist/templates/__tests__/manifest.test.d.ts +18 -0
- package/dist/templates/__tests__/manifest.test.d.ts.map +1 -0
- package/dist/templates/__tests__/manifest.test.js +123 -0
- package/dist/templates/__tests__/manifest.test.js.map +1 -0
- package/dist/templates/manifest.d.ts +186 -0
- package/dist/templates/manifest.d.ts.map +1 -0
- package/dist/templates/manifest.js +165 -0
- package/dist/templates/manifest.js.map +1 -0
- package/package.json +2 -2
- package/src/__tests__/cli-meta-suggested-next.test.ts +76 -0
- package/src/config/__tests__/manifest.test.ts +176 -0
- package/src/config/manifest.ts +218 -0
- package/src/docs/__tests__/__snapshots__/provenance.test.ts.snap +252 -0
- package/src/docs/__tests__/provenance.test.ts +648 -0
- package/src/docs/provenance.ts +601 -0
- package/src/docs-taxonomy.ts +55 -0
- package/src/doctor.ts +111 -0
- package/src/errors.ts +4 -0
- package/src/exit-codes.ts +39 -0
- package/src/index.ts +90 -0
- package/src/invariants/__tests__/adr-056-release.test.ts +102 -0
- package/src/invariants/__tests__/adr-070-orchestration.test.ts +186 -0
- package/src/invariants/adr-056-release.ts +154 -0
- package/src/invariants/adr-070-orchestration.ts +317 -0
- package/src/invariants/index.ts +11 -2
- package/src/operations/__tests__/input-contract.test.ts +188 -0
- package/src/operations/docs.ts +16 -0
- package/src/operations/input-contract.ts +301 -0
- package/src/templates/__tests__/manifest.test.ts +140 -0
- package/src/templates/manifest.ts +208 -0
package/src/docs-taxonomy.ts
CHANGED
|
@@ -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
|
+
});
|