@cleocode/contracts 2026.5.112 → 2026.5.114
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 +12 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -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/__tests__/tasks-input-contracts.test.d.ts +18 -0
- package/dist/operations/__tests__/tasks-input-contracts.test.d.ts.map +1 -0
- package/dist/operations/__tests__/tasks-input-contracts.test.js +80 -0
- package/dist/operations/__tests__/tasks-input-contracts.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/operations/tasks.d.ts +96 -0
- package/dist/operations/tasks.d.ts.map +1 -1
- package/dist/operations/tasks.js +237 -1
- package/dist/operations/tasks.js.map +1 -1
- 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 +102 -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/__tests__/tasks-input-contracts.test.ts +106 -0
- package/src/operations/docs.ts +16 -0
- package/src/operations/input-contract.ts +301 -0
- package/src/operations/tasks.ts +308 -0
- package/src/templates/__tests__/manifest.test.ts +140 -0
- package/src/templates/manifest.ts +208 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for the ADR-070 ORC-### orchestration invariants (T10336).
|
|
3
|
+
*
|
|
4
|
+
* The ORC codes are the Orchestrator / Lead / Worker behavioural contract
|
|
5
|
+
* surfaced through the central INVARIANTS_REGISTRY. The tests below assert:
|
|
6
|
+
*
|
|
7
|
+
* 1. At least 10 ORC-### entries are registered (we ship 14).
|
|
8
|
+
* 2. ORC-012 (the only ORC rule with a hard-enforced dispatch gate today)
|
|
9
|
+
* points at the `enforceThinAgent` function in
|
|
10
|
+
* `packages/core/src/orchestration/thin-agent.ts`.
|
|
11
|
+
* 3. Every `severity:'warning'` entry with `runtimeGate: null` carries
|
|
12
|
+
* explanatory `description` text — the gap MUST be documented, not
|
|
13
|
+
* silent.
|
|
14
|
+
* 4. ORC keys are emitted under the `ADR-070.` prefix in declaration
|
|
15
|
+
* order and are merged into the central registry.
|
|
16
|
+
*
|
|
17
|
+
* @epic T10327 — E-INVARIANT-REGISTRY-SSOT
|
|
18
|
+
* @saga T10326 — SG-SUBSTRATE-RECONCILIATION
|
|
19
|
+
* @task T10336 — R2: enumerate ADR-070 ORC codes + register
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { describe, expect, it } from 'vitest';
|
|
23
|
+
import {
|
|
24
|
+
ADR_070_INVARIANT_COUNT,
|
|
25
|
+
ADR_070_INVARIANTS,
|
|
26
|
+
ADR_070_MIN_ENTRIES,
|
|
27
|
+
} from '../adr-070-orchestration.js';
|
|
28
|
+
import {
|
|
29
|
+
getInvariant,
|
|
30
|
+
getInvariantsByAdr,
|
|
31
|
+
INVARIANTS_REGISTRY,
|
|
32
|
+
type RegisteredInvariant,
|
|
33
|
+
} from '../index.js';
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Minimum ORC-### entry count required by AC1 — "Inventory ~ORC-001
|
|
37
|
+
* through ~ORC-020". We ship 14 today; the floor is 10 so future
|
|
38
|
+
* removals trip the gate before silently dropping coverage.
|
|
39
|
+
*/
|
|
40
|
+
const MIN_ORC_ENTRIES = 10;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The ORC entry that has a hard-enforced dispatch-time guard today.
|
|
44
|
+
* Other entries either prompt-time (skill text) or filed-but-unshipped
|
|
45
|
+
* (ORC-010 / ORC-011 → T10278 / T10279).
|
|
46
|
+
*/
|
|
47
|
+
const HARD_ENFORCED_ORC_CODE = 'ORC-012';
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Expected runtime-gate module + function for ORC-012. Asserting both
|
|
51
|
+
* fields prevents silent drift if the thin-agent module is renamed or
|
|
52
|
+
* the export is shadowed.
|
|
53
|
+
*/
|
|
54
|
+
const ORC_012_RUNTIME_GATE = {
|
|
55
|
+
module: 'packages/core/src/orchestration/thin-agent.ts',
|
|
56
|
+
functionName: 'enforceThinAgent',
|
|
57
|
+
} as const;
|
|
58
|
+
|
|
59
|
+
describe('ADR_070_INVARIANTS — enumeration shape', () => {
|
|
60
|
+
it('exposes at least 10 ORC-### entries', () => {
|
|
61
|
+
expect(ADR_070_INVARIANTS.length).toBeGreaterThanOrEqual(MIN_ORC_ENTRIES);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('keeps ADR_070_INVARIANT_COUNT in sync with the array length', () => {
|
|
65
|
+
expect(ADR_070_INVARIANT_COUNT).toBe(ADR_070_INVARIANTS.length);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('declares ADR_070_MIN_ENTRIES at or above the AC1 floor', () => {
|
|
69
|
+
expect(ADR_070_MIN_ENTRIES).toBeGreaterThanOrEqual(MIN_ORC_ENTRIES);
|
|
70
|
+
expect(ADR_070_MIN_ENTRIES).toBeLessThanOrEqual(ADR_070_INVARIANTS.length);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('every entry sets adr="ADR-070" and a non-empty ORC-#### code', () => {
|
|
74
|
+
for (const entry of ADR_070_INVARIANTS) {
|
|
75
|
+
expect(entry.adr).toBe('ADR-070');
|
|
76
|
+
expect(entry.code).toMatch(/^ORC-\d{3}$/);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('every entry carries a non-empty name and description', () => {
|
|
81
|
+
for (const entry of ADR_070_INVARIANTS) {
|
|
82
|
+
expect(entry.name.length).toBeGreaterThan(0);
|
|
83
|
+
expect(entry.description.length).toBeGreaterThan(0);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('codes are unique across the ADR-070 enumeration', () => {
|
|
88
|
+
const codes = ADR_070_INVARIANTS.map((e) => e.code);
|
|
89
|
+
const uniqueCodes = new Set(codes);
|
|
90
|
+
expect(uniqueCodes.size).toBe(codes.length);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('ADR-070 ORC-012 — thin-agent dispatch gate (hard-enforced)', () => {
|
|
95
|
+
it('points at enforceThinAgent in packages/core/src/orchestration/thin-agent.ts', () => {
|
|
96
|
+
const entry = ADR_070_INVARIANTS.find((e) => e.code === HARD_ENFORCED_ORC_CODE);
|
|
97
|
+
expect(entry).toBeDefined();
|
|
98
|
+
expect(entry?.severity).toBe('error');
|
|
99
|
+
expect(entry?.runtimeGate).not.toBeNull();
|
|
100
|
+
expect(entry?.runtimeGate?.module).toBe(ORC_012_RUNTIME_GATE.module);
|
|
101
|
+
expect(entry?.runtimeGate?.functionName).toBe(ORC_012_RUNTIME_GATE.functionName);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('is reachable via INVARIANTS_REGISTRY["ADR-070.ORC-012"]', () => {
|
|
105
|
+
const fromRegistry = INVARIANTS_REGISTRY['ADR-070.ORC-012'];
|
|
106
|
+
expect(fromRegistry).toBeDefined();
|
|
107
|
+
expect(fromRegistry?.runtimeGate?.functionName).toBe(ORC_012_RUNTIME_GATE.functionName);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe('ADR-070 gap documentation — warning + runtimeGate:null entries', () => {
|
|
112
|
+
it('every warning entry with runtimeGate:null carries explanatory description text', () => {
|
|
113
|
+
const gaps: RegisteredInvariant[] = ADR_070_INVARIANTS.filter(
|
|
114
|
+
(e) => e.severity === 'warning' && e.runtimeGate === null,
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
// We expect at least the prompt-only ORC entries (ORC-001..003, 008, 009)
|
|
118
|
+
// plus the filed-but-unshipped depth/lead entries (ORC-010 / ORC-011).
|
|
119
|
+
expect(gaps.length).toBeGreaterThanOrEqual(5);
|
|
120
|
+
|
|
121
|
+
// Each gap MUST explain WHY no runtime gate exists. We probe for the
|
|
122
|
+
// documented surface markers ("UNENFORCED", "Prompt-time",
|
|
123
|
+
// "FILED but UNSHIPPED" — and the related verb forms). At least one of
|
|
124
|
+
// these phrases is required so the R6 doctor audit can rely on the
|
|
125
|
+
// description as a human-readable gap explanation.
|
|
126
|
+
const gapMarkers = ['UNENFORCED', 'prompt-time', 'Prompt-time', 'UNSHIPPED', 'unshipped'];
|
|
127
|
+
for (const entry of gaps) {
|
|
128
|
+
const hasMarker = gapMarkers.some((m) => entry.description.includes(m));
|
|
129
|
+
expect(hasMarker, `Entry ${entry.code} description is missing a gap marker`).toBe(true);
|
|
130
|
+
// Sanity check: description is substantive, not a stub.
|
|
131
|
+
expect(entry.description.length).toBeGreaterThanOrEqual(80);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('filed-but-unshipped ORC-010 / ORC-011 reference their tracking tasks', () => {
|
|
136
|
+
const orc010 = ADR_070_INVARIANTS.find((e) => e.code === 'ORC-010');
|
|
137
|
+
const orc011 = ADR_070_INVARIANTS.find((e) => e.code === 'ORC-011');
|
|
138
|
+
expect(orc010?.description).toContain('T10278');
|
|
139
|
+
expect(orc011?.description).toContain('T10279');
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe('central registry — ADR-070 entries merged via index.ts', () => {
|
|
144
|
+
it('every ADR-070 entry is reachable via getInvariant("ADR-070.<code>")', () => {
|
|
145
|
+
for (const entry of ADR_070_INVARIANTS) {
|
|
146
|
+
const key = `ADR-070.${entry.code}`;
|
|
147
|
+
const fromRegistry = getInvariant(key);
|
|
148
|
+
expect(fromRegistry, `Missing registry entry for ${key}`).toBeDefined();
|
|
149
|
+
expect(fromRegistry?.code).toBe(entry.code);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('getInvariantsByAdr("ADR-070") returns every entry in declaration order', () => {
|
|
154
|
+
const adr070 = getInvariantsByAdr('ADR-070');
|
|
155
|
+
expect(adr070).toHaveLength(ADR_070_INVARIANTS.length);
|
|
156
|
+
for (let i = 0; i < ADR_070_INVARIANTS.length; i++) {
|
|
157
|
+
expect(adr070[i]?.code).toBe(ADR_070_INVARIANTS[i]?.code);
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('does not collide with ADR-073 entries under the merged key space', () => {
|
|
162
|
+
const adr070Keys = ADR_070_INVARIANTS.map((e) => `ADR-070.${e.code}`);
|
|
163
|
+
const adr073Keys = getInvariantsByAdr('ADR-073').map((e) => `ADR-073.${e.code}`);
|
|
164
|
+
const intersection = adr070Keys.filter((k) => adr073Keys.includes(k));
|
|
165
|
+
expect(intersection).toEqual([]);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
describe('ADR-070 severity:"error" entries — runtime gate invariant', () => {
|
|
170
|
+
it('every error-tier entry has a non-null runtimeGate (R4 T10338 strict gate)', () => {
|
|
171
|
+
const errorTier: RegisteredInvariant[] = ADR_070_INVARIANTS.filter(
|
|
172
|
+
(e) => e.severity === 'error',
|
|
173
|
+
);
|
|
174
|
+
expect(errorTier.length).toBeGreaterThan(0);
|
|
175
|
+
for (const entry of errorTier) {
|
|
176
|
+
expect(
|
|
177
|
+
entry.runtimeGate,
|
|
178
|
+
`Error-tier entry ${entry.code} missing runtimeGate`,
|
|
179
|
+
).not.toBeNull();
|
|
180
|
+
if (entry.runtimeGate !== null) {
|
|
181
|
+
expect(entry.runtimeGate.module.length).toBeGreaterThan(0);
|
|
182
|
+
expect(entry.runtimeGate.functionName.length).toBeGreaterThan(0);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
});
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADR-056 decisions D1-D6 registered against the central invariants
|
|
3
|
+
* registry (`./index.ts`).
|
|
4
|
+
*
|
|
5
|
+
* The six decisions govern the per-domain SQLite SSoT topology + the
|
|
6
|
+
* release-completion invariant (archiveReason enum + post-release gate).
|
|
7
|
+
*
|
|
8
|
+
* - D1 — Database topology: keep per-domain split (no consolidation).
|
|
9
|
+
* - D2 — Naming convention `<domain>-schema.ts` + `<domain>-sqlite.ts`.
|
|
10
|
+
* - D3 — Migration runner SSoT under `migration-manager.ts`.
|
|
11
|
+
* - D4 — `archiveReason` 6-value enum with tombstone semantics.
|
|
12
|
+
* - D5 — Post-release reconciliation: registry-driven `cleo verify --release`.
|
|
13
|
+
* - D6 — Commit-message lint for release commits.
|
|
14
|
+
*
|
|
15
|
+
* D4 carries a runtime guard (the `assertArchiveReason` function in
|
|
16
|
+
* `@cleocode/contracts` tasks/archive module) and surfaces as the
|
|
17
|
+
* `E_ARCHIVE_REASON_TOMBSTONE` LAFS error code on tombstone-write attempts.
|
|
18
|
+
* D5 carries a registry-driven runtime gate at
|
|
19
|
+
* `packages/core/src/release/invariants/registry.ts` (the per-release
|
|
20
|
+
* executable invariants subsystem this central entry catalogues). D1, D2,
|
|
21
|
+
* D3, D6 are topology / convention / CI-hook decisions — they carry
|
|
22
|
+
* `lintRule` or `runtimeGate:null` markers per the per-decision
|
|
23
|
+
* enforcement surface, matching the ADR-073 pattern for I1, I2, I4, I6, I8.
|
|
24
|
+
*
|
|
25
|
+
* @epic T10327 — E-INVARIANT-REGISTRY-SSOT
|
|
26
|
+
* @saga T10326 — SG-SUBSTRATE-RECONCILIATION
|
|
27
|
+
* @task T10339 — R5: release-registry consumes central substrate
|
|
28
|
+
* @see ADR-056-db-ssot-and-release-completion-invariant.md §Decision
|
|
29
|
+
* @see packages/core/src/release/invariants/registry.ts — D5 executable subsystem
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import type { RegisteredInvariant } from './index.js';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Module path that hosts the `assertArchiveReason` runtime guard.
|
|
36
|
+
*
|
|
37
|
+
* Held as a constant so the D4 entry shares one source-of-truth pointer —
|
|
38
|
+
* touching the path once propagates to every guard ref.
|
|
39
|
+
*/
|
|
40
|
+
const ARCHIVE_REASON_MODULE = 'packages/contracts/src/tasks/archive.ts';
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Module path that hosts the release-invariants registry — the executable
|
|
44
|
+
* subsystem catalogued by D5. The registry exposes `registerInvariant`,
|
|
45
|
+
* `getInvariants`, and `runInvariants`; the archive-reason invariant is its
|
|
46
|
+
* first customer.
|
|
47
|
+
*/
|
|
48
|
+
const RELEASE_INVARIANTS_REGISTRY_MODULE = 'packages/core/src/release/invariants/registry.ts';
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Repo-rooted path to the release commit-msg lint script (D6 enforcement).
|
|
52
|
+
*/
|
|
53
|
+
const RELEASE_COMMIT_MSG_HOOK = 'scripts/hooks/commit-msg-release-lint.mjs';
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* ADR-056 invariants D1-D6 in declaration order.
|
|
57
|
+
*
|
|
58
|
+
* Severity mapping (see `RegisteredInvariant` JSDoc for tier semantics):
|
|
59
|
+
* - D1, D2, D3, D6 → `info` (topology / convention / CI hook concerns).
|
|
60
|
+
* - D4 → `error` (tombstone-write rejection — `E_ARCHIVE_REASON_TOMBSTONE`).
|
|
61
|
+
* - D5 → `warning` (post-release gate produces follow-up tasks rather than
|
|
62
|
+
* throwing — failures are caught + recorded via the audit log).
|
|
63
|
+
*/
|
|
64
|
+
export const ADR_056_INVARIANTS: readonly RegisteredInvariant[] = Object.freeze([
|
|
65
|
+
{
|
|
66
|
+
adr: 'ADR-056',
|
|
67
|
+
code: 'D1',
|
|
68
|
+
name: 'Database topology: keep per-domain split',
|
|
69
|
+
description:
|
|
70
|
+
'CLEO retains the six per-domain SQLite databases (tasks, brain, conduit, nexus, signaldock, telemetry) documented in DATABASE-ERDS.md. Consolidation is rejected to preserve per-DB WAL throughput, per-DB rollback granularity, and avoid single-writer contention during multi-agent waves.',
|
|
71
|
+
severity: 'info',
|
|
72
|
+
// D1 is a topology decision; the absence of consolidation is its only
|
|
73
|
+
// enforcement surface. No runtime guard, no lint script.
|
|
74
|
+
runtimeGate: null,
|
|
75
|
+
lintRule: null,
|
|
76
|
+
doctorAudit: null,
|
|
77
|
+
tests: [],
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
adr: 'ADR-056',
|
|
81
|
+
code: 'D2',
|
|
82
|
+
name: 'Store-layer naming convention',
|
|
83
|
+
description:
|
|
84
|
+
'New always-on store-layer domains MUST use the kebab-case pair `packages/core/src/store/<domain>-schema.ts` (Drizzle defs) + `packages/core/src/store/<domain>-sqlite.ts` (open/init/CRUD). Opt-in / isolated domains MAY use the folder variant `packages/core/src/<domain>/{schema,sqlite}.ts` (currently telemetry only).',
|
|
85
|
+
severity: 'info',
|
|
86
|
+
// D2 is a naming-convention decision; enforced by code review and the
|
|
87
|
+
// ADR-073/ADR-056 doctor audit. No runtime guard.
|
|
88
|
+
runtimeGate: null,
|
|
89
|
+
lintRule: null,
|
|
90
|
+
doctorAudit: null,
|
|
91
|
+
tests: [],
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
adr: 'ADR-056',
|
|
95
|
+
code: 'D3',
|
|
96
|
+
name: 'Migration runner SSoT under migration-manager.ts',
|
|
97
|
+
description:
|
|
98
|
+
'All six SQLite databases MUST be initialized via `packages/core/src/migration/migration-manager.ts` (`migrateWithRetry()` + `reconcileJournal()`). Per-DB bespoke runners are prohibited for new domains. Rust Diesel migrations for cloud signaldock-storage MUST NOT touch the local SQLite signaldock.db at runtime.',
|
|
99
|
+
severity: 'info',
|
|
100
|
+
// D3 is enforced by the absence of bespoke runners — every domain's
|
|
101
|
+
// `<domain>-sqlite.ts` open path delegates to migration-manager. No
|
|
102
|
+
// single runtime guard; per-DB chokepoints are the enforcement surface.
|
|
103
|
+
runtimeGate: null,
|
|
104
|
+
lintRule: null,
|
|
105
|
+
doctorAudit: null,
|
|
106
|
+
tests: [],
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
adr: 'ADR-056',
|
|
110
|
+
code: 'D4',
|
|
111
|
+
name: 'archiveReason 6-value enum with tombstone semantics',
|
|
112
|
+
description:
|
|
113
|
+
"The tasks.archive_reason column is constrained to exactly six values (verified, reconciled, superseded, shadowed, cancelled, completed-unverified) via SQLite CHECK constraint AND Zod z.enum validation. Writing 'completed-unverified' from non-migration code MUST throw E_ARCHIVE_REASON_TOMBSTONE — the tombstone is reserved for the T1408 backfill migration only.",
|
|
114
|
+
severity: 'error',
|
|
115
|
+
runtimeGate: {
|
|
116
|
+
module: ARCHIVE_REASON_MODULE,
|
|
117
|
+
functionName: 'assertArchiveReason',
|
|
118
|
+
},
|
|
119
|
+
lintRule: null,
|
|
120
|
+
doctorAudit: null,
|
|
121
|
+
tests: ['packages/contracts/src/tasks/__tests__/archive.test.ts'],
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
adr: 'ADR-056',
|
|
125
|
+
code: 'D5',
|
|
126
|
+
name: 'Post-release reconciliation: registry-driven cleo verify --release',
|
|
127
|
+
description:
|
|
128
|
+
'Post-release reconciliation flows through the executable invariants registry at packages/core/src/release/invariants/registry.ts. Customers register via registerInvariant() and the CLI runs every entry on `cleo verify --release <tag>`. First customer: archive-reason-invariant.ts (stamps verified tasks done; creates follow-up tasks for unverified references).',
|
|
129
|
+
severity: 'warning',
|
|
130
|
+
runtimeGate: {
|
|
131
|
+
module: RELEASE_INVARIANTS_REGISTRY_MODULE,
|
|
132
|
+
functionName: 'runInvariants',
|
|
133
|
+
},
|
|
134
|
+
lintRule: null,
|
|
135
|
+
doctorAudit: null,
|
|
136
|
+
tests: ['packages/core/src/release/invariants/__tests__/archive-reason-invariant.test.ts'],
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
adr: 'ADR-056',
|
|
140
|
+
code: 'D6',
|
|
141
|
+
name: 'Commit-message lint for release commits',
|
|
142
|
+
description:
|
|
143
|
+
'Every commit whose subject matches `^(chore|feat)\\(release\\):` MUST contain at least one `T\\d+` task reference in the commit body. Enforced by scripts/hooks/commit-msg-release-lint.mjs (T1410). Bypass via CLEO_OWNER_OVERRIDE=1 with audited justification.',
|
|
144
|
+
severity: 'info',
|
|
145
|
+
// D6 is a CI-hook concern. The hook itself is the enforcement surface
|
|
146
|
+
// — represented here via lintRule for cross-reference rendering.
|
|
147
|
+
runtimeGate: null,
|
|
148
|
+
lintRule: {
|
|
149
|
+
lintScript: RELEASE_COMMIT_MSG_HOOK,
|
|
150
|
+
},
|
|
151
|
+
doctorAudit: null,
|
|
152
|
+
tests: [],
|
|
153
|
+
},
|
|
154
|
+
]);
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADR-070 ORC-### orchestration invariants registered against the central
|
|
3
|
+
* invariants registry (`./index.ts`).
|
|
4
|
+
*
|
|
5
|
+
* ORC codes govern the three-tier spawn topology — Orchestrator (HITL
|
|
6
|
+
* interface) → Lead (phase coordinator) → Worker (leaf task) — defined by
|
|
7
|
+
* ADR-070 and refined by ADR-083 §2 (Cleo persona + recursion bounds).
|
|
8
|
+
*
|
|
9
|
+
* Severity mapping (see `RegisteredInvariant` JSDoc for tier semantics):
|
|
10
|
+
*
|
|
11
|
+
* - **ORC-012** (thin-agent inversion-of-control) is the only ORC rule with
|
|
12
|
+
* a hard-enforced runtime gate today (`enforceThinAgent` +
|
|
13
|
+
* `ThinAgentViolationError`). It surfaces as
|
|
14
|
+
* `E_THIN_AGENT_VIOLATION` (LAFS error code + exit 68). Tier: `error`.
|
|
15
|
+
* - **ORC-001..ORC-009** are the skill-injection rules shipped through
|
|
16
|
+
* `ct-orchestrator/SKILL.md`. They constrain the Orchestrator's runtime
|
|
17
|
+
* behaviour (no code writes, dependency-ordered spawning, 10 K context
|
|
18
|
+
* budget, etc.) but live as prompt-time invariants rather than dispatch
|
|
19
|
+
* guards. Tier: `warning`, `runtimeGate: null` — the gap is documented
|
|
20
|
+
* explicitly so the R6 doctor audit (T10340) can surface it.
|
|
21
|
+
* - **ORC-010** (Lead-interposition: a Worker spawn for an Epic-child Task
|
|
22
|
+
* requires a preceding Lead spawn) and **ORC-011** (Orchestrator-depth
|
|
23
|
+
* ≤ 3) are filed against ADR-083 §2.4 + §6 but the dispatch-time gate
|
|
24
|
+
* has not yet shipped (tracked T10278 + T10279). They are registered
|
|
25
|
+
* here as `warning` with `runtimeGate: null` so the gap surfaces in the
|
|
26
|
+
* audit pipeline.
|
|
27
|
+
* - **ORC-013** (worktree provisioning per ADR-055 / D009) IS enforced at
|
|
28
|
+
* `createWorktree` time via `assertCanonicalWorktreeLocation` — it
|
|
29
|
+
* carries `severity: 'error'` and a runtime gate. The companion CI gate
|
|
30
|
+
* `lint-worktree-location.mjs` is recorded under `lintRule`.
|
|
31
|
+
* - **ORC-014** (Lead-bypass detection at session end) IS enforced by
|
|
32
|
+
* `endSession` via `LeadBypassDetectedError`. Tier: `error`.
|
|
33
|
+
*
|
|
34
|
+
* @epic T10327 — E-INVARIANT-REGISTRY-SSOT
|
|
35
|
+
* @saga T10326 — SG-SUBSTRATE-RECONCILIATION
|
|
36
|
+
* @task T10336 — R2 (ADR-070 ORC codes)
|
|
37
|
+
* @see ADR-070-three-tier-orchestration.md
|
|
38
|
+
* @see ADR-083-section-2.4 — Cleo persona + recursion bounds
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
import type { RegisteredInvariant } from './index.js';
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Module path that hosts the thin-agent enforcement function.
|
|
45
|
+
*/
|
|
46
|
+
const THIN_AGENT_MODULE = 'packages/core/src/orchestration/thin-agent.ts';
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Module path that hosts the session lifecycle entrypoint enforcing
|
|
50
|
+
* Lead-bypass detection at session end.
|
|
51
|
+
*/
|
|
52
|
+
const SESSIONS_MODULE = 'packages/core/src/sessions/index.ts';
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Module path that hosts the worktree creation guard (canonical XDG
|
|
56
|
+
* location enforcement per ADR-055 + Council D009).
|
|
57
|
+
*/
|
|
58
|
+
const WORKTREE_CREATE_MODULE = 'packages/worktree/src/worktree-create.ts';
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Canonical ct-orchestrator skill that injects ORC-001..ORC-009 into the
|
|
62
|
+
* Orchestrator's prompt at spawn time. Referenced from every prompt-time
|
|
63
|
+
* ORC entry so downstream tooling (R8 docs renderer) can chase the source.
|
|
64
|
+
*/
|
|
65
|
+
const CT_ORCHESTRATOR_SKILL = 'packages/skills/skills/ct-orchestrator/SKILL.md';
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Path to the spawn-prompt builder that materialises ORC-006 worker budget
|
|
69
|
+
* constraints into spawn payloads (referenced from ORC-006 below).
|
|
70
|
+
*/
|
|
71
|
+
const SPAWN_PROMPT_MODULE = 'packages/core/src/orchestration/spawn-prompt.ts';
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Path to the validate-spawn module that materialises ORC-006 atomic-scope
|
|
75
|
+
* checks via `V_ATOMIC_SCOPE_MISSING` / `V_ATOMIC_SCOPE_TOO_LARGE` codes.
|
|
76
|
+
*/
|
|
77
|
+
const VALIDATE_SPAWN_MODULE = 'packages/core/src/orchestration/validate-spawn.ts';
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Skill-validator test that exercises ORC-004 / ORC-005 manifest checks.
|
|
81
|
+
*/
|
|
82
|
+
const SKILL_VALIDATOR_TESTS = 'packages/core/src/skills/orchestrator/__tests__/validator.test.ts';
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* ADR-070 ORC-### invariants in declaration order.
|
|
86
|
+
*
|
|
87
|
+
* The 14 entries cover:
|
|
88
|
+
* - ORC-001..ORC-009 — ct-orchestrator skill behavioural constraints.
|
|
89
|
+
* - ORC-010..ORC-011 — ADR-083 §2.4 recursion-bound gaps (filed,
|
|
90
|
+
* runtime gate unimplemented — tracked T10278 / T10279).
|
|
91
|
+
* - ORC-012 — thin-agent inversion-of-control (hard-enforced).
|
|
92
|
+
* - ORC-013 — worktree provisioning per ADR-055 / D009 (hard-enforced).
|
|
93
|
+
* - ORC-014 — Lead-bypass detection at session end (hard-enforced).
|
|
94
|
+
*
|
|
95
|
+
* Every `severity: 'warning'` entry with `runtimeGate: null` carries an
|
|
96
|
+
* explicit description of WHY the gate is absent, satisfying the gap-
|
|
97
|
+
* documentation invariant asserted in the unit test below.
|
|
98
|
+
*/
|
|
99
|
+
export const ADR_070_INVARIANTS: readonly RegisteredInvariant[] = Object.freeze([
|
|
100
|
+
{
|
|
101
|
+
adr: 'ADR-070',
|
|
102
|
+
code: 'ORC-001',
|
|
103
|
+
name: 'Orchestrator is the HITL interface',
|
|
104
|
+
description:
|
|
105
|
+
'The Orchestrator (Cleo) is the single subagent that talks to the human operator. It plans, decomposes, and delegates — it never produces line-level implementation. Source-of-truth lives in ct-orchestrator/SKILL.md row ORC-001 and is injected into the Orchestrator prompt at spawn time. UNENFORCED at the dispatch layer: this is a prompt-time invariant with no runtime guard today.',
|
|
106
|
+
severity: 'warning',
|
|
107
|
+
runtimeGate: null,
|
|
108
|
+
lintRule: null,
|
|
109
|
+
doctorAudit: null,
|
|
110
|
+
tests: [CT_ORCHESTRATOR_SKILL],
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
adr: 'ADR-070',
|
|
114
|
+
code: 'ORC-002',
|
|
115
|
+
name: 'Orchestrator MUST NOT write or edit code',
|
|
116
|
+
description:
|
|
117
|
+
'Every line of code is written by a spawned subagent. The Orchestrator delegates implementation work via cleo orchestrate spawn / delegate_task. UNENFORCED at the dispatch layer: this is a prompt-time invariant — there is no runtime gate that blocks the Orchestrator from calling Edit/Write, so the contract is held by the ct-orchestrator skill text.',
|
|
118
|
+
severity: 'warning',
|
|
119
|
+
runtimeGate: null,
|
|
120
|
+
lintRule: null,
|
|
121
|
+
doctorAudit: null,
|
|
122
|
+
tests: [CT_ORCHESTRATOR_SKILL],
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
adr: 'ADR-070',
|
|
126
|
+
code: 'ORC-003',
|
|
127
|
+
name: 'Orchestrator MUST NOT read full source files',
|
|
128
|
+
description:
|
|
129
|
+
'Orchestrator reads only pipeline manifests, task envelopes, and rolled-up phase summaries returned by Phase Leads. Workers read code; the Orchestrator reads summaries. UNENFORCED at the dispatch layer — held by the ct-orchestrator skill text and reinforced by ORC-005 budget pressure.',
|
|
130
|
+
severity: 'warning',
|
|
131
|
+
runtimeGate: null,
|
|
132
|
+
lintRule: null,
|
|
133
|
+
doctorAudit: null,
|
|
134
|
+
tests: [CT_ORCHESTRATOR_SKILL],
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
adr: 'ADR-070',
|
|
138
|
+
code: 'ORC-004',
|
|
139
|
+
name: 'Dependency-ordered spawning',
|
|
140
|
+
description:
|
|
141
|
+
'Spawns within a wave are ordered by task.depends — a Worker MUST NOT be dispatched until its declared dependencies are status=done. Surfaced by validateSpawnReadiness via V_MISSING_DEP / V_UNMET_DEP codes; surfaced by the skill-orchestrator validator via the ORC-004_DEPENDENCY_ORDER warning emitted from packages/core/src/skills/orchestrator/validator.ts. Tier: warning because the validator surfaces ordering issues but does not throw — workers can still proceed if the operator overrides.',
|
|
142
|
+
severity: 'warning',
|
|
143
|
+
runtimeGate: {
|
|
144
|
+
module: VALIDATE_SPAWN_MODULE,
|
|
145
|
+
functionName: 'validateSpawnReadiness',
|
|
146
|
+
},
|
|
147
|
+
lintRule: null,
|
|
148
|
+
doctorAudit: null,
|
|
149
|
+
tests: [
|
|
150
|
+
'packages/core/src/orchestration/__tests__/validate-spawn.test.ts',
|
|
151
|
+
SKILL_VALIDATOR_TESTS,
|
|
152
|
+
],
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
adr: 'ADR-070',
|
|
156
|
+
code: 'ORC-005',
|
|
157
|
+
name: 'Orchestrator context budget ≈ 10 K tokens',
|
|
158
|
+
description:
|
|
159
|
+
'The Orchestrator MUST keep its working context under ~10 K tokens; delegate at 80 %. Surfaced by cleo orchestrate context (estimateContext) and by the skill-orchestrator validator (ORC-005_NO_MANIFEST / ORC-005_EMPTY_MANIFEST). UNENFORCED as a hard gate — the budget is advisory, surfaced to the Orchestrator as a warning so the human operator can intervene.',
|
|
160
|
+
severity: 'warning',
|
|
161
|
+
runtimeGate: {
|
|
162
|
+
module: 'packages/core/src/orchestration/context.ts',
|
|
163
|
+
functionName: 'estimateContext',
|
|
164
|
+
},
|
|
165
|
+
lintRule: null,
|
|
166
|
+
doctorAudit: null,
|
|
167
|
+
tests: ['packages/core/src/orchestration/__tests__/'],
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
adr: 'ADR-070',
|
|
171
|
+
code: 'ORC-006',
|
|
172
|
+
name: 'Worker scope ≤ 3 files per spawn',
|
|
173
|
+
description:
|
|
174
|
+
'Cross-file reasoning quality degrades beyond ~3 files for a single Worker. Enforced at spawn-time by validateSpawnReadiness — V_ATOMIC_SCOPE_MISSING when task.files is empty, V_ATOMIC_SCOPE_TOO_LARGE when files.length > MAX_WORKER_FILES (currently 3). Spawn-prompt builder injects a Worker Budget Constraints section so the Worker sees the budget inline. Tier: error because the gate refuses to spawn an over-scoped Worker.',
|
|
175
|
+
severity: 'error',
|
|
176
|
+
runtimeGate: {
|
|
177
|
+
module: VALIDATE_SPAWN_MODULE,
|
|
178
|
+
functionName: 'validateSpawnReadiness',
|
|
179
|
+
},
|
|
180
|
+
lintRule: null,
|
|
181
|
+
doctorAudit: null,
|
|
182
|
+
tests: [
|
|
183
|
+
'packages/core/src/orchestration/__tests__/validate-spawn.test.ts',
|
|
184
|
+
'packages/core/src/orchestration/__tests__/spawn-prompt.test.ts',
|
|
185
|
+
],
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
adr: 'ADR-070',
|
|
189
|
+
code: 'ORC-007',
|
|
190
|
+
name: 'All work traced to an Epic',
|
|
191
|
+
description:
|
|
192
|
+
'Every Task and Subtask MUST attach to a parent Epic (directly or transitively). No orphan work — orphans are filed against the ADR-066 acceptance-criteria gate and surface via cleo find. UNENFORCED at the dispatch layer; the parent-id requirement is materialised through cleo add validation rather than a single ORC-named guard. R6 doctor audit should walk the task graph to surface orphans.',
|
|
193
|
+
severity: 'warning',
|
|
194
|
+
runtimeGate: null,
|
|
195
|
+
lintRule: null,
|
|
196
|
+
doctorAudit: null,
|
|
197
|
+
tests: [CT_ORCHESTRATOR_SKILL],
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
adr: 'ADR-070',
|
|
201
|
+
code: 'ORC-008',
|
|
202
|
+
name: 'Zero architectural decisions during execution',
|
|
203
|
+
description:
|
|
204
|
+
'Architectural choices MUST be pre-decided via RCASD consensus or HITL — never inside a worker session. UNENFORCED at the dispatch layer: this is a behavioural invariant held by the ct-orchestrator skill text plus the ADR-066 acceptance criterion that every task must declare its architecture-relevant decisions before spawn.',
|
|
205
|
+
severity: 'warning',
|
|
206
|
+
runtimeGate: null,
|
|
207
|
+
lintRule: null,
|
|
208
|
+
doctorAudit: null,
|
|
209
|
+
tests: [CT_ORCHESTRATOR_SKILL],
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
adr: 'ADR-070',
|
|
213
|
+
code: 'ORC-009',
|
|
214
|
+
name: 'Manifest-mediated handoffs',
|
|
215
|
+
description:
|
|
216
|
+
'Orchestrator reads only the key_findings field of pipeline_manifest rows when reconciling worker output. Subagents read the full task description and supporting files. UNENFORCED at the dispatch layer: the contract is held by the ct-orchestrator skill text and reinforced by ORC-003 + ORC-005 budget pressure.',
|
|
217
|
+
severity: 'warning',
|
|
218
|
+
runtimeGate: null,
|
|
219
|
+
lintRule: null,
|
|
220
|
+
doctorAudit: null,
|
|
221
|
+
tests: [CT_ORCHESTRATOR_SKILL],
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
adr: 'ADR-070',
|
|
225
|
+
code: 'ORC-010',
|
|
226
|
+
name: 'Lead-interposition required for Epic-child Workers',
|
|
227
|
+
description:
|
|
228
|
+
'A Worker spawn against a Task whose parent is type=epic MUST be preceded by a Lead spawn for the same Task (ADR-083 §2.4 / §6). The intended runtime gate (composeSpawnPayload throwing E_LEAD_REQUIRED_FOR_EPIC_CHILD) is FILED but UNSHIPPED — tracked under T10278. Registered here as a warning + runtimeGate:null so the gap is visible in the R6 doctor audit.',
|
|
229
|
+
severity: 'warning',
|
|
230
|
+
runtimeGate: null,
|
|
231
|
+
lintRule: null,
|
|
232
|
+
doctorAudit: null,
|
|
233
|
+
tests: [],
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
adr: 'ADR-070',
|
|
237
|
+
code: 'ORC-011',
|
|
238
|
+
name: 'Orchestrator-depth cap at 3',
|
|
239
|
+
description:
|
|
240
|
+
'Recursive Orchestrator spawns (Cleo → sub-Orchestrator → sub-Orchestrator → …) MUST stop at depth 3 (ADR-083 §2.2 + §2.4). The intended runtime gate (composeSpawnPayload throwing E_ORCHESTRATOR_DEPTH_EXCEEDED) is FILED but UNSHIPPED — tracked under T10279. Registered here as a warning + runtimeGate:null so the gap is visible in the R6 doctor audit.',
|
|
241
|
+
severity: 'warning',
|
|
242
|
+
runtimeGate: null,
|
|
243
|
+
lintRule: null,
|
|
244
|
+
doctorAudit: null,
|
|
245
|
+
tests: [],
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
adr: 'ADR-070',
|
|
249
|
+
code: 'ORC-012',
|
|
250
|
+
name: 'Thin-agent inversion-of-control',
|
|
251
|
+
description:
|
|
252
|
+
'Workers MUST NOT spawn other subagents. The spawn-capable tools (Agent / Task / TaskCreate) are stripped from the Worker tool list at .cant compile time, and any survivor at spawn time triggers ThinAgentViolationError → E_THIN_AGENT_VIOLATION (exit 68). The only ORC rule with a hard-enforced dispatch-time gate today.',
|
|
253
|
+
severity: 'error',
|
|
254
|
+
runtimeGate: {
|
|
255
|
+
module: THIN_AGENT_MODULE,
|
|
256
|
+
functionName: 'enforceThinAgent',
|
|
257
|
+
},
|
|
258
|
+
lintRule: null,
|
|
259
|
+
doctorAudit: null,
|
|
260
|
+
tests: [
|
|
261
|
+
'packages/core/src/orchestration/__tests__/thin-agent.test.ts',
|
|
262
|
+
'packages/cant/src/__tests__/hierarchy.test.ts',
|
|
263
|
+
],
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
adr: 'ADR-070',
|
|
267
|
+
code: 'ORC-013',
|
|
268
|
+
name: 'Worktree provisioning at canonical XDG location',
|
|
269
|
+
description:
|
|
270
|
+
'Every agent worktree MUST be created under <cleoHome>/worktrees/<projectHash>/<taskId>/ (ADR-055 + Council D009). createWorktree throws E_WT_LOCATION_FORBIDDEN before any git worktree add call when the computed path falls outside the canonical root. CI gate lint-worktree-location.mjs enforces the same invariant on every PR. The single-most-load-bearing orchestration guard after ORC-012.',
|
|
271
|
+
severity: 'error',
|
|
272
|
+
runtimeGate: {
|
|
273
|
+
module: WORKTREE_CREATE_MODULE,
|
|
274
|
+
functionName: 'assertCanonicalWorktreeLocation',
|
|
275
|
+
},
|
|
276
|
+
lintRule: {
|
|
277
|
+
lintScript: 'scripts/lint-worktree-location.mjs',
|
|
278
|
+
},
|
|
279
|
+
doctorAudit: null,
|
|
280
|
+
tests: ['packages/worktree/src/__tests__/'],
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
adr: 'ADR-070',
|
|
284
|
+
code: 'ORC-014',
|
|
285
|
+
name: 'Lead-bypass detection at session end',
|
|
286
|
+
description:
|
|
287
|
+
'A Lead session (CLEO_AGENT_ROLE=lead) that ends with tasks_completed > 0 AND delegate_task_count = 0 is rejected with LeadBypassDetectedError → E_LEAD_BYPASS_DETECTED (exit 107). Leads MUST fan out work to Workers; a Lead that did the work itself defeats the three-tier topology. Override via CLEO_OWNER_OVERRIDE=1 (audited to force-bypass.jsonl).',
|
|
288
|
+
severity: 'error',
|
|
289
|
+
runtimeGate: {
|
|
290
|
+
module: SESSIONS_MODULE,
|
|
291
|
+
functionName: 'endSession',
|
|
292
|
+
},
|
|
293
|
+
lintRule: null,
|
|
294
|
+
doctorAudit: null,
|
|
295
|
+
tests: ['packages/core/src/sessions/__tests__/'],
|
|
296
|
+
},
|
|
297
|
+
]);
|
|
298
|
+
|
|
299
|
+
/** Sentinel referenced by the unit test — keep in sync with array length. */
|
|
300
|
+
export const ADR_070_INVARIANT_COUNT = ADR_070_INVARIANTS.length;
|
|
301
|
+
|
|
302
|
+
/** Sentinel — minimum entries the central registry must merge from this file. */
|
|
303
|
+
export const ADR_070_MIN_ENTRIES = 14;
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Export the prompt-only module reference so other ADR-070 ORC modules can
|
|
307
|
+
* cross-reference the canonical skill location without restating the path.
|
|
308
|
+
* @internal
|
|
309
|
+
*/
|
|
310
|
+
export const ADR_070_PROMPT_SOURCE = CT_ORCHESTRATOR_SKILL;
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Re-export for symmetry with the spawn-prompt builder consumers — used by
|
|
314
|
+
* the R8 docs renderer to chase the prompt-injection source for ORC-006.
|
|
315
|
+
* @internal
|
|
316
|
+
*/
|
|
317
|
+
export const ADR_070_SPAWN_PROMPT_MODULE = SPAWN_PROMPT_MODULE;
|