sdd-agent-platform 0.1.0 → 0.3.0

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.
@@ -1,5 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { archiveRun, applyAiToolEntries, applySyncBack, createWorktreeLifecycle, createRun, doctor, evaluateLifecycleDecisionGate, evaluateGovernancePolicy, getProjectStatus, getDelegationStateMachine, getSddInstructions, initProject, inspectRun, inspectSddTask, inspectToolPluginContract, inspectToolCapability, inspectDelegationQueueItem, inspectSyncBack, inspectTaskGraph, inspectWavePlan, inspectWaveExecutor, inspectBackgroundExecutor, inspectArtifactResultIngestions, inspectWorktreeLifecycle, inspectWorkerAdapterContract, inspectWorktreeIsolation, inspectGovernancePolicy, keepWorktreeLifecycle, removeWorktreeLifecycle, listRuns, rebuildLocalRunIndex, inspectLocalRunIndex, queryLocalRunIndex, listToolPluginContracts, listToolCapabilities, listDelegationQueueItems, listWorkerAdapterContracts, ingestArtifactResult, parseSddBranch, readRunState, recordLifecycleDecision, renderDoctorReport, renderGoalVerifyResult, renderLifecycleDecisionGate, renderSddResultArtifactTemplate, renderSddInstructions, renderSingleTaskLoopResult, renderTaskGapReport, renderTaskInspect, renderTaskList, runGoalVerify, runSingleTaskLoop, runBackgroundExecutor, runWaveExecutor, SDD_VERSION, summarizeAiProjectionStatus, validateSddResultArtifact, } from '../../core/src/index.js';
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { readFile } from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { writeArtifact, archiveRun, applyAiToolEntries, applySyncBack, createWorktreeLifecycle, createRun, doctor, buildContextBuildPackage, buildEvidenceSummaryProjection, evaluateLifecycleDecisionGate, extractLifecycleRiskSignalsFromText, evaluateGovernancePolicy, getProjectStatus, getDelegationStateMachine, getSddInstructions, initProject, inspectRun, claimResidentWorkerRuntime, heartbeatResidentWorkerRuntime, inspectSddTask, inspectToolPluginContract, inspectToolCapability, inspectDelegationQueueItem, inspectSyncBack, inspectTaskGraph, inspectWavePlan, inspectWaveExecutor, inspectBackgroundExecutor, inspectResidentWorkerRuntime, inspectArtifactResultIngestions, inspectWorktreeLifecycle, inspectWorkerAdapterContract, inspectWorktreeIsolation, inspectGovernancePolicy, inspectWorkflowGate, inspectAgentRegistryEntry, inspectQueryStatusContract, inspectSkillAgentEvalContract, inspectHarnessLearningContract, inspectProjectContextPackContract, inspectAgentSkillTeamRuntime, inspectSkillCapability, inspectCapabilitySource, inspectExternalAgentPackImport, inspectTeamModePolicy, keepWorktreeLifecycle, removeWorktreeLifecycle, listRuns, rebuildLocalRunIndex, inspectLocalRunIndex, queryLocalRunIndex, listToolPluginContracts, listToolCapabilities, listDelegationQueueItems, listWorkerAdapterContracts, listResidentWorkerRuntimes, listWorkflowGates, listAgentRegistry, listSkillCapabilities, listCapabilitySources, ingestArtifactResult, parseSddBranch, readRunState, resolveSddContext, recordLifecycleDecision, parseContextProfile, renderDoctorReport, renderGoalVerifyResult, renderLifecycleDecisionGate, renderSddResultArtifactTemplate, renderSddInstructions, renderSingleTaskLoopResult, renderTaskGapReport, renderTaskInspect, renderTaskList, runGoalVerify, runSingleTaskLoop, runBackgroundExecutor, runWaveExecutor, SDD_VERSION, summarizeAiProjectionStatus, validateSddResultArtifact, validateWorkflowGates, validateAgentRegistry, validateQueryStatusContract, validateSkillAgentEvalContract, validateHarnessLearningContract, validateProjectContextPackContract, routeSddTask, validateAgentSkillTeamRuntime, toArtifactRootRelativePath } from '../../core/src/index.js';
7
+ import { readOption, readPositiveIntegerOption, readRepeatedOption, readRepeatedOptions } from './options.js';
3
8
  async function main(args) {
4
9
  const projectRoot = process.cwd();
5
10
  const [command, subcommand, ...rest] = args;
@@ -9,22 +14,30 @@ async function main(args) {
9
14
  output: helpText()
10
15
  };
11
16
  }
