@cleocode/contracts 2026.5.60 → 2026.5.62
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__/nexus-scope-map.test.d.ts +8 -0
- package/dist/__tests__/nexus-scope-map.test.d.ts.map +1 -0
- package/dist/__tests__/nexus-scope-map.test.js +145 -0
- package/dist/__tests__/nexus-scope-map.test.js.map +1 -0
- package/dist/branch-lock.d.ts +11 -0
- package/dist/branch-lock.d.ts.map +1 -1
- package/dist/branch-lock.js +11 -0
- package/dist/branch-lock.js.map +1 -1
- package/dist/config.d.ts +21 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/data-accessor.d.ts +2 -0
- package/dist/data-accessor.d.ts.map +1 -1
- package/dist/data-accessor.js.map +1 -1
- package/dist/engine-result.d.ts +170 -0
- package/dist/engine-result.d.ts.map +1 -0
- package/dist/engine-result.js +123 -0
- package/dist/engine-result.js.map +1 -0
- package/dist/errors.d.ts +24 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +31 -0
- package/dist/errors.js.map +1 -1
- package/dist/exit-codes.d.ts +8 -1
- package/dist/exit-codes.d.ts.map +1 -1
- package/dist/exit-codes.js +7 -0
- package/dist/exit-codes.js.map +1 -1
- package/dist/graph.d.ts +88 -0
- package/dist/graph.d.ts.map +1 -1
- package/dist/graph.js +35 -0
- package/dist/graph.js.map +1 -1
- package/dist/index.d.ts +10 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/operations/nexus-scope-map.d.ts +540 -0
- package/dist/operations/nexus-scope-map.d.ts.map +1 -0
- package/dist/operations/nexus-scope-map.js +556 -0
- package/dist/operations/nexus-scope-map.js.map +1 -0
- package/dist/operations/nexus-scope.d.ts +185 -0
- package/dist/operations/nexus-scope.d.ts.map +1 -0
- package/dist/operations/nexus-scope.js +9 -0
- package/dist/operations/nexus-scope.js.map +1 -0
- package/dist/operations/session.d.ts +79 -0
- package/dist/operations/session.d.ts.map +1 -1
- package/dist/operations/tasks.d.ts +38 -0
- package/dist/operations/tasks.d.ts.map +1 -1
- package/dist/operations/worktree.d.ts +33 -0
- package/dist/operations/worktree.d.ts.map +1 -1
- package/dist/spawn.d.ts +94 -0
- package/dist/spawn.d.ts.map +1 -1
- package/dist/spawn.js +65 -1
- package/dist/spawn.js.map +1 -1
- package/dist/task.d.ts +17 -2
- package/dist/task.d.ts.map +1 -1
- package/dist/task.js +11 -1
- package/dist/task.js.map +1 -1
- package/dist/tasks.d.ts +0 -2
- package/dist/tasks.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/nexus-scope-map.test.ts +183 -0
- package/src/branch-lock.ts +11 -0
- package/src/config.ts +22 -0
- package/src/data-accessor.ts +3 -0
- package/src/engine-result.ts +220 -0
- package/src/errors.ts +34 -0
- package/src/exit-codes.ts +8 -0
- package/src/graph.ts +112 -0
- package/src/index.ts +49 -2
- package/src/operations/nexus-scope-map.ts +597 -0
- package/src/operations/nexus-scope.ts +217 -0
- package/src/operations/session.ts +87 -0
- package/src/operations/tasks.ts +40 -0
- package/src/operations/worktree.ts +33 -0
- package/src/spawn.ts +141 -0
- package/src/task.ts +30 -1
- package/src/tasks.ts +0 -2
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nexus operation scope contracts — discriminated unions and descriptor
|
|
3
|
+
* interface used by the NEXUS_SCOPE_MAP SSoT.
|
|
4
|
+
*
|
|
5
|
+
* @task T9145
|
|
6
|
+
* @module operations/nexus-scope
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// NexusScope — five-state discriminated union
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Five-state scope classification for Nexus operations.
|
|
15
|
+
*
|
|
16
|
+
* - `project` — Operates on a single registered project graph.
|
|
17
|
+
* - `living-brain` — Reads/writes the BRAIN (memory) store.
|
|
18
|
+
* - `cross` — Spans multiple project graphs or compares them.
|
|
19
|
+
* - `hybrid` — Touches both the project graph AND BRAIN.
|
|
20
|
+
* - `global` — Operates on the global Nexus registry (all projects).
|
|
21
|
+
*/
|
|
22
|
+
export type NexusScope = 'project' | 'living-brain' | 'cross' | 'hybrid' | 'global';
|
|
23
|
+
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// NexusEffect — read / write / admin axis
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Side-effect classification for a Nexus operation.
|
|
30
|
+
*
|
|
31
|
+
* - `read` — Pure query; no persistent state change.
|
|
32
|
+
* - `write` — Mutates the target store(s).
|
|
33
|
+
* - `admin` — Administrative operation (register/unregister/permission).
|
|
34
|
+
*/
|
|
35
|
+
export type NexusEffect = 'read' | 'write' | 'admin';
|
|
36
|
+
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// NexusStore — target data stores
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The persistent stores a Nexus operation may touch.
|
|
43
|
+
*
|
|
44
|
+
* - `nexus-graph` — The graph DB (nodes + relations for a project).
|
|
45
|
+
* - `nexus-registry` — The global project registry.
|
|
46
|
+
* - `brain` — The BRAIN memory / observation store.
|
|
47
|
+
* - `tasks` — The task store (nexus → task bridge operations).
|
|
48
|
+
* - `fs` — The local filesystem (scan / walk / snapshot).
|
|
49
|
+
*/
|
|
50
|
+
export type NexusStore = 'nexus-graph' | 'nexus-registry' | 'brain' | 'tasks' | 'fs';
|
|
51
|
+
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// ScopeBinding — links an operation key to scope + effect metadata
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* A single scope binding, attaching NexusScope and NexusEffect metadata to
|
|
58
|
+
* a named Nexus operation.
|
|
59
|
+
*/
|
|
60
|
+
export interface ScopeBinding {
|
|
61
|
+
/** The operation key as declared in {@link NexusOps}. */
|
|
62
|
+
readonly op: string;
|
|
63
|
+
/** Scope classification of this operation. */
|
|
64
|
+
readonly scope: NexusScope;
|
|
65
|
+
/** Side-effect classification. */
|
|
66
|
+
readonly effect: NexusEffect;
|
|
67
|
+
/** Stores this operation reads or writes. */
|
|
68
|
+
readonly stores: ReadonlyArray<NexusStore>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
// NexusOperationDescriptor — full operation metadata
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Rich metadata descriptor for a single Nexus operation.
|
|
77
|
+
*
|
|
78
|
+
* Used by the NEXUS_SCOPE_MAP SSoT to provide compile-time exhaustiveness
|
|
79
|
+
* checking and runtime helpers (`getNexusDescriptor`, `listOpsByScope`).
|
|
80
|
+
*/
|
|
81
|
+
export interface NexusOperationDescriptor {
|
|
82
|
+
/** The operation key as declared in {@link NexusOps}. */
|
|
83
|
+
readonly op: string;
|
|
84
|
+
/** Human-readable summary of the operation. */
|
|
85
|
+
readonly description: string;
|
|
86
|
+
/** Scope classification. */
|
|
87
|
+
readonly scope: NexusScope;
|
|
88
|
+
/** Side-effect classification. */
|
|
89
|
+
readonly effect: NexusEffect;
|
|
90
|
+
/** Stores this operation reads or writes. */
|
|
91
|
+
readonly stores: ReadonlyArray<NexusStore>;
|
|
92
|
+
/**
|
|
93
|
+
* Whether this operation requires a `projectId` parameter.
|
|
94
|
+
* Operations with `scope === 'global'` typically do NOT require one.
|
|
95
|
+
* @defaultValue `true`
|
|
96
|
+
*/
|
|
97
|
+
readonly requiresProject: boolean;
|
|
98
|
+
/**
|
|
99
|
+
* When `true`, the decorator stamps `meta._nexus.indexFreshness` on the
|
|
100
|
+
* response envelope (top-level CLI invocations only; sub-calls inherit via
|
|
101
|
+
* `meta.requestId` lineage to avoid per-call git-status shell-outs).
|
|
102
|
+
*
|
|
103
|
+
* @defaultValue `false`
|
|
104
|
+
* @task T9146
|
|
105
|
+
*/
|
|
106
|
+
readonly indexSensitive?: boolean;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
// W2: NexusScopeMeta — namespaced _nexus block stamped on every response
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Source of the `projectId` binding for a nexus operation.
|
|
115
|
+
*
|
|
116
|
+
* - `arg-project-id` — caller supplied `--project-id` explicitly.
|
|
117
|
+
* - `arg-path` — resolved from `--path` argument.
|
|
118
|
+
* - `cwd` — derived from current working directory.
|
|
119
|
+
* - `registry` — looked up via the global project registry.
|
|
120
|
+
* - `none` — operation does not require a project binding.
|
|
121
|
+
*
|
|
122
|
+
* @task T9146
|
|
123
|
+
*/
|
|
124
|
+
export type NexusBindingSource = 'arg-project-id' | 'arg-path' | 'cwd' | 'registry' | 'none';
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* A structured suggestion for the next action an agent should take after a
|
|
128
|
+
* Nexus operation completes.
|
|
129
|
+
*
|
|
130
|
+
* Machine-readable — display strings are derived from these fields.
|
|
131
|
+
*
|
|
132
|
+
* @task T9146
|
|
133
|
+
*/
|
|
134
|
+
export interface SuggestedNextOp {
|
|
135
|
+
/** Nexus operation key (must exist in NEXUS_SCOPE_MAP). */
|
|
136
|
+
readonly op: string;
|
|
137
|
+
/** Arguments to pass to the operation. */
|
|
138
|
+
readonly args: Readonly<Record<string, unknown>>;
|
|
139
|
+
/** Scope of the suggested operation. */
|
|
140
|
+
readonly scope: NexusScope;
|
|
141
|
+
/** Side-effect classification of the suggested operation. */
|
|
142
|
+
readonly effect: NexusEffect;
|
|
143
|
+
/** Whether the agent should confirm with the user before executing. */
|
|
144
|
+
readonly requiresConfirmation: boolean;
|
|
145
|
+
/** Human-readable rationale for why this next step is suggested. */
|
|
146
|
+
readonly reason: string;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Namespaced `_nexus` block attached to every nexus-domain dispatch response
|
|
151
|
+
* under `meta._nexus`.
|
|
152
|
+
*
|
|
153
|
+
* Consumed by agents, renderers, and downstream middleware for scope-aware
|
|
154
|
+
* routing and display.
|
|
155
|
+
*
|
|
156
|
+
* @task T9146
|
|
157
|
+
*/
|
|
158
|
+
export interface NexusScopeMeta {
|
|
159
|
+
/** Scope classification of the operation that produced this envelope. */
|
|
160
|
+
readonly scope: NexusScope;
|
|
161
|
+
/** Side-effect classification of the operation. */
|
|
162
|
+
readonly effect: NexusEffect;
|
|
163
|
+
/**
|
|
164
|
+
* Resolved project identifier (undefined for `global` / `living-brain`
|
|
165
|
+
* scope operations that do not require a project).
|
|
166
|
+
*/
|
|
167
|
+
readonly projectId?: string;
|
|
168
|
+
/** Human-readable project name from the registry (if available). */
|
|
169
|
+
readonly projectName?: string;
|
|
170
|
+
/** Absolute filesystem path to the project root. */
|
|
171
|
+
readonly projectPath?: string;
|
|
172
|
+
/** Absolute path to the nexus DB or registry file. */
|
|
173
|
+
readonly registryPath?: string;
|
|
174
|
+
/** How the `projectId` was resolved for this request. */
|
|
175
|
+
readonly bindingSource: NexusBindingSource;
|
|
176
|
+
/**
|
|
177
|
+
* For `hybrid`-scope operations: the ID of the secondary project
|
|
178
|
+
* (the one whose data is being compared or merged with the primary).
|
|
179
|
+
*/
|
|
180
|
+
readonly counterpartProjectId?: string;
|
|
181
|
+
/**
|
|
182
|
+
* Whether the nexus index was considered fresh at the time of this call.
|
|
183
|
+
* Only present when `descriptor.indexSensitive === true` AND this was a
|
|
184
|
+
* top-level CLI invocation (sub-calls inherit via `meta.requestId` lineage).
|
|
185
|
+
*/
|
|
186
|
+
readonly indexFreshness?: 'fresh' | 'stale' | 'unknown';
|
|
187
|
+
/**
|
|
188
|
+
* The canonical CLI command string for this operation (e.g. `"cleo nexus context"`).
|
|
189
|
+
*/
|
|
190
|
+
readonly canonicalCommand: string;
|
|
191
|
+
/**
|
|
192
|
+
* If this operation is a legacy alias, the canonical op key it maps to.
|
|
193
|
+
* Absent when the operation is already canonical.
|
|
194
|
+
*/
|
|
195
|
+
readonly legacyAliasFor?: string;
|
|
196
|
+
/** Non-fatal warnings surfaced from descriptor or runtime resolution. */
|
|
197
|
+
readonly warnings?: ReadonlyArray<string>;
|
|
198
|
+
/**
|
|
199
|
+
* Structured suggestions for what the agent should do next.
|
|
200
|
+
* Every entry's `.op` MUST resolve to a known NEXUS_SCOPE_MAP entry
|
|
201
|
+
* (enforced at build time by the typed-registry gate in nexus-decorator.ts).
|
|
202
|
+
*/
|
|
203
|
+
readonly suggestedNext?: ReadonlyArray<SuggestedNextOp>;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Utility type that intersects {@link NexusScopeMeta} into `meta._nexus` for
|
|
208
|
+
* nexus-domain dispatch responses.
|
|
209
|
+
*
|
|
210
|
+
* Use this to narrow the `meta` field when processing nexus responses.
|
|
211
|
+
*
|
|
212
|
+
* @task T9146
|
|
213
|
+
*/
|
|
214
|
+
export interface MetaWithNexusScope {
|
|
215
|
+
_nexus: NexusScopeMeta;
|
|
216
|
+
[key: string]: unknown;
|
|
217
|
+
}
|
|
@@ -200,6 +200,91 @@ export interface SessionHandoffShowResult {
|
|
|
200
200
|
handoff: unknown;
|
|
201
201
|
}
|
|
202
202
|
|
|
203
|
+
// ---------------------------------------------------------------------------
|
|
204
|
+
// BriefingFieldContract — per-field staleness + dedup rules (T1905 / BBTT-W1-3)
|
|
205
|
+
// ---------------------------------------------------------------------------
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Provenance category for briefing observation exclusion.
|
|
209
|
+
*
|
|
210
|
+
* @task T1905
|
|
211
|
+
*/
|
|
212
|
+
export type BriefingExcludeProvenance = 'test-fixture' | 'synthetic' | 'imported';
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Per-field contract rule for a single briefing field.
|
|
216
|
+
*
|
|
217
|
+
* Defines staleness and deduplication constraints for one named section
|
|
218
|
+
* of the `SessionBriefingShowResult`. `assertBriefingContract` evaluates
|
|
219
|
+
* these rules and emits a `ContractViolation` for each breach.
|
|
220
|
+
*
|
|
221
|
+
* @task T1905
|
|
222
|
+
*/
|
|
223
|
+
export interface BriefingFieldRule {
|
|
224
|
+
/**
|
|
225
|
+
* Maximum acceptable age (in days) for any observation surfaced in this field.
|
|
226
|
+
* A violation is emitted when `now - capturedAt > maxAgeDays * 86_400_000`.
|
|
227
|
+
*/
|
|
228
|
+
maxAgeDays?: number;
|
|
229
|
+
/**
|
|
230
|
+
* Deduplication key — field path within each list item used to detect
|
|
231
|
+
* duplicate entries (e.g. `'id'` deduplicates by task ID).
|
|
232
|
+
* When set, the contract checker warns if two items share the same key value.
|
|
233
|
+
*/
|
|
234
|
+
dedupBy?: string;
|
|
235
|
+
/**
|
|
236
|
+
* Provenance tags whose observations must be excluded from this field.
|
|
237
|
+
* Violations are emitted when an item carries one of these provenance values.
|
|
238
|
+
*/
|
|
239
|
+
excludeProvenance?: BriefingExcludeProvenance[];
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Full briefing field contract — maps each named briefing section to its rule.
|
|
244
|
+
*
|
|
245
|
+
* Pass to `assertBriefingContract` together with the computed briefing to
|
|
246
|
+
* obtain `ContractViolation[]`.
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* ```ts
|
|
250
|
+
* const contract: BriefingFieldContract = {
|
|
251
|
+
* recentObservations: { maxAgeDays: 7, excludeProvenance: ['test-fixture'] },
|
|
252
|
+
* nextTasks: { dedupBy: 'id' },
|
|
253
|
+
* };
|
|
254
|
+
* ```
|
|
255
|
+
*
|
|
256
|
+
* @task T1905
|
|
257
|
+
*/
|
|
258
|
+
export interface BriefingFieldContract {
|
|
259
|
+
recentObservations?: BriefingFieldRule;
|
|
260
|
+
nextTasks?: BriefingFieldRule;
|
|
261
|
+
openBugs?: BriefingFieldRule;
|
|
262
|
+
blockedTasks?: BriefingFieldRule;
|
|
263
|
+
activeEpics?: BriefingFieldRule;
|
|
264
|
+
[field: string]: BriefingFieldRule | undefined;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* A single contract violation emitted by `assertBriefingContract`.
|
|
269
|
+
*
|
|
270
|
+
* @task T1905
|
|
271
|
+
*/
|
|
272
|
+
export interface ContractViolation {
|
|
273
|
+
/** Name of the briefing field that violated its rule. */
|
|
274
|
+
field: string;
|
|
275
|
+
/** Human-readable description of the violation. */
|
|
276
|
+
message: string;
|
|
277
|
+
/**
|
|
278
|
+
* Violation kind for programmatic handling.
|
|
279
|
+
* - `stale` — field data exceeds `maxAgeDays`.
|
|
280
|
+
* - `duplicate` — two items share the same `dedupBy` key value.
|
|
281
|
+
* - `excluded-provenance` — item carries a banned provenance tag.
|
|
282
|
+
*/
|
|
283
|
+
kind: 'stale' | 'duplicate' | 'excluded-provenance';
|
|
284
|
+
/** Severity — P0 violations block `cleo briefing --strict`. */
|
|
285
|
+
severity: 'P0' | 'P1';
|
|
286
|
+
}
|
|
287
|
+
|
|
203
288
|
// session.briefing.show
|
|
204
289
|
/** Parameters for `session.briefing.show`. */
|
|
205
290
|
export interface SessionBriefingShowParams {
|
|
@@ -208,6 +293,8 @@ export interface SessionBriefingShowParams {
|
|
|
208
293
|
maxBlocked?: number;
|
|
209
294
|
maxEpics?: number;
|
|
210
295
|
scope?: string;
|
|
296
|
+
/** When true, exit non-zero if any contract violation is detected (T1905). */
|
|
297
|
+
strict?: boolean;
|
|
211
298
|
}
|
|
212
299
|
|
|
213
300
|
/** Compact task entry in a session briefing's next-tasks list. */
|
package/src/operations/tasks.ts
CHANGED
|
@@ -630,6 +630,29 @@ export interface TasksRelatesAddResult {
|
|
|
630
630
|
added: boolean;
|
|
631
631
|
}
|
|
632
632
|
|
|
633
|
+
// tasks.relates.remove
|
|
634
|
+
export interface TasksRelatesRemoveParams {
|
|
635
|
+
/** Source task ID. */
|
|
636
|
+
taskId: string;
|
|
637
|
+
/** Target task ID to remove the relation to. */
|
|
638
|
+
relatedId: string;
|
|
639
|
+
/** Optional relation type to narrow the deletion (omit to remove any type). */
|
|
640
|
+
type?: string;
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Result of `tasks.relates.remove` — relation deletion confirmation.
|
|
644
|
+
*
|
|
645
|
+
* @task T9240
|
|
646
|
+
*/
|
|
647
|
+
export interface TasksRelatesRemoveResult {
|
|
648
|
+
/** Source task ID. */
|
|
649
|
+
from: string;
|
|
650
|
+
/** Target (related) task ID. */
|
|
651
|
+
to: string;
|
|
652
|
+
/** Whether a relation was actually deleted. */
|
|
653
|
+
removed: boolean;
|
|
654
|
+
}
|
|
655
|
+
|
|
633
656
|
// tasks.add (dispatch-level params — extends TasksCreateParams)
|
|
634
657
|
export interface TasksAddParams {
|
|
635
658
|
title: string;
|
|
@@ -661,6 +684,16 @@ export interface TasksAddParams {
|
|
|
661
684
|
* @task T1633
|
|
662
685
|
*/
|
|
663
686
|
forceDuplicate?: boolean;
|
|
687
|
+
/**
|
|
688
|
+
* Path to an existing verifier script for this task (T9218 / ADR-070).
|
|
689
|
+
*
|
|
690
|
+
* Required when priority=critical OR size=large OR type=epic. The path
|
|
691
|
+
* must point to an existing `.mjs` file. Omitting this on high-consequence
|
|
692
|
+
* tasks causes rejection with E_VERIFIER_REQUIRED.
|
|
693
|
+
*
|
|
694
|
+
* Use `cleo verify backfill <taskId>` to auto-generate a stub verifier.
|
|
695
|
+
*/
|
|
696
|
+
verifier?: string;
|
|
664
697
|
}
|
|
665
698
|
/**
|
|
666
699
|
* Result of `tasks.add` — the newly created task.
|
|
@@ -698,6 +731,10 @@ export interface TasksUpdateQueryParams {
|
|
|
698
731
|
type?: string; // SSoT-EXEMPT:kind≠type — 'type' is hierarchy(epic|task|subtask), 'kind' is intent(work|bug|...) — separate axes T944
|
|
699
732
|
size?: string;
|
|
700
733
|
files?: string[];
|
|
734
|
+
/** Add files incrementally (mirrors --add-labels pattern). @task T9242 */
|
|
735
|
+
addFiles?: string[];
|
|
736
|
+
/** Remove files incrementally (mirrors --remove-labels pattern). @task T9242 */
|
|
737
|
+
removeFiles?: string[];
|
|
701
738
|
pipelineStage?: string;
|
|
702
739
|
/** Canonical wire field for task kind axis (T9072: renamed from role). */
|
|
703
740
|
kind?: string;
|
|
@@ -715,6 +752,8 @@ export interface TasksUpdateQueryParams {
|
|
|
715
752
|
reason?: string;
|
|
716
753
|
/** Dependency declaration waiver for critical-priority tasks (T1856). */
|
|
717
754
|
dependsWaiver?: string;
|
|
755
|
+
/** Clear the blockedBy free-text reason (set to undefined). @task T9241 */
|
|
756
|
+
clearBlockedBy?: boolean;
|
|
718
757
|
}
|
|
719
758
|
/**
|
|
720
759
|
* Result of `tasks.update` — the updated task record with change list.
|
|
@@ -907,6 +946,7 @@ export type TasksOps = {
|
|
|
907
946
|
readonly reparent: readonly [TasksReparentQueryParams, TasksReparentDispatchResult];
|
|
908
947
|
readonly reorder: readonly [TasksReorderQueryParams, TasksReorderDispatchResult];
|
|
909
948
|
readonly 'relates.add': readonly [TasksRelatesAddParams, TasksRelatesAddResult];
|
|
949
|
+
readonly 'relates.remove': readonly [TasksRelatesRemoveParams, TasksRelatesRemoveResult];
|
|
910
950
|
readonly start: readonly [TasksStartQueryParams, TasksStartQueryResult];
|
|
911
951
|
readonly stop: readonly [TasksStopQueryParams, TasksStopQueryResult];
|
|
912
952
|
readonly 'sync.reconcile': readonly [TasksSyncReconcileParams, TasksSyncReconcileResult];
|
|
@@ -135,6 +135,39 @@ export interface CreateWorktreeOptions {
|
|
|
135
135
|
* @default true
|
|
136
136
|
*/
|
|
137
137
|
lockWorktree?: boolean;
|
|
138
|
+
/**
|
|
139
|
+
* Glob patterns to exclude from the worktree via sparse-checkout after
|
|
140
|
+
* creation (T9226 spawn-clone-exclude filter).
|
|
141
|
+
*
|
|
142
|
+
* When set, `createWorktree` enables sparse-checkout on the new worktree
|
|
143
|
+
* and hides all files matching any pattern. Callers should also supply
|
|
144
|
+
* `spawnCloneExcludeExempt` to preserve the task-scoped file.
|
|
145
|
+
*
|
|
146
|
+
* @task T9226
|
|
147
|
+
*/
|
|
148
|
+
spawnCloneExclude?: readonly string[];
|
|
149
|
+
/**
|
|
150
|
+
* Specific file paths to re-include after the exclude filter is applied.
|
|
151
|
+
*
|
|
152
|
+
* Used to preserve the task's own verifier (`scripts/verify-<taskId>.mjs`)
|
|
153
|
+
* after the broad `scripts/verify-*.mjs` exclusion.
|
|
154
|
+
*
|
|
155
|
+
* @task T9226
|
|
156
|
+
*/
|
|
157
|
+
spawnCloneExcludeExempt?: readonly string[];
|
|
158
|
+
/**
|
|
159
|
+
* When `true`, forcibly reset an existing `task/<taskId>` branch that has
|
|
160
|
+
* orphan commits (commits not reachable from `baseRef`).
|
|
161
|
+
*
|
|
162
|
+
* Without this flag, `createWorktree` throws `E_DIRTY_BRANCH` when an orphan
|
|
163
|
+
* branch is detected so the caller can investigate before losing history.
|
|
164
|
+
* Set to `true` only when you are certain the prior branch state is stale and
|
|
165
|
+
* safe to discard (e.g. CI retry or integration-test re-runs).
|
|
166
|
+
*
|
|
167
|
+
* @default false
|
|
168
|
+
* @task T1927
|
|
169
|
+
*/
|
|
170
|
+
forceReset?: boolean;
|
|
138
171
|
}
|
|
139
172
|
|
|
140
173
|
/**
|
package/src/spawn.ts
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Spawn provider interface for CLEO provider adapters.
|
|
3
3
|
*
|
|
4
4
|
* @task T5240
|
|
5
|
+
* @task T9214 — orchestrator-defer waiver field (W4 UX hardening)
|
|
6
|
+
* @task T9215 — DelegateTaskEnvelope discriminated-union type (W1 wiring)
|
|
5
7
|
*/
|
|
6
8
|
|
|
7
9
|
export interface AdapterSpawnProvider {
|
|
@@ -16,6 +18,21 @@ export interface SpawnContext {
|
|
|
16
18
|
prompt: string;
|
|
17
19
|
workingDirectory?: string;
|
|
18
20
|
options?: Record<string, unknown>;
|
|
21
|
+
/**
|
|
22
|
+
* Atomicity scope declaration for the spawned worker.
|
|
23
|
+
*
|
|
24
|
+
* When set to `'orchestrator-defer'`, a tier-1+ orchestrator signals that
|
|
25
|
+
* the spawned worker will declare its own file scope at commit time. This
|
|
26
|
+
* bypasses `E_ATOMICITY_NO_SCOPE` for the child task while preserving
|
|
27
|
+
* auditability via the `atomicity_waiver` field in the returned
|
|
28
|
+
* {@link AtomicityResult}.
|
|
29
|
+
*
|
|
30
|
+
* MUST NOT be set by tier-0 (direct user / CLI) callers — only by
|
|
31
|
+
* orchestrators making delegated tier-1 dispatch calls.
|
|
32
|
+
*
|
|
33
|
+
* @task T9214
|
|
34
|
+
*/
|
|
35
|
+
scope?: 'orchestrator-defer';
|
|
19
36
|
}
|
|
20
37
|
|
|
21
38
|
export interface SpawnResult {
|
|
@@ -32,3 +49,127 @@ export interface SpawnResult {
|
|
|
32
49
|
/** Error message when status is 'failed'. Contains details about what went wrong. */
|
|
33
50
|
error?: string;
|
|
34
51
|
}
|
|
52
|
+
|
|
53
|
+
// =============================================================================
|
|
54
|
+
// delegate_task JSON envelope (T9215 / W1 — ADR-070 hierarchical orchestration)
|
|
55
|
+
// =============================================================================
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Descriptor for a single child task within a `delegate_task` batch.
|
|
59
|
+
*
|
|
60
|
+
* Each child carries its task ID, agent role, and optional spawn parameters.
|
|
61
|
+
* The adapter recognizer uses this to call `orchestrateSpawnExecute` for each.
|
|
62
|
+
*
|
|
63
|
+
* @task T9215
|
|
64
|
+
*/
|
|
65
|
+
export interface DelegateTaskChild {
|
|
66
|
+
/** Task ID of the child to spawn (e.g. 'T9101'). */
|
|
67
|
+
taskId: string;
|
|
68
|
+
/** Agent role assigned to the child ('leaf' | 'worker'). */
|
|
69
|
+
role: 'leaf' | 'worker';
|
|
70
|
+
/** Subagent type for the spawn provider (e.g. 'cleo-subagent'). */
|
|
71
|
+
subagent_type?: string;
|
|
72
|
+
/** LLM model to use for this child (e.g. 'sonnet'). */
|
|
73
|
+
model?: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Parent identity block within a `delegate_task` envelope.
|
|
78
|
+
*
|
|
79
|
+
* Identifies the calling Lead agent for hierarchy validation.
|
|
80
|
+
*
|
|
81
|
+
* @task T9215
|
|
82
|
+
*/
|
|
83
|
+
export interface DelegateTaskParent {
|
|
84
|
+
/** Task ID of the calling Lead agent. */
|
|
85
|
+
taskId: string;
|
|
86
|
+
/** Role of the calling agent ('orchestrator' | 'lead'). */
|
|
87
|
+
role: 'orchestrator' | 'lead';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Discriminated-union JSON envelope emitted by Lead agents to fan out workers.
|
|
92
|
+
*
|
|
93
|
+
* The `tool` field discriminates this type from arbitrary JSON in adapter
|
|
94
|
+
* stdout. Adapters scan their stdout stream for lines containing this sentinel
|
|
95
|
+
* and dispatch via `orchestrateSpawnExecute` for each child in `tasks`.
|
|
96
|
+
*
|
|
97
|
+
* Shape aligned with `ct-lead/references/spawn-pattern.md` (T9082).
|
|
98
|
+
*
|
|
99
|
+
* @task T9215 — W1 delegate_task wiring
|
|
100
|
+
* @adr ADR-070
|
|
101
|
+
*/
|
|
102
|
+
export interface DelegateTaskEnvelope {
|
|
103
|
+
/** Discriminant — always 'delegate_task'. */
|
|
104
|
+
tool: 'delegate_task';
|
|
105
|
+
args: {
|
|
106
|
+
/** Identity of the calling Lead agent. Used for hierarchy validation. */
|
|
107
|
+
parent: DelegateTaskParent;
|
|
108
|
+
/** Conduit topic for wave coordination (e.g. 'epic-T9080.wave-2'). */
|
|
109
|
+
conduitTopic?: string;
|
|
110
|
+
/** Total timeout in seconds for the entire batch. */
|
|
111
|
+
timeoutSeconds?: number;
|
|
112
|
+
/** Array of child task descriptors to spawn in parallel. */
|
|
113
|
+
tasks: DelegateTaskChild[];
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Parse and validate a `delegate_task` JSON envelope from adapter stdout.
|
|
119
|
+
*
|
|
120
|
+
* Scans a line of stdout for the sentinel `"tool":"delegate_task"` and
|
|
121
|
+
* attempts to parse the full envelope. Returns `null` if the line is not
|
|
122
|
+
* a valid `DelegateTaskEnvelope` — callers should pass through non-matching
|
|
123
|
+
* lines without transformation.
|
|
124
|
+
*
|
|
125
|
+
* Validation checks:
|
|
126
|
+
* - `tool` equals `'delegate_task'`
|
|
127
|
+
* - `args.tasks` is a non-empty array
|
|
128
|
+
* - Each task has a non-empty `taskId`
|
|
129
|
+
* - `args.parent` has a valid `taskId` and `role`
|
|
130
|
+
*
|
|
131
|
+
* @param line - Single line of stdout from the spawned agent process.
|
|
132
|
+
* @returns Parsed envelope or `null` if the line is not a sentinel.
|
|
133
|
+
*
|
|
134
|
+
* @task T9215
|
|
135
|
+
*/
|
|
136
|
+
export function parseDelegateTaskEnvelope(line: string): DelegateTaskEnvelope | null {
|
|
137
|
+
if (!line.includes('"delegate_task"')) return null;
|
|
138
|
+
|
|
139
|
+
let parsed: unknown;
|
|
140
|
+
try {
|
|
141
|
+
// Find the first JSON object on the line
|
|
142
|
+
const start = line.indexOf('{');
|
|
143
|
+
const end = line.lastIndexOf('}');
|
|
144
|
+
if (start === -1 || end === -1 || end <= start) return null;
|
|
145
|
+
parsed = JSON.parse(line.slice(start, end + 1));
|
|
146
|
+
} catch {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (typeof parsed !== 'object' || parsed === null) return null;
|
|
151
|
+
const obj = parsed as Record<string, unknown>;
|
|
152
|
+
|
|
153
|
+
if (obj['tool'] !== 'delegate_task') return null;
|
|
154
|
+
|
|
155
|
+
const args = obj['args'];
|
|
156
|
+
if (typeof args !== 'object' || args === null) return null;
|
|
157
|
+
const argsObj = args as Record<string, unknown>;
|
|
158
|
+
|
|
159
|
+
const tasks = argsObj['tasks'];
|
|
160
|
+
if (!Array.isArray(tasks) || tasks.length === 0) return null;
|
|
161
|
+
|
|
162
|
+
for (const t of tasks) {
|
|
163
|
+
if (typeof t !== 'object' || t === null) return null;
|
|
164
|
+
const task = t as Record<string, unknown>;
|
|
165
|
+
if (typeof task['taskId'] !== 'string' || task['taskId'].length === 0) return null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const parent = argsObj['parent'];
|
|
169
|
+
if (typeof parent !== 'object' || parent === null) return null;
|
|
170
|
+
const parentObj = parent as Record<string, unknown>;
|
|
171
|
+
if (typeof parentObj['taskId'] !== 'string' || parentObj['taskId'].length === 0) return null;
|
|
172
|
+
if (parentObj['role'] !== 'orchestrator' && parentObj['role'] !== 'lead') return null;
|
|
173
|
+
|
|
174
|
+
return parsed as DelegateTaskEnvelope;
|
|
175
|
+
}
|
package/src/task.ts
CHANGED
|
@@ -87,8 +87,12 @@ export type TaskSize = 'small' | 'medium' | 'large';
|
|
|
87
87
|
/** Epic lifecycle states. */
|
|
88
88
|
export type EpicLifecycle = 'backlog' | 'planning' | 'active' | 'review' | 'released' | 'archived';
|
|
89
89
|
|
|
90
|
-
/** Task origin (provenance). */
|
|
90
|
+
/** Task origin (provenance). T1899: added production/test-fixture/imported/migrated. */
|
|
91
91
|
export type TaskOrigin =
|
|
92
|
+
| 'production'
|
|
93
|
+
| 'test-fixture'
|
|
94
|
+
| 'imported'
|
|
95
|
+
| 'migrated'
|
|
92
96
|
| 'internal'
|
|
93
97
|
| 'bug-report'
|
|
94
98
|
| 'feature-request'
|
|
@@ -97,6 +101,19 @@ export type TaskOrigin =
|
|
|
97
101
|
| 'dependency'
|
|
98
102
|
| 'regression';
|
|
99
103
|
|
|
104
|
+
/** Canonical origin values for the T1899 CHECK constraint. */
|
|
105
|
+
export const TASK_ORIGIN_CANONICAL = [
|
|
106
|
+
'production',
|
|
107
|
+
'test-fixture',
|
|
108
|
+
'imported',
|
|
109
|
+
'migrated',
|
|
110
|
+
] as const satisfies readonly TaskOrigin[];
|
|
111
|
+
|
|
112
|
+
/** Whether an origin value is a test-fixture (should be excluded from live briefing). */
|
|
113
|
+
export function isTestFixtureOrigin(origin: string | null | undefined): boolean {
|
|
114
|
+
return origin === 'test-fixture';
|
|
115
|
+
}
|
|
116
|
+
|
|
100
117
|
/** Verification agent types. */
|
|
101
118
|
export type VerificationAgent =
|
|
102
119
|
| 'planner'
|
|
@@ -463,6 +480,18 @@ export interface Task {
|
|
|
463
480
|
|
|
464
481
|
/** Agent ID that has claimed/is assigned to this task. Null when unclaimed. @defaultValue undefined */
|
|
465
482
|
assignee?: string | null;
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Abort reason when a worker was stopped due to runaway detection (T1658).
|
|
486
|
+
*
|
|
487
|
+
* Set by the sentient monitor when a worker exceeds its size-based budget
|
|
488
|
+
* (`small` ≥ 30 min, `medium` ≥ 2 h, `large` ≥ 4 h). The field is
|
|
489
|
+
* informational — callers may surface it in `cleo sentient monitor` output.
|
|
490
|
+
*
|
|
491
|
+
* @task T1658
|
|
492
|
+
* @defaultValue undefined
|
|
493
|
+
*/
|
|
494
|
+
abortReason?: string | null;
|
|
466
495
|
}
|
|
467
496
|
|
|
468
497
|
// ---------------------------------------------------------------------------
|
package/src/tasks.ts
CHANGED
|
@@ -24,8 +24,6 @@ import type { TaskPriority, TaskStatus } from './task.js';
|
|
|
24
24
|
/**
|
|
25
25
|
* RCASD-IVTR+C pipeline stage values valid for `TaskView.pipelineStage`.
|
|
26
26
|
*
|
|
27
|
-
* Mirrors `TaskViewPipelineStage` from `packages/core/src/tasks/compute-task-view.ts`.
|
|
28
|
-
*
|
|
29
27
|
* @task T1703
|
|
30
28
|
*/
|
|
31
29
|
export type TaskViewPipelineStage =
|