sdd-agent-platform 0.1.0 → 0.2.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,7 @@
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 { readFile } from 'node:fs/promises';
3
+ import { writeArtifact, archiveRun, applyAiToolEntries, applySyncBack, createWorktreeLifecycle, createRun, doctor, 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, 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';
4
+ import { readOption, readPositiveIntegerOption, readRepeatedOption, readRepeatedOptions } from './options.js';
3
5
  async function main(args) {
4
6
  const projectRoot = process.cwd();
5
7
  const [command, subcommand, ...rest] = args;
@@ -9,6 +11,12 @@ async function main(args) {
9
11
  output: helpText()
10
12
  };
11
13
  }
14
+ if (command === 'help') {
15
+ return {
16
+ exitCode: 0,
17
+ output: helpText(subcommand)
18
+ };
19
+ }
12
20
  if (command === '--version' || command === '-v') {
13
21
  return {
14
22
  exitCode: 0,
@@ -19,12 +27,13 @@ async function main(args) {
19
27
  const initArgs = [subcommand, ...rest].filter(Boolean);
20
28
  const force = initArgs.includes('--force');
21
29
  const aiTool = readAiToolSelection(initArgs, true);
22
- const branch = readOption(initArgs, '--branch') ?? 'master';
23
- const scaffoldDocuments = !initArgs.includes('--no-scaffold-docs');
30
+ const branch = readOption(initArgs, '--branch') ?? undefined;
31
+ const scaffoldDocuments = initArgs.includes('--no-scaffold-docs') ? false : initArgs.includes('--scaffold-docs') || branch !== undefined;
24
32
  const result = await initProject(projectRoot, { force, aiTool, branch, scaffoldDocuments });
33
+ const json = wantsJson(initArgs);
25
34
  return {
26
35
  exitCode: 0,
27
- output: JSON.stringify({ command: 'init', ...result }, null, 2)
36
+ output: json ? jsonOutput({ command: 'init', ...result }, initArgs) : renderInitResult(result)
28
37
  };
29
38
  }
30
39
  if (command === 'update') {
@@ -42,10 +51,9 @@ async function main(args) {
42
51
  const instructionArgs = [subcommand, ...rest].filter(Boolean);
43
52
  const action = instructionArgs.find((item) => !item.startsWith('--')) ?? 'overview';
44
53
  const payload = getSddInstructions(action);
45
- const json = instructionArgs.includes('--json');
46
54
  return {
47
55
  exitCode: 0,
48
- output: json ? JSON.stringify(payload, null, 2) : renderSddInstructions(payload)
56
+ output: renderTextOrJson(instructionArgs, payload, renderSddInstructions)
49
57
  };
50
58
  }
51
59
  if (command === 'doctor') {
@@ -60,18 +68,19 @@ async function main(args) {
60
68
  latestOnly: doctorArgs.includes('--latest-only'),
61
69
  allRuns: doctorArgs.includes('--all-runs')
62
70
  });
71
+ const json = wantsJson(doctorArgs);
63
72
  return {
64
73
  exitCode: report.status === 'FAIL' ? 1 : 0,
65
- output: renderDoctorReport(report)
74
+ output: json ? jsonOutput(report, doctorArgs) : renderDoctorReport(report)
66
75
  };
67
76
  }
68
77
  if (command === 'status') {
69
78
  const statusArgs = [subcommand, ...rest].filter(Boolean);
70
- const result = await getProjectStatus(projectRoot, { branch: readOption(statusArgs, '--branch') ?? 'master' });
71
- const json = statusArgs.includes('--json');
79
+ const result = await getProjectStatus(projectRoot, readBranchContext(statusArgs));
80
+ const json = wantsJson(statusArgs);
72
81
  return {
73
82
  exitCode: result.gaps.some((gap) => gap.severity === 'blocking') ? 1 : 0,
74
- output: json ? JSON.stringify(result, null, 2) : renderProjectStatus(result)
83
+ output: json ? jsonOutput(result, statusArgs) : renderProjectStatus(result)
75
84
  };
76
85
  }
77
86
  if (command === 'run' && subcommand === 'create') {
@@ -105,19 +114,19 @@ async function main(args) {
105
114
  }
106
115
  if (command === 'run' && subcommand === 'index') {
107
116
  const action = rest[0];
108
- const json = rest.includes('--json');
117
+ const json = wantsJson(rest);
109
118
  if (action === 'rebuild') {
110
119
  const index = await rebuildLocalRunIndex(projectRoot);
111
120
  return {
112
121
  exitCode: 0,
113
- output: json ? JSON.stringify(index, null, 2) : renderLocalRunIndex(index)
122
+ output: json ? jsonOutput(index, rest) : renderLocalRunIndex(index)
114
123
  };
115
124
  }
116
125
  if (action === 'inspect') {
117
126
  const inspection = await inspectLocalRunIndex(projectRoot);
118
127
  return {
119
128
  exitCode: inspection.valid ? 0 : 1,
120
- output: json ? JSON.stringify(inspection, null, 2) : renderLocalRunIndexInspection(inspection)
129
+ output: json ? jsonOutput(inspection, rest) : renderLocalRunIndexInspection(inspection)
121
130
  };
122
131
  }
123
132
  if (action === 'query') {
@@ -125,7 +134,7 @@ async function main(args) {
125
134
  if (readOption(rest, '--status') && !status) {
126
135
  return {
127
136
  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]'
137
+ 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
138
  };
130
139
  }
131
140
  const index = await queryLocalRunIndex(projectRoot, {
@@ -136,7 +145,7 @@ async function main(args) {
136
145
  });
137
146
  return {
138
147
  exitCode: 0,
139
- output: json ? JSON.stringify(index, null, 2) : renderLocalRunIndex(index)
148
+ output: json ? jsonOutput(index, rest) : renderLocalRunIndex(index)
140
149
  };
141
150
  }
142
151
  return {
@@ -153,10 +162,10 @@ async function main(args) {
153
162
  };
154
163
  }
155
164
  const result = await inspectRun(projectRoot, runId);
156
- const json = rest.includes('--json');
165
+ const json = wantsJson(rest);
157
166
  return {
158
167
  exitCode: 0,
159
- output: json ? JSON.stringify(result, null, 2) : renderRunInspection(result)
168
+ output: json ? jsonOutput(result, rest) : renderRunInspection(result)
160
169
  };
161
170
  }
162
171
  if (command === 'run' && subcommand === 'archive') {
@@ -174,42 +183,44 @@ async function main(args) {
174
183
  };
175
184
  }
176
185
  if (command === 'sync-back' && subcommand === 'inspect') {
177
- const runId = rest[0];
178
- if (!runId) {
186
+ const runId = readOptionalPositionalArgument(rest);
187
+ const taskId = readOption(rest, '--task') ?? undefined;
188
+ if (!runId && !taskId) {
179
189
  return {
180
190
  exitCode: 2,
181
- error: 'Usage: sdd sync-back inspect <run_id> [--branch <branch>] [--task <task_id>] [--json]'
191
+ error: 'Usage: sdd sync-back inspect [<run_id>] [--branch <branch>] --task <task_id> [--json|--compact-json]'
182
192
  };
183
193
  }
184
194
  const result = await inspectSyncBack(projectRoot, {
185
195
  runId,
186
- branch: readOption(rest, '--branch') ?? 'master',
187
- taskId: readOption(rest, '--task') ?? undefined
196
+ branch: readBranchOption(rest),
197
+ taskId
188
198
  });
189
- const json = rest.includes('--json');
199
+ const json = wantsJson(rest);
190
200
  return {
191
201
  exitCode: result.status === 'blocked' ? 1 : 0,
192
- output: json ? JSON.stringify(result, null, 2) : renderSyncBackInspection(result)
202
+ output: json ? jsonOutput(result, rest) : renderSyncBackInspection(result)
193
203
  };
194
204
  }
195
205
  if (command === 'sync-back' && subcommand === 'apply') {
196
- const runId = rest[0];
197
- if (!runId) {
206
+ const runId = readOptionalPositionalArgument(rest);
207
+ const taskId = readOption(rest, '--task') ?? undefined;
208
+ if (!runId && !taskId) {
198
209
  return {
199
210
  exitCode: 2,
200
- error: 'Usage: sdd sync-back apply <run_id> [--branch <branch>] [--task <task_id>] [--approved] [--json]'
211
+ error: 'Usage: sdd sync-back apply [<run_id>] [--branch <branch>] --task <task_id> [--approved] [--json|--compact-json]'
201
212
  };
202
213
  }
203
214
  const result = await applySyncBack(projectRoot, {
204
215
  runId,
205
- branch: readOption(rest, '--branch') ?? 'master',
206
- taskId: readOption(rest, '--task') ?? undefined,
216
+ branch: readBranchOption(rest),
217
+ taskId,
207
218
  approved: rest.includes('--approved')
208
219
  });
209
- const json = rest.includes('--json');
220
+ const json = wantsJson(rest);
210
221
  return {
211
222
  exitCode: 0,
212
- output: json ? JSON.stringify(result, null, 2) : renderSyncBackApplyResult(result)
223
+ output: json ? jsonOutput(result, rest) : renderSyncBackApplyResult(result)
213
224
  };
214
225
  }
215
226
  if (command === 'tasks' && subcommand === 'format') {
@@ -219,7 +230,7 @@ async function main(args) {
219
230
  };
220
231
  }
221
232
  if (command === 'tasks' && subcommand === 'list') {
222
- const model = await parseSddBranch(projectRoot, readOption(rest, '--branch') ?? 'master');
233
+ const model = await parseSddBranch(projectRoot, await readResolvedBranch(projectRoot, rest));
223
234
  return {
224
235
  exitCode: model.gaps.some((gap) => gap.severity === 'blocking') ? 1 : 0,
225
236
  output: renderTaskList(model)
@@ -230,10 +241,10 @@ async function main(args) {
230
241
  if (!taskId) {
231
242
  return {
232
243
  exitCode: 2,
233
- error: 'Usage: sdd tasks inspect <task_id> [--branch <branch>]'
244
+ error: 'Usage: sdd tasks inspect <task_id> [--branch <branch>] [--json]'
234
245
  };
235
246
  }
236
- const model = await parseSddBranch(projectRoot, readOption(rest, '--branch') ?? 'master');
247
+ const model = await parseSddBranch(projectRoot, await readResolvedBranch(projectRoot, rest));
237
248
  const result = inspectSddTask(model, taskId);
238
249
  if (!result.task && result.gaps.length === 0) {
239
250
  return {
@@ -241,20 +252,46 @@ async function main(args) {
241
252
  error: `Task not found: ${taskId}`
242
253
  };
243
254
  }
255
+ const json = wantsJson(rest);
244
256
  return {
245
257
  exitCode: result.task === null || result.gaps.some((gap) => gap.severity === 'blocking') ? 1 : 0,
246
- output: renderTaskInspect(result.task, result.gaps)
258
+ output: json ? jsonOutput(result, rest) : renderTaskInspect(result.task, result.gaps)
259
+ };
260
+ }
261
+ if (command === 'tasks' && subcommand === 'route') {
262
+ const taskId = rest.find((item) => !item.startsWith('--'));
263
+ if (!taskId) {
264
+ return {
265
+ exitCode: 2,
266
+ error: 'Usage: sdd tasks route <task_id> [--branch <branch>] [--team-mode [auto|force|off]] [--no-team-mode] [--json]'
267
+ };
268
+ }
269
+ const decision = await routeSddTask(projectRoot, {
270
+ taskId,
271
+ branch: readBranchOption(rest),
272
+ teamModeActivation: readTeamModeActivation(rest)
273
+ });
274
+ return {
275
+ exitCode: decision.blockedReason ? 1 : 0,
276
+ output: wantsJson(rest) ? jsonOutput(decision, rest) : renderAgentRouterDecision(decision)
247
277
  };
248
278
  }
249
279
  if (command === 'tasks' && subcommand === 'gaps') {
250
- const model = await parseSddBranch(projectRoot, readOption(rest, '--branch') ?? 'master');
280
+ const model = await parseSddBranch(projectRoot, await readResolvedBranch(projectRoot, rest));
251
281
  return {
252
282
  exitCode: model.gaps.some((gap) => gap.severity === 'blocking') ? 1 : 0,
253
283
  output: renderTaskGapReport(model)
254
284
  };
255
285
  }
256
286
  if (command === 'lifecycle' && subcommand === 'decide') {
257
- const result = evaluateLifecycleDecisionGate(readLifecycleSignalOptions(rest));
287
+ const lifecycleInput = await readLifecycleSignalOptions(rest);
288
+ if (lifecycleInput.error) {
289
+ return {
290
+ exitCode: 2,
291
+ error: lifecycleInput.error
292
+ };
293
+ }
294
+ const result = evaluateLifecycleDecisionGate(lifecycleInput.signals);
258
295
  const runId = readOption(rest, '--run');
259
296
  if (runId) {
260
297
  await recordLifecycleDecision(projectRoot, runId, result.record);
@@ -262,7 +299,9 @@ async function main(args) {
262
299
  const json = rest.includes('--json');
263
300
  return {
264
301
  exitCode: 0,
265
- output: json ? JSON.stringify({ ...result, recordedRunId: runId ?? null }, null, 2) : `${renderLifecycleDecisionGate(result)}${runId ? `\nrecorded_run=${runId}` : ''}`
302
+ output: json
303
+ ? JSON.stringify({ riskExtraction: lifecycleInput.riskExtraction, ...result, recordedRunId: runId ?? null }, null, 2)
304
+ : `${renderLifecycleRiskExtraction(lifecycleInput.riskExtraction)}${renderLifecycleDecisionGate(result)}${runId ? `\nrecorded_run=${runId}` : ''}`
266
305
  };
267
306
  }
268
307
  if (command === 'do' && subcommand === 'task') {
@@ -270,42 +309,45 @@ async function main(args) {
270
309
  if (!taskId) {
271
310
  return {
272
311
  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]'
312
+ 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
313
  };
275
314
  }
276
315
  const result = await runSingleTaskLoop(projectRoot, {
277
316
  taskId,
278
- branch: readOption(rest, '--branch') ?? 'master',
317
+ branch: readBranchOption(rest),
279
318
  runId: readOption(rest, '--run') ?? undefined,
280
319
  implementArtifact: readOption(rest, '--implement-artifact') ?? undefined,
281
320
  reviewArtifact: readOption(rest, '--review-artifact') ?? undefined,
282
321
  debugArtifact: readOption(rest, '--debug-artifact') ?? undefined,
283
- validationArtifact: readOption(rest, '--validation-artifact') ?? undefined
322
+ validationArtifact: readOption(rest, '--validation-artifact') ?? undefined,
323
+ teamModeActivation: readTeamModeActivation(rest)
284
324
  });
325
+ const json = wantsJson(rest);
285
326
  return {
286
327
  exitCode: result.status === 'completed' ? 0 : 1,
287
- output: renderSingleTaskLoopResult(result)
328
+ output: json ? jsonOutput(result, rest) : renderSingleTaskLoopResult(result)
288
329
  };
289
330
  }
290
331
  if (command === 'verify' && subcommand === 'task') {
291
- const taskId = rest.find((item) => !item.startsWith('--'));
332
+ const taskId = readOptionalPositionalArgument(rest);
292
333
  const runId = readOption(rest, '--run');
293
- if (!taskId || !runId) {
334
+ if (!taskId) {
294
335
  return {
295
336
  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]'
337
+ error: 'Usage: sdd verify task <task_id> [--run <run_id>] [--branch <branch>] [--review-artifact artifacts/path.md] [--validation-artifact artifacts/path.md] [--json]'
297
338
  };
298
339
  }
299
340
  const result = await runGoalVerify(projectRoot, {
300
341
  taskId,
301
- runId,
302
- branch: readOption(rest, '--branch') ?? 'master',
342
+ runId: runId ?? undefined,
343
+ branch: readBranchOption(rest),
303
344
  reviewArtifact: readOption(rest, '--review-artifact') ?? undefined,
304
345
  validationArtifact: readOption(rest, '--validation-artifact') ?? undefined
305
346
  });
347
+ const json = wantsJson(rest);
306
348
  return {
307
349
  exitCode: result.status === 'PASS' ? 0 : 1,
308
- output: renderGoalVerifyResult(result)
350
+ output: json ? jsonOutput(result, rest) : renderGoalVerifyResult(result)
309
351
  };
310
352
  }
311
353
  if (command === 'governance' && subcommand === 'inspect') {
@@ -334,6 +376,217 @@ async function main(args) {
334
376
  output: rest.includes('--json') ? JSON.stringify(decision, null, 2) : renderGovernancePolicyDecision(decision)
335
377
  };
336
378
  }
379
+ if (command === 'workflow' && subcommand === 'list') {
380
+ const registry = await listWorkflowGates(projectRoot);
381
+ return {
382
+ exitCode: 0,
383
+ output: rest.includes('--json') ? JSON.stringify(registry, null, 2) : renderWorkflowGateList(registry.workflows)
384
+ };
385
+ }
386
+ if (command === 'workflow' && subcommand === 'inspect') {
387
+ const workflowId = rest.find((item) => !item.startsWith('--'));
388
+ if (!workflowId) {
389
+ return {
390
+ exitCode: 2,
391
+ error: 'Usage: sdd workflow inspect <workflow_id> [--json]'
392
+ };
393
+ }
394
+ const workflow = await inspectWorkflowGate(projectRoot, workflowId);
395
+ if (!workflow) {
396
+ return {
397
+ exitCode: 1,
398
+ error: `Unknown workflow: ${workflowId}`
399
+ };
400
+ }
401
+ return {
402
+ exitCode: 0,
403
+ output: rest.includes('--json') ? JSON.stringify(workflow, null, 2) : renderWorkflowGateInspect(workflow)
404
+ };
405
+ }
406
+ if (command === 'workflow' && subcommand === 'validate') {
407
+ const result = await validateWorkflowGates(projectRoot);
408
+ return {
409
+ exitCode: result.valid ? 0 : 1,
410
+ output: rest.includes('--json') ? JSON.stringify(result, null, 2) : renderWorkflowGateValidation(result)
411
+ };
412
+ }
413
+ if (command === 'agents' && subcommand === 'list') {
414
+ const registry = await listAgentRegistry(projectRoot);
415
+ return {
416
+ exitCode: 0,
417
+ output: rest.includes('--json') ? JSON.stringify(registry, null, 2) : renderAgentRegistryList(registry.agents)
418
+ };
419
+ }
420
+ if (command === 'agents' && subcommand === 'inspect') {
421
+ const agentId = rest.find((item) => !item.startsWith('--'));
422
+ if (!agentId) {
423
+ return {
424
+ exitCode: 2,
425
+ error: 'Usage: sdd agents inspect <agent_id> [--json]'
426
+ };
427
+ }
428
+ const agent = await inspectAgentRegistryEntry(projectRoot, agentId);
429
+ if (!agent) {
430
+ return {
431
+ exitCode: 1,
432
+ error: `Unknown agent: ${agentId}`
433
+ };
434
+ }
435
+ return {
436
+ exitCode: 0,
437
+ output: rest.includes('--json') ? JSON.stringify(agent, null, 2) : renderAgentRegistryInspect(agent)
438
+ };
439
+ }
440
+ if (command === 'agents' && subcommand === 'validate') {
441
+ const result = await validateAgentRegistry(projectRoot);
442
+ return {
443
+ exitCode: result.valid ? 0 : 1,
444
+ output: rest.includes('--json') ? JSON.stringify(result, null, 2) : renderAgentRegistryValidation(result)
445
+ };
446
+ }
447
+ if (command === 'agent-runtime' && subcommand === 'inspect') {
448
+ const inspection = await inspectAgentSkillTeamRuntime(projectRoot);
449
+ return {
450
+ exitCode: 0,
451
+ output: rest.includes('--json') ? JSON.stringify(inspection, null, 2) : renderAgentSkillTeamRuntimeInspection(inspection)
452
+ };
453
+ }
454
+ if (command === 'agent-runtime' && subcommand === 'validate') {
455
+ const result = await validateAgentSkillTeamRuntime(projectRoot);
456
+ return {
457
+ exitCode: result.valid ? 0 : 1,
458
+ output: rest.includes('--json') ? JSON.stringify(result, null, 2) : renderAgentSkillTeamRuntimeValidation(result)
459
+ };
460
+ }
461
+ if (command === 'skill-capabilities' && subcommand === 'list') {
462
+ const registry = await listSkillCapabilities(projectRoot);
463
+ return {
464
+ exitCode: 0,
465
+ output: rest.includes('--json') ? JSON.stringify(registry, null, 2) : renderSkillCapabilityList(registry.capabilities, registry.registrySources)
466
+ };
467
+ }
468
+ if (command === 'skill-capabilities' && subcommand === 'inspect') {
469
+ const capabilityId = rest.find((item) => !item.startsWith('--'));
470
+ if (!capabilityId) {
471
+ return {
472
+ exitCode: 2,
473
+ error: 'Usage: sdd skill-capabilities inspect <capability_id> [--json]'
474
+ };
475
+ }
476
+ const capability = await inspectSkillCapability(projectRoot, capabilityId);
477
+ if (!capability) {
478
+ return { exitCode: 1, error: `Unknown skill capability: ${capabilityId}` };
479
+ }
480
+ return {
481
+ exitCode: 0,
482
+ output: rest.includes('--json') ? JSON.stringify(capability, null, 2) : renderSkillCapabilityInspect(capability)
483
+ };
484
+ }
485
+ if (command === 'capability-sources' && subcommand === 'list') {
486
+ const catalog = await listCapabilitySources(projectRoot);
487
+ return {
488
+ exitCode: 0,
489
+ output: rest.includes('--json') ? JSON.stringify(catalog, null, 2) : renderCapabilitySourceList(catalog.sources, catalog.registrySources)
490
+ };
491
+ }
492
+ if (command === 'capability-sources' && subcommand === 'inspect') {
493
+ const sourceId = rest.find((item) => !item.startsWith('--'));
494
+ if (!sourceId) {
495
+ return {
496
+ exitCode: 2,
497
+ error: 'Usage: sdd capability-sources inspect <source_id> [--json]'
498
+ };
499
+ }
500
+ const source = await inspectCapabilitySource(projectRoot, sourceId);
501
+ if (!source) {
502
+ return { exitCode: 1, error: `Unknown capability source: ${sourceId}` };
503
+ }
504
+ return {
505
+ exitCode: 0,
506
+ output: rest.includes('--json') ? JSON.stringify(source, null, 2) : renderCapabilitySourceInspect(source)
507
+ };
508
+ }
509
+ if (command === 'external-packs' && subcommand === 'inspect') {
510
+ const sourceId = rest.find((item) => !item.startsWith('--'));
511
+ if (!sourceId) {
512
+ return {
513
+ exitCode: 2,
514
+ error: 'Usage: sdd external-packs inspect <source_id> [--json]'
515
+ };
516
+ }
517
+ const inspection = await inspectExternalAgentPackImport(projectRoot, sourceId);
518
+ return {
519
+ exitCode: inspection.status === 'denied' ? 1 : 0,
520
+ output: rest.includes('--json') ? JSON.stringify(inspection, null, 2) : renderExternalAgentPackImportInspection(inspection)
521
+ };
522
+ }
523
+ if (command === 'team-mode' && subcommand === 'inspect') {
524
+ const policy = await inspectTeamModePolicy(projectRoot, {
525
+ taskId: readOption(rest, '--task') ?? undefined,
526
+ branch: readBranchOption(rest),
527
+ teamModeActivation: readTeamModeActivation(rest, rest.includes('--enabled') ? 'force' : undefined)
528
+ });
529
+ return {
530
+ exitCode: policy.decision === 'blocked' ? 1 : 0,
531
+ output: rest.includes('--json') ? JSON.stringify(policy, null, 2) : renderTeamModePolicy(policy)
532
+ };
533
+ }
534
+ if (command === 'query-status' && subcommand === 'inspect') {
535
+ const contract = await inspectQueryStatusContract(projectRoot);
536
+ return {
537
+ exitCode: 0,
538
+ output: rest.includes('--json') ? JSON.stringify(contract, null, 2) : renderQueryStatusContract(contract)
539
+ };
540
+ }
541
+ if (command === 'query-status' && subcommand === 'validate') {
542
+ const result = await validateQueryStatusContract(projectRoot);
543
+ return {
544
+ exitCode: result.valid ? 0 : 1,
545
+ output: rest.includes('--json') ? JSON.stringify(result, null, 2) : renderQueryStatusValidation(result)
546
+ };
547
+ }
548
+ if (command === 'eval' && subcommand === 'inspect') {
549
+ const contract = await inspectSkillAgentEvalContract(projectRoot);
550
+ return {
551
+ exitCode: 0,
552
+ output: rest.includes('--json') ? JSON.stringify(contract, null, 2) : renderSkillAgentEvalContract(contract)
553
+ };
554
+ }
555
+ if (command === 'eval' && subcommand === 'validate') {
556
+ const result = await validateSkillAgentEvalContract(projectRoot);
557
+ return {
558
+ exitCode: result.valid ? 0 : 1,
559
+ output: rest.includes('--json') ? JSON.stringify(result, null, 2) : renderSkillAgentEvalValidation(result)
560
+ };
561
+ }
562
+ if (command === 'learning' && subcommand === 'inspect') {
563
+ const contract = await inspectHarnessLearningContract(projectRoot);
564
+ return {
565
+ exitCode: 0,
566
+ output: rest.includes('--json') ? JSON.stringify(contract, null, 2) : renderHarnessLearningContract(contract)
567
+ };
568
+ }
569
+ if (command === 'learning' && subcommand === 'validate') {
570
+ const result = await validateHarnessLearningContract(projectRoot);
571
+ return {
572
+ exitCode: result.valid ? 0 : 1,
573
+ output: rest.includes('--json') ? JSON.stringify(result, null, 2) : renderHarnessLearningValidation(result)
574
+ };
575
+ }
576
+ if (command === 'context-pack' && subcommand === 'inspect') {
577
+ const contract = await inspectProjectContextPackContract(projectRoot);
578
+ return {
579
+ exitCode: 0,
580
+ output: rest.includes('--json') ? JSON.stringify(contract, null, 2) : renderProjectContextPackContract(contract)
581
+ };
582
+ }
583
+ if (command === 'context-pack' && subcommand === 'validate') {
584
+ const result = await validateProjectContextPackContract(projectRoot);
585
+ return {
586
+ exitCode: result.valid ? 0 : 1,
587
+ output: rest.includes('--json') ? JSON.stringify(result, null, 2) : renderProjectContextPackValidation(result)
588
+ };
589
+ }
337
590
  if (command === 'capabilities' && subcommand === 'list') {
338
591
  const registry = await listToolCapabilities(projectRoot);
339
592
  return {
@@ -432,7 +685,7 @@ async function main(args) {
432
685
  }
433
686
  const decision = await inspectWorktreeIsolation(projectRoot, {
434
687
  taskId,
435
- branch: readOption(rest, '--branch') ?? 'master',
688
+ branch: readBranchOption(rest),
436
689
  capabilityId: readOption(rest, '--capability') ?? undefined,
437
690
  peerTaskIds: readRepeatedOptions(rest, '--peer-task')
438
691
  });
@@ -442,7 +695,7 @@ async function main(args) {
442
695
  };
443
696
  }
444
697
  if (command === 'graph' && subcommand === 'inspect') {
445
- const graph = await inspectTaskGraph(projectRoot, { branch: readOption(rest, '--branch') ?? 'master' });
698
+ const graph = await inspectTaskGraph(projectRoot, { branch: readBranchOption(rest) });
446
699
  return {
447
700
  exitCode: graph.valid ? 0 : 1,
448
701
  output: rest.includes('--json') ? JSON.stringify(graph, null, 2) : renderTaskGraphPlan(graph)
@@ -450,7 +703,7 @@ async function main(args) {
450
703
  }
451
704
  if (command === 'wave' && subcommand === 'inspect') {
452
705
  const wavePlan = await inspectWavePlan(projectRoot, {
453
- branch: readOption(rest, '--branch') ?? 'master',
706
+ branch: readBranchOption(rest),
454
707
  capabilityId: readOption(rest, '--capability') ?? undefined
455
708
  });
456
709
  return {
@@ -467,7 +720,7 @@ async function main(args) {
467
720
  };
468
721
  }
469
722
  const result = await runWaveExecutor(projectRoot, {
470
- branch: readOption(rest, '--branch') ?? 'master',
723
+ branch: readBranchOption(rest),
471
724
  runId: readOption(rest, '--run') ?? undefined,
472
725
  capabilityId: readOption(rest, '--capability') ?? undefined,
473
726
  agent: readOption(rest, '--agent') ?? undefined,
@@ -503,7 +756,7 @@ async function main(args) {
503
756
  };
504
757
  }
505
758
  const result = await runBackgroundExecutor(projectRoot, {
506
- branch: readOption(rest, '--branch') ?? 'master',
759
+ branch: readBranchOption(rest),
507
760
  runId: readOption(rest, '--run') ?? undefined,
508
761
  taskId,
509
762
  agent: readOption(rest, '--agent') ?? undefined,
@@ -530,6 +783,78 @@ async function main(args) {
530
783
  output: rest.includes('--json') ? JSON.stringify(inspection, null, 2) : renderBackgroundExecutorInspection(inspection)
531
784
  };
532
785
  }
786
+ if (command === 'worker-runtime' && subcommand === 'claim') {
787
+ const taskId = rest[0];
788
+ if (!taskId) {
789
+ return {
790
+ exitCode: 2,
791
+ 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]'
792
+ };
793
+ }
794
+ const result = await claimResidentWorkerRuntime(projectRoot, {
795
+ branch: readBranchOption(rest),
796
+ runId: readOption(rest, '--run') ?? undefined,
797
+ taskId,
798
+ runtimeId: readOption(rest, '--runtime') ?? undefined,
799
+ agent: readOption(rest, '--agent') ?? undefined,
800
+ workerAdapterId: readOption(rest, '--worker') ?? undefined,
801
+ delegationId: readOption(rest, '--delegation') ?? undefined,
802
+ leaseSeconds: readPositiveIntegerOption(rest, '--lease-seconds') ?? undefined
803
+ });
804
+ return {
805
+ exitCode: result.status === 'blocked' ? 1 : 0,
806
+ output: rest.includes('--json') ? JSON.stringify(result, null, 2) : renderResidentWorkerRuntimeClaimResult(result)
807
+ };
808
+ }
809
+ if (command === 'worker-runtime' && subcommand === 'heartbeat') {
810
+ const runtimeId = rest[0];
811
+ const runId = readOption(rest, '--run');
812
+ if (!runtimeId || !runId) {
813
+ return {
814
+ exitCode: 2,
815
+ error: 'Usage: sdd worker-runtime heartbeat <runtime_id> --run <run_id> [--lease-seconds <n>] [--json]'
816
+ };
817
+ }
818
+ const result = await heartbeatResidentWorkerRuntime(projectRoot, {
819
+ runId,
820
+ runtimeId,
821
+ leaseSeconds: readPositiveIntegerOption(rest, '--lease-seconds') ?? undefined
822
+ });
823
+ return {
824
+ exitCode: result.status === 'blocked' ? 1 : 0,
825
+ output: rest.includes('--json') ? JSON.stringify(result, null, 2) : renderResidentWorkerRuntimeHeartbeatResult(result)
826
+ };
827
+ }
828
+ if (command === 'worker-runtime' && subcommand === 'status') {
829
+ const positionalRunId = rest[0] && !rest[0].startsWith('--') ? rest[0] : null;
830
+ const runId = readOption(rest, '--run') ?? positionalRunId;
831
+ if (!runId) {
832
+ return {
833
+ exitCode: 2,
834
+ error: 'Usage: sdd worker-runtime status --run <run_id> [--json]'
835
+ };
836
+ }
837
+ const result = await listResidentWorkerRuntimes(projectRoot, { runId });
838
+ return {
839
+ exitCode: result.valid ? 0 : 1,
840
+ output: rest.includes('--json') ? JSON.stringify(result, null, 2) : renderResidentWorkerRuntimeList(result)
841
+ };
842
+ }
843
+ if (command === 'worker-runtime' && subcommand === 'inspect') {
844
+ const runtimeId = rest[0];
845
+ const runId = readOption(rest, '--run');
846
+ if (!runtimeId || !runId) {
847
+ return {
848
+ exitCode: 2,
849
+ error: 'Usage: sdd worker-runtime inspect <runtime_id> --run <run_id> [--json]'
850
+ };
851
+ }
852
+ const result = await inspectResidentWorkerRuntime(projectRoot, { runId, runtimeId });
853
+ return {
854
+ exitCode: result.valid ? 0 : 1,
855
+ output: rest.includes('--json') ? JSON.stringify(result, null, 2) : renderResidentWorkerRuntimeInspection(result)
856
+ };
857
+ }
533
858
  if (command === 'worktree' && subcommand === 'create') {
534
859
  const runId = rest[0];
535
860
  const taskId = rest[1];
@@ -627,18 +952,33 @@ async function main(args) {
627
952
  if (!artifactPath || !taskId || !agent) {
628
953
  return {
629
954
  exitCode: 2,
630
- error: 'Usage: sdd artifact template <artifacts/path.md> --task <task_id> --agent <agent> [--branch <branch>] [--status <status>]'
955
+ error: 'Usage: sdd artifact template <artifacts/path.md> --task <task_id> --agent <agent> [--run <run_id> --write] [--branch <branch>] [--status <status>]'
956
+ };
957
+ }
958
+ const template = await renderSddResultArtifactTemplate(projectRoot, {
959
+ artifactPath,
960
+ taskId,
961
+ agent,
962
+ branch: readBranchOption(rest),
963
+ status: readSddResultStatus(rest, '--status') ?? 'PASS'
964
+ });
965
+ const runId = readOption(rest, '--run');
966
+ if (rest.includes('--write')) {
967
+ if (!runId) {
968
+ return {
969
+ exitCode: 2,
970
+ error: 'Usage: sdd artifact template <artifacts/path.md> --task <task_id> --agent <agent> --run <run_id> --write'
971
+ };
972
+ }
973
+ const written = await writeArtifact(projectRoot, runId, toArtifactRootRelativePath(artifactPath), template);
974
+ return {
975
+ exitCode: 0,
976
+ output: `Artifact template written: ${written.runRelativePath}\nphysical_path=${written.absolutePath}`
631
977
  };
632
978
  }
633
979
  return {
634
980
  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
- })
981
+ output: template
642
982
  };
643
983
  }
644
984
  if (command === 'artifact' && subcommand === 'validate') {
@@ -647,7 +987,7 @@ async function main(args) {
647
987
  if (!runId || !artifactPath) {
648
988
  return {
649
989
  exitCode: 2,
650
- error: 'Usage: sdd artifact validate <run_id> <artifacts/path.md> [--task <task_id>] [--agent <agent>] [--json]'
990
+ error: 'Usage: sdd artifact validate <run_id> <artifacts/path.md> [--task <task_id>] [--agent <agent>] [--json|--compact-json]'
651
991
  };
652
992
  }
653
993
  const expectedTask = readOption(rest, '--task') ?? undefined;
@@ -658,7 +998,7 @@ async function main(args) {
658
998
  });
659
999
  return {
660
1000
  exitCode: report.valid ? 0 : 1,
661
- output: rest.includes('--json') ? JSON.stringify(report, null, 2) : renderArtifactValidationReport(artifactPath, report, expectedTask, expectedAgent)
1001
+ output: renderTextOrJson(rest, report, (value) => renderArtifactValidationReport(artifactPath, value, expectedTask, expectedAgent))
662
1002
  };
663
1003
  }
664
1004
  if (command === 'artifact' && subcommand === 'ingest') {
@@ -696,175 +1036,193 @@ async function main(args) {
696
1036
  error: `Unknown command: ${args.join(' ')}\n\n${helpText()}`
697
1037
  };
698
1038
  }
699
- function helpText() {
1039
+ function helpText(topic) {
1040
+ if (topic === 'advanced') {
1041
+ return advancedHelpText();
1042
+ }
1043
+ if (topic === 'workflow') {
1044
+ return workflowHelpText();
1045
+ }
700
1046
  return `sdd Phase 2 platform CLI
701
1047
 
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
1048
+ Common workflow:
1049
+ sdd init [--force] [--ai <mode>] [--scaffold-docs] [--json]
1050
+ sdd status [--branch <branch>] [--json|--compact-json]
1051
+ sdd tasks inspect <task_id> [--branch <branch>] [--json|--compact-json]
1052
+ sdd tasks route <task_id> [--branch <branch>] [--json|--compact-json]
1053
+ sdd do task <task_id> [options]
1054
+ sdd verify task <task_id> [--branch <branch>] [--run <run_id>] [--json|--compact-json]
1055
+ sdd sync-back inspect [<run_id>] [--task <task_id>] [--branch <branch>] [--json|--compact-json]
1056
+ sdd sync-back apply [<run_id>] [--task <task_id>] [--branch <branch>] [--approved] [--json|--compact-json]
1057
+ sdd doctor [--latest-only] [--all-runs] [--json|--compact-json]
750
1058
 
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
1059
+ Evidence helpers:
1060
+ sdd run create
1061
+ sdd run list [--json]
1062
+ sdd run inspect <run_id> [--json|--compact-json]
1063
+ sdd run index rebuild|inspect|query [options] [--json|--compact-json]
1064
+ sdd artifact template <path> --task <task_id> --agent <agent> [--run <run_id> --write]
1065
+ sdd artifact validate <run_id> <path> [--task <task_id>] [--agent <agent>] [--json|--compact-json]
755
1066
 
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
1067
+ Generated AI entries:
1068
+ sdd update [--check] [--ai <mode>]
1069
+ sdd instructions [action] [--json|--compact-json]
759
1070
 
760
- Doctor options:
761
- --latest-only Inspect only the newest non-archived run evidence
762
- --all-runs Inspect every run, including archived runs
1071
+ More help:
1072
+ sdd help workflow Show core workflow options.
1073
+ sdd help advanced Show platform/agent/runtime commands.
763
1074
 
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
777
-
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
783
-
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
790
-
791
- Instructions actions:
792
- overview | init | doctor | update | spec | plan | tasks | do | verify | run-task | verify-task
793
-
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
1075
+ Notes:
1076
+ /sdd:spec owns workflow partition docs after project init.
1077
+ init --branch is legacy starter-doc scaffolding; prefer sdd status --branch or /sdd:spec --branch for workflow partitions.
1078
+ `;
1079
+ }
1080
+ function workflowHelpText() {
1081
+ return `sdd workflow help
807
1082
 
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
1083
+ Core path:
1084
+ 1. sdd status [--branch <branch>]
1085
+ 2. sdd tasks inspect <task_id> [--branch <branch>]
1086
+ 3. sdd tasks route <task_id> [--branch <branch>]
1087
+ 4. sdd artifact template artifacts/<agent>-<task_id>.md --task <task_id> --agent <agent> --run <run_id> --write
1088
+ 5. sdd do task <task_id> --run <run_id> --implement-artifact <path> --review-artifact <path> --validation-artifact <path>
1089
+ 6. sdd verify task <task_id> [--branch <branch>]
1090
+ 7. sdd sync-back inspect --task <task_id> [--branch <branch>]
1091
+ 8. sdd sync-back apply --task <task_id> [--branch <branch>]
821
1092
 
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
1093
+ JSON:
1094
+ --json prints readable JSON; --compact-json prints one-line JSON for logs and scripts.
1095
+ `;
1096
+ }
1097
+ function advancedHelpText() {
1098
+ return `sdd advanced help
826
1099
 
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
1100
+ Runtime/catalog:
1101
+ sdd agent-runtime inspect|validate [--json]
1102
+ sdd skill-capabilities list|inspect [--json]
1103
+ sdd capability-sources list|inspect [--json]
1104
+ sdd external-packs inspect <source_id> [--json]
1105
+ sdd team-mode inspect [--task <id>] [--team-mode [auto|force|off]] [--no-team-mode] [--json]
831
1106
 
832
- Graph options:
833
- --branch <branch> Read specs/<branch>/tasks.md
1107
+ Harness/platform:
1108
+ sdd workflow list|inspect|validate [--json]
1109
+ sdd agents list|inspect|validate [--json]
1110
+ sdd query-status inspect|validate [--json]
1111
+ sdd eval inspect|validate [--json]
1112
+ sdd learning inspect|validate [--json]
1113
+ sdd context-pack inspect|validate [--json]
1114
+ sdd capabilities list|inspect [--json]
1115
+ sdd governance inspect|evaluate [options]
1116
+ sdd plugins list|inspect [--json]
1117
+ sdd queue list|inspect [options]
1118
+ sdd state-machine inspect [--json]
1119
+ sdd workers list|inspect [--json]
834
1120
 
835
- Wave options:
836
- --branch <branch> Read specs/<branch>/tasks.md
837
- --capability <capability_id> Capability side effect used for isolation decisions
1121
+ Execution/isolation:
1122
+ sdd background run|inspect [options]
1123
+ sdd worker-runtime claim|heartbeat|status|inspect [options]
1124
+ sdd isolation inspect <task_id> [options]
1125
+ sdd graph inspect [--branch <branch>] [--json]
1126
+ sdd wave inspect|run|executor [options]
1127
+ sdd worktree create|inspect|keep|remove [options]
838
1128
 
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
- `;
1129
+ Legacy init partition option:
1130
+ sdd init --branch <branch> creates starter docs for that branch, but normal workflow partitioning belongs to /sdd:spec and sdd status --branch.
1131
+ `;
851
1132
  }
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}`);
1133
+ function wantsJson(args) {
1134
+ return args.includes('--json') || args.includes('--compact-json');
1135
+ }
1136
+ function jsonOutput(value, args) {
1137
+ return JSON.stringify(value, null, args.includes('--compact-json') ? 0 : 2);
1138
+ }
1139
+ function renderTextOrJson(args, value, renderText) {
1140
+ return wantsJson(args) ? jsonOutput(value, args) : renderText(value);
1141
+ }
1142
+ function renderInitResult(result) {
1143
+ const aiEntries = result.aiTools.flatMap((tool) => tool.entries);
1144
+ const aiCounts = new Map();
1145
+ for (const entry of aiEntries) {
1146
+ aiCounts.set(entry.status, (aiCounts.get(entry.status) ?? 0) + 1);
1147
+ }
1148
+ const aiSummary = Array.from(aiCounts.entries()).map(([status, count]) => `${status}=${count}`).join(' ') || 'none';
1149
+ const scaffoldedDocuments = result.documents.documents.filter((document) => document.status !== 'skipped');
1150
+ const lines = ['SDD init', 'changed'];
1151
+ lines.push(`- config ${result.created ? 'created/updated' : 'unchanged'} at ${result.configPath}`);
1152
+ lines.push(`- semantic docs ${scaffoldedDocuments.map((document) => `${document.status}:${document.relativePath}`).join(', ') || 'none'}`);
1153
+ lines.push(`- ai entries ${aiSummary}`);
1154
+ lines.push('decision');
1155
+ if (scaffoldedDocuments.length > 0) {
1156
+ lines.push(`- legacy_scaffold_branch=${result.documents.branch}`);
1157
+ lines.push(`- legacy_spec_dir=${result.documents.root}`);
1158
+ }
1159
+ lines.push('- sdd init is project-level setup; /sdd:spec is the workflow partition/spec entry');
1160
+ lines.push('evidence');
1161
+ for (const document of scaffoldedDocuments) {
1162
+ lines.push(`- [${document.status}] ${document.relativePath}: ${document.message}`);
1163
+ }
1164
+ if (aiEntries.length === 0) {
1165
+ lines.push('- ai entries skipped');
858
1166
  }
859
1167
  else {
860
- lines.push('latest_run none');
1168
+ lines.push(`- ${aiEntries.length} managed AI entry projection(s) checked/applied`);
861
1169
  }
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}`);
1170
+ lines.push('- doctor checks git repository health; run git init first in fresh temporary projects before relying on doctor/run-index checks');
1171
+ lines.push('gaps');
1172
+ const driftEntries = aiEntries.filter((entry) => entry.status === 'drifted' || entry.status === 'user-modified' || entry.status === 'foreign' || entry.status === 'conflict');
1173
+ if (driftEntries.length === 0) {
1174
+ lines.push('- none');
1175
+ }
1176
+ else {
1177
+ for (const entry of driftEntries) {
1178
+ lines.push(`- [${entry.status}] ${entry.relativePath}: ${entry.action ?? entry.message}`);
1179
+ }
1180
+ }
1181
+ lines.push('next');
1182
+ lines.push('- /sdd:spec');
1183
+ return lines.join('\n');
1184
+ }
1185
+ function renderProjectStatus(status) {
1186
+ const lines = [`SDD status for ${status.branch}`];
1187
+ const staleDocuments = [
1188
+ status.documents.planStale ? 'plan' : null,
1189
+ status.documents.tasksStale ? 'tasks' : null
1190
+ ].filter((item) => item !== null);
1191
+ const hasDocumentHashes = Boolean(status.documents.specHash
1192
+ || status.documents.planHash
1193
+ || status.documents.tasksHash
1194
+ || status.documents.planBasedOnSpecHash
1195
+ || status.documents.tasksBasedOnPlanHash);
1196
+ lines.push('decision');
1197
+ lines.push(`- workflow_status=${status.workflowStatus}`);
1198
+ lines.push(`- context raw_branch=${status.context.rawBranch} partition=${status.context.partition} source=${status.context.branchSource} spec_dir=${status.context.specDir}`);
1199
+ lines.push(`- git current_branch=${status.context.currentGitBranch ?? 'none'} working_tree_matched=${status.context.workingTreeMatched ?? 'unknown'}`);
1200
+ lines.push(`- documents spec=${status.documents.specExists} plan=${status.documents.planExists} tasks=${status.documents.tasksExists} stale=${staleDocuments.join(',') || 'none'}`);
1201
+ if (hasDocumentHashes) {
1202
+ 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'}`);
1203
+ }
1204
+ 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}`);
1205
+ if (status.latestRun) {
1206
+ 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}`);
1207
+ if (status.latestRunEvidence) {
1208
+ 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}`);
1209
+ if (status.latestRunEvidence.tasksChangedAfterRun && status.latestRun.syncBackStatus !== 'applied') {
1210
+ 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.');
1211
+ }
1212
+ else if (status.latestRunEvidence.tasksChangedAfterRun) {
1213
+ lines.push('- latest_run_evidence tasks.md changed after sync-back apply; task completion state is already recorded.');
1214
+ }
866
1215
  }
867
1216
  }
1217
+ else {
1218
+ lines.push('- latest_run none');
1219
+ }
1220
+ lines.push('evidence');
1221
+ lines.push(`- branch documents loaded from ${status.context.specDir}`);
1222
+ lines.push(status.gitRoot
1223
+ ? `- git repository detected at ${status.gitRoot}; doctor and run-index checks can use Git repository context`
1224
+ : '- doctor and run-index checks expect Git repository context; run git init first in fresh temporary projects');
1225
+ renderDocumentGaps(lines, status.gaps);
868
1226
  lines.push(`next ${status.recommendedNextCommand}`);
869
1227
  return lines.join('\n');
870
1228
  }
@@ -892,13 +1250,7 @@ function renderLocalRunIndexInspection(inspection) {
892
1250
  if (inspection.index) {
893
1251
  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
1252
  }
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
- }
1253
+ renderIssues(lines, inspection.issues);
902
1254
  return lines.join('\n');
903
1255
  }
904
1256
  function renderGovernancePolicy(policy) {
@@ -919,13 +1271,7 @@ function renderGovernancePolicyDecision(decision) {
919
1271
  for (const reason of decision.reasons) {
920
1272
  lines.push(`- ${reason}`);
921
1273
  }
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
- }
1274
+ renderIssues(lines, decision.issues);
929
1275
  return lines.join('\n');
930
1276
  }
931
1277
  function renderRunInspection(inspection) {
@@ -933,6 +1279,7 @@ function renderRunInspection(inspection) {
933
1279
  lines.push(`status=${inspection.summary.status} phase=${inspection.summary.phase ?? 'n/a'} task=${inspection.summary.currentTask ?? 'n/a'} updated=${inspection.summary.updatedAt}`);
934
1280
  lines.push(`validation=${inspection.validation.status} evidence=${inspection.validation.evidence.join(',') || 'none'}`);
935
1281
  lines.push(`sync_back=${inspection.syncBack.status} proposal=${inspection.syncBack.proposalPath ?? 'none'}`);
1282
+ lines.push(`task_run_evidence=${inspection.taskRunEvidence.version} gaps=${inspection.taskRunEvidence.gaps.length} sync_back=${inspection.taskRunEvidence.syncBackProposal ?? 'none'}`);
936
1283
  lines.push(`artifacts=${inspection.artifacts.length}`);
937
1284
  for (const artifact of inspection.artifacts) {
938
1285
  lines.push(`- ${artifact.path} kind=${artifact.kind} task=${artifact.task ?? 'n/a'} agent=${artifact.agent ?? 'n/a'}`);
@@ -941,6 +1288,18 @@ function renderRunInspection(inspection) {
941
1288
  for (const ingestion of inspection.artifactIngestions) {
942
1289
  lines.push(`- ${ingestion.delegationId} ${ingestion.status} artifact=${ingestion.artifactPath} result=${ingestion.resultStatus ?? 'n/a'} delegation=${ingestion.delegationStatus ?? 'n/a'}`);
943
1290
  }
1291
+ lines.push(`agent_executions=${inspection.agentExecutions.length}`);
1292
+ for (const execution of inspection.agentExecutions) {
1293
+ lines.push(`- ${execution.executionId} profile=${execution.profile} status=${execution.status} task=${execution.taskId} artifacts=${execution.artifacts.join(',') || 'none'}`);
1294
+ }
1295
+ lines.push(`team_sessions=${inspection.teamSessions.length}`);
1296
+ for (const session of inspection.teamSessions) {
1297
+ 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'}`);
1298
+ }
1299
+ lines.push(`worker_runtimes=${inspection.workerRuntimes.length}`);
1300
+ for (const runtime of inspection.workerRuntimes) {
1301
+ lines.push(`- ${runtime.runtimeId} status=${runtime.status} task=${runtime.taskId} agent=${runtime.agent} delegation=${runtime.delegationId} lease_expires=${runtime.leaseExpiresAt}`);
1302
+ }
944
1303
  lines.push(`events=${inspection.eventCount}`);
945
1304
  for (const event of inspection.recentEvents) {
946
1305
  lines.push(`- ${event.time} ${event.event}${event.summary ? `: ${event.summary}` : ''}`);
@@ -958,12 +1317,7 @@ function renderSyncBackInspection(inspection) {
958
1317
  lines.push(`- ${reason}`);
959
1318
  }
960
1319
  }
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
- }
1320
+ renderDocumentGaps(lines, inspection.gaps);
967
1321
  lines.push(`apply_policy=${inspection.applyPolicy.mode} approval_required=${inspection.applyPolicy.requiresApproval}`);
968
1322
  for (const reason of inspection.applyPolicy.reasons) {
969
1323
  lines.push(`- policy: ${reason}`);
@@ -981,6 +1335,399 @@ function renderSyncBackApplyResult(result) {
981
1335
  lines.push(`sync_back=${result.inspection.status}`);
982
1336
  return lines.join('\n');
983
1337
  }
1338
+ function renderWorkflowGateList(workflows) {
1339
+ const lines = ['SDD workflow gates'];
1340
+ for (const workflow of workflows) {
1341
+ lines.push(`- ${workflow.id} command=${workflow.command} agents=${workflow.allowedAgents.join(',') || 'none'}`);
1342
+ }
1343
+ return lines.join('\n');
1344
+ }
1345
+ function renderWorkflowGateInspect(workflow) {
1346
+ const lines = [`Workflow gate ${workflow.id}`];
1347
+ lines.push(`version=${workflow.version}`);
1348
+ lines.push(`command=${workflow.command}`);
1349
+ lines.push(`agents=${workflow.allowedAgents.join(',') || 'none'}`);
1350
+ lines.push('required_inputs');
1351
+ for (const input of workflow.requiredInputs) {
1352
+ lines.push(`- ${input}`);
1353
+ }
1354
+ lines.push('required_artifacts');
1355
+ for (const artifact of workflow.requiredArtifacts) {
1356
+ lines.push(`- ${artifact}`);
1357
+ }
1358
+ lines.push('gate_conditions');
1359
+ for (const condition of workflow.gateConditions) {
1360
+ lines.push(`- ${condition}`);
1361
+ }
1362
+ lines.push(`gap_closure=${workflow.gapClosureBehavior}`);
1363
+ lines.push(`next=${workflow.nextAction}`);
1364
+ return lines.join('\n');
1365
+ }
1366
+ function renderWorkflowGateValidation(result) {
1367
+ const lines = ['SDD workflow gate validation'];
1368
+ lines.push(`valid=${result.valid}`);
1369
+ lines.push(`workflows=${result.workflows.length}`);
1370
+ lines.push('issues');
1371
+ if (result.issues.length === 0) {
1372
+ lines.push('- none');
1373
+ }
1374
+ else {
1375
+ for (const issue of result.issues) {
1376
+ lines.push(`- ${issue.field}: ${issue.message}`);
1377
+ }
1378
+ }
1379
+ return lines.join('\n');
1380
+ }
1381
+ function renderAgentRegistryList(agents) {
1382
+ const lines = ['SDD agent registry'];
1383
+ for (const agent of agents) {
1384
+ lines.push(`- ${agent.id} stages=${agent.allowedStages.join(',')} autonomy=${agent.autonomyCeiling}`);
1385
+ }
1386
+ return lines.join('\n');
1387
+ }
1388
+ function renderAgentRegistryInspect(agent) {
1389
+ const lines = [`Agent ${agent.id}`];
1390
+ lines.push(`version=${agent.version}`);
1391
+ lines.push(`role=${agent.role}`);
1392
+ lines.push(`allowed_stages=${agent.allowedStages.join(',')}`);
1393
+ lines.push(`autonomy_ceiling=${agent.autonomyCeiling}`);
1394
+ lines.push(`required_artifact=${agent.requiredArtifact}`);
1395
+ lines.push(`verification=${agent.verificationExpectation}`);
1396
+ lines.push('capabilities');
1397
+ for (const capability of agent.capabilities) {
1398
+ lines.push(`- ${capability}`);
1399
+ }
1400
+ lines.push('read_boundary');
1401
+ for (const item of agent.readBoundary) {
1402
+ lines.push(`- ${item}`);
1403
+ }
1404
+ lines.push('write_boundary');
1405
+ for (const item of agent.writeBoundary) {
1406
+ lines.push(`- ${item}`);
1407
+ }
1408
+ lines.push('tool_allowlist');
1409
+ for (const tool of agent.toolAllowlist) {
1410
+ lines.push(`- ${tool}`);
1411
+ }
1412
+ lines.push(`stop_condition=${agent.stopCondition}`);
1413
+ return lines.join('\n');
1414
+ }
1415
+ function renderAgentRegistryValidation(result) {
1416
+ const lines = ['SDD agent registry validation'];
1417
+ lines.push(`valid=${result.valid}`);
1418
+ lines.push(`agents=${result.agents.length}`);
1419
+ lines.push('issues');
1420
+ if (result.issues.length === 0) {
1421
+ lines.push('- none');
1422
+ }
1423
+ else {
1424
+ for (const issue of result.issues) {
1425
+ lines.push(`- ${issue.field}: ${issue.message}`);
1426
+ }
1427
+ }
1428
+ return lines.join('\n');
1429
+ }
1430
+ function renderRegistryOriginCounts(sources) {
1431
+ if (!sources || sources.length === 0) {
1432
+ return 'none';
1433
+ }
1434
+ const counts = new Map();
1435
+ for (const source of sources) {
1436
+ counts.set(source.origin, (counts.get(source.origin) ?? 0) + 1);
1437
+ }
1438
+ return [...counts.entries()].map(([origin, count]) => `${origin}:${count}`).join(',');
1439
+ }
1440
+ function idsByOrigin(sources, kind, origin) {
1441
+ const ids = sources?.filter((source) => source.kind === kind && source.origin === origin).map((source) => source.id) ?? [];
1442
+ return ids.join(',') || 'none';
1443
+ }
1444
+ function renderAgentRouterDecision(decision) {
1445
+ const lines = [`Agent router decision ${decision.taskId}`];
1446
+ lines.push(`version=${decision.version}`);
1447
+ lines.push(`branch=${decision.branch} category=${decision.category}`);
1448
+ lines.push(`recommended_profile=${decision.recommendedProfile ?? 'none'} autonomy_ceiling=${decision.autonomyCeiling}`);
1449
+ lines.push(`allowed_profiles=${decision.allowedProfiles.join(',') || 'none'}`);
1450
+ lines.push(`required_capabilities=${decision.requiredCapabilities.join(',') || 'none'}`);
1451
+ lines.push(`source_capability=${decision.sourceCapability ?? 'none'} reuse=${decision.reuseDecision ?? 'none'}`);
1452
+ if (decision.registrySources && decision.registrySources.length > 0) {
1453
+ lines.push(`registry_sources=${decision.registrySources.map((source) => `${source.kind}:${source.id}:${source.origin}`).join(',')}`);
1454
+ }
1455
+ if (decision.resolvedAliases && decision.resolvedAliases.length > 0) {
1456
+ lines.push(`alias_resolutions=${decision.resolvedAliases.map((alias) => `${alias.input}->${alias.resolved}:${alias.source}`).join(',')}`);
1457
+ }
1458
+ if (decision.routingRuleHits && decision.routingRuleHits.length > 0) {
1459
+ lines.push(`routing_rule_hits=${decision.routingRuleHits.join(',')}`);
1460
+ }
1461
+ if (decision.quarantineWarnings && decision.quarantineWarnings.length > 0) {
1462
+ lines.push('quarantine_warnings');
1463
+ for (const warning of decision.quarantineWarnings) {
1464
+ lines.push(`- ${warning}`);
1465
+ }
1466
+ }
1467
+ if (decision.adapterMapping) {
1468
+ lines.push(`adapter_mapping profile=${decision.adapterMapping.profile} host=${decision.adapterMapping.hostAdapter} projection=${decision.adapterMapping.projection}`);
1469
+ }
1470
+ if (decision.toolPermission) {
1471
+ lines.push(`tool_permission profile=${decision.toolPermission.profile} policy=${decision.toolPermission.policy} groups=${decision.toolPermission.toolGroups.join(',')}`);
1472
+ lines.push(`approval=${decision.toolPermission.approvalPolicy}`);
1473
+ }
1474
+ lines.push(`model_policy=${decision.modelPolicy.id} category=${decision.modelPolicy.category}`);
1475
+ lines.push(`team_mode=${decision.teamMode.decision} mode=${decision.teamMode.mode} activation=${decision.teamMode.activation} cost=${decision.teamMode.costClass} waves=${decision.teamMode.waveRecommendation.join(',') || 'none'}`);
1476
+ lines.push(`team_mode_reason=${decision.teamMode.reason}`);
1477
+ lines.push(`required_artifacts=${decision.requiredArtifacts.join(',') || 'none'}`);
1478
+ if (decision.blockedReason) {
1479
+ lines.push(`blocked_reason=${decision.blockedReason}`);
1480
+ }
1481
+ lines.push(`next=${decision.nextAction}`);
1482
+ return lines.join('\n');
1483
+ }
1484
+ function renderAgentSkillTeamRuntimeInspection(inspection) {
1485
+ const lines = ['SDD agent/skill/team runtime'];
1486
+ lines.push(`version=${inspection.version}`);
1487
+ lines.push(`profiles=${inspection.profiles.length} skill_capabilities=${inspection.skillCapabilities.length} capability_sources=${inspection.capabilitySources.length}`);
1488
+ lines.push(`registry_origins=${renderRegistryOriginCounts(inspection.registrySources)}`);
1489
+ lines.push(`project_profiles=${idsByOrigin(inspection.registrySources, 'profile', 'project_config')}`);
1490
+ lines.push(`project_capabilities=${idsByOrigin(inspection.registrySources, 'skill_capability', 'project_config')}`);
1491
+ lines.push(`project_sources=${idsByOrigin(inspection.registrySources, 'capability_source', 'project_config')}`);
1492
+ if (inspection.aliases && Object.keys(inspection.aliases).length > 0) {
1493
+ lines.push(`aliases=${Object.entries(inspection.aliases).map(([alias, target]) => `${alias}->${target}`).join(',')}`);
1494
+ }
1495
+ if (inspection.routingRules && inspection.routingRules.length > 0) {
1496
+ lines.push(`routing_rules=${inspection.routingRules.map((rule) => rule.id).join(',')}`);
1497
+ }
1498
+ if (inspection.adapterMappings && inspection.adapterMappings.length > 0) {
1499
+ lines.push(`adapter_mappings=${inspection.adapterMappings.map((mapping) => `${mapping.profile}:${mapping.hostAdapter}`).join(',')}`);
1500
+ }
1501
+ lines.push(`host_adapter=${inspection.hostAdapter.id} host=${inspection.hostAdapter.host}`);
1502
+ lines.push(`team_mode_default=${inspection.teamMode.decision}`);
1503
+ lines.push(`reuse_policy=${inspection.reusePolicy}`);
1504
+ lines.push('profiles');
1505
+ for (const profile of inspection.profiles) {
1506
+ lines.push(`- ${profile.id} stages=${profile.stageScope.join(',')} risk_ceiling=${profile.riskCeiling}`);
1507
+ }
1508
+ lines.push('capabilities');
1509
+ for (const capability of inspection.skillCapabilities) {
1510
+ lines.push(`- ${capability.id} reuse=${capability.reuseDecision} evidence=${capability.evidenceType}`);
1511
+ }
1512
+ return lines.join('\n');
1513
+ }
1514
+ function renderAgentSkillTeamRuntimeValidation(result) {
1515
+ const lines = ['SDD agent/skill/team runtime validation'];
1516
+ lines.push(`valid=${result.valid}`);
1517
+ lines.push(`profiles=${result.inspection.profiles.length}`);
1518
+ lines.push(`capabilities=${result.inspection.skillCapabilities.length}`);
1519
+ lines.push(`registry_origins=${renderRegistryOriginCounts(result.inspection.registrySources)}`);
1520
+ lines.push('issues');
1521
+ if (result.issues.length === 0) {
1522
+ lines.push('- none');
1523
+ }
1524
+ else {
1525
+ for (const issue of result.issues) {
1526
+ lines.push(`- ${issue.field}: ${issue.message}`);
1527
+ }
1528
+ }
1529
+ return lines.join('\n');
1530
+ }
1531
+ function renderSkillCapabilityList(capabilities, registrySources) {
1532
+ const lines = ['SDD skill capabilities'];
1533
+ lines.push(`registry_origins=${renderRegistryOriginCounts(registrySources)}`);
1534
+ for (const capability of capabilities) {
1535
+ const source = registrySources?.find((candidate) => candidate.kind === 'skill_capability' && candidate.id === capability.id);
1536
+ lines.push(`- ${capability.id} domain=${capability.capabilityDomain.join(',')} reuse=${capability.reuseDecision} evidence=${capability.evidenceType} origin=${source?.origin ?? 'unknown'}`);
1537
+ }
1538
+ return lines.join('\n');
1539
+ }
1540
+ function renderSkillCapabilityInspect(capability) {
1541
+ const lines = [`Skill capability ${capability.id}`];
1542
+ lines.push(`version=${capability.version}`);
1543
+ lines.push(`name=${capability.name}`);
1544
+ lines.push(`kind=${capability.kind} source=${capability.source} source_ref=${capability.sourceRef}`);
1545
+ lines.push(`domain=${capability.capabilityDomain.join(',')}`);
1546
+ lines.push(`allowed_stages=${capability.allowedStages.join(',')}`);
1547
+ lines.push(`risk_ceiling=${capability.requiredRiskCeiling}`);
1548
+ lines.push(`evidence_type=${capability.evidenceType}`);
1549
+ lines.push(`reuse_decision=${capability.reuseDecision}`);
1550
+ if (capability.buildExceptionReason) {
1551
+ lines.push(`build_exception=${capability.buildExceptionReason}`);
1552
+ }
1553
+ return lines.join('\n');
1554
+ }
1555
+ function renderCapabilitySourceList(sources, registrySources) {
1556
+ const lines = ['SDD capability sources'];
1557
+ lines.push(`registry_origins=${renderRegistryOriginCounts(registrySources)}`);
1558
+ for (const source of sources) {
1559
+ const registrySource = registrySources?.find((candidate) => candidate.kind === 'capability_source' && candidate.id === source.id);
1560
+ lines.push(`- ${source.id} kind=${source.kind} reuse=${source.reuseDecision} quarantine=${source.quarantineRequired} origin=${registrySource?.origin ?? 'unknown'}`);
1561
+ }
1562
+ return lines.join('\n');
1563
+ }
1564
+ function renderCapabilitySourceInspect(source) {
1565
+ const lines = [`Capability source ${source.id}`];
1566
+ lines.push(`version=${source.version}`);
1567
+ lines.push(`name=${source.name}`);
1568
+ lines.push(`kind=${source.kind} reuse=${source.reuseDecision} quarantine=${source.quarantineRequired}`);
1569
+ lines.push(`source_ref=${source.sourceRef}`);
1570
+ lines.push(`allowed_use=${source.allowedUse}`);
1571
+ lines.push(`attribution=${source.attribution}`);
1572
+ lines.push(`rationale=${source.rationale}`);
1573
+ return lines.join('\n');
1574
+ }
1575
+ function renderExternalAgentPackImportInspection(inspection) {
1576
+ const lines = [`External pack import ${inspection.sourceId}`];
1577
+ lines.push(`version=${inspection.version}`);
1578
+ lines.push(`status=${inspection.status} risk_ceiling=${inspection.riskCeiling}`);
1579
+ lines.push(`allowed_profiles=${inspection.allowedProfiles.join(',') || 'none'}`);
1580
+ lines.push(`mapping=${inspection.mappingResult}`);
1581
+ lines.push(`reason=${inspection.reason}`);
1582
+ lines.push('checks');
1583
+ for (const check of inspection.checks) {
1584
+ lines.push(`- ${check.check} status=${check.status} evidence=${check.evidence}`);
1585
+ }
1586
+ return lines.join('\n');
1587
+ }
1588
+ function renderTeamModePolicy(policy) {
1589
+ const lines = ['SDD team-mode policy'];
1590
+ lines.push(`version=${policy.version}`);
1591
+ lines.push(`enabled=${policy.enabled} decision=${policy.decision} mode=${policy.mode} activation=${policy.activation} cost=${policy.costClass}`);
1592
+ lines.push(`reason=${policy.reason}`);
1593
+ lines.push(`chief=${policy.chiefProfile} members=${policy.memberProfiles.join(',') || 'none'} max_members=${policy.maxMembers}`);
1594
+ lines.push(`require_artifacts=${policy.requireArtifacts}`);
1595
+ lines.push(`waves=${policy.waveRecommendation.join(',') || 'none'}`);
1596
+ if (policy.blockedReason) {
1597
+ lines.push(`blocked_reason=${policy.blockedReason}`);
1598
+ }
1599
+ for (const wave of policy.allowedWaves) {
1600
+ lines.push(`- ${wave.id} kind=${wave.waveKind} members=${wave.memberProfiles.join(',')} merge_gate=${wave.mergeGate}`);
1601
+ }
1602
+ return lines.join('\n');
1603
+ }
1604
+ function renderQueryStatusContract(contract) {
1605
+ const lines = ['SDD query status contract'];
1606
+ lines.push(`version=${contract.version}`);
1607
+ lines.push(`source=${contract.sourceDocument}`);
1608
+ for (const surface of contract.surfaces) {
1609
+ lines.push(`- ${surface.id} command=${surface.command}`);
1610
+ lines.push(` responsibility=${surface.responsibility}`);
1611
+ lines.push(` includes=${surface.includes.join(',')}`);
1612
+ lines.push(` excludes=${surface.excludes.join(',')}`);
1613
+ lines.push(` next=${surface.nextActionRule}`);
1614
+ }
1615
+ return lines.join('\n');
1616
+ }
1617
+ function renderQueryStatusValidation(result) {
1618
+ const lines = ['SDD query status validation'];
1619
+ lines.push(`valid=${result.valid}`);
1620
+ lines.push(`surfaces=${result.surfaces.length}`);
1621
+ lines.push('issues');
1622
+ if (result.issues.length === 0) {
1623
+ lines.push('- none');
1624
+ }
1625
+ else {
1626
+ for (const issue of result.issues) {
1627
+ lines.push(`- ${issue.field}: ${issue.message}`);
1628
+ }
1629
+ }
1630
+ return lines.join('\n');
1631
+ }
1632
+ function renderSkillAgentEvalContract(contract) {
1633
+ const lines = ['SDD skill/agent eval contract'];
1634
+ lines.push(`version=${contract.version}`);
1635
+ lines.push(`source=${contract.sourceReport}`);
1636
+ lines.push(`corpus=${contract.corpus.length}`);
1637
+ lines.push('dimensions');
1638
+ for (const dimension of contract.dimensions) {
1639
+ lines.push(`- ${dimension.id} threshold=${dimension.passThreshold}`);
1640
+ lines.push(` expectation=${dimension.expectation}`);
1641
+ lines.push(` baseline=${dimension.baselineFinding}`);
1642
+ }
1643
+ lines.push('regression_assertions');
1644
+ for (const assertion of contract.regressionAssertions) {
1645
+ lines.push(`- ${assertion}`);
1646
+ }
1647
+ return lines.join('\n');
1648
+ }
1649
+ function renderSkillAgentEvalValidation(result) {
1650
+ const lines = ['SDD skill/agent eval validation'];
1651
+ lines.push(`valid=${result.valid}`);
1652
+ lines.push(`dimensions=${result.contract.dimensions.length}`);
1653
+ lines.push(`corpus=${result.contract.corpus.length}`);
1654
+ lines.push('issues');
1655
+ if (result.issues.length === 0) {
1656
+ lines.push('- none');
1657
+ }
1658
+ else {
1659
+ for (const issue of result.issues) {
1660
+ lines.push(`- ${issue.field}: ${issue.message}`);
1661
+ }
1662
+ }
1663
+ return lines.join('\n');
1664
+ }
1665
+ function renderHarnessLearningContract(contract) {
1666
+ const lines = ['SDD harness learning contract'];
1667
+ lines.push(`version=${contract.version}`);
1668
+ lines.push(`source=${contract.sourceTrial}`);
1669
+ lines.push(`promotion=${contract.promotionRule}`);
1670
+ lines.push('allowed_sinks');
1671
+ for (const sink of contract.allowedSinks) {
1672
+ lines.push(`- ${sink.id}: ${sink.output}`);
1673
+ lines.push(` boundary=${sink.boundary}`);
1674
+ }
1675
+ lines.push('forbidden_outputs');
1676
+ for (const output of contract.forbiddenOutputs) {
1677
+ lines.push(`- ${output}`);
1678
+ }
1679
+ return lines.join('\n');
1680
+ }
1681
+ function renderHarnessLearningValidation(result) {
1682
+ const lines = ['SDD harness learning validation'];
1683
+ lines.push(`valid=${result.valid}`);
1684
+ lines.push(`allowed_sinks=${result.contract.allowedSinks.length}`);
1685
+ lines.push(`forbidden_outputs=${result.contract.forbiddenOutputs.length}`);
1686
+ lines.push('issues');
1687
+ if (result.issues.length === 0) {
1688
+ lines.push('- none');
1689
+ }
1690
+ else {
1691
+ for (const issue of result.issues) {
1692
+ lines.push(`- ${issue.field}: ${issue.message}`);
1693
+ }
1694
+ }
1695
+ return lines.join('\n');
1696
+ }
1697
+ function renderProjectContextPackContract(contract) {
1698
+ const lines = ['SDD project context pack contract'];
1699
+ lines.push(`version=${contract.version}`);
1700
+ lines.push(`entry=${contract.entryPoint}`);
1701
+ lines.push('durable_context');
1702
+ for (const item of contract.durableContext) {
1703
+ lines.push(`- ${item}`);
1704
+ }
1705
+ lines.push('runtime_sources_of_truth');
1706
+ for (const source of contract.runtimeSourcesOfTruth) {
1707
+ lines.push(`- ${source}`);
1708
+ }
1709
+ lines.push('boundaries');
1710
+ for (const boundary of contract.boundaries) {
1711
+ lines.push(`- ${boundary}`);
1712
+ }
1713
+ return lines.join('\n');
1714
+ }
1715
+ function renderProjectContextPackValidation(result) {
1716
+ const lines = ['SDD project context pack validation'];
1717
+ lines.push(`valid=${result.valid}`);
1718
+ lines.push(`entry=${result.contract.entryPoint}`);
1719
+ lines.push(`runtime_sources=${result.contract.runtimeSourcesOfTruth.length}`);
1720
+ lines.push('issues');
1721
+ if (result.issues.length === 0) {
1722
+ lines.push('- none');
1723
+ }
1724
+ else {
1725
+ for (const issue of result.issues) {
1726
+ lines.push(`- ${issue.field}: ${issue.message}`);
1727
+ }
1728
+ }
1729
+ return lines.join('\n');
1730
+ }
984
1731
  function renderCapabilityList(capabilities) {
985
1732
  const lines = ['SDD tool capabilities'];
986
1733
  for (const capability of capabilities) {
@@ -1116,13 +1863,7 @@ function renderArtifactIngestionInspection(inspection) {
1116
1863
  for (const record of inspection.records) {
1117
1864
  lines.push(`- ${record.delegationId} ${record.status} artifact=${record.artifactPath} result=${record.resultStatus ?? 'n/a'} delegation=${record.delegationStatus ?? 'n/a'}`);
1118
1865
  }
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
- }
1866
+ renderIssues(lines, inspection.issues);
1126
1867
  return lines.join('\n');
1127
1868
  }
1128
1869
  function renderBackgroundExecutorResult(result) {
@@ -1131,13 +1872,7 @@ function renderBackgroundExecutorResult(result) {
1131
1872
  lines.push(`run=${result.runId} delegation=${result.delegationId ?? 'n/a'} queue=${result.queueItemId ?? 'n/a'} worker=${result.workerAdapterId}`);
1132
1873
  lines.push(`artifact=${result.artifactPath ?? 'pending'}`);
1133
1874
  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
- }
1875
+ renderIssues(lines, result.issues);
1141
1876
  return lines.join('\n');
1142
1877
  }
1143
1878
  function renderBackgroundExecutorInspection(inspection) {
@@ -1147,15 +1882,77 @@ function renderBackgroundExecutorInspection(inspection) {
1147
1882
  for (const delegation of inspection.delegations) {
1148
1883
  lines.push(`- ${delegation.delegationId} ${delegation.status} task=${delegation.taskId} agent=${delegation.agent} artifact=${delegation.expectedArtifact}`);
1149
1884
  }
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
- }
1885
+ renderIssues(lines, inspection.issues);
1886
+ return lines.join('\n');
1887
+ }
1888
+ function renderResidentWorkerRuntimeClaimResult(result) {
1889
+ const lines = [`Resident worker runtime ${result.status} for ${result.taskId}`];
1890
+ lines.push(`version=${result.version}`);
1891
+ lines.push(`run=${result.runId} runtime=${result.runtimeId ?? 'n/a'} delegation=${result.delegationId ?? 'n/a'} queue=${result.queueItemId ?? 'n/a'} worker=${result.workerAdapterId}`);
1892
+ lines.push(`artifact=${result.expectedArtifact ?? 'pending'} lease_expires=${result.leaseExpiresAt ?? 'n/a'}`);
1893
+ lines.push(result.message);
1894
+ renderIssues(lines, result.issues);
1895
+ if (result.runtimeId && result.leaseExpiresAt) {
1896
+ lines.push(`next sdd worker-runtime heartbeat ${result.runtimeId} --run ${result.runId}`);
1897
+ lines.push(`inspect sdd worker-runtime inspect ${result.runtimeId} --run ${result.runId}`);
1898
+ }
1899
+ return lines.join('\n');
1900
+ }
1901
+ function renderResidentWorkerRuntimeHeartbeatResult(result) {
1902
+ const lines = [`Resident worker runtime ${result.status}: ${result.runtimeId}`];
1903
+ lines.push(`version=${result.version}`);
1904
+ lines.push(`run=${result.runId} lease_expires=${result.leaseExpiresAt ?? 'n/a'}`);
1905
+ lines.push(result.message);
1906
+ renderIssues(lines, result.issues);
1907
+ if (result.runtime) {
1908
+ lines.push(`next ${result.status === 'terminal' ? `sdd background inspect ${result.runId}` : `sdd worker-runtime inspect ${result.runtimeId} --run ${result.runId}`}`);
1156
1909
  }
1157
1910
  return lines.join('\n');
1158
1911
  }
1912
+ function renderResidentWorkerRuntimeList(result) {
1913
+ const lines = [`Resident worker runtimes for ${result.runId}`];
1914
+ lines.push(`version=${result.version}`);
1915
+ lines.push(`runtimes=${result.runtimes.length} active=${result.activeRuntimes} stale=${result.staleRuntimes} terminal=${result.terminalRuntimes} blocked=${result.blockedRuntimes}`);
1916
+ for (const runtime of result.runtimes) {
1917
+ lines.push(`- ${runtime.runtimeId} ${runtime.status} task=${runtime.taskId} agent=${runtime.agent} delegation=${runtime.delegationId} lease_expires=${runtime.leaseExpiresAt}`);
1918
+ }
1919
+ renderIssues(lines, result.issues);
1920
+ return lines.join('\n');
1921
+ }
1922
+ function renderResidentWorkerRuntimeInspection(inspection) {
1923
+ const lines = [`Resident worker runtime ${inspection.status}: ${inspection.runtimeId}`];
1924
+ lines.push(`version=${inspection.version}`);
1925
+ lines.push(`run=${inspection.runId} valid=${inspection.valid} lease_expired=${inspection.leaseExpired}`);
1926
+ if (inspection.runtime) {
1927
+ lines.push(`task=${inspection.runtime.taskId} agent=${inspection.runtime.agent} worker=${inspection.runtime.workerAdapterId}`);
1928
+ lines.push(`delegation=${inspection.runtime.delegationId} queue=${inspection.runtime.queueItemId} artifact=${inspection.runtime.expectedArtifact}`);
1929
+ lines.push(`claimed=${inspection.runtime.claimedAt} heartbeat=${inspection.runtime.lastHeartbeatAt ?? 'none'} lease_expires=${inspection.runtime.leaseExpiresAt}`);
1930
+ lines.push(`evidence=${inspection.runtime.evidenceSummary}`);
1931
+ }
1932
+ lines.push(`queue_status=${inspection.queueItem?.status ?? 'missing'} adapter=${inspection.workerAdapter?.id ?? 'missing'}`);
1933
+ lines.push(`next ${inspection.recommendedNextCommand}`);
1934
+ renderIssues(lines, inspection.issues);
1935
+ return lines.join('\n');
1936
+ }
1937
+ function renderIssues(lines, issues) {
1938
+ if (issues.length === 0) {
1939
+ return;
1940
+ }
1941
+ lines.push('issues');
1942
+ for (const issue of issues) {
1943
+ lines.push(`- ${issue.field}: ${issue.message}`);
1944
+ lines.push(` recommendation: ${issue.recommendation}`);
1945
+ }
1946
+ }
1947
+ function renderDocumentGaps(lines, gaps) {
1948
+ if (gaps.length === 0) {
1949
+ return;
1950
+ }
1951
+ lines.push('gaps');
1952
+ for (const gap of gaps) {
1953
+ lines.push(`- [${gap.severity}] ${gap.type} ${gap.taskId ?? 'document'} ${gap.field}: ${gap.message}`);
1954
+ }
1955
+ }
1159
1956
  function renderWaveExecutorResult(result) {
1160
1957
  const lines = [`Wave executor ${result.status} for ${result.branch}`];
1161
1958
  lines.push(`version=${result.version}`);
@@ -1176,13 +1973,7 @@ function renderWaveExecutorResult(result) {
1176
1973
  lines.push(`- ${gate.taskId}: ${gate.reasons.join(' | ')}`);
1177
1974
  }
1178
1975
  }
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
- }
1976
+ renderIssues(lines, result.issues);
1186
1977
  return lines.join('\n');
1187
1978
  }
1188
1979
  function renderWaveExecutorInspection(inspection) {
@@ -1192,13 +1983,7 @@ function renderWaveExecutorInspection(inspection) {
1192
1983
  for (const event of inspection.waveEvents) {
1193
1984
  lines.push(`- ${event.event}: ${event.summary ?? ''}`);
1194
1985
  }
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
- }
1986
+ renderIssues(lines, inspection.issues);
1202
1987
  return lines.join('\n');
1203
1988
  }
1204
1989
  function renderWorktreeIsolationDecision(decision) {
@@ -1237,24 +2022,19 @@ function renderWorktreeLifecycleInspection(inspection) {
1237
2022
  for (const record of inspection.records) {
1238
2023
  lines.push(`- ${record.worktreeId} ${record.status} task=${record.taskId} path=${record.worktreePath} dirty=${record.dirty}`);
1239
2024
  }
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
- }
2025
+ renderIssues(lines, inspection.issues);
1247
2026
  return lines.join('\n');
1248
2027
  }
1249
2028
  function renderTaskGraphPlan(graph) {
1250
2029
  const lines = [`Task graph ${graph.valid ? 'valid' : 'blocked'} for ${graph.branch}`];
1251
2030
  lines.push(`version=${graph.version}`);
2031
+ lines.push(`contract=${graph.contract}`);
1252
2032
  lines.push(`tasks=${graph.summary.tasks} dependencies=${graph.summary.dependencies} file_overlaps=${graph.summary.fileOverlaps}`);
1253
2033
  lines.push(`high_risk_tasks=${graph.summary.highRiskTasks.length > 0 ? graph.summary.highRiskTasks.join(',') : 'none'}`);
1254
2034
  lines.push(`validation=${graph.summary.validationCommands.length > 0 ? graph.summary.validationCommands.join(' | ') : 'none'}`);
1255
2035
  lines.push('nodes');
1256
2036
  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}`);
2037
+ 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
2038
  }
1259
2039
  if (graph.dependencyEdges.length > 0) {
1260
2040
  lines.push('dependency_edges');
@@ -1309,12 +2089,50 @@ function renderWavePlan(plan) {
1309
2089
  }
1310
2090
  return lines.join('\n');
1311
2091
  }
1312
- function readOption(args, name) {
1313
- const index = args.indexOf(name);
1314
- if (index < 0) {
1315
- return null;
2092
+ function readTeamModeActivation(args, fallback) {
2093
+ if (args.includes('--no-team-mode')) {
2094
+ return 'off';
2095
+ }
2096
+ const inline = args.find((item) => item.startsWith('--team-mode='));
2097
+ const inlineValue = inline?.split('=', 2)[1];
2098
+ if (inlineValue === 'auto' || inlineValue === 'force' || inlineValue === 'off') {
2099
+ return inlineValue;
2100
+ }
2101
+ const index = args.indexOf('--team-mode');
2102
+ if (index >= 0) {
2103
+ const value = args[index + 1];
2104
+ if (value === 'auto' || value === 'force' || value === 'off') {
2105
+ return value;
2106
+ }
2107
+ return 'force';
2108
+ }
2109
+ return fallback;
2110
+ }
2111
+ function readBranchContext(args) {
2112
+ const branch = readBranchOption(args);
2113
+ return branch ? { branch, branchSource: 'cli_option' } : {};
2114
+ }
2115
+ function readBranchOption(args) {
2116
+ return readOption(args, '--branch') ?? undefined;
2117
+ }
2118
+ function readOptionalPositionalArgument(args) {
2119
+ 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']);
2120
+ for (let index = 0; index < args.length; index += 1) {
2121
+ const item = args[index];
2122
+ if (!item.startsWith('--')) {
2123
+ return item;
2124
+ }
2125
+ if (item.includes('=')) {
2126
+ continue;
2127
+ }
2128
+ if (!booleanOptions.has(item) && args[index + 1] && !args[index + 1].startsWith('--')) {
2129
+ index += 1;
2130
+ }
1316
2131
  }
1317
- return args[index + 1] ?? null;
2132
+ return undefined;
2133
+ }
2134
+ async function readResolvedBranch(projectRoot, args) {
2135
+ return (await resolveSddContext(projectRoot, readBranchContext(args))).branch;
1318
2136
  }
1319
2137
  function readWaveExecutorStrategy(args, name) {
1320
2138
  const value = readOption(args, name) ?? 'fast-stop';
@@ -1327,15 +2145,6 @@ function readRunStatus(args, name) {
1327
2145
  function readGovernancePolicyOperation(value) {
1328
2146
  return value === 'background_executor' || value === 'wave_executor' || value === 'sync_back_apply' || value === 'destructive_git' || value === 'external_interaction' || value === 'cleanup' ? value : null;
1329
2147
  }
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
2148
  function readTaskArtifactOptions(args) {
1340
2149
  const artifacts = {};
1341
2150
  for (let index = 0; index < args.length; index += 1) {
@@ -1376,24 +2185,35 @@ function readAiToolSelection(args, allowNone) {
1376
2185
  }
1377
2186
  throw new Error(`Unsupported --ai value: ${value}`);
1378
2187
  }
1379
- function readLifecycleSignalOptions(args) {
2188
+ async function readLifecycleSignalOptions(args) {
1380
2189
  const directSafe = args.includes('--direct-safe');
1381
2190
  const riskTags = readRepeatedOptions(args, '--risk');
1382
2191
  const contracts = readRepeatedOptions(args, '--contract');
1383
2192
  const permissions = readRepeatedOptions(args, '--permission');
1384
- return {
2193
+ const fromText = readOption(args, '--from-text');
2194
+ const fromFile = readOption(args, '--from-file');
2195
+ if (fromText && fromFile) {
2196
+ return { signals: {}, riskExtraction: null, error: 'Usage: sdd lifecycle decide accepts only one of --from-text or --from-file' };
2197
+ }
2198
+ const riskExtraction = fromText
2199
+ ? extractLifecycleRiskSignalsFromText(fromText, 'from_text')
2200
+ : fromFile
2201
+ ? extractLifecycleRiskSignalsFromText(await readFile(fromFile, 'utf8'), 'from_file')
2202
+ : null;
2203
+ const extracted = riskExtraction?.signals ?? {};
2204
+ const signals = {
1385
2205
  intent_clarity: directSafe ? 'high' : readSignalClarity(args, '--intent') ?? 'medium',
1386
2206
  acceptance_clarity: directSafe ? 'high' : readSignalClarity(args, '--acceptance') ?? 'medium',
1387
2207
  estimated_change_size: directSafe ? 'tiny' : readEstimatedChangeSize(args, '--size') ?? 'small',
1388
2208
  task_count_estimate: Number(readOption(args, '--tasks') ?? (directSafe ? '1' : '1')),
1389
2209
  file_count_estimate: Number(readOption(args, '--files') ?? (directSafe ? '1' : '1')),
1390
2210
  affected_layers: readRepeatedOptions(args, '--layer'),
1391
- affected_contracts: contracts,
2211
+ affected_contracts: uniqueCliStrings([...contracts, ...(extracted.affected_contracts ?? [])]),
1392
2212
  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',
2213
+ impact_confidence: directSafe ? 'high' : extracted.impact_confidence ?? readImpactConfidence(args, '--impact-confidence') ?? 'medium',
2214
+ risk_tags: uniqueCliStrings([...riskTags, ...(extracted.risk_tags ?? [])]),
2215
+ reversibility: directSafe ? 'reversible' : extracted.reversibility ?? readReversibility(args, '--reversibility') ?? 'unknown',
2216
+ validation_clarity: directSafe ? 'clear' : extracted.validation_clarity ?? readValidationClarity(args, '--validation') ?? 'partial',
1397
2217
  validation_available: directSafe || args.includes('--validation-available'),
1398
2218
  validation_cost: directSafe ? 'cheap' : readValidationCost(args, '--validation-cost') ?? 'unknown',
1399
2219
  policy_hits: readRepeatedOptions(args, '--policy'),
@@ -1405,21 +2225,44 @@ function readLifecycleSignalOptions(args) {
1405
2225
  orchestration_uncertainty: directSafe ? 'low' : readOrchestrationUncertainty(args, '--orchestration') ?? 'medium',
1406
2226
  human_checkpoint_required: args.includes('--checkpoint'),
1407
2227
  approval_reason: readRepeatedOptions(args, '--approval-reason'),
1408
- source_artifacts: readRepeatedOptions(args, '--source-artifact'),
2228
+ source_artifacts: uniqueCliStrings([...readRepeatedOptions(args, '--source-artifact'), ...(fromFile ? [fromFile] : [])]),
1409
2229
  can_scout_impact: !args.includes('--cannot-scout-impact'),
1410
- architecture_decision_required: args.includes('--architecture'),
1411
- external_unknown: args.includes('--external-unknown')
2230
+ architecture_decision_required: args.includes('--architecture') || Boolean(extracted.architecture_decision_required),
2231
+ external_unknown: args.includes('--external-unknown') || Boolean(extracted.external_unknown)
1412
2232
  };
2233
+ return { signals, riskExtraction };
1413
2234
  }
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;
2235
+ function uniqueCliStrings(values) {
2236
+ return Array.from(new Set(values.filter((value) => value.length > 0)));
2237
+ }
2238
+ function renderLifecycleRiskExtraction(extraction) {
2239
+ if (!extraction) {
2240
+ return '';
2241
+ }
2242
+ const lines = [
2243
+ 'Lifecycle Risk Gate',
2244
+ 'changed',
2245
+ '- deterministic risk signals extracted',
2246
+ 'decision',
2247
+ `- source=${extraction.source}`,
2248
+ `- risk_tags=${extraction.riskTags.join(',') || 'none'}`,
2249
+ `- affected_contracts=${extraction.affectedContracts.join(',') || 'none'}`,
2250
+ `- external_unknown=${extraction.externalUnknown}`,
2251
+ 'evidence'
2252
+ ];
2253
+ if (extraction.evidence.length === 0) {
2254
+ lines.push('- none');
2255
+ }
2256
+ else {
2257
+ for (const item of extraction.evidence) {
2258
+ lines.push(`- ${item.category}: ${item.matched} -> ${item.riskTag}`);
1420
2259
  }
1421
2260
  }
1422
- return values;
2261
+ lines.push('gaps');
2262
+ lines.push('- none');
2263
+ lines.push('next');
2264
+ lines.push('- Evaluate extracted signals with lifecycle decision gate.');
2265
+ return `${lines.join('\n')}\n`;
1423
2266
  }
1424
2267
  function readSignalClarity(args, name) {
1425
2268
  const value = readOption(args, name);
@@ -1460,14 +2303,36 @@ id: T1
1460
2303
  status: pending
1461
2304
  wave: 1
1462
2305
  depends_on: []
2306
+ acceptance_refs:
2307
+ - AC-1
2308
+ plan_refs:
2309
+ - "§4 Target Design Overview"
1463
2310
  affected_files:
1464
2311
  - path/to/file
1465
2312
  validation:
1466
2313
  - command string
1467
- risk: []
2314
+ risk:
2315
+ - state-machine
2316
+ agent_fit:
2317
+ - scout
2318
+ - implementer
2319
+ - reviewer
2320
+ - validator
2321
+ verification_availability:
2322
+ - unit:command string
2323
+ - build:command string
2324
+ autonomy: full_sdd_with_checkpoint
2325
+ allowed_agents:
2326
+ - scout
2327
+ - implementer
2328
+ - reviewer
2329
+ - validator
2330
+ required_artifacts:
2331
+ - artifacts/review-T1.md
2332
+ - artifacts/validation-T1.md
1468
2333
  \`\`\`
1469
2334
 
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.
2335
+ 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
2336
 
1472
2337
  #### Boundary
1473
2338