12
- if (command === '--version' || command === '-v') {
17
+ if (command === 'help') {
13
18
  return {
14
19
  exitCode: 0,
15
- output: SDD_VERSION
20
+ output: helpText(subcommand)
21
+ };
22
+ }
23
+ if (command === '--version' || command === '-v' || command === 'version') {
24
+ const identity = getCliIdentity();
25
+ return {
26
+ exitCode: 0,
27
+ output: wantsJson(args) ? jsonOutput(identity, args) : identity.version
16
28
  };
17
29
  }
18
30
  if (command === 'init') {
19
31
  const initArgs = [subcommand, ...rest].filter(Boolean);
20
32
  const force = initArgs.includes('--force');
21
33
  const aiTool = readAiToolSelection(initArgs, true);
22
- const branch = readOption(initArgs, '--branch') ?? 'master';
23
- const scaffoldDocuments = !initArgs.includes('--no-scaffold-docs');
34
+ const branch = readOption(initArgs, '--branch') ?? undefined;
35
+ const scaffoldDocuments = initArgs.includes('--no-scaffold-docs') ? false : initArgs.includes('--scaffold-docs') || branch !== undefined;
24
36
  const result = await initProject(projectRoot, { force, aiTool, branch, scaffoldDocuments });
37
+ const json = wantsJson(initArgs);
25
38
  return {
26
39
  exitCode: 0,
27
- output: JSON.stringify({ command: 'init', ...result }, null, 2)
40
+ output: json ? jsonOutput({ command: 'init', ...result }, initArgs) : renderInitResult(result)
28
41
  };
29
42
  }
30
43
  if (command === 'update') {
@@ -42,10 +55,9 @@ async function main(args) {
42
55
  const instructionArgs = [subcommand, ...rest].filter(Boolean);
43
56
  const action = instructionArgs.find((item) => !item.startsWith('--')) ?? 'overview';
44
57
  const payload = getSddInstructions(action);
45
- const json = instructionArgs.includes('--json');
46
58
  return {
47
59
  exitCode: 0,
48
- output: json ? JSON.stringify(payload, null, 2) : renderSddInstructions(payload)
60
+ output: renderTextOrJson(instructionArgs, payload, renderSddInstructions)
49
61
  };
50
62
  }
51
63
  if (command === 'doctor') {
@@ -53,25 +65,27 @@ async function main(args) {
53
65
  if (doctorArgs.includes('--latest-only') && doctorArgs.includes('--all-runs')) {
54
66
  return {
55
67
  exitCode: 2,
56
- error: 'Usage: sdd doctor [--latest-only] [--all-runs] (choose only one scope flag)'
68
+ error: 'Usage: sdd doctor [--latest-only] [--all-runs] [--branch <branch>] (choose only one scope flag)'
57
69
  };
58
70
  }
59
71
  const report = await doctor(projectRoot, {
60
72
  latestOnly: doctorArgs.includes('--latest-only'),
61
- allRuns: doctorArgs.includes('--all-runs')
73
+ allRuns: doctorArgs.includes('--all-runs'),
74
+ branch: readBranchOption(doctorArgs)
62
75
  });
76
+ const json = wantsJson(doctorArgs);
63
77
  return {
64
78
  exitCode: report.status === 'FAIL' ? 1 : 0,
65
- output: renderDoctorReport(report)
79
+ output: json ? jsonOutput(report, doctorArgs) : renderDoctorReport(report)
66
80
  };
67
81
  }
68
82
  if (command === 'status') {
69
83
  const statusArgs = [subcommand, ...rest].filter(Boolean);
70
- const result = await getProjectStatus(projectRoot, { branch: readOption(statusArgs, '--branch') ?? 'master' });
71
- const json = statusArgs.includes('--json');
84
+ const result = await getProjectStatus(projectRoot, readBranchContext(statusArgs));
85
+ const json = wantsJson(statusArgs);
72
86
  return {
73
87
  exitCode: result.gaps.some((gap) => gap.severity === 'blocking') ? 1 : 0,
74
- output: json ? JSON.stringify(result, null, 2) : renderProjectStatus(result)
88
+ output: json ? jsonOutput(result, statusArgs) : renderProjectStatus(result)
75
89
  };
76
90
  }
77
91
  if (command === 'run' && subcommand === 'create') {
@@ -105,19 +119,19 @@ async function main(args) {
105
119
  }
106
120
  if (command === 'run' && subcommand === 'index') {
107
121
  const action = rest[0];
108
- const json = rest.includes('--json');
122
+ const json = wantsJson(rest);
109
123
  if (action === 'rebuild') {
110
124
  const index = await rebuildLocalRunIndex(projectRoot);
111
125
  return {
112
126
  exitCode: 0,
113
- output: json ? JSON.stringify(index, null, 2) : renderLocalRunIndex(index)
127
+ output: json ? jsonOutput(index, rest) : renderLocalRunIndex(index)
114
128
  };
115
129
  }
116
130
  if (action === 'inspect') {
117
131
  const inspection = await inspectLocalRunIndex(projectRoot);
118
132
  return {
119
133
  exitCode: inspection.valid ? 0 : 1,
120
- output: json ? JSON.stringify(inspection, null, 2) : renderLocalRunIndexInspection(inspection)
134
+ output: json ? jsonOutput(inspection, rest) : renderLocalRunIndexInspection(inspection)
121
135
  };
122
136
  }
123
137
  if (action === 'query') {
@@ -125,7 +139,7 @@ async function main(args) {
125
139
  if (readOption(rest, '--status') && !status) {
126
140
  return {
127
141
  exitCode: 2,
128
- error: 'Usage: sdd run index query [--run <run_id>] [--task <task_id>] [--status created|running|completed|blocked|failed|archived] [--artifact <path>] [--json]'
142
+ error: 'Usage: sdd run index query [--run <run_id>] [--task <task_id>] [--status created|running|completed|blocked|failed|archived] [--artifact <path>] [--json|--compact-json]'
129
143
  };
130
144
  }
131
145
  const index = await queryLocalRunIndex(projectRoot, {
@@ -136,7 +150,7 @@ async function main(args) {
136
150
  });
137
151
  return {
138
152
  exitCode: 0,
139
- output: json ? JSON.stringify(index, null, 2) : renderLocalRunIndex(index)
153
+ output: json ? jsonOutput(index, rest) : renderLocalRunIndex(index)
140
154
  };
141
155
  }
142
156
  return {
@@ -153,10 +167,10 @@ async function main(args) {
153
167
  };
154
168
  }
155
169
  const result = await inspectRun(projectRoot, runId);
156
- const json = rest.includes('--json');
170
+ const json = wantsJson(rest);
157
171
  return {
158
172
  exitCode: 0,
159
- output: json ? JSON.stringify(result, null, 2) : renderRunInspection(result)
173
+ output: json ? jsonOutput(result, rest) : renderRunInspection(result)
160
174
  };
161
175
  }
162
176
  if (command === 'run' && subcommand === 'archive') {
@@ -173,43 +187,85 @@ async function main(args) {
173
187
  output: JSON.stringify({ runId: state.runId, status: state.status, updatedAt: state.updatedAt }, null, 2)
174
188
  };
175
189
  }
176
- if (command === 'sync-back' && subcommand === 'inspect') {
177
- const runId = rest[0];
190
+ if (command === 'evidence' && subcommand === 'summary') {
191
+ const runId = rest.find((item) => !item.startsWith('--'));
178
192
  if (!runId) {
179
193
  return {
180
194
  exitCode: 2,
181
- error: 'Usage: sdd sync-back inspect <run_id> [--branch <branch>] [--task <task_id>] [--json]'
195
+ error: 'Usage: sdd evidence summary <run_id> [--task <task_id>] [--json|--compact-json]'
182
196
  };
183
197
  }
184
- const result = await inspectSyncBack(projectRoot, {
198
+ const summary = await buildEvidenceSummaryProjection(projectRoot, {
185
199
  runId,
186
- branch: readOption(rest, '--branch') ?? 'master',
187
200
  taskId: readOption(rest, '--task') ?? undefined
188
201
  });
189
- const json = rest.includes('--json');
202
+ const json = wantsJson(rest);
203
+ return {
204
+ exitCode: 0,
205
+ output: json ? jsonOutput(summary, rest) : renderEvidenceSummaryProjection(summary)
206
+ };
207
+ }
208
+ if (command === 'context' && subcommand === 'build') {
209
+ const taskId = readOption(rest, '--task') ?? undefined;
210
+ const mode = readContextBuildMode(rest, '--mode');
211
+ if (!taskId || !mode) {
212
+ return {
213
+ exitCode: 2,
214
+ error: 'Usage: sdd context build --task <task_id> --mode do|verify|sync-back|doctor [--agent <agent>] [--branch <branch>] [--profile brief|normal|forensic] [--json|--compact-json]'
215
+ };
216
+ }
217
+ const contextPackage = await buildContextBuildPackage(projectRoot, {
218
+ taskId,
219
+ branch: readBranchOption(rest),
220
+ mode,
221
+ agent: readOption(rest, '--agent') ?? undefined,
222
+ profile: parseContextProfile(readOption(rest, '--profile'))
223
+ });
224
+ const json = wantsJson(rest);
225
+ return {
226
+ exitCode: 0,
227
+ output: json ? jsonOutput(contextPackage, rest) : renderContextBuildPackage(contextPackage)
228
+ };
229
+ }
230
+ if (command === 'sync-back' && subcommand === 'inspect') {
231
+ const runId = readOptionalPositionalArgument(rest);
232
+ const taskId = readOption(rest, '--task') ?? undefined;
233
+ if (!runId && !taskId) {
234
+ return {
235
+ exitCode: 2,
236
+ error: 'Usage: sdd sync-back inspect [<run_id>] [--branch <branch>] --task <task_id> [--json|--compact-json]'
237
+ };
238
+ }
239
+ const result = await inspectSyncBack(projectRoot, {
240
+ runId,
241
+ branch: readBranchOption(rest),
242
+ taskId
243
+ });
244
+ const json = wantsJson(rest);
190
245
  return {
191
246
  exitCode: result.status === 'blocked' ? 1 : 0,
192
- output: json ? JSON.stringify(result, null, 2) : renderSyncBackInspection(result)
247
+ output: json ? jsonOutput(result, rest) : renderSyncBackInspection(result)
193
248
  };
194
249
  }
195
250
  if (command === 'sync-back' && subcommand === 'apply') {
196
- const runId = rest[0];
197
- if (!runId) {
251
+ const runId = readOptionalPositionalArgument(rest);
252
+ const taskId = readOption(rest, '--task') ?? undefined;
253
+ if (!runId && !taskId) {
198
254
  return {
199
255
  exitCode: 2,
200
- error: 'Usage: sdd sync-back apply <run_id> [--branch <branch>] [--task <task_id>] [--approved] [--json]'
256
+ error: 'Usage: sdd sync-back apply [<run_id>] [--branch <branch>] --task <task_id> [--approved] [--json|--compact-json]'
201
257
  };
202
258
  }
203
259
  const result = await applySyncBack(projectRoot, {
204
260
  runId,
205
- branch: readOption(rest, '--branch') ?? 'master',
206
- taskId: readOption(rest, '--task') ?? undefined,
261
+ branch: readBranchOption(rest),
262
+ taskId,
207
263
  approved: rest.includes('--approved')
208
264
  });
209
- const json = rest.includes('--json');
265
+ const json = wantsJson(rest);
210
266
  return {
211
267
  exitCode: 0,
212
- output: json ? JSON.stringify(result, null, 2) : renderSyncBackApplyResult(result)
268
+ output: json ? jsonOutput(result, rest) : renderSyncBackApplyResult(result)
213
269
  };
214
270
  }
215
271
  if (command === 'tasks' && subcommand === 'format') {
@@ -219,7 +275,7 @@ async function main(args) {
219
275
  };
220
276
  }
221
277
  if (command === 'tasks' && subcommand === 'list') {
222
- const model = await parseSddBranch(projectRoot, readOption(rest, '--branch') ?? 'master');
278
+ const model = await parseSddBranch(projectRoot, await readResolvedBranch(projectRoot, rest));
223
279
  return {
224
280
  exitCode: model.gaps.some((gap) => gap.severity === 'blocking') ? 1 : 0,
225
281
  output: renderTaskList(model)
@@ -230,10 +286,10 @@ async function main(args) {
230
286
  if (!taskId) {
231
287
  return {
232
288
  exitCode: 2,
233
- error: 'Usage: sdd tasks inspect <task_id> [--branch <branch>]'
289
+ error: 'Usage: sdd tasks inspect <task_id> [--branch <branch>] [--json]'
234
290
  };
235
291
  }
236
- const model = await parseSddBranch(projectRoot, readOption(rest, '--branch') ?? 'master');
292
+ const model = await parseSddBranch(projectRoot, await readResolvedBranch(projectRoot, rest));
237
293
  const result = inspectSddTask(model, taskId);
238
294
  if (!result.task && result.gaps.length === 0) {
239
295
  return {
@@ -241,20 +297,48 @@ async function main(args) {
241
297
  error: `Task not found: ${taskId}`
242
298
  };
243
299
  }
300
+ const json = wantsJson(rest);
244
301
  return {
245
302
  exitCode: result.task === null || result.gaps.some((gap) => gap.severity === 'blocking') ? 1 : 0,
246
- output: renderTaskInspect(result.task, result.gaps)
303
+ output: json ? jsonOutput(result, rest) : renderTaskInspect(result.task, result.gaps)
304
+ };
305
+ }
306
+ if (command === 'tasks' && subcommand === 'route') {
307
+ const taskId = rest.find((item) => !item.startsWith('--'));
308
+ if (!taskId) {
309
+ return {
310
+ exitCode: 2,
311
+ error: 'Usage: sdd tasks route <task_id> [--branch <branch>] [--team-mode [auto|force|off]] [--no-team-mode] [--profile] [--cache] [--json]'
312
+ };
313
+ }
314
+ const decision = await routeSddTask(projectRoot, {
315
+ taskId,
316
+ branch: readBranchOption(rest),
317
+ teamModeActivation: readTeamModeActivation(rest),
318
+ profile: rest.includes('--profile'),
319
+ cache: rest.includes('--cache'),
320
+ });
321
+ return {
322
+ exitCode: decision.blockedReason ? 1 : 0,
323
+ output: wantsJson(rest) ? jsonOutput(decision, rest) : renderAgentRouterDecision(decision)
247
324
  };
248
325
  }
249
326
  if (command === 'tasks' && subcommand === 'gaps') {
250
- const model = await parseSddBranch(projectRoot, readOption(rest, '--branch') ?? 'master');
327
+ const model = await parseSddBranch(projectRoot, await readResolvedBranch(projectRoot, rest));
251
328
  return {
252
329
  exitCode: model.gaps.some((gap) => gap.severity === 'blocking') ? 1 : 0,
253
330
  output: renderTaskGapReport(model)
254
331
  };
255
332
  }
256
333
  if (command === 'lifecycle' && subcommand === 'decide') {
257
- const result = evaluateLifecycleDecisionGate(readLifecycleSignalOptions(rest));
334
+ const lifecycleInput = await readLifecycleSignalOptions(rest);
335
+ if (lifecycleInput.error) {
336
+ return {
337
+ exitCode: 2,
338
+ error: lifecycleInput.error
339
+ };
340
+ }
341
+ const result = evaluateLifecycleDecisionGate(lifecycleInput.signals);
258
342
  const runId = readOption(rest, '--run');
259
343
  if (runId) {
260
344
  await recordLifecycleDecision(projectRoot, runId, result.record);
@@ -262,7 +346,9 @@ async function main(args) {
262
346
  const json = rest.includes('--json');
263
347
  return {
264
348
  exitCode: 0,
265
- output: json ? JSON.stringify({ ...result, recordedRunId: runId ?? null }, null, 2) : `${renderLifecycleDecisionGate(result)}${runId ? `\nrecorded_run=${runId}` : ''}`
349
+ output: json
350
+ ? JSON.stringify({ riskExtraction: lifecycleInput.riskExtraction, ...result, recordedRunId: runId ?? null }, null, 2)
351
+ : `${renderLifecycleRiskExtraction(lifecycleInput.riskExtraction)}${renderLifecycleDecisionGate(result)}${runId ? `\nrecorded_run=${runId}` : ''}`
266
352
  };
267
353
  }
268
354
  if (command === 'do' && subcommand === 'task') {
@@ -270,42 +356,45 @@ async function main(args) {
270
356
  if (!taskId) {
271
357
  return {
272
358
  exitCode: 2,
273
- error: 'Usage: sdd do task <task_id> [--branch <branch>] [--run <run_id>] [--implement-artifact artifacts/path.md] [--review-artifact artifacts/path.md] [--debug-artifact artifacts/path.md] [--validation-artifact artifacts/path.md]'
359
+ error: 'Usage: sdd do task <task_id> [--branch <branch>] [--run <run_id>] [--team-mode [auto|force|off]] [--no-team-mode] [--implement-artifact artifacts/path.md] [--review-artifact artifacts/path.md] [--debug-artifact artifacts/path.md] [--validation-artifact artifacts/path.md]'
274
360
  };
275
361
  }
276
362
  const result = await runSingleTaskLoop(projectRoot, {
277
363
  taskId,
278
- branch: readOption(rest, '--branch') ?? 'master',
364
+ branch: readBranchOption(rest),
279
365
  runId: readOption(rest, '--run') ?? undefined,
280
366
  implementArtifact: readOption(rest, '--implement-artifact') ?? undefined,
281
367
  reviewArtifact: readOption(rest, '--review-artifact') ?? undefined,
282
368
  debugArtifact: readOption(rest, '--debug-artifact') ?? undefined,
283
- validationArtifact: readOption(rest, '--validation-artifact') ?? undefined
369
+ validationArtifact: readOption(rest, '--validation-artifact') ?? undefined,
370
+ teamModeActivation: readTeamModeActivation(rest)
284
371
  });
372
+ const json = wantsJson(rest);
285
373
  return {
286
374
  exitCode: result.status === 'completed' ? 0 : 1,
287
- output: renderSingleTaskLoopResult(result)
375
+ output: json ? jsonOutput(result, rest) : renderSingleTaskLoopResult(result)
288
376
  };
289
377
  }
290
378
  if (command === 'verify' && subcommand === 'task') {
291
- const taskId = rest.find((item) => !item.startsWith('--'));
379
+ const taskId = readOptionalPositionalArgument(rest);
292
380
  const runId = readOption(rest, '--run');
293
- if (!taskId || !runId) {
381
+ if (!taskId) {
294
382
  return {
295
383
  exitCode: 2,
296
- error: 'Usage: sdd verify task <task_id> --run <run_id> [--branch <branch>] [--review-artifact artifacts/path.md] [--validation-artifact artifacts/path.md]'
384
+ error: 'Usage: sdd verify task <task_id> [--run <run_id>] [--branch <branch>] [--review-artifact artifacts/path.md] [--validation-artifact artifacts/path.md] [--json]'
297
385
  };
298
386
  }
299
387
  const result = await runGoalVerify(projectRoot, {
300
388
  taskId,
301
- runId,
302
- branch: readOption(rest, '--branch') ?? 'master',
389
+ runId: runId ?? undefined,
390
+ branch: readBranchOption(rest),
303
391
  reviewArtifact: readOption(rest, '--review-artifact') ?? undefined,
304
392
  validationArtifact: readOption(rest, '--validation-artifact') ?? undefined
305
393
  });
394
+ const json = wantsJson(rest);
306
395
  return {
307
396
  exitCode: result.status === 'PASS' ? 0 : 1,
308
- output: renderGoalVerifyResult(result)
397
+ output: json ? jsonOutput(result, rest) : renderGoalVerifyResult(result)
309
398
  };
310
399
  }
311
400
  if (command === 'governance' && subcommand === 'inspect') {
@@ -334,6 +423,217 @@ async function main(args) {
334
423
  output: rest.includes('--json') ? JSON.stringify(decision, null, 2) : renderGovernancePolicyDecision(decision)
335
424
  };
336
425
  }
426
+ if (command === 'workflow' && subcommand === 'list') {
427
+ const registry = await listWorkflowGates(projectRoot);
428
+ return {
429
+ exitCode: 0,
430
+ output: rest.includes('--json') ? JSON.stringify(registry, null, 2) : renderWorkflowGateList(registry.workflows)
431
+ };
432
+ }
433
+ if (command === 'workflow' && subcommand === 'inspect') {
434
+ const workflowId = rest.find((item) => !item.startsWith('--'));
435
+ if (!workflowId) {
436
+ return {
437
+ exitCode: 2,
438
+ error: 'Usage: sdd workflow inspect <workflow_id> [--json]'
439
+ };
440
+ }
441
+ const workflow = await inspectWorkflowGate(projectRoot, workflowId);
442
+ if (!workflow) {
443
+ return {
444
+ exitCode: 1,
445
+ error: `Unknown workflow: ${workflowId}`
446
+ };
447
+ }
448
+ return {
449
+ exitCode: 0,
450
+ output: rest.includes('--json') ? JSON.stringify(workflow, null, 2) : renderWorkflowGateInspect(workflow)
451
+ };
452
+ }
453
+ if (command === 'workflow' && subcommand === 'validate') {
454
+ const result = await validateWorkflowGates(projectRoot);
455
+ return {
456
+ exitCode: result.valid ? 0 : 1,
457
+ output: rest.includes('--json') ? JSON.stringify(result, null, 2) : renderWorkflowGateValidation(result)
458
+ };
459
+ }
460
+ if (command === 'agents' && subcommand === 'list') {
461
+ const registry = await listAgentRegistry(projectRoot);
462
+ return {
463
+ exitCode: 0,
464
+ output: rest.includes('--json') ? JSON.stringify(registry, null, 2) : renderAgentRegistryList(registry.agents)
465
+ };
466
+ }
467
+ if (command === 'agents' && subcommand === 'inspect') {
468
+ const agentId = rest.find((item) => !item.startsWith('--'));
469
+ if (!agentId) {
470
+ return {
471
+ exitCode: 2,
472
+ error: 'Usage: sdd agents inspect <agent_id> [--json]'
473
+ };
474
+ }
475
+ const agent = await inspectAgentRegistryEntry(projectRoot, agentId);
476
+ if (!agent) {
477
+ return {
478
+ exitCode: 1,
479
+ error: `Unknown agent: ${agentId}`
480
+ };
481
+ }
482
+ return {
483
+ exitCode: 0,
484
+ output: rest.includes('--json') ? JSON.stringify(agent, null, 2) : renderAgentRegistryInspect(agent)
485
+ };
486
+ }
487
+ if (command === 'agents' && subcommand === 'validate') {
488
+ const result = await validateAgentRegistry(projectRoot);
489
+ return {
490
+ exitCode: result.valid ? 0 : 1,
491
+ output: rest.includes('--json') ? JSON.stringify(result, null, 2) : renderAgentRegistryValidation(result)
492
+ };
493
+ }
494
+ if (command === 'agent-runtime' && subcommand === 'inspect') {
495
+ const inspection = await inspectAgentSkillTeamRuntime(projectRoot);
496
+ return {
497
+ exitCode: 0,
498
+ output: rest.includes('--json') ? JSON.stringify(inspection, null, 2) : renderAgentSkillTeamRuntimeInspection(inspection)
499
+ };
500
+ }
501
+ if (command === 'agent-runtime' && subcommand === 'validate') {
502
+ const result = await validateAgentSkillTeamRuntime(projectRoot);
503
+ return {
504
+ exitCode: result.valid ? 0 : 1,
505
+ output: rest.includes('--json') ? JSON.stringify(result, null, 2) : renderAgentSkillTeamRuntimeValidation(result)
506
+ };
507
+ }
508
+ if (command === 'skill-capabilities' && subcommand === 'list') {
509
+ const registry = await listSkillCapabilities(projectRoot);
510
+ return {
511
+ exitCode: 0,
512
+ output: rest.includes('--json') ? JSON.stringify(registry, null, 2) : renderSkillCapabilityList(registry.capabilities, registry.registrySources)
513
+ };
514
+ }
515
+ if (command === 'skill-capabilities' && subcommand === 'inspect') {
516
+ const capabilityId = rest.find((item) => !item.startsWith('--'));
517
+ if (!capabilityId) {
518
+ return {
519
+ exitCode: 2,
520
+ error: 'Usage: sdd skill-capabilities inspect <capability_id> [--json]'
521
+ };
522
+ }
523
+ const capability = await inspectSkillCapability(projectRoot, capabilityId);
524
+ if (!capability) {
525
+ return { exitCode: 1, error: `Unknown skill capability: ${capabilityId}` };
526
+ }
527
+ return {
528
+ exitCode: 0,
529
+ output: rest.includes('--json') ? JSON.stringify(capability, null, 2) : renderSkillCapabilityInspect(capability)
530
+ };
531
+ }
532
+ if (command === 'capability-sources' && subcommand === 'list') {
533
+ const catalog = await listCapabilitySources(projectRoot);
534
+ return {
535
+ exitCode: 0,
536
+ output: rest.includes('--json') ? JSON.stringify(catalog, null, 2) : renderCapabilitySourceList(catalog.sources, catalog.registrySources)
537
+ };
538
+ }
539
+ if (command === 'capability-sources' && subcommand === 'inspect') {
540
+ const sourceId = rest.find((item) => !item.startsWith('--'));
541
+ if (!sourceId) {
542
+ return {
543
+ exitCode: 2,
544
+ error: 'Usage: sdd capability-sources inspect <source_id> [--json]'
545
+ };
546
+ }
547
+ const source = await inspectCapabilitySource(projectRoot, sourceId);
548
+ if (!source) {
549
+ return { exitCode: 1, error: `Unknown capability source: ${sourceId}` };
550
+ }
551
+ return {
552
+ exitCode: 0,
553
+ output: rest.includes('--json') ? JSON.stringify(source, null, 2) : renderCapabilitySourceInspect(source)
554
+ };
555
+ }
556
+ if (command === 'external-packs' && subcommand === 'inspect') {
557
+ const sourceId = rest.find((item) => !item.startsWith('--'));
558
+ if (!sourceId) {
559
+ return {
560
+ exitCode: 2,
561
+ error: 'Usage: sdd external-packs inspect <source_id> [--json]'
562
+ };
563
+ }
564
+ const inspection = await inspectExternalAgentPackImport(projectRoot, sourceId);
565
+ return {
566
+ exitCode: inspection.status === 'denied' ? 1 : 0,
567
+ output: rest.includes('--json') ? JSON.stringify(inspection, null, 2) : renderExternalAgentPackImportInspection(inspection)
568
+ };
569
+ }
570
+ if (command === 'team-mode' && subcommand === 'inspect') {
571
+ const policy = await inspectTeamModePolicy(projectRoot, {
572
+ taskId: readOption(rest, '--task') ?? undefined,
573
+ branch: readBranchOption(rest),
574
+ teamModeActivation: readTeamModeActivation(rest, rest.includes('--enabled') ? 'force' : undefined)
575
+ });
576
+ return {
577
+ exitCode: policy.decision === 'blocked' ? 1 : 0,
578
+ output: rest.includes('--json') ? JSON.stringify(policy, null, 2) : renderTeamModePolicy(policy)
579
+ };
580
+ }
581
+ if (command === 'query-status' && subcommand === 'inspect') {
582
+ const contract = await inspectQueryStatusContract(projectRoot);
583
+ return {
584
+ exitCode: 0,
585
+ output: rest.includes('--json') ? JSON.stringify(contract, null, 2) : renderQueryStatusContract(contract)
586
+ };
587
+ }
588
+ if (command === 'query-status' && subcommand === 'validate') {
589
+ const result = await validateQueryStatusContract(projectRoot);
590
+ return {
591
+ exitCode: result.valid ? 0 : 1,
592
+ output: rest.includes('--json') ? JSON.stringify(result, null, 2) : renderQueryStatusValidation(result)
593
+ };
594
+ }
595
+ if (command === 'eval' && subcommand === 'inspect') {
596
+ const contract = await inspectSkillAgentEvalContract(projectRoot);
597
+ return {
598
+ exitCode: 0,
599
+ output: rest.includes('--json') ? JSON.stringify(contract, null, 2) : renderSkillAgentEvalContract(contract)
600
+ };
601
+ }
602
+ if (command === 'eval' && subcommand === 'validate') {
603
+ const result = await validateSkillAgentEvalContract(projectRoot);
604
+ return {
605
+ exitCode: result.valid ? 0 : 1,
606
+ output: rest.includes('--json') ? JSON.stringify(result, null, 2) : renderSkillAgentEvalValidation(result)
607
+ };
608
+ }
609
+ if (command === 'learning' && subcommand === 'inspect') {
610
+ const contract = await inspectHarnessLearningContract(projectRoot);
611
+ return {
612
+ exitCode: 0,
613
+ output: rest.includes('--json') ? JSON.stringify(contract, null, 2) : renderHarnessLearningContract(contract)
614
+ };
615
+ }
616
+ if (command === 'learning' && subcommand === 'validate') {
617
+ const result = await validateHarnessLearningContract(projectRoot);
618
+ return {
619
+ exitCode: result.valid ? 0 : 1,
620
+ output: rest.includes('--json') ? JSON.stringify(result, null, 2) : renderHarnessLearningValidation(result)
621
+ };
622
+ }
623
+ if (command === 'context-pack' && subcommand === 'inspect') {
624
+ const contract = await inspectProjectContextPackContract(projectRoot);
625
+ return {
626
+ exitCode: 0,
627
+ output: rest.includes('--json') ? JSON.stringify(contract, null, 2) : renderProjectContextPackContract(contract)
628
+ };
629
+ }
630
+ if (command === 'context-pack' && subcommand === 'validate') {
631
+ const result = await validateProjectContextPackContract(projectRoot);
632
+ return {
633
+ exitCode: result.valid ? 0 : 1,
634
+ output: rest.includes('--json') ? JSON.stringify(result, null, 2) : renderProjectContextPackValidation(result)
635
+ };
636
+ }
337
637
  if (command === 'capabilities' && subcommand === 'list') {
338
638
  const registry = await listToolCapabilities(projectRoot);
339
639
  return {
@@ -432,7 +732,7 @@ async function main(args) {
432
732
  }
433
733
  const decision = await inspectWorktreeIsolation(projectRoot, {
434
734
  taskId,
435
- branch: readOption(rest, '--branch') ?? 'master',
735
+ branch: readBranchOption(rest),
436
736
  capabilityId: readOption(rest, '--capability') ?? undefined,
437
737
  peerTaskIds: readRepeatedOptions(rest, '--peer-task')
438
738
  });
@@ -442,7 +742,7 @@ async function main(args) {
442
742
  };
443
743
  }
444
744
  if (command === 'graph' && subcommand === 'inspect') {
445
- const graph = await inspectTaskGraph(projectRoot, { branch: readOption(rest, '--branch') ?? 'master' });
745
+ const graph = await inspectTaskGraph(projectRoot, { branch: readBranchOption(rest) });
446
746
  return {
447
747
  exitCode: graph.valid ? 0 : 1,
448
748
  output: rest.includes('--json') ? JSON.stringify(graph, null, 2) : renderTaskGraphPlan(graph)
@@ -450,7 +750,7 @@ async function main(args) {
450
750
  }
451
751
  if (command === 'wave' && subcommand === 'inspect') {
452
752
  const wavePlan = await inspectWavePlan(projectRoot, {
453
- branch: readOption(rest, '--branch') ?? 'master',
753
+ branch: readBranchOption(rest),
454
754
  capabilityId: readOption(rest, '--capability') ?? undefined
455
755
  });
456
756
  return {
@@ -467,7 +767,7 @@ async function main(args) {
467
767
  };
468
768
  }
469
769
  const result = await runWaveExecutor(projectRoot, {
470
- branch: readOption(rest, '--branch') ?? 'master',
770
+ branch: readBranchOption(rest),
471
771
  runId: readOption(rest, '--run') ?? undefined,
472
772
  capabilityId: readOption(rest, '--capability') ?? undefined,
473
773
  agent: readOption(rest, '--agent') ?? undefined,
@@ -503,7 +803,7 @@ async function main(args) {
503
803
  };
504
804
  }
505
805
  const result = await runBackgroundExecutor(projectRoot, {
506
- branch: readOption(rest, '--branch') ?? 'master',
806
+ branch: readBranchOption(rest),
507
807
  runId: readOption(rest, '--run') ?? undefined,
508
808
  taskId,
509
809
  agent: readOption(rest, '--agent') ?? undefined,
@@ -530,6 +830,78 @@ async function main(args) {
530
830
  output: rest.includes('--json') ? JSON.stringify(inspection, null, 2) : renderBackgroundExecutorInspection(inspection)
531
831
  };
532
832
  }
833
+ if (command === 'worker-runtime' && subcommand === 'claim') {
834
+ const taskId = rest[0];
835
+ if (!taskId) {
836
+ return {
837
+ exitCode: 2,
838
+ error: 'Usage: sdd worker-runtime claim <task_id> [--run <run_id>] [--runtime <runtime_id>] [--agent <agent>] [--worker <adapter_id>] [--delegation <delegation_id>] [--lease-seconds <n>] [--branch <branch>] [--json]'
839
+ };
840
+ }
841
+ const result = await claimResidentWorkerRuntime(projectRoot, {
842
+ branch: readBranchOption(rest),
843
+ runId: readOption(rest, '--run') ?? undefined,
844
+ taskId,
845
+ runtimeId: readOption(rest, '--runtime') ?? undefined,
846
+ agent: readOption(rest, '--agent') ?? undefined,
847
+ workerAdapterId: readOption(rest, '--worker') ?? undefined,
848
+ delegationId: readOption(rest, '--delegation') ?? undefined,
849
+ leaseSeconds: readPositiveIntegerOption(rest, '--lease-seconds') ?? undefined
850
+ });
851
+ return {
852
+ exitCode: result.status === 'blocked' ? 1 : 0,
853
+ output: rest.includes('--json') ? JSON.stringify(result, null, 2) : renderResidentWorkerRuntimeClaimResult(result)
854
+ };
855
+ }
856
+ if (command === 'worker-runtime' && subcommand === 'heartbeat') {
857
+ const runtimeId = rest[0];
858
+ const runId = readOption(rest, '--run');
859
+ if (!runtimeId || !runId) {
860
+ return {
861
+ exitCode: 2,
862
+ error: 'Usage: sdd worker-runtime heartbeat <runtime_id> --run <run_id> [--lease-seconds <n>] [--json]'
863
+ };
864
+ }
865
+ const result = await heartbeatResidentWorkerRuntime(projectRoot, {
866
+ runId,
867
+ runtimeId,
868
+ leaseSeconds: readPositiveIntegerOption(rest, '--lease-seconds') ?? undefined
869
+ });
870
+ return {
871
+ exitCode: result.status === 'blocked' ? 1 : 0,
872
+ output: rest.includes('--json') ? JSON.stringify(result, null, 2) : renderResidentWorkerRuntimeHeartbeatResult(result)
873
+ };
874
+ }
875
+ if (command === 'worker-runtime' && subcommand === 'status') {
876
+ const positionalRunId = rest[0] && !rest[0].startsWith('--') ? rest[0] : null;
877
+ const runId = readOption(rest, '--run') ?? positionalRunId;
878
+ if (!runId) {
879
+ return {
880
+ exitCode: 2,
881
+ error: 'Usage: sdd worker-runtime status --run <run_id> [--json]'
882
+ };
883
+ }
884
+ const result = await listResidentWorkerRuntimes(projectRoot, { runId });
885
+ return {
886
+ exitCode: result.valid ? 0 : 1,
887
+ output: rest.includes('--json') ? JSON.stringify(result, null, 2) : renderResidentWorkerRuntimeList(result)
888
+ };
889
+ }
890
+ if (command === 'worker-runtime' && subcommand === 'inspect') {
891
+ const runtimeId = rest[0];
892
+ const runId = readOption(rest, '--run');
893
+ if (!runtimeId || !runId) {
894
+ return {
895
+ exitCode: 2,
896
+ error: 'Usage: sdd worker-runtime inspect <runtime_id> --run <run_id> [--json]'
897
+ };
898
+ }
899
+ const result = await inspectResidentWorkerRuntime(projectRoot, { runId, runtimeId });
900
+ return {
901
+ exitCode: result.valid ? 0 : 1,
902
+ output: rest.includes('--json') ? JSON.stringify(result, null, 2) : renderResidentWorkerRuntimeInspection(result)
903
+ };
904
+ }
533
905
  if (command === 'worktree' && subcommand === 'create') {
534
906
  const runId = rest[0];
535
907
  const taskId = rest[1];
@@ -627,18 +999,34 @@ async function main(args) {
627
999
  if (!artifactPath || !taskId || !agent) {
628
1000
  return {
629
1001
  exitCode: 2,
630
- error: 'Usage: sdd artifact template <artifacts/path.md> --task <task_id> --agent <agent> [--branch <branch>] [--status <status>]'
1002
+ error: 'Usage: sdd artifact template <artifacts/path.md> --task <task_id> --agent <agent> [--run <run_id> --write] [--branch <branch>] [--status <status>]'
1003
+ };
1004
+ }
1005
+ const runId = readOption(rest, '--run');
1006
+ const template = await renderSddResultArtifactTemplate(projectRoot, {
1007
+ artifactPath,
1008
+ taskId,
1009
+ agent,
1010
+ branch: readBranchOption(rest),
1011
+ runId: runId ?? undefined,
1012
+ status: readSddResultStatus(rest, '--status') ?? 'PASS'
1013
+ });
1014
+ if (rest.includes('--write')) {
1015
+ if (!runId) {
1016
+ return {
1017
+ exitCode: 2,
1018
+ error: 'Usage: sdd artifact template <artifacts/path.md> --task <task_id> --agent <agent> --run <run_id> --write'
1019
+ };
1020
+ }
1021
+ const written = await writeArtifact(projectRoot, runId, toArtifactRootRelativePath(artifactPath), template);
1022
+ return {
1023
+ exitCode: 0,
1024
+ output: `Artifact template written: ${written.runRelativePath}\nphysical_path=${written.absolutePath}`
631
1025
  };
632
1026
  }
633
1027
  return {
634
1028
  exitCode: 0,
635
- output: await renderSddResultArtifactTemplate(projectRoot, {
636
- artifactPath,
637
- taskId,
638
- agent,
639
- branch: readOption(rest, '--branch') ?? 'master',
640
- status: readSddResultStatus(rest, '--status') ?? 'PASS'
641
- })
1029
+ output: template
642
1030
  };
643
1031
  }
644
1032
  if (command === 'artifact' && subcommand === 'validate') {
@@ -647,7 +1035,7 @@ async function main(args) {
647
1035
  if (!runId || !artifactPath) {
648
1036
  return {
649
1037
  exitCode: 2,
650
- error: 'Usage: sdd artifact validate <run_id> <artifacts/path.md> [--task <task_id>] [--agent <agent>] [--json]'
1038
+ error: 'Usage: sdd artifact validate <run_id> <artifacts/path.md> [--task <task_id>] [--agent <agent>] [--json|--compact-json]'
651
1039
  };
652
1040
  }
653
1041
  const expectedTask = readOption(rest, '--task') ?? undefined;
@@ -658,7 +1046,7 @@ async function main(args) {
658
1046
  });
659
1047
  return {
660
1048
  exitCode: report.valid ? 0 : 1,
661
- output: rest.includes('--json') ? JSON.stringify(report, null, 2) : renderArtifactValidationReport(artifactPath, report, expectedTask, expectedAgent)
1049
+ output: renderTextOrJson(rest, report, (value) => renderArtifactValidationReport(artifactPath, value, expectedTask, expectedAgent))
662
1050
  };
663
1051
  }
664
1052
  if (command === 'artifact' && subcommand === 'ingest') {
@@ -696,175 +1084,290 @@ async function main(args) {
696
1084
  error: `Unknown command: ${args.join(' ')}\n\n${helpText()}`
697
1085
  };
698
1086
  }
699
- function helpText() {
1087
+ function helpText(topic) {
1088
+ if (topic === 'advanced') {
1089
+ return advancedHelpText();
1090
+ }
1091
+ if (topic === 'workflow') {
1092
+ return workflowHelpText();
1093
+ }
700
1094
  return `sdd Phase 2 platform CLI
701
1095
 
702
- Commands:
703
- sdd --version Print CLI/core version
704
- sdd init [--force] [--ai <mode>] [--branch <branch>] [--no-scaffold-docs]
705
- Create .sdd config, starter SDD docs, and generated AI entries
706
- sdd update [--check] [--ai <mode>] Refresh or check managed generated AI entries
707
- sdd instructions [action] [--json] Print dynamic SDD instruction payload
708
- sdd doctor [--latest-only] [--all-runs] Check config, run evidence, specs, and AI entry drift
709
- sdd status [--branch <branch>] [--json] Show tasks, latest run, gaps, and recommended next command
710
- sdd run create Create .sdd/runs/<run_id> with state/events/artifacts
711
- sdd run status <run_id> Print current run status
712
- sdd run list [--json] List recorded runs by updated time
713
- sdd run index rebuild|inspect|query [options] Rebuild, inspect, or query Phase 3.13 local run index
714
- sdd run inspect <run_id> [--json] Inspect run state, events, artifacts, validation, sync-back
715
- sdd run archive <run_id> [--reason] Archive a run without deleting evidence
716
- sdd sync-back inspect <run_id> [options] Inspect explicit proposal-to-tasks.md write-back readiness
717
- sdd sync-back apply <run_id> [--approved] Apply verified sync-back proposal to tasks.md
718
- sdd lifecycle decide [options] Evaluate lifecycle decision gate
719
- sdd do task <task_id> [options] Run ingestion-aware task workflow over supplied artifacts
720
- sdd verify task <task_id> --run <run_id> Run goal-level acceptance coverage verify
721
- sdd tasks format Print canonical sdd-task fenced block format
722
- sdd tasks list [--branch <branch>] Parse and list sdd-task blocks
723
- sdd tasks inspect <task_id> [--branch] Inspect one parsed task
724
- sdd tasks gaps [--branch <branch>] Render parser task gap report
725
- sdd artifact template <path> [options] Print a valid sdd-result artifact template
726
- sdd artifact validate <run_id> <path> Validate a run-relative sdd-result artifact
727
- sdd artifact ingest <run_id> <delegation_id> <path>
728
- sdd artifact ingestions <run_id> [--json] Inspect Phase 3.6 artifact ingestion ledger
729
- sdd capabilities list [--json] List Phase 3.1 tool/capability declarations
730
- sdd capabilities inspect <id> [--json] Inspect one capability declaration
731
- sdd governance inspect|evaluate [options] Inspect or evaluate Phase 3.14 governance policy
732
- sdd plugins list [--json] List Phase 3.2 plugin loading contracts
733
- sdd plugins inspect <id> [--json] Inspect one plugin loading contract
734
- sdd queue list [--run <run_id>] [--json] List Phase 3.3 delegation queue items
735
- sdd queue inspect <id> [--json] Inspect one delegation queue item
736
- sdd state-machine inspect [--json] Inspect Phase 3.4 delegation state machine
737
- sdd workers list [--json] List Phase 3.5 worker adapter contracts
738
- sdd workers inspect <id> [--json] Inspect one worker adapter contract
739
- sdd isolation inspect <task_id> [options] Dry-run Phase 3.7 worktree isolation decision
740
- sdd graph inspect [--branch <branch>] [--json] Inspect Phase 3.9 task dependency graph
741
- sdd wave inspect [--branch <branch>] [--capability <id>] [--json] Inspect Phase 3.10 dependency wave plan
742
- sdd wave run [options] Run Phase 3.12 planner-driven wave executor
743
- sdd wave executor <run_id> [--json] Inspect Phase 3.12 wave executor evidence
744
- sdd background run <task_id> [options] Claim one Phase 3.11 background delegation; ingest supplied artifact if provided
745
- sdd background inspect <run_id> [--json] Inspect Phase 3.11 background executor evidence
746
- sdd worktree create <run_id> <task_id> [options]
747
- sdd worktree inspect <run_id> [--json] Inspect Phase 3.8 worktree lifecycle records
748
- sdd worktree keep <run_id> <id> Mark a worktree retained for inspection
749
- sdd worktree remove <run_id> <id> Remove a clean tracked worktree
750
-
751
- AI options:
752
- --ai auto Project Claude Code entries when supported
753
- --ai claude-code Project Claude Code entries explicitly
754
- --ai none Skip AI entry projection during init
755
-
756
- Init options:
757
- --branch <branch> Create starter docs under specs/<branch>; default master
758
- --no-scaffold-docs Skip starter spec.md/plan.md/tasks.md creation
759
-
760
- Doctor options:
761
- --latest-only Inspect only the newest non-archived run evidence
762
- --all-runs Inspect every run, including archived runs
763
-
764
- Run index options:
765
- --run <run_id> Filter local run index query by run id
766
- --task <task_id> Filter local run index query by task id
767
- --status <status> Filter query by run status
768
- --artifact <path> Filter query by indexed artifact path
769
- --json Print machine-readable local run index output
770
-
771
- Artifact options:
772
- --task <task_id> Expected artifact task id
773
- --agent <agent> Expected producing agent name
774
- --branch <branch> Branch used to copy validator Acceptance mapping
775
- --status <status> Template status; default PASS
776
- --json Print machine-readable validation result
1096
+ Common workflow:
1097
+ sdd init [--force] [--ai <mode>] [--scaffold-docs] [--json]
1098
+ sdd status [--branch <branch>] [--json|--compact-json]
1099
+ sdd tasks inspect <task_id> [--branch <branch>] [--json|--compact-json]
1100
+ sdd tasks route <task_id> [--branch <branch>] [--json|--compact-json]
1101
+ sdd do task <task_id> [options]
1102
+ sdd verify task <task_id> [--branch <branch>] [--run <run_id>] [--json|--compact-json]
1103
+ sdd sync-back inspect [<run_id>] [--task <task_id>] [--branch <branch>] [--json|--compact-json]
1104
+ sdd sync-back apply [<run_id>] [--task <task_id>] [--branch <branch>] [--approved] [--json|--compact-json]
1105
+ sdd doctor [--latest-only] [--all-runs] [--json|--compact-json]
777
1106
 
778
- Governance options:
779
- --worker <adapter_id> Worker adapter to evaluate
780
- --risk <tag> Risk tag to evaluate; repeatable
781
- --approved Record explicit approval for confirmation-gated operations
782
- --json Print machine-readable governance output
1107
+ Evidence helpers:
1108
+ sdd run create
1109
+ sdd run list [--json]
1110
+ sdd run inspect <run_id> [--json|--compact-json]
1111
+ sdd run index rebuild|inspect|query [options] [--json|--compact-json]
1112
+ sdd artifact template <path> --task <task_id> --agent <agent> [--run <run_id> --write]
1113
+ sdd artifact validate <run_id> <path> [--task <task_id>] [--agent <agent>] [--json|--compact-json]
1114
+ sdd evidence summary <run_id> [--task <task_id>] [--json|--compact-json]
1115
+ sdd context build --task <task_id> --mode do|verify|sync-back|doctor [--agent <agent>] [--branch <branch>] [--profile brief|normal|forensic] [--json|--compact-json]
783
1116
 
784
- Wave executor options:
785
- --branch <branch> Read specs/<branch>/tasks.md; default master
786
- --run <run_id> Reuse an existing run; default creates one
787
- --capability <id> Capability used for planner isolation decisions
788
- --strategy fast-stop|safe-continue Stop on first task failure or finish current safe wave
789
- --artifact <task_id:path> Supply task-specific run-relative artifact; repeatable
1117
+ Generated AI entries:
1118
+ sdd update [--check] [--ai <mode>]
1119
+ sdd instructions [action] [--json|--compact-json]
790
1120
 
791
- Instructions actions:
792
- overview | init | doctor | update | spec | plan | tasks | do | verify | run-task | verify-task
1121
+ More help:
1122
+ sdd help workflow Show core workflow options.
1123
+ sdd help advanced Show platform/agent/runtime commands.
793
1124
 
794
- Do task options:
795
- --branch <branch> Read specs/<branch>/spec.md, plan.md, tasks.md
796
- --run <run_id> Reuse an existing run instead of creating one
797
- --implement-artifact <path> Run-relative implementer artifact, if available
798
- --review-artifact <path> Run-relative reviewer artifact, required to complete
799
- --debug-artifact <path> Optional single debugger artifact after review failure
800
- --validation-artifact <path> Run-relative validator artifact, required to complete
801
-
802
- Verify task options:
803
- --branch <branch> Read specs/<branch>/spec.md, plan.md, tasks.md
804
- --run <run_id> Required run id containing state/events/artifacts
805
- --review-artifact <path> Optional reviewer artifact override
806
- --validation-artifact <path> Optional validator artifact override
1125
+ Notes:
1126
+ /sdd:spec owns workflow partition docs after project init.
1127
+ init --branch is legacy starter-doc scaffolding; prefer sdd status --branch or /sdd:spec --branch for workflow partitions.
1128
+ `;
1129
+ }
1130
+ function workflowHelpText() {
1131
+ return `sdd workflow help
807
1132
 
808
- Lifecycle decide options:
809
- --direct-safe High clarity, high confidence, reversible, cheap local validation
810
- --risk <tag> Add risk tag; api/schema/database/security/state-machine/ci force gates
811
- --contract <name> Mark affected API/schema/contract
812
- --impact-confidence <high|medium|low> Set impact confidence
813
- --acceptance <high|medium|low> Set acceptance clarity
814
- --validation <clear|partial|unclear> Set validation clarity
815
- --external-unknown Require research route
816
- --architecture Require architecture/research route
817
- --checkpoint Force human checkpoint
818
- --permission <name> Mark permission/checkpoint requirement
819
- --run <run_id> Record decision to existing run state/events
820
- --json Print machine-readable result
1133
+ Core path:
1134
+ 1. sdd status [--branch <branch>]
1135
+ 2. sdd tasks inspect <task_id> [--branch <branch>]
1136
+ 3. sdd tasks route <task_id> [--branch <branch>]
1137
+ 4. sdd artifact template artifacts/<agent>-<task_id>.md --task <task_id> --agent <agent> --run <run_id> --write
1138
+ 5. sdd do task <task_id> --run <run_id> --implement-artifact <path> --review-artifact <path> --validation-artifact <path>
1139
+ 6. sdd verify task <task_id> [--branch <branch>]
1140
+ 7. sdd sync-back inspect --task <task_id> [--branch <branch>]
1141
+ 8. sdd sync-back apply --task <task_id> [--branch <branch>]
821
1142
 
822
- Sync-back options:
823
- --branch <branch> Read target specs/<branch>/tasks.md
824
- --task <task_id> Override run currentTask when inspecting or applying
825
- --json Print machine-readable result
1143
+ JSON:
1144
+ --json prints readable JSON; --compact-json prints one-line JSON for logs and scripts.
1145
+ `;
1146
+ }
1147
+ function advancedHelpText() {
1148
+ return `sdd advanced help
826
1149
 
827
- Isolation options:
828
- --branch <branch> Read specs/<branch>/tasks.md
829
- --capability <capability_id> Capability side effect used for isolation decision
830
- --peer-task <task_id> Compare affected_files overlap against peer task
1150
+ Runtime/catalog:
1151
+ sdd agent-runtime inspect|validate [--json]
1152
+ sdd skill-capabilities list|inspect [--json]
1153
+ sdd capability-sources list|inspect [--json]
1154
+ sdd external-packs inspect <source_id> [--json]
1155
+ sdd team-mode inspect [--task <id>] [--team-mode [auto|force|off]] [--no-team-mode] [--json]
831
1156
 
832
- Graph options:
833
- --branch <branch> Read specs/<branch>/tasks.md
1157
+ Harness/platform:
1158
+ sdd workflow list|inspect|validate [--json]
1159
+ sdd agents list|inspect|validate [--json]
1160
+ sdd query-status inspect|validate [--json]
1161
+ sdd eval inspect|validate [--json]
1162
+ sdd learning inspect|validate [--json]
1163
+ sdd context-pack inspect|validate [--json]
1164
+ sdd capabilities list|inspect [--json]
1165
+ sdd governance inspect|evaluate [options]
1166
+ sdd plugins list|inspect [--json]
1167
+ sdd queue list|inspect [options]
1168
+ sdd state-machine inspect [--json]
1169
+ sdd workers list|inspect [--json]
834
1170
 
835
- Wave options:
836
- --branch <branch> Read specs/<branch>/tasks.md
837
- --capability <capability_id> Capability side effect used for isolation decisions
1171
+ Execution/isolation:
1172
+ sdd background run|inspect [options]
1173
+ sdd worker-runtime claim|heartbeat|status|inspect [options]
1174
+ sdd isolation inspect <task_id> [options]
1175
+ sdd graph inspect [--branch <branch>] [--json]
1176
+ sdd wave inspect|run|executor [options]
1177
+ sdd worktree create|inspect|keep|remove [options]
838
1178
 
839
- Background options:
840
- --branch <branch> Read specs/<branch>/tasks.md
841
- --run <run_id> Reuse an existing run; default creates one
842
- --agent <agent> Delegated agent name; default implementer
843
- --worker <adapter_id> Worker adapter id; default sdd-cli-task-worker
844
- --artifact <path> Run-relative sdd-result artifact to ingest
845
- --delegation <delegation_id> Stable delegation id for retry/ingest
846
- Worktree options:
847
- --base <ref> Base ref for git worktree add; default HEAD
848
- --id <worktree_id> Stable lifecycle id; default wt-<run>-<task>
849
- --reason <text> Reason when keeping a worktree
850
- `;
1179
+ Legacy init partition option:
1180
+ sdd init --branch <branch> creates starter docs for that branch, but normal workflow partitioning belongs to /sdd:spec and sdd status --branch.
1181
+ `;
851
1182
  }
852
- function renderProjectStatus(status) {
853
- const lines = [`SDD status for ${status.branch}`];
854
- lines.push(`documents spec=${status.documents.specExists} plan=${status.documents.planExists} tasks=${status.documents.tasksExists}`);
855
- lines.push(`tasks total=${status.tasks.total} pending=${status.tasks.pending} in_progress=${status.tasks.inProgress} completed=${status.tasks.completed} blocked=${status.tasks.blocked} deferred=${status.tasks.deferred} unknown=${status.tasks.unknown} gaps=${status.tasks.gaps}`);
856
- if (status.latestRun) {
857
- lines.push(`latest_run ${status.latestRun.runId} status=${status.latestRun.status} phase=${status.latestRun.phase ?? 'n/a'} task=${status.latestRun.currentTask ?? 'n/a'} validation=${status.latestRun.validationStatus} sync_back=${status.latestRun.syncBackStatus}`);
1183
+ function wantsJson(args) {
1184
+ return args.includes('--json') || args.includes('--compact-json');
1185
+ }
1186
+ function jsonOutput(value, args) {
1187
+ return JSON.stringify(value, null, args.includes('--compact-json') ? 0 : 2);
1188
+ }
1189
+ function getCliIdentity() {
1190
+ const cliEntryPath = fileURLToPath(import.meta.url);
1191
+ const packageRoot = findPackageRoot(path.dirname(cliEntryPath));
1192
+ const packageJsonPath = packageRoot ? path.join(packageRoot, 'package.json') : null;
1193
+ return {
1194
+ version: readPackageVersion(packageJsonPath) ?? SDD_VERSION,
1195
+ cliEntryPath,
1196
+ packageRoot,
1197
+ packageJsonPath
1198
+ };
1199
+ }
1200
+ function findPackageRoot(startDir) {
1201
+ let current = startDir;
1202
+ while (true) {
1203
+ const packageJsonPath = path.join(current, 'package.json');
1204
+ if (existsSync(packageJsonPath) && isCliPackageRoot(packageJsonPath)) {
1205
+ return current;
1206
+ }
1207
+ const parent = path.dirname(current);
1208
+ if (parent === current) {
1209
+ return null;
1210
+ }
1211
+ current = parent;
1212
+ }
1213
+ }
1214
+ function isCliPackageRoot(packageJsonPath) {
1215
+ try {
1216
+ const parsed = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
1217
+ return parsed.name === '@sdd-agent-platform/cli' || parsed.name === 'sdd-agent-platform';
1218
+ }
1219
+ catch {
1220
+ return false;
1221
+ }
1222
+ }
1223
+ function readPackageVersion(packageJsonPath) {
1224
+ if (!packageJsonPath) {
1225
+ return null;
1226
+ }
1227
+ try {
1228
+ const parsed = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
1229
+ return typeof parsed.version === 'string' ? parsed.version : null;
1230
+ }
1231
+ catch {
1232
+ return null;
1233
+ }
1234
+ }
1235
+ function readContextBuildMode(args, name) {
1236
+ const value = readOption(args, name);
1237
+ return value === 'do' || value === 'verify' || value === 'sync-back' || value === 'doctor' ? value : null;
1238
+ }
1239
+ function renderEvidenceSummaryProjection(summary) {
1240
+ const lines = [`Evidence summary ${summary.runId}`];
1241
+ lines.push(`task=${summary.taskId ?? 'none'} authoritative=${summary.authoritative} usable_for_pass=${summary.usableForPass}`);
1242
+ lines.push(`pass=${summary.passCount} blocked=${summary.blockedCount} fail=${summary.failCount} issues=${summary.issueCodes.join(',') || 'none'}`);
1243
+ lines.push('highlights');
1244
+ for (const highlight of summary.highlights.slice(0, 8)) {
1245
+ lines.push(`- ${highlight}`);
1246
+ }
1247
+ lines.push('sources');
1248
+ for (const source of summary.sources.slice(0, 12)) {
1249
+ lines.push(`- ${source.kind}:${source.path} hash=${source.hash.slice(0, 12)}`);
1250
+ }
1251
+ if (summary.sources.length > 12) {
1252
+ lines.push(`- omitted_sources=${summary.sources.length - 12}`);
1253
+ }
1254
+ return lines.join('\n');
1255
+ }
1256
+ function renderContextBuildPackage(contextPackage) {
1257
+ const lines = [`Context package ${contextPackage.taskId}`];
1258
+ lines.push(`mode=${contextPackage.mode} agent=${contextPackage.agent ?? 'none'} profile=${contextPackage.profile} branch=${contextPackage.branch}`);
1259
+ lines.push(`authoritative=${contextPackage.authoritative} usable_for_pass=${contextPackage.usableForPass}`);
1260
+ renderContextRefs(lines, 'must_read', contextPackage.mustRead, 10);
1261
+ renderContextRefs(lines, 'optional_read', contextPackage.optionalRead, 8);
1262
+ renderContextRefs(lines, 'deferred', contextPackage.doNotReadUnlessNeeded, 6);
1263
+ lines.push('next_commands');
1264
+ for (const command of contextPackage.nextCommands) {
1265
+ lines.push(`- ${command}`);
1266
+ }
1267
+ if (contextPackage.warnings.length > 0) {
1268
+ lines.push('warnings');
1269
+ for (const warning of contextPackage.warnings.slice(0, 6)) {
1270
+ lines.push(`- ${warning}`);
1271
+ }
1272
+ }
1273
+ return lines.join('\n');
1274
+ }
1275
+ function renderContextRefs(lines, label, refs, limit) {
1276
+ lines.push(label);
1277
+ for (const ref of refs.slice(0, limit)) {
1278
+ lines.push(`- ${ref.kind}:${ref.path} hash=${ref.hash.slice(0, 12)}`);
1279
+ }
1280
+ if (refs.length > limit) {
1281
+ lines.push(`- omitted_${label}=${refs.length - limit}`);
1282
+ }
1283
+ }
1284
+ function renderTextOrJson(args, value, renderText) {
1285
+ return wantsJson(args) ? jsonOutput(value, args) : renderText(value);
1286
+ }
1287
+ function renderInitResult(result) {
1288
+ const aiEntries = result.aiTools.flatMap((tool) => tool.entries);
1289
+ const aiCounts = new Map();
1290
+ for (const entry of aiEntries) {
1291
+ aiCounts.set(entry.status, (aiCounts.get(entry.status) ?? 0) + 1);
1292
+ }
1293
+ const aiSummary = Array.from(aiCounts.entries()).map(([status, count]) => `${status}=${count}`).join(' ') || 'none';
1294
+ const scaffoldedDocuments = result.documents.documents.filter((document) => document.status !== 'skipped');
1295
+ const lines = ['SDD init', 'changed'];
1296
+ lines.push(`- config ${result.created ? 'created/updated' : 'unchanged'} at ${result.configPath}`);
1297
+ lines.push(`- semantic docs ${scaffoldedDocuments.map((document) => `${document.status}:${document.relativePath}`).join(', ') || 'none'}`);
1298
+ lines.push(`- ai entries ${aiSummary}`);
1299
+ lines.push('decision');
1300
+ if (scaffoldedDocuments.length > 0) {
1301
+ lines.push(`- legacy_scaffold_branch=${result.documents.branch}`);
1302
+ lines.push(`- legacy_spec_dir=${result.documents.root}`);
1303
+ }
1304
+ lines.push('- sdd init is project-level setup; /sdd:spec is the workflow partition/spec entry');
1305
+ lines.push('evidence');
1306
+ for (const document of scaffoldedDocuments) {
1307
+ lines.push(`- [${document.status}] ${document.relativePath}: ${document.message}`);
1308
+ }
1309
+ if (aiEntries.length === 0) {
1310
+ lines.push('- ai entries skipped');
858
1311
  }
859
1312
  else {
860
- lines.push('latest_run none');
1313
+ lines.push(`- ${aiEntries.length} managed AI entry projection(s) checked/applied`);
861
1314
  }
862
- if (status.gaps.length > 0) {
863
- lines.push('gaps');
864
- for (const gap of status.gaps) {
865
- lines.push(`- [${gap.severity}] ${gap.type} ${gap.taskId ?? 'document'} ${gap.field}: ${gap.message}`);
1315
+ lines.push('- doctor checks git repository health; run git init first in fresh temporary projects before relying on doctor/run-index checks');
1316
+ lines.push('gaps');
1317
+ const driftEntries = aiEntries.filter((entry) => entry.status === 'drifted' || entry.status === 'user-modified' || entry.status === 'foreign' || entry.status === 'conflict');
1318
+ if (driftEntries.length === 0) {
1319
+ lines.push('- none');
1320
+ }
1321
+ else {
1322
+ for (const entry of driftEntries) {
1323
+ lines.push(`- [${entry.status}] ${entry.relativePath}: ${entry.action ?? entry.message}`);
1324
+ }
1325
+ }
1326
+ lines.push('next');
1327
+ lines.push('- /sdd:spec');
1328
+ return lines.join('\n');
1329
+ }
1330
+ function renderProjectStatus(status) {
1331
+ const lines = [`SDD status for ${status.branch}`];
1332
+ const staleDocuments = [
1333
+ status.documents.planStale ? 'plan' : null,
1334
+ status.documents.tasksStale ? 'tasks' : null
1335
+ ].filter((item) => item !== null);
1336
+ const hasDocumentHashes = Boolean(status.documents.specHash
1337
+ || status.documents.planHash
1338
+ || status.documents.tasksHash
1339
+ || status.documents.planBasedOnSpecHash
1340
+ || status.documents.tasksBasedOnPlanHash);
1341
+ lines.push('decision');
1342
+ lines.push(`- workflow_status=${status.workflowStatus}`);
1343
+ lines.push(`- context raw_branch=${status.context.rawBranch} partition=${status.context.partition} source=${status.context.branchSource} spec_dir=${status.context.specDir}`);
1344
+ lines.push(`- git current_branch=${status.context.currentGitBranch ?? 'none'} working_tree_matched=${status.context.workingTreeMatched ?? 'unknown'}`);
1345
+ lines.push(`- documents spec=${status.documents.specExists} plan=${status.documents.planExists} tasks=${status.documents.tasksExists} stale=${staleDocuments.join(',') || 'none'}`);
1346
+ if (hasDocumentHashes) {
1347
+ lines.push(`- document_hashes spec=${status.documents.specHash ?? 'none'} plan=${status.documents.planHash ?? 'none'} tasks=${status.documents.tasksHash ?? 'none'} plan_based_on_spec=${status.documents.planBasedOnSpecHash ?? 'none'} tasks_based_on_plan=${status.documents.tasksBasedOnPlanHash ?? 'none'}`);
1348
+ }
1349
+ lines.push(`- tasks total=${status.tasks.total} pending=${status.tasks.pending} in_progress=${status.tasks.inProgress} completed=${status.tasks.completed} blocked=${status.tasks.blocked} deferred=${status.tasks.deferred} unknown=${status.tasks.unknown} gaps=${status.tasks.gaps}`);
1350
+ if (status.latestRun) {
1351
+ lines.push(`- latest_run ${status.latestRun.runId} status=${status.latestRun.status} phase=${status.latestRun.phase ?? 'n/a'} task=${status.latestRun.currentTask ?? 'n/a'} validation=${status.latestRun.validationStatus} sync_back=${status.latestRun.syncBackStatus}`);
1352
+ if (status.latestRunEvidence) {
1353
+ lines.push(`- latest_run_evidence route_preflight=${status.latestRunEvidence.routePreflight} agent_executions=${status.latestRunEvidence.agentExecutions} team_sessions=${status.latestRunEvidence.teamSessions} worker_runtimes=${status.latestRunEvidence.workerRuntimes} stale_worker_runtimes=${status.latestRunEvidence.staleWorkerRuntimes} artifact_ingestions=${status.latestRunEvidence.artifactIngestions}`);
1354
+ if (status.latestRunEvidence.tasksChangedAfterRun && status.latestRun.syncBackStatus !== 'applied') {
1355
+ lines.push('- latest_run_evidence may be stale: tasks.md changed after the latest run; inspect the task or rerun before relying on this run.');
1356
+ }
1357
+ else if (status.latestRunEvidence.tasksChangedAfterRun) {
1358
+ lines.push('- latest_run_evidence tasks.md changed after sync-back apply; task completion state is already recorded.');
1359
+ }
866
1360
  }
867
1361
  }
1362
+ else {
1363
+ lines.push('- latest_run none');
1364
+ }
1365
+ lines.push('evidence');
1366
+ lines.push(`- branch documents loaded from ${status.context.specDir}`);
1367
+ lines.push(status.gitRoot
1368
+ ? `- git repository detected at ${status.gitRoot}; doctor and run-index checks can use Git repository context`
1369
+ : '- doctor and run-index checks expect Git repository context; run git init first in fresh temporary projects');
1370
+ renderDocumentGaps(lines, status.gaps);
868
1371
  lines.push(`next ${status.recommendedNextCommand}`);
869
1372
  return lines.join('\n');
870
1373
  }
@@ -892,13 +1395,7 @@ function renderLocalRunIndexInspection(inspection) {
892
1395
  if (inspection.index) {
893
1396
  lines.push(`contract=${inspection.index.contract} runs=${inspection.index.runs.length} tasks=${inspection.index.tasks.length} delegations=${inspection.index.delegations.length} artifacts=${inspection.index.artifacts.length} waves=${inspection.index.waves.length}`);
894
1397
  }
895
- if (inspection.issues.length > 0) {
896
- lines.push('issues');
897
- for (const issue of inspection.issues) {
898
- lines.push(`- ${issue.field}: ${issue.message}`);
899
- lines.push(` recommendation: ${issue.recommendation}`);
900
- }
901
- }
1398
+ renderIssues(lines, inspection.issues);
902
1399
  return lines.join('\n');
903
1400
  }
904
1401
  function renderGovernancePolicy(policy) {
@@ -919,13 +1416,7 @@ function renderGovernancePolicyDecision(decision) {
919
1416
  for (const reason of decision.reasons) {
920
1417
  lines.push(`- ${reason}`);
921
1418
  }
922
- if (decision.issues.length > 0) {
923
- lines.push('issues');
924
- for (const issue of decision.issues) {
925
- lines.push(`- ${issue.field}: ${issue.message}`);
926
- lines.push(` recommendation: ${issue.recommendation}`);
927
- }
928
- }
1419
+ renderIssues(lines, decision.issues);
929
1420
  return lines.join('\n');
930
1421
  }
931
1422
  function renderRunInspection(inspection) {
@@ -933,6 +1424,7 @@ function renderRunInspection(inspection) {
933
1424
  lines.push(`status=${inspection.summary.status} phase=${inspection.summary.phase ?? 'n/a'} task=${inspection.summary.currentTask ?? 'n/a'} updated=${inspection.summary.updatedAt}`);
934
1425
  lines.push(`validation=${inspection.validation.status} evidence=${inspection.validation.evidence.join(',') || 'none'}`);
935
1426
  lines.push(`sync_back=${inspection.syncBack.status} proposal=${inspection.syncBack.proposalPath ?? 'none'}`);
1427
+ lines.push(`task_run_evidence=${inspection.taskRunEvidence.version} gaps=${inspection.taskRunEvidence.gaps.length} sync_back=${inspection.taskRunEvidence.syncBackProposal ?? 'none'}`);
936
1428
  lines.push(`artifacts=${inspection.artifacts.length}`);
937
1429
  for (const artifact of inspection.artifacts) {
938
1430
  lines.push(`- ${artifact.path} kind=${artifact.kind} task=${artifact.task ?? 'n/a'} agent=${artifact.agent ?? 'n/a'}`);
@@ -941,6 +1433,18 @@ function renderRunInspection(inspection) {
941
1433
  for (const ingestion of inspection.artifactIngestions) {
942
1434
  lines.push(`- ${ingestion.delegationId} ${ingestion.status} artifact=${ingestion.artifactPath} result=${ingestion.resultStatus ?? 'n/a'} delegation=${ingestion.delegationStatus ?? 'n/a'}`);
943
1435
  }
1436
+ lines.push(`agent_executions=${inspection.agentExecutions.length}`);
1437
+ for (const execution of inspection.agentExecutions) {
1438
+ lines.push(`- ${execution.executionId} profile=${execution.profile} status=${execution.status} task=${execution.taskId} artifacts=${execution.artifacts.join(',') || 'none'}`);
1439
+ }
1440
+ lines.push(`team_sessions=${inspection.teamSessions.length}`);
1441
+ for (const session of inspection.teamSessions) {
1442
+ lines.push(`- ${session.teamId} status=${session.status} mode=${session.teamMode.mode} activation=${session.teamMode.activation} cost=${session.teamMode.costClass} chief=${session.chiefProfile} members=${session.memberProfiles.join(',') || 'none'} artifacts=${session.artifacts.join(',') || 'none'}`);
1443
+ }
1444
+ lines.push(`worker_runtimes=${inspection.workerRuntimes.length}`);
1445
+ for (const runtime of inspection.workerRuntimes) {
1446
+ lines.push(`- ${runtime.runtimeId} status=${runtime.status} task=${runtime.taskId} agent=${runtime.agent} delegation=${runtime.delegationId} lease_expires=${runtime.leaseExpiresAt}`);
1447
+ }
944
1448
  lines.push(`events=${inspection.eventCount}`);
945
1449
  for (const event of inspection.recentEvents) {
946
1450
  lines.push(`- ${event.time} ${event.event}${event.summary ? `: ${event.summary}` : ''}`);
@@ -958,12 +1462,7 @@ function renderSyncBackInspection(inspection) {
958
1462
  lines.push(`- ${reason}`);
959
1463
  }
960
1464
  }
961
- if (inspection.gaps.length > 0) {
962
- lines.push('gaps');
963
- for (const gap of inspection.gaps) {
964
- lines.push(`- [${gap.severity}] ${gap.type} ${gap.taskId ?? 'document'} ${gap.field}: ${gap.message}`);
965
- }
966
- }
1465
+ renderDocumentGaps(lines, inspection.gaps);
967
1466
  lines.push(`apply_policy=${inspection.applyPolicy.mode} approval_required=${inspection.applyPolicy.requiresApproval}`);
968
1467
  for (const reason of inspection.applyPolicy.reasons) {
969
1468
  lines.push(`- policy: ${reason}`);
@@ -981,6 +1480,412 @@ function renderSyncBackApplyResult(result) {
981
1480
  lines.push(`sync_back=${result.inspection.status}`);
982
1481
  return lines.join('\n');
983
1482
  }
1483
+ function renderWorkflowGateList(workflows) {
1484
+ const lines = ['SDD workflow gates'];
1485
+ for (const workflow of workflows) {
1486
+ lines.push(`- ${workflow.id} command=${workflow.command} agents=${workflow.allowedAgents.join(',') || 'none'}`);
1487
+ }
1488
+ return lines.join('\n');
1489
+ }
1490
+ function renderWorkflowGateInspect(workflow) {
1491
+ const lines = [`Workflow gate ${workflow.id}`];
1492
+ lines.push(`version=${workflow.version}`);
1493
+ lines.push(`command=${workflow.command}`);
1494
+ lines.push(`agents=${workflow.allowedAgents.join(',') || 'none'}`);
1495
+ lines.push('required_inputs');
1496
+ for (const input of workflow.requiredInputs) {
1497
+ lines.push(`- ${input}`);
1498
+ }
1499
+ lines.push('required_artifacts');
1500
+ for (const artifact of workflow.requiredArtifacts) {
1501
+ lines.push(`- ${artifact}`);
1502
+ }
1503
+ lines.push('gate_conditions');
1504
+ for (const condition of workflow.gateConditions) {
1505
+ lines.push(`- ${condition}`);
1506
+ }
1507
+ lines.push(`gap_closure=${workflow.gapClosureBehavior}`);
1508
+ lines.push(`next=${workflow.nextAction}`);
1509
+ return lines.join('\n');
1510
+ }
1511
+ function renderWorkflowGateValidation(result) {
1512
+ const lines = ['SDD workflow gate validation'];
1513
+ lines.push(`valid=${result.valid}`);
1514
+ lines.push(`workflows=${result.workflows.length}`);
1515
+ lines.push('issues');
1516
+ if (result.issues.length === 0) {
1517
+ lines.push('- none');
1518
+ }
1519
+ else {
1520
+ for (const issue of result.issues) {
1521
+ lines.push(`- ${issue.field}: ${issue.message}`);
1522
+ }
1523
+ }
1524
+ return lines.join('\n');
1525
+ }
1526
+ function renderAgentRegistryList(agents) {
1527
+ const lines = ['SDD agent registry'];
1528
+ for (const agent of agents) {
1529
+ lines.push(`- ${agent.id} stages=${agent.allowedStages.join(',')} autonomy=${agent.autonomyCeiling}`);
1530
+ }
1531
+ return lines.join('\n');
1532
+ }
1533
+ function renderAgentRegistryInspect(agent) {
1534
+ const lines = [`Agent ${agent.id}`];
1535
+ lines.push(`version=${agent.version}`);
1536
+ lines.push(`role=${agent.role}`);
1537
+ lines.push(`allowed_stages=${agent.allowedStages.join(',')}`);
1538
+ lines.push(`autonomy_ceiling=${agent.autonomyCeiling}`);
1539
+ lines.push(`required_artifact=${agent.requiredArtifact}`);
1540
+ lines.push(`verification=${agent.verificationExpectation}`);
1541
+ lines.push('capabilities');
1542
+ for (const capability of agent.capabilities) {
1543
+ lines.push(`- ${capability}`);
1544
+ }
1545
+ lines.push('read_boundary');
1546
+ for (const item of agent.readBoundary) {
1547
+ lines.push(`- ${item}`);
1548
+ }
1549
+ lines.push('write_boundary');
1550
+ for (const item of agent.writeBoundary) {
1551
+ lines.push(`- ${item}`);
1552
+ }
1553
+ lines.push('tool_allowlist');
1554
+ for (const tool of agent.toolAllowlist) {
1555
+ lines.push(`- ${tool}`);
1556
+ }
1557
+ lines.push(`stop_condition=${agent.stopCondition}`);
1558
+ return lines.join('\n');
1559
+ }
1560
+ function renderAgentRegistryValidation(result) {
1561
+ const lines = ['SDD agent registry validation'];
1562
+ lines.push(`valid=${result.valid}`);
1563
+ lines.push(`agents=${result.agents.length}`);
1564
+ lines.push('issues');
1565
+ if (result.issues.length === 0) {
1566
+ lines.push('- none');
1567
+ }
1568
+ else {
1569
+ for (const issue of result.issues) {
1570
+ lines.push(`- ${issue.field}: ${issue.message}`);
1571
+ }
1572
+ }
1573
+ return lines.join('\n');
1574
+ }
1575
+ function renderRegistryOriginCounts(sources) {
1576
+ if (!sources || sources.length === 0) {
1577
+ return 'none';
1578
+ }
1579
+ const counts = new Map();
1580
+ for (const source of sources) {
1581
+ counts.set(source.origin, (counts.get(source.origin) ?? 0) + 1);
1582
+ }
1583
+ return [...counts.entries()].map(([origin, count]) => `${origin}:${count}`).join(',');
1584
+ }
1585
+ function idsByOrigin(sources, kind, origin) {
1586
+ const ids = sources?.filter((source) => source.kind === kind && source.origin === origin).map((source) => source.id) ?? [];
1587
+ return ids.join(',') || 'none';
1588
+ }
1589
+ function renderAgentRouterDecision(decision) {
1590
+ const lines = [`Agent router decision ${decision.taskId}`];
1591
+ lines.push(`version=${decision.version}`);
1592
+ lines.push(`branch=${decision.branch} category=${decision.category}`);
1593
+ lines.push(`recommended_profile=${decision.recommendedProfile ?? 'none'} autonomy_ceiling=${decision.autonomyCeiling}`);
1594
+ lines.push(`allowed_profiles=${decision.allowedProfiles.join(',') || 'none'}`);
1595
+ lines.push(`required_capabilities=${decision.requiredCapabilities.join(',') || 'none'}`);
1596
+ lines.push(`source_capability=${decision.sourceCapability ?? 'none'} reuse=${decision.reuseDecision ?? 'none'}`);
1597
+ if (decision.registrySources && decision.registrySources.length > 0) {
1598
+ lines.push(`registry_sources=${decision.registrySources.map((source) => `${source.kind}:${source.id}:${source.origin}`).join(',')}`);
1599
+ }
1600
+ if (decision.resolvedAliases && decision.resolvedAliases.length > 0) {
1601
+ lines.push(`alias_resolutions=${decision.resolvedAliases.map((alias) => `${alias.input}->${alias.resolved}:${alias.source}`).join(',')}`);
1602
+ }
1603
+ if (decision.routingRuleHits && decision.routingRuleHits.length > 0) {
1604
+ lines.push(`routing_rule_hits=${decision.routingRuleHits.join(',')}`);
1605
+ }
1606
+ if (decision.quarantineWarnings && decision.quarantineWarnings.length > 0) {
1607
+ lines.push('quarantine_warnings');
1608
+ for (const warning of decision.quarantineWarnings) {
1609
+ lines.push(`- ${warning}`);
1610
+ }
1611
+ }
1612
+ if (decision.adapterMapping) {
1613
+ lines.push(`adapter_mapping profile=${decision.adapterMapping.profile} host=${decision.adapterMapping.hostAdapter} projection=${decision.adapterMapping.projection}`);
1614
+ }
1615
+ if (decision.toolPermission) {
1616
+ lines.push(`tool_permission profile=${decision.toolPermission.profile} policy=${decision.toolPermission.policy} groups=${decision.toolPermission.toolGroups.join(',')}`);
1617
+ lines.push(`approval=${decision.toolPermission.approvalPolicy}`);
1618
+ }
1619
+ lines.push(`model_policy=${decision.modelPolicy.id} category=${decision.modelPolicy.category}`);
1620
+ lines.push(`team_mode=${decision.teamMode.decision} mode=${decision.teamMode.mode} activation=${decision.teamMode.activation} cost=${decision.teamMode.costClass} cost_route=${decision.teamMode.costRoute} waves=${decision.teamMode.waveRecommendation.join(',') || 'none'}`);
1621
+ lines.push(`team_mode_reason=${decision.teamMode.reason}`);
1622
+ if (decision.teamMode.downgradeReason) {
1623
+ lines.push(`team_mode_downgrade=${decision.teamMode.downgradeReason}`);
1624
+ }
1625
+ lines.push(`trust_policy_enforced=${decision.teamMode.trustPolicyEnforced}`);
1626
+ if (decision.cache) {
1627
+ lines.push(`route_cache=${decision.cache.status} key=${decision.cache.key} authoritative=${decision.cache.authoritative}`);
1628
+ }
1629
+ if (decision.profile && decision.profile.length > 0) {
1630
+ lines.push('profile');
1631
+ for (const span of decision.profile) {
1632
+ lines.push(`- ${span.name}: ${span.durationMs}ms`);
1633
+ }
1634
+ }
1635
+ lines.push(`required_artifacts=${decision.requiredArtifacts.join(',') || 'none'}`);
1636
+ if (decision.blockedReason) {
1637
+ lines.push(`blocked_reason=${decision.blockedReason}`);
1638
+ }
1639
+ lines.push(`next=${decision.nextAction}`);
1640
+ return lines.join('\n');
1641
+ }
1642
+ function renderAgentSkillTeamRuntimeInspection(inspection) {
1643
+ const lines = ['SDD agent/skill/team runtime'];
1644
+ lines.push(`version=${inspection.version}`);
1645
+ lines.push(`profiles=${inspection.profiles.length} skill_capabilities=${inspection.skillCapabilities.length} capability_sources=${inspection.capabilitySources.length}`);
1646
+ lines.push(`registry_origins=${renderRegistryOriginCounts(inspection.registrySources)}`);
1647
+ lines.push(`project_profiles=${idsByOrigin(inspection.registrySources, 'profile', 'project_config')}`);
1648
+ lines.push(`project_capabilities=${idsByOrigin(inspection.registrySources, 'skill_capability', 'project_config')}`);
1649
+ lines.push(`project_sources=${idsByOrigin(inspection.registrySources, 'capability_source', 'project_config')}`);
1650
+ if (inspection.aliases && Object.keys(inspection.aliases).length > 0) {
1651
+ lines.push(`aliases=${Object.entries(inspection.aliases).map(([alias, target]) => `${alias}->${target}`).join(',')}`);
1652
+ }
1653
+ if (inspection.routingRules && inspection.routingRules.length > 0) {
1654
+ lines.push(`routing_rules=${inspection.routingRules.map((rule) => rule.id).join(',')}`);
1655
+ }
1656
+ if (inspection.adapterMappings && inspection.adapterMappings.length > 0) {
1657
+ lines.push(`adapter_mappings=${inspection.adapterMappings.map((mapping) => `${mapping.profile}:${mapping.hostAdapter}`).join(',')}`);
1658
+ }
1659
+ lines.push(`host_adapter=${inspection.hostAdapter.id} host=${inspection.hostAdapter.host}`);
1660
+ lines.push(`team_mode_default=${inspection.teamMode.decision}`);
1661
+ lines.push(`reuse_policy=${inspection.reusePolicy}`);
1662
+ lines.push('profiles');
1663
+ for (const profile of inspection.profiles) {
1664
+ lines.push(`- ${profile.id} stages=${profile.stageScope.join(',')} risk_ceiling=${profile.riskCeiling}`);
1665
+ }
1666
+ lines.push('capabilities');
1667
+ for (const capability of inspection.skillCapabilities) {
1668
+ lines.push(`- ${capability.id} reuse=${capability.reuseDecision} evidence=${capability.evidenceType}`);
1669
+ }
1670
+ return lines.join('\n');
1671
+ }
1672
+ function renderAgentSkillTeamRuntimeValidation(result) {
1673
+ const lines = ['SDD agent/skill/team runtime validation'];
1674
+ lines.push(`valid=${result.valid}`);
1675
+ lines.push(`profiles=${result.inspection.profiles.length}`);
1676
+ lines.push(`capabilities=${result.inspection.skillCapabilities.length}`);
1677
+ lines.push(`registry_origins=${renderRegistryOriginCounts(result.inspection.registrySources)}`);
1678
+ lines.push('issues');
1679
+ if (result.issues.length === 0) {
1680
+ lines.push('- none');
1681
+ }
1682
+ else {
1683
+ for (const issue of result.issues) {
1684
+ lines.push(`- ${issue.field}: ${issue.message}`);
1685
+ }
1686
+ }
1687
+ return lines.join('\n');
1688
+ }
1689
+ function renderSkillCapabilityList(capabilities, registrySources) {
1690
+ const lines = ['SDD skill capabilities'];
1691
+ lines.push(`registry_origins=${renderRegistryOriginCounts(registrySources)}`);
1692
+ for (const capability of capabilities) {
1693
+ const source = registrySources?.find((candidate) => candidate.kind === 'skill_capability' && candidate.id === capability.id);
1694
+ lines.push(`- ${capability.id} domain=${capability.capabilityDomain.join(',')} reuse=${capability.reuseDecision} evidence=${capability.evidenceType} origin=${source?.origin ?? 'unknown'}`);
1695
+ }
1696
+ return lines.join('\n');
1697
+ }
1698
+ function renderSkillCapabilityInspect(capability) {
1699
+ const lines = [`Skill capability ${capability.id}`];
1700
+ lines.push(`version=${capability.version}`);
1701
+ lines.push(`name=${capability.name}`);
1702
+ lines.push(`kind=${capability.kind} source=${capability.source} source_ref=${capability.sourceRef}`);
1703
+ lines.push(`domain=${capability.capabilityDomain.join(',')}`);
1704
+ lines.push(`allowed_stages=${capability.allowedStages.join(',')}`);
1705
+ lines.push(`risk_ceiling=${capability.requiredRiskCeiling}`);
1706
+ lines.push(`evidence_type=${capability.evidenceType}`);
1707
+ lines.push(`reuse_decision=${capability.reuseDecision}`);
1708
+ if (capability.buildExceptionReason) {
1709
+ lines.push(`build_exception=${capability.buildExceptionReason}`);
1710
+ }
1711
+ return lines.join('\n');
1712
+ }
1713
+ function renderCapabilitySourceList(sources, registrySources) {
1714
+ const lines = ['SDD capability sources'];
1715
+ lines.push(`registry_origins=${renderRegistryOriginCounts(registrySources)}`);
1716
+ for (const source of sources) {
1717
+ const registrySource = registrySources?.find((candidate) => candidate.kind === 'capability_source' && candidate.id === source.id);
1718
+ lines.push(`- ${source.id} kind=${source.kind} reuse=${source.reuseDecision} quarantine=${source.quarantineRequired} origin=${registrySource?.origin ?? 'unknown'}`);
1719
+ }
1720
+ return lines.join('\n');
1721
+ }
1722
+ function renderCapabilitySourceInspect(source) {
1723
+ const lines = [`Capability source ${source.id}`];
1724
+ lines.push(`version=${source.version}`);
1725
+ lines.push(`name=${source.name}`);
1726
+ lines.push(`kind=${source.kind} reuse=${source.reuseDecision} quarantine=${source.quarantineRequired}`);
1727
+ lines.push(`source_ref=${source.sourceRef}`);
1728
+ lines.push(`allowed_use=${source.allowedUse}`);
1729
+ lines.push(`attribution=${source.attribution}`);
1730
+ lines.push(`rationale=${source.rationale}`);
1731
+ return lines.join('\n');
1732
+ }
1733
+ function renderExternalAgentPackImportInspection(inspection) {
1734
+ const lines = [`External pack import ${inspection.sourceId}`];
1735
+ lines.push(`version=${inspection.version}`);
1736
+ lines.push(`status=${inspection.status} risk_ceiling=${inspection.riskCeiling}`);
1737
+ lines.push(`allowed_profiles=${inspection.allowedProfiles.join(',') || 'none'}`);
1738
+ lines.push(`mapping=${inspection.mappingResult}`);
1739
+ lines.push(`reason=${inspection.reason}`);
1740
+ lines.push('checks');
1741
+ for (const check of inspection.checks) {
1742
+ lines.push(`- ${check.check} status=${check.status} evidence=${check.evidence}`);
1743
+ }
1744
+ return lines.join('\n');
1745
+ }
1746
+ function renderTeamModePolicy(policy) {
1747
+ const lines = ['SDD team-mode policy'];
1748
+ lines.push(`version=${policy.version}`);
1749
+ lines.push(`enabled=${policy.enabled} decision=${policy.decision} mode=${policy.mode} activation=${policy.activation} cost=${policy.costClass}`);
1750
+ lines.push(`reason=${policy.reason}`);
1751
+ lines.push(`chief=${policy.chiefProfile} members=${policy.memberProfiles.join(',') || 'none'} max_members=${policy.maxMembers}`);
1752
+ lines.push(`require_artifacts=${policy.requireArtifacts}`);
1753
+ lines.push(`waves=${policy.waveRecommendation.join(',') || 'none'}`);
1754
+ if (policy.blockedReason) {
1755
+ lines.push(`blocked_reason=${policy.blockedReason}`);
1756
+ }
1757
+ for (const wave of policy.allowedWaves) {
1758
+ lines.push(`- ${wave.id} kind=${wave.waveKind} members=${wave.memberProfiles.join(',')} merge_gate=${wave.mergeGate}`);
1759
+ }
1760
+ return lines.join('\n');
1761
+ }
1762
+ function renderQueryStatusContract(contract) {
1763
+ const lines = ['SDD query status contract'];
1764
+ lines.push(`version=${contract.version}`);
1765
+ lines.push(`source=${contract.sourceDocument}`);
1766
+ for (const surface of contract.surfaces) {
1767
+ lines.push(`- ${surface.id} command=${surface.command}`);
1768
+ lines.push(` responsibility=${surface.responsibility}`);
1769
+ lines.push(` includes=${surface.includes.join(',')}`);
1770
+ lines.push(` excludes=${surface.excludes.join(',')}`);
1771
+ lines.push(` next=${surface.nextActionRule}`);
1772
+ }
1773
+ return lines.join('\n');
1774
+ }
1775
+ function renderQueryStatusValidation(result) {
1776
+ const lines = ['SDD query status validation'];
1777
+ lines.push(`valid=${result.valid}`);
1778
+ lines.push(`surfaces=${result.surfaces.length}`);
1779
+ lines.push('issues');
1780
+ if (result.issues.length === 0) {
1781
+ lines.push('- none');
1782
+ }
1783
+ else {
1784
+ for (const issue of result.issues) {
1785
+ lines.push(`- ${issue.field}: ${issue.message}`);
1786
+ }
1787
+ }
1788
+ return lines.join('\n');
1789
+ }
1790
+ function renderSkillAgentEvalContract(contract) {
1791
+ const lines = ['SDD skill/agent eval contract'];
1792
+ lines.push(`version=${contract.version}`);
1793
+ lines.push(`source=${contract.sourceReport}`);
1794
+ lines.push(`corpus=${contract.corpus.length}`);
1795
+ lines.push('dimensions');
1796
+ for (const dimension of contract.dimensions) {
1797
+ lines.push(`- ${dimension.id} threshold=${dimension.passThreshold}`);
1798
+ lines.push(` expectation=${dimension.expectation}`);
1799
+ lines.push(` baseline=${dimension.baselineFinding}`);
1800
+ }
1801
+ lines.push('regression_assertions');
1802
+ for (const assertion of contract.regressionAssertions) {
1803
+ lines.push(`- ${assertion}`);
1804
+ }
1805
+ return lines.join('\n');
1806
+ }
1807
+ function renderSkillAgentEvalValidation(result) {
1808
+ const lines = ['SDD skill/agent eval validation'];
1809
+ lines.push(`valid=${result.valid}`);
1810
+ lines.push(`dimensions=${result.contract.dimensions.length}`);
1811
+ lines.push(`corpus=${result.contract.corpus.length}`);
1812
+ lines.push('issues');
1813
+ if (result.issues.length === 0) {
1814
+ lines.push('- none');
1815
+ }
1816
+ else {
1817
+ for (const issue of result.issues) {
1818
+ lines.push(`- ${issue.field}: ${issue.message}`);
1819
+ }
1820
+ }
1821
+ return lines.join('\n');
1822
+ }
1823
+ function renderHarnessLearningContract(contract) {
1824
+ const lines = ['SDD harness learning contract'];
1825
+ lines.push(`version=${contract.version}`);
1826
+ lines.push(`source=${contract.sourceTrial}`);
1827
+ lines.push(`promotion=${contract.promotionRule}`);
1828
+ lines.push('allowed_sinks');
1829
+ for (const sink of contract.allowedSinks) {
1830
+ lines.push(`- ${sink.id}: ${sink.output}`);
1831
+ lines.push(` boundary=${sink.boundary}`);
1832
+ }
1833
+ lines.push('forbidden_outputs');
1834
+ for (const output of contract.forbiddenOutputs) {
1835
+ lines.push(`- ${output}`);
1836
+ }
1837
+ return lines.join('\n');
1838
+ }
1839
+ function renderHarnessLearningValidation(result) {
1840
+ const lines = ['SDD harness learning validation'];
1841
+ lines.push(`valid=${result.valid}`);
1842
+ lines.push(`allowed_sinks=${result.contract.allowedSinks.length}`);
1843
+ lines.push(`forbidden_outputs=${result.contract.forbiddenOutputs.length}`);
1844
+ lines.push('issues');
1845
+ if (result.issues.length === 0) {
1846
+ lines.push('- none');
1847
+ }
1848
+ else {
1849
+ for (const issue of result.issues) {
1850
+ lines.push(`- ${issue.field}: ${issue.message}`);
1851
+ }
1852
+ }
1853
+ return lines.join('\n');
1854
+ }
1855
+ function renderProjectContextPackContract(contract) {
1856
+ const lines = ['SDD project context pack contract'];
1857
+ lines.push(`version=${contract.version}`);
1858
+ lines.push(`entry=${contract.entryPoint}`);
1859
+ lines.push('durable_context');
1860
+ for (const item of contract.durableContext) {
1861
+ lines.push(`- ${item}`);
1862
+ }
1863
+ lines.push('runtime_sources_of_truth');
1864
+ for (const source of contract.runtimeSourcesOfTruth) {
1865
+ lines.push(`- ${source}`);
1866
+ }
1867
+ lines.push('boundaries');
1868
+ for (const boundary of contract.boundaries) {
1869
+ lines.push(`- ${boundary}`);
1870
+ }
1871
+ return lines.join('\n');
1872
+ }
1873
+ function renderProjectContextPackValidation(result) {
1874
+ const lines = ['SDD project context pack validation'];
1875
+ lines.push(`valid=${result.valid}`);
1876
+ lines.push(`entry=${result.contract.entryPoint}`);
1877
+ lines.push(`runtime_sources=${result.contract.runtimeSourcesOfTruth.length}`);
1878
+ lines.push('issues');
1879
+ if (result.issues.length === 0) {
1880
+ lines.push('- none');
1881
+ }
1882
+ else {
1883
+ for (const issue of result.issues) {
1884
+ lines.push(`- ${issue.field}: ${issue.message}`);
1885
+ }
1886
+ }
1887
+ return lines.join('\n');
1888
+ }
984
1889
  function renderCapabilityList(capabilities) {
985
1890
  const lines = ['SDD tool capabilities'];
986
1891
  for (const capability of capabilities) {
@@ -1116,13 +2021,7 @@ function renderArtifactIngestionInspection(inspection) {
1116
2021
  for (const record of inspection.records) {
1117
2022
  lines.push(`- ${record.delegationId} ${record.status} artifact=${record.artifactPath} result=${record.resultStatus ?? 'n/a'} delegation=${record.delegationStatus ?? 'n/a'}`);
1118
2023
  }
1119
- if (inspection.issues.length > 0) {
1120
- lines.push('issues');
1121
- for (const issue of inspection.issues) {
1122
- lines.push(`- ${issue.field}: ${issue.message}`);
1123
- lines.push(` recommendation: ${issue.recommendation}`);
1124
- }
1125
- }
2024
+ renderIssues(lines, inspection.issues);
1126
2025
  return lines.join('\n');
1127
2026
  }
1128
2027
  function renderBackgroundExecutorResult(result) {
@@ -1131,13 +2030,7 @@ function renderBackgroundExecutorResult(result) {
1131
2030
  lines.push(`run=${result.runId} delegation=${result.delegationId ?? 'n/a'} queue=${result.queueItemId ?? 'n/a'} worker=${result.workerAdapterId}`);
1132
2031
  lines.push(`artifact=${result.artifactPath ?? 'pending'}`);
1133
2032
  lines.push(result.message);
1134
- if (result.issues.length > 0) {
1135
- lines.push('issues');
1136
- for (const issue of result.issues) {
1137
- lines.push(`- ${issue.field}: ${issue.message}`);
1138
- lines.push(` recommendation: ${issue.recommendation}`);
1139
- }
1140
- }
2033
+ renderIssues(lines, result.issues);
1141
2034
  return lines.join('\n');
1142
2035
  }
1143
2036
  function renderBackgroundExecutorInspection(inspection) {
@@ -1147,15 +2040,77 @@ function renderBackgroundExecutorInspection(inspection) {
1147
2040
  for (const delegation of inspection.delegations) {
1148
2041
  lines.push(`- ${delegation.delegationId} ${delegation.status} task=${delegation.taskId} agent=${delegation.agent} artifact=${delegation.expectedArtifact}`);
1149
2042
  }
1150
- if (inspection.issues.length > 0) {
1151
- lines.push('issues');
1152
- for (const issue of inspection.issues) {
1153
- lines.push(`- ${issue.field}: ${issue.message}`);
1154
- lines.push(` recommendation: ${issue.recommendation}`);
1155
- }
2043
+ renderIssues(lines, inspection.issues);
2044
+ return lines.join('\n');
2045
+ }
2046
+ function renderResidentWorkerRuntimeClaimResult(result) {
2047
+ const lines = [`Resident worker runtime ${result.status} for ${result.taskId}`];
2048
+ lines.push(`version=${result.version}`);
2049
+ lines.push(`run=${result.runId} runtime=${result.runtimeId ?? 'n/a'} delegation=${result.delegationId ?? 'n/a'} queue=${result.queueItemId ?? 'n/a'} worker=${result.workerAdapterId}`);
2050
+ lines.push(`artifact=${result.expectedArtifact ?? 'pending'} lease_expires=${result.leaseExpiresAt ?? 'n/a'}`);
2051
+ lines.push(result.message);
2052
+ renderIssues(lines, result.issues);
2053
+ if (result.runtimeId && result.leaseExpiresAt) {
2054
+ lines.push(`next sdd worker-runtime heartbeat ${result.runtimeId} --run ${result.runId}`);
2055
+ lines.push(`inspect sdd worker-runtime inspect ${result.runtimeId} --run ${result.runId}`);
2056
+ }
2057
+ return lines.join('\n');
2058
+ }
2059
+ function renderResidentWorkerRuntimeHeartbeatResult(result) {
2060
+ const lines = [`Resident worker runtime ${result.status}: ${result.runtimeId}`];
2061
+ lines.push(`version=${result.version}`);
2062
+ lines.push(`run=${result.runId} lease_expires=${result.leaseExpiresAt ?? 'n/a'}`);
2063
+ lines.push(result.message);
2064
+ renderIssues(lines, result.issues);
2065
+ if (result.runtime) {
2066
+ lines.push(`next ${result.status === 'terminal' ? `sdd background inspect ${result.runId}` : `sdd worker-runtime inspect ${result.runtimeId} --run ${result.runId}`}`);
2067
+ }
2068
+ return lines.join('\n');
2069
+ }
2070
+ function renderResidentWorkerRuntimeList(result) {
2071
+ const lines = [`Resident worker runtimes for ${result.runId}`];
2072
+ lines.push(`version=${result.version}`);
2073
+ lines.push(`runtimes=${result.runtimes.length} active=${result.activeRuntimes} stale=${result.staleRuntimes} terminal=${result.terminalRuntimes} blocked=${result.blockedRuntimes}`);
2074
+ for (const runtime of result.runtimes) {
2075
+ lines.push(`- ${runtime.runtimeId} ${runtime.status} task=${runtime.taskId} agent=${runtime.agent} delegation=${runtime.delegationId} lease_expires=${runtime.leaseExpiresAt}`);
1156
2076
  }
2077
+ renderIssues(lines, result.issues);
2078
+ return lines.join('\n');
2079
+ }
2080
+ function renderResidentWorkerRuntimeInspection(inspection) {
2081
+ const lines = [`Resident worker runtime ${inspection.status}: ${inspection.runtimeId}`];
2082
+ lines.push(`version=${inspection.version}`);
2083
+ lines.push(`run=${inspection.runId} valid=${inspection.valid} lease_expired=${inspection.leaseExpired}`);
2084
+ if (inspection.runtime) {
2085
+ lines.push(`task=${inspection.runtime.taskId} agent=${inspection.runtime.agent} worker=${inspection.runtime.workerAdapterId}`);
2086
+ lines.push(`delegation=${inspection.runtime.delegationId} queue=${inspection.runtime.queueItemId} artifact=${inspection.runtime.expectedArtifact}`);
2087
+ lines.push(`claimed=${inspection.runtime.claimedAt} heartbeat=${inspection.runtime.lastHeartbeatAt ?? 'none'} lease_expires=${inspection.runtime.leaseExpiresAt}`);
2088
+ lines.push(`evidence=${inspection.runtime.evidenceSummary}`);
2089
+ }
2090
+ lines.push(`queue_status=${inspection.queueItem?.status ?? 'missing'} adapter=${inspection.workerAdapter?.id ?? 'missing'}`);
2091
+ lines.push(`next ${inspection.recommendedNextCommand}`);
2092
+ renderIssues(lines, inspection.issues);
1157
2093
  return lines.join('\n');
1158
2094
  }
2095
+ function renderIssues(lines, issues) {
2096
+ if (issues.length === 0) {
2097
+ return;
2098
+ }
2099
+ lines.push('issues');
2100
+ for (const issue of issues) {
2101
+ lines.push(`- ${issue.field}: ${issue.message}`);
2102
+ lines.push(` recommendation: ${issue.recommendation}`);
2103
+ }
2104
+ }
2105
+ function renderDocumentGaps(lines, gaps) {
2106
+ if (gaps.length === 0) {
2107
+ return;
2108
+ }
2109
+ lines.push('gaps');
2110
+ for (const gap of gaps) {
2111
+ lines.push(`- [${gap.severity}] ${gap.type} ${gap.taskId ?? 'document'} ${gap.field}: ${gap.message}`);
2112
+ }
2113
+ }
1159
2114
  function renderWaveExecutorResult(result) {
1160
2115
  const lines = [`Wave executor ${result.status} for ${result.branch}`];
1161
2116
  lines.push(`version=${result.version}`);
@@ -1176,13 +2131,7 @@ function renderWaveExecutorResult(result) {
1176
2131
  lines.push(`- ${gate.taskId}: ${gate.reasons.join(' | ')}`);
1177
2132
  }
1178
2133
  }
1179
- if (result.issues.length > 0) {
1180
- lines.push('issues');
1181
- for (const issue of result.issues) {
1182
- lines.push(`- ${issue.field}: ${issue.message}`);
1183
- lines.push(` recommendation: ${issue.recommendation}`);
1184
- }
1185
- }
2134
+ renderIssues(lines, result.issues);
1186
2135
  return lines.join('\n');
1187
2136
  }
1188
2137
  function renderWaveExecutorInspection(inspection) {
@@ -1192,13 +2141,7 @@ function renderWaveExecutorInspection(inspection) {
1192
2141
  for (const event of inspection.waveEvents) {
1193
2142
  lines.push(`- ${event.event}: ${event.summary ?? ''}`);
1194
2143
  }
1195
- if (inspection.issues.length > 0) {
1196
- lines.push('issues');
1197
- for (const issue of inspection.issues) {
1198
- lines.push(`- ${issue.field}: ${issue.message}`);
1199
- lines.push(` recommendation: ${issue.recommendation}`);
1200
- }
1201
- }
2144
+ renderIssues(lines, inspection.issues);
1202
2145
  return lines.join('\n');
1203
2146
  }
1204
2147
  function renderWorktreeIsolationDecision(decision) {
@@ -1237,24 +2180,19 @@ function renderWorktreeLifecycleInspection(inspection) {
1237
2180
  for (const record of inspection.records) {
1238
2181
  lines.push(`- ${record.worktreeId} ${record.status} task=${record.taskId} path=${record.worktreePath} dirty=${record.dirty}`);
1239
2182
  }
1240
- if (inspection.issues.length > 0) {
1241
- lines.push('issues');
1242
- for (const issue of inspection.issues) {
1243
- lines.push(`- ${issue.field}: ${issue.message}`);
1244
- lines.push(` recommendation: ${issue.recommendation}`);
1245
- }
1246
- }
2183
+ renderIssues(lines, inspection.issues);
1247
2184
  return lines.join('\n');
1248
2185
  }
1249
2186
  function renderTaskGraphPlan(graph) {
1250
2187
  const lines = [`Task graph ${graph.valid ? 'valid' : 'blocked'} for ${graph.branch}`];
1251
2188
  lines.push(`version=${graph.version}`);
2189
+ lines.push(`contract=${graph.contract}`);
1252
2190
  lines.push(`tasks=${graph.summary.tasks} dependencies=${graph.summary.dependencies} file_overlaps=${graph.summary.fileOverlaps}`);
1253
2191
  lines.push(`high_risk_tasks=${graph.summary.highRiskTasks.length > 0 ? graph.summary.highRiskTasks.join(',') : 'none'}`);
1254
2192
  lines.push(`validation=${graph.summary.validationCommands.length > 0 ? graph.summary.validationCommands.join(' | ') : 'none'}`);
1255
2193
  lines.push('nodes');
1256
2194
  for (const node of graph.nodes) {
1257
- lines.push(`- ${node.taskId} status=${node.status} wave=${node.wave ?? 'n/a'} deps=${node.dependsOn.join(',') || 'none'} files=${node.affectedFiles.length}`);
2195
+ lines.push(`- ${node.taskId} status=${node.status} wave=${node.wave ?? 'n/a'} deps=${node.dependsOn.join(',') || 'none'} files=${node.affectedFiles.length} agent_fit=${node.agentFit.join(',') || 'none'} verification=${node.verificationAvailability.join(',') || 'none'} autonomy=${node.autonomy ?? 'n/a'}`);
1258
2196
  }
1259
2197
  if (graph.dependencyEdges.length > 0) {
1260
2198
  lines.push('dependency_edges');
@@ -1309,12 +2247,50 @@ function renderWavePlan(plan) {
1309
2247
  }
1310
2248
  return lines.join('\n');
1311
2249
  }
1312
- function readOption(args, name) {
1313
- const index = args.indexOf(name);
1314
- if (index < 0) {
1315
- return null;
2250
+ function readTeamModeActivation(args, fallback) {
2251
+ if (args.includes('--no-team-mode')) {
2252
+ return 'off';
2253
+ }
2254
+ const inline = args.find((item) => item.startsWith('--team-mode='));
2255
+ const inlineValue = inline?.split('=', 2)[1];
2256
+ if (inlineValue === 'auto' || inlineValue === 'force' || inlineValue === 'off') {
2257
+ return inlineValue;
2258
+ }
2259
+ const index = args.indexOf('--team-mode');
2260
+ if (index >= 0) {
2261
+ const value = args[index + 1];
2262
+ if (value === 'auto' || value === 'force' || value === 'off') {
2263
+ return value;
2264
+ }
2265
+ return 'force';
1316
2266
  }
1317
- return args[index + 1] ?? null;
2267
+ return fallback;
2268
+ }
2269
+ function readBranchContext(args) {
2270
+ const branch = readBranchOption(args);
2271
+ return branch ? { branch, branchSource: 'cli_option' } : {};
2272
+ }
2273
+ function readBranchOption(args) {
2274
+ return readOption(args, '--branch') ?? undefined;
2275
+ }
2276
+ function readOptionalPositionalArgument(args) {
2277
+ const booleanOptions = new Set(['--approved', '--json', '--no-team-mode', '--force', '--check', '--latest-only', '--all-runs', '--scaffold-docs', '--no-scaffold-docs', '--direct-safe', '--external-unknown', '--architecture', '--checkpoint']);
2278
+ for (let index = 0; index < args.length; index += 1) {
2279
+ const item = args[index];
2280
+ if (!item.startsWith('--')) {
2281
+ return item;
2282
+ }
2283
+ if (item.includes('=')) {
2284
+ continue;
2285
+ }
2286
+ if (!booleanOptions.has(item) && args[index + 1] && !args[index + 1].startsWith('--')) {
2287
+ index += 1;
2288
+ }
2289
+ }
2290
+ return undefined;
2291
+ }
2292
+ async function readResolvedBranch(projectRoot, args) {
2293
+ return (await resolveSddContext(projectRoot, readBranchContext(args))).branch;
1318
2294
  }
1319
2295
  function readWaveExecutorStrategy(args, name) {
1320
2296
  const value = readOption(args, name) ?? 'fast-stop';
@@ -1327,15 +2303,6 @@ function readRunStatus(args, name) {
1327
2303
  function readGovernancePolicyOperation(value) {
1328
2304
  return value === 'background_executor' || value === 'wave_executor' || value === 'sync_back_apply' || value === 'destructive_git' || value === 'external_interaction' || value === 'cleanup' ? value : null;
1329
2305
  }
1330
- function readRepeatedOption(args, name) {
1331
- const values = [];
1332
- for (let index = 0; index < args.length; index += 1) {
1333
- if (args[index] === name && args[index + 1]) {
1334
- values.push(args[index + 1]);
1335
- }
1336
- }
1337
- return values;
1338
- }
1339
2306
  function readTaskArtifactOptions(args) {
1340
2307
  const artifacts = {};
1341
2308
  for (let index = 0; index < args.length; index += 1) {
@@ -1376,24 +2343,35 @@ function readAiToolSelection(args, allowNone) {
1376
2343
  }
1377
2344
  throw new Error(`Unsupported --ai value: ${value}`);
1378
2345
  }
1379
- function readLifecycleSignalOptions(args) {
2346
+ async function readLifecycleSignalOptions(args) {
1380
2347
  const directSafe = args.includes('--direct-safe');
1381
2348
  const riskTags = readRepeatedOptions(args, '--risk');
1382
2349
  const contracts = readRepeatedOptions(args, '--contract');
1383
2350
  const permissions = readRepeatedOptions(args, '--permission');
1384
- return {
2351
+ const fromText = readOption(args, '--from-text');
2352
+ const fromFile = readOption(args, '--from-file');
2353
+ if (fromText && fromFile) {
2354
+ return { signals: {}, riskExtraction: null, error: 'Usage: sdd lifecycle decide accepts only one of --from-text or --from-file' };
2355
+ }
2356
+ const riskExtraction = fromText
2357
+ ? extractLifecycleRiskSignalsFromText(fromText, 'from_text')
2358
+ : fromFile
2359
+ ? extractLifecycleRiskSignalsFromText(await readFile(fromFile, 'utf8'), 'from_file')
2360
+ : null;
2361
+ const extracted = riskExtraction?.signals ?? {};
2362
+ const signals = {
1385
2363
  intent_clarity: directSafe ? 'high' : readSignalClarity(args, '--intent') ?? 'medium',
1386
2364
  acceptance_clarity: directSafe ? 'high' : readSignalClarity(args, '--acceptance') ?? 'medium',
1387
2365
  estimated_change_size: directSafe ? 'tiny' : readEstimatedChangeSize(args, '--size') ?? 'small',
1388
2366
  task_count_estimate: Number(readOption(args, '--tasks') ?? (directSafe ? '1' : '1')),
1389
2367
  file_count_estimate: Number(readOption(args, '--files') ?? (directSafe ? '1' : '1')),
1390
2368
  affected_layers: readRepeatedOptions(args, '--layer'),
1391
- affected_contracts: contracts,
2369
+ affected_contracts: uniqueCliStrings([...contracts, ...(extracted.affected_contracts ?? [])]),
1392
2370
  dependency_fanout: readDependencyFanout(args, '--fanout') ?? 'local',
1393
- impact_confidence: directSafe ? 'high' : readImpactConfidence(args, '--impact-confidence') ?? 'medium',
1394
- risk_tags: riskTags,
1395
- reversibility: directSafe ? 'reversible' : readReversibility(args, '--reversibility') ?? 'unknown',
1396
- validation_clarity: directSafe ? 'clear' : readValidationClarity(args, '--validation') ?? 'partial',
2371
+ impact_confidence: directSafe ? 'high' : extracted.impact_confidence ?? readImpactConfidence(args, '--impact-confidence') ?? 'medium',
2372
+ risk_tags: uniqueCliStrings([...riskTags, ...(extracted.risk_tags ?? [])]),
2373
+ reversibility: directSafe ? 'reversible' : extracted.reversibility ?? readReversibility(args, '--reversibility') ?? 'unknown',
2374
+ validation_clarity: directSafe ? 'clear' : extracted.validation_clarity ?? readValidationClarity(args, '--validation') ?? 'partial',
1397
2375
  validation_available: directSafe || args.includes('--validation-available'),
1398
2376
  validation_cost: directSafe ? 'cheap' : readValidationCost(args, '--validation-cost') ?? 'unknown',
1399
2377
  policy_hits: readRepeatedOptions(args, '--policy'),
@@ -1405,21 +2383,44 @@ function readLifecycleSignalOptions(args) {
1405
2383
  orchestration_uncertainty: directSafe ? 'low' : readOrchestrationUncertainty(args, '--orchestration') ?? 'medium',
1406
2384
  human_checkpoint_required: args.includes('--checkpoint'),
1407
2385
  approval_reason: readRepeatedOptions(args, '--approval-reason'),
1408
- source_artifacts: readRepeatedOptions(args, '--source-artifact'),
2386
+ source_artifacts: uniqueCliStrings([...readRepeatedOptions(args, '--source-artifact'), ...(fromFile ? [fromFile] : [])]),
1409
2387
  can_scout_impact: !args.includes('--cannot-scout-impact'),
1410
- architecture_decision_required: args.includes('--architecture'),
1411
- external_unknown: args.includes('--external-unknown')
2388
+ architecture_decision_required: args.includes('--architecture') || Boolean(extracted.architecture_decision_required),
2389
+ external_unknown: args.includes('--external-unknown') || Boolean(extracted.external_unknown)
1412
2390
  };
2391
+ return { signals, riskExtraction };
1413
2392
  }
1414
- function readRepeatedOptions(args, name) {
1415
- const values = [];
1416
- for (let index = 0; index < args.length; index += 1) {
1417
- if (args[index] === name && args[index + 1]) {
1418
- values.push(args[index + 1]);
1419
- index += 1;
2393
+ function uniqueCliStrings(values) {
2394
+ return Array.from(new Set(values.filter((value) => value.length > 0)));
2395
+ }
2396
+ function renderLifecycleRiskExtraction(extraction) {
2397
+ if (!extraction) {
2398
+ return '';
2399
+ }
2400
+ const lines = [
2401
+ 'Lifecycle Risk Gate',
2402
+ 'changed',
2403
+ '- deterministic risk signals extracted',
2404
+ 'decision',
2405
+ `- source=${extraction.source}`,
2406
+ `- risk_tags=${extraction.riskTags.join(',') || 'none'}`,
2407
+ `- affected_contracts=${extraction.affectedContracts.join(',') || 'none'}`,
2408
+ `- external_unknown=${extraction.externalUnknown}`,
2409
+ 'evidence'
2410
+ ];
2411
+ if (extraction.evidence.length === 0) {
2412
+ lines.push('- none');
2413
+ }
2414
+ else {
2415
+ for (const item of extraction.evidence) {
2416
+ lines.push(`- ${item.category}: ${item.matched} -> ${item.riskTag}`);
1420
2417
  }
1421
2418
  }
1422
- return values;
2419
+ lines.push('gaps');
2420
+ lines.push('- none');
2421
+ lines.push('next');
2422
+ lines.push('- Evaluate extracted signals with lifecycle decision gate.');
2423
+ return `${lines.join('\n')}\n`;
1423
2424
  }
1424
2425
  function readSignalClarity(args, name) {
1425
2426
  const value = readOption(args, name);
@@ -1460,14 +2461,36 @@ id: T1
1460
2461
  status: pending
1461
2462
  wave: 1
1462
2463
  depends_on: []
2464
+ acceptance_refs:
2465
+ - AC-1
2466
+ plan_refs:
2467
+ - "§4 Target Design Overview"
1463
2468
  affected_files:
1464
2469
  - path/to/file
1465
2470
  validation:
1466
2471
  - command string
1467
- risk: []
2472
+ risk:
2473
+ - state-machine
2474
+ agent_fit:
2475
+ - scout
2476
+ - implementer
2477
+ - reviewer
2478
+ - validator
2479
+ verification_availability:
2480
+ - unit:command string
2481
+ - build:command string
2482
+ autonomy: full_sdd_with_checkpoint
2483
+ allowed_agents:
2484
+ - scout
2485
+ - implementer
2486
+ - reviewer
2487
+ - validator
2488
+ required_artifacts:
2489
+ - artifacts/review-T1.md
2490
+ - artifacts/validation-T1.md
1468
2491
  \`\`\`
1469
2492
 
1470
- Companion sections such as #### Boundary, #### Acceptance, and #### Implementation Notes must stay outside the fenced sdd-task metadata block. Keep only metadata inside the fence.
2493
+ Put contract metadata inside the fenced block: acceptance_refs, plan_refs, agent_fit, verification_availability, autonomy, allowed_agents, and required_artifacts. Companion sections such as #### Boundary, #### Acceptance, and #### Implementation Notes must stay outside the fenced sdd-task metadata block.
1471
2494
 
1472
2495
  #### Boundary
1473
2496