@cassiomc1/forgeloop 1.5.0 → 1.6.1

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.
Files changed (133) hide show
  1. package/CONTRACT_COVERAGE.md +1 -0
  2. package/DOCS_INDEX.md +20 -10
  3. package/EXECUTION_STATE.md +20 -0
  4. package/LOOP_ENGINEERING.md +103 -1
  5. package/LOOP_SYSTEM_DESIGN.md +32 -0
  6. package/PROTOCOL_INTEGRATION.md +90 -0
  7. package/QUALITY_SCORECARD.md +2 -0
  8. package/README.md +47 -9
  9. package/THIRD_PARTY_NOTICES.md +15 -0
  10. package/THREAT_MODEL.md +41 -1
  11. package/docs/ARTIFACT_REFERENCE.md +159 -0
  12. package/docs/CLI_REFERENCE.md +294 -3
  13. package/docs/DIAGNOSTIC_MODEL.md +181 -0
  14. package/docs/DOCUMENTATION_GUIDE.md +22 -13
  15. package/docs/EXECUTION_TRACE.md +76 -0
  16. package/docs/MCP.md +34 -1
  17. package/docs/RECIPES.md +67 -0
  18. package/docs/RELEASE_CHECKLIST_1_6_1.md +121 -0
  19. package/docs/TROUBLESHOOTING.md +133 -2
  20. package/docs/assets/diagrams/forgeloop-engineering-flow.html +13797 -0
  21. package/docs/assets/diagrams/forgeloop-engineering-flow.receipt.json +37 -0
  22. package/docs/assets/diagrams/forgeloop-engineering-flow.svg +5002 -0
  23. package/docs/diagrams/README.md +55 -0
  24. package/docs/diagrams/forgeloop-engineering-flow.workflow.json +122 -0
  25. package/docs/diagrams/manifest.json +42 -0
  26. package/docs/diagrams/reviews/forgeloop-engineering-flow.review.json +20 -0
  27. package/package.json +13 -8
  28. package/schemas/action.schema.json +100 -0
  29. package/schemas/approval.schema.json +51 -0
  30. package/schemas/capability-policy.schema.json +41 -0
  31. package/schemas/diagnostic-case.schema.json +85 -0
  32. package/schemas/execution-receipt.schema.json +16 -0
  33. package/schemas/execution.schema.json +15 -0
  34. package/schemas/hypothesis-disposition.schema.json +16 -0
  35. package/schemas/intervention.schema.json +27 -0
  36. package/schemas/policy-lock.schema.json +1 -0
  37. package/schemas/policy-snapshot.schema.json +2 -0
  38. package/schemas/trajectory-evaluation.schema.json +64 -0
  39. package/schemas/trajectory-scenario.schema.json +42 -0
  40. package/src/cli.js +94 -0
  41. package/src/commands/action-authorize.js +41 -0
  42. package/src/commands/action-propose.js +10 -0
  43. package/src/commands/action-reconcile.js +10 -0
  44. package/src/commands/action-record.js +47 -0
  45. package/src/commands/action-show.js +10 -0
  46. package/src/commands/action-verify.js +10 -0
  47. package/src/commands/advance.js +7 -2
  48. package/src/commands/approval-request.js +64 -0
  49. package/src/commands/approval-resolve.js +10 -0
  50. package/src/commands/baseline.js +3 -3
  51. package/src/commands/eval.js +6 -0
  52. package/src/commands/history.js +18 -0
  53. package/src/commands/init.js +2 -2
  54. package/src/commands/inspect.js +49 -0
  55. package/src/commands/metrics.js +7 -0
  56. package/src/commands/next.js +8 -2
  57. package/src/commands/policy-discover.js +2 -2
  58. package/src/commands/record-diagnosis.js +37 -1
  59. package/src/commands/record-hypothesis-disposition.js +45 -0
  60. package/src/commands/record-intervention.js +35 -0
  61. package/src/commands/reflect.js +38 -0
  62. package/src/commands/report.js +9 -1
  63. package/src/commands/run-action.js +18 -0
  64. package/src/commands/run-check.js +1 -0
  65. package/src/commands/trace.js +34 -0
  66. package/src/commands/validate-protocol.js +21 -13
  67. package/src/core/action-authorization.js +106 -0
  68. package/src/core/action-constants.js +86 -0
  69. package/src/core/action-execution.js +106 -0
  70. package/src/core/action-ledger-projection.js +302 -0
  71. package/src/core/action-model.js +581 -0
  72. package/src/core/action-readiness.js +141 -0
  73. package/src/core/action-reconciliation-policy.js +49 -0
  74. package/src/core/action-reconciliation.js +66 -0
  75. package/src/core/action-verification.js +111 -0
  76. package/src/core/actions.js +462 -0
  77. package/src/core/approvals.js +405 -0
  78. package/src/core/artifact-registry.js +48 -0
  79. package/src/core/audit.js +25 -0
  80. package/src/core/bundles.js +15 -0
  81. package/src/core/capability-policy.js +226 -0
  82. package/src/core/cli-command-definitions.js +210 -1
  83. package/src/core/command-executors.js +171 -15
  84. package/src/core/command-runtime.js +12 -1
  85. package/src/core/completion-artifacts.js +71 -14
  86. package/src/core/completion-recovery-rebind.js +194 -0
  87. package/src/core/completion.js +70 -0
  88. package/src/core/continuity-reconciliation.js +24 -5
  89. package/src/core/diagnostic-model.js +396 -0
  90. package/src/core/diagnostic-projection.js +51 -0
  91. package/src/core/diagnostic-record.js +360 -0
  92. package/src/core/error-codes.js +363 -0
  93. package/src/core/events.js +41 -1
  94. package/src/core/execution-prerequisites.js +4 -1
  95. package/src/core/execution.js +29 -188
  96. package/src/core/failure-signature.js +70 -0
  97. package/src/core/failure-surface.js +57 -0
  98. package/src/core/history.js +110 -0
  99. package/src/core/hypothesis-projection.js +85 -0
  100. package/src/core/information-gain-projection.js +283 -0
  101. package/src/core/information-gain.js +138 -0
  102. package/src/core/inspect.js +105 -7
  103. package/src/core/integration-invocation-policy.js +55 -0
  104. package/src/core/integration-resources.js +51 -0
  105. package/src/core/next-action-model.js +35 -1
  106. package/src/core/next-action.js +459 -3
  107. package/src/core/phase.js +40 -21
  108. package/src/core/policy-engine.js +113 -6
  109. package/src/core/preflight-consistency.js +31 -5
  110. package/src/core/preflight.js +19 -2
  111. package/src/core/prepared-execution.js +256 -0
  112. package/src/core/progress.js +41 -4
  113. package/src/core/protocol-info.js +56 -0
  114. package/src/core/protocol.js +14 -0
  115. package/src/core/receipt.js +1 -0
  116. package/src/core/reconcile-closure.js +15 -12
  117. package/src/core/reflection.js +305 -0
  118. package/src/core/resumability.js +57 -3
  119. package/src/core/runtime-context.js +19 -1
  120. package/src/core/schema-validation.js +8 -0
  121. package/src/core/strategy-analysis.js +97 -0
  122. package/src/core/task-paths.js +28 -0
  123. package/src/core/task-snapshot.js +53 -0
  124. package/src/core/templates.js +8 -0
  125. package/src/core/trace.js +548 -0
  126. package/src/core/trajectory-evaluation.js +71 -0
  127. package/src/core/trajectory-metrics.js +80 -0
  128. package/src/core/transaction.js +8 -0
  129. package/src/core/verification-execution.js +257 -0
  130. package/src/core/work-state.js +10 -5
  131. package/src/integration.js +8 -0
  132. package/docs/assets/forgeloop-flow.svg +0 -1
  133. package/docs/forgeloop-flow.mmd +0 -51
@@ -2,6 +2,7 @@ import { CLI_COMMAND_DEFINITIONS } from "./cli-command-definitions.js";
2
2
  import { COMMAND_EXECUTORS } from "./command-executors.js";
3
3
  import { PROTOCOL_VERSION } from "./protocol.js";
4
4
  import { FORGELOOP_INTEGRATION_RUNTIME_VERSION } from "./command-runtime.js";
5
+ import { VERIFICATION_ISOLATION_MODES } from "./verification-execution.js";
5
6
 
6
7
  /**
7
8
  * Integration risk classes. They classify the *invocation*, not only the
@@ -14,6 +15,8 @@ export const INTEGRATION_RISK_CLASSES = Object.freeze({
14
15
  LOOP_MUTATION: "LOOP_MUTATION",
15
16
  CLAIM_REACQUISITION: "CLAIM_REACQUISITION",
16
17
  EXTERNAL_EXECUTION: "EXTERNAL_EXECUTION",
18
+ AUTHORITY_MUTATION: "AUTHORITY_MUTATION",
19
+ EXTERNAL_STATE_ATTESTATION: "EXTERNAL_STATE_ATTESTATION",
17
20
  MAINTENANCE: "MAINTENANCE",
18
21
  CLAIM_RELEASE_RECOVERY: "CLAIM_RELEASE_RECOVERY",
19
22
  LEGACY_MIGRATION: "LEGACY_MIGRATION",
@@ -25,6 +28,7 @@ const READ_ONLY_COMMANDS = Object.freeze(new Set([
25
28
  "task-list", "task-show", "task-lock-status", "progress", "audit", "report",
26
29
  "inspect", "validate-state", "validate-protocol", "validate-receipt",
27
30
  "policy-status", "policy-diff", "rule-verify", "policy",
31
+ "history", "trace", "reflect",
28
32
  ]));
29
33
 
30
34
  const LOOP_MUTATION_COMMANDS = Object.freeze(new Set([
@@ -32,6 +36,7 @@ const LOOP_MUTATION_COMMANDS = Object.freeze(new Set([
32
36
  "record-continuity", "clear-continuity", "prepare-completion",
33
37
  "record-check", "record-diagnosis", "record-decision-criterion",
34
38
  "record-terminal-result", "complete",
39
+ "record-intervention", "record-hypothesis-disposition",
35
40
  ]));
36
41
 
37
42
  const STATIC_RISK_CLASSES = Object.freeze({
@@ -39,7 +44,18 @@ const STATIC_RISK_CLASSES = Object.freeze({
39
44
  ...Object.fromEntries([...LOOP_MUTATION_COMMANDS].map((name) => [name, INTEGRATION_RISK_CLASSES.LOOP_MUTATION])),
40
45
  "task-resume": INTEGRATION_RISK_CLASSES.CLAIM_REACQUISITION,
41
46
  "run-check": INTEGRATION_RISK_CLASSES.EXTERNAL_EXECUTION,
47
+ "run-action": INTEGRATION_RISK_CLASSES.EXTERNAL_EXECUTION,
42
48
  "reconcile-closure": INTEGRATION_RISK_CLASSES.EXTERNAL_EXECUTION,
49
+ "action-propose": INTEGRATION_RISK_CLASSES.LOOP_MUTATION,
50
+ "action-authorize": INTEGRATION_RISK_CLASSES.LOOP_MUTATION,
51
+ "action-record": INTEGRATION_RISK_CLASSES.LOOP_MUTATION,
52
+ "action-verify": INTEGRATION_RISK_CLASSES.LOOP_MUTATION,
53
+ "action-reconcile": INTEGRATION_RISK_CLASSES.LOOP_MUTATION,
54
+ "approval-request": INTEGRATION_RISK_CLASSES.LOOP_MUTATION,
55
+ "approval-resolve": INTEGRATION_RISK_CLASSES.AUTHORITY_MUTATION,
56
+ eval: INTEGRATION_RISK_CLASSES.LOOP_MUTATION,
57
+ "action-show": INTEGRATION_RISK_CLASSES.READ_ONLY,
58
+ metrics: INTEGRATION_RISK_CLASSES.READ_ONLY,
43
59
  init: INTEGRATION_RISK_CLASSES.MAINTENANCE,
44
60
  update: INTEGRATION_RISK_CLASSES.MAINTENANCE,
45
61
  activate: INTEGRATION_RISK_CLASSES.MAINTENANCE,
@@ -63,6 +79,15 @@ function refineRiskClass(command, input) {
63
79
  if (command === "task-unlock" && input?.force === true) {
64
80
  return INTEGRATION_RISK_CLASSES.FORCE_DESTRUCTIVE;
65
81
  }
82
+ // Settling external commit state is independently gated: recording an
83
+ // UNKNOWN observation stays LOOP_MUTATION, while COMMITTED / NOT_COMMITTED
84
+ // settlements attest external state and require their own capability.
85
+ if (
86
+ command === "action-reconcile"
87
+ && ["COMMITTED", "NOT_COMMITTED"].includes(input?.reconciliationOutcome)
88
+ ) {
89
+ return INTEGRATION_RISK_CLASSES.EXTERNAL_STATE_ATTESTATION;
90
+ }
66
91
  // Fail closed: every canonical command must be explicitly classified.
67
92
  return baseRiskClass(command);
68
93
  }
@@ -99,6 +124,23 @@ export function getForgeLoopCapabilities({ packageVersion = null } = {}) {
99
124
  explicitResume: true,
100
125
  validatedClaimProjection: true,
101
126
  },
127
+ durableActions: {
128
+ version: 1,
129
+ readOnlyResources: true,
130
+ externalExecutionOverMcp: false,
131
+ },
132
+ trajectoryEvaluation: {
133
+ version: 1,
134
+ readOnlyMetrics: true,
135
+ projectLocalReference: true,
136
+ },
137
+ verificationExecutionIsolation: {
138
+ version: 1,
139
+ supported: true,
140
+ adapter: true,
141
+ modes: [...VERIFICATION_ISOLATION_MODES],
142
+ protocolProjectRootSeparateFromExecutionCwd: true,
143
+ },
102
144
  },
103
145
  commands,
104
146
  resources: [
@@ -108,6 +150,12 @@ export function getForgeLoopCapabilities({ packageVersion = null } = {}) {
108
150
  { name: "task/ownership", scope: "TASK" },
109
151
  { name: "task/contract", scope: "TASK" },
110
152
  { name: "task/continuity", scope: "TASK" },
153
+ { name: "task/actions", scope: "TASK" },
154
+ { name: "task/action", scope: "TASK" },
155
+ { name: "task/approvals", scope: "TASK" },
156
+ { name: "task/metrics", scope: "TASK" },
157
+ { name: "task/evaluations", scope: "TASK" },
158
+ { name: "project/capability-policy", scope: "PROJECT" },
111
159
  ],
112
160
  };
113
161
  }
@@ -128,6 +176,10 @@ export function classifyForgeLoopInvocation(command, input = {}) {
128
176
  switch (riskClass) {
129
177
  case INTEGRATION_RISK_CLASSES.EXTERNAL_EXECUTION:
130
178
  return "allowExternalExecution";
179
+ case INTEGRATION_RISK_CLASSES.AUTHORITY_MUTATION:
180
+ return "allowApprovalResolution";
181
+ case INTEGRATION_RISK_CLASSES.EXTERNAL_STATE_ATTESTATION:
182
+ return "allowActionReconciliationSettlement";
131
183
  case INTEGRATION_RISK_CLASSES.MAINTENANCE:
132
184
  return "allowMaintenance";
133
185
  case INTEGRATION_RISK_CLASSES.CLAIM_RELEASE_RECOVERY:
@@ -148,6 +200,8 @@ export function classifyForgeLoopInvocation(command, input = {}) {
148
200
  INTEGRATION_RISK_CLASSES.LOOP_MUTATION,
149
201
  INTEGRATION_RISK_CLASSES.CLAIM_REACQUISITION,
150
202
  INTEGRATION_RISK_CLASSES.EXTERNAL_EXECUTION,
203
+ INTEGRATION_RISK_CLASSES.AUTHORITY_MUTATION,
204
+ INTEGRATION_RISK_CLASSES.EXTERNAL_STATE_ATTESTATION,
151
205
  INTEGRATION_RISK_CLASSES.MAINTENANCE,
152
206
  INTEGRATION_RISK_CLASSES.CLAIM_RELEASE_RECOVERY,
153
207
  INTEGRATION_RISK_CLASSES.LEGACY_MIGRATION,
@@ -160,6 +214,7 @@ export function classifyForgeLoopInvocation(command, input = {}) {
160
214
  "task-create", "task-scope", "complete",
161
215
  ].includes(command),
162
216
  destructive: [
217
+ INTEGRATION_RISK_CLASSES.AUTHORITY_MUTATION,
163
218
  INTEGRATION_RISK_CLASSES.FORCE_DESTRUCTIVE,
164
219
  INTEGRATION_RISK_CLASSES.CLAIM_RELEASE_RECOVERY,
165
220
  INTEGRATION_RISK_CLASSES.LEGACY_MIGRATION,
@@ -1,9 +1,17 @@
1
+ import path from "node:path";
1
2
  import { runProtocolInfo } from "../commands/protocol-info.js";
2
3
  import { readContract } from "./contract.js";
3
4
  import { discoverTasks } from "./task-discovery.js";
4
5
  import { resolveTaskClaimState } from "./task-claim-state.js";
5
6
  import { runStatus } from "../commands/status.js";
6
7
  import { runContinuity } from "../commands/continuity.js";
8
+ import { listActions, readAction } from "./actions.js";
9
+ import { listApprovals } from "./approvals.js";
10
+ import { buildTrajectoryMetrics } from "./trajectory-metrics.js";
11
+ import { loadCapabilityPolicy } from "./capability-policy.js";
12
+ import { readdir } from "node:fs/promises";
13
+ import { readJsonArtifact } from "./artifacts.js";
14
+ import { taskDirectory } from "./task-paths.js";
7
15
 
8
16
  /**
9
17
  * Canonical integration resource allowlist.
@@ -43,6 +51,12 @@ export const INTEGRATION_RESOURCE_DEFINITIONS = Object.freeze({
43
51
  scope: "TASK",
44
52
  description: "Cross-harness continuity state for one task.",
45
53
  }),
54
+ "task/actions": Object.freeze({ scope: "TASK", description: "Canonical durable action summaries for one task." }),
55
+ "task/action": Object.freeze({ scope: "TASK", description: "One canonical durable action artifact." }),
56
+ "task/approvals": Object.freeze({ scope: "TASK", description: "Durable approval artifacts for one task." }),
57
+ "task/metrics": Object.freeze({ scope: "TASK", description: "Read-only trajectory metrics for one task." }),
58
+ "task/evaluations": Object.freeze({ scope: "TASK", description: "Persisted trajectory evaluations for one task." }),
59
+ "project/capability-policy": Object.freeze({ scope: "PROJECT", description: "Project capability policy, never host authority." }),
46
60
  });
47
61
 
48
62
  function ownershipProjection(projection) {
@@ -64,6 +78,7 @@ export async function readForgeLoopIntegrationResource(uri, {
64
78
  packageRoot = undefined,
65
79
  packageVersion = null,
66
80
  taskId = null,
81
+ actionId = null,
67
82
  } = {}) {
68
83
  const resource = INTEGRATION_RESOURCE_DEFINITIONS[uri];
69
84
  if (!resource) {
@@ -102,12 +117,48 @@ export async function readForgeLoopIntegrationResource(uri, {
102
117
  }
103
118
  break;
104
119
  }
120
+ case "task/actions":
121
+ case "task/action":
122
+ case "task/approvals":
123
+ case "task/metrics":
124
+ case "task/evaluations": {
125
+ if (typeof taskId !== "string" || !taskId) {
126
+ const error = new Error(`Resource ${uri} requires a taskId`); error.code = "E_TASK_REQUIRED"; throw error;
127
+ }
128
+ break;
129
+ }
105
130
  }
106
131
 
107
132
  if (uri === "task/ownership") {
108
133
  const projection = await resolveTaskClaimState(projectPath, { taskId, packageRoot });
109
134
  return { uri, taskId, data: ownershipProjection(projection) };
110
135
  }
136
+ if (uri === "task/actions") {
137
+ const actions = await listActions(projectPath, { packageRoot, taskId });
138
+ return { uri, taskId, data: { actions } };
139
+ }
140
+ if (uri === "task/action") {
141
+ if (!actionId) { const error = new Error("Resource task/action requires actionId"); error.code = "E_ACTION_INVALID"; throw error; }
142
+ return { uri, taskId, data: await readAction(projectPath, { packageRoot, taskId, actionId }) };
143
+ }
144
+ if (uri === "task/approvals") {
145
+ return { uri, taskId, data: { approvals: await listApprovals(projectPath, { packageRoot, taskId }) } };
146
+ }
147
+ if (uri === "task/metrics") {
148
+ return { uri, taskId, data: await buildTrajectoryMetrics({ target: projectPath, packageRoot, taskId }) };
149
+ }
150
+ if (uri === "task/evaluations") {
151
+ const dir = path.join(projectPath, taskDirectory(taskId), "evaluations");
152
+ let names = []; try { names = await readdir(dir); } catch { /* absent is an empty projection */ }
153
+ const evaluations = [];
154
+ for (const name of names.filter((entry) => /^eval-[A-Za-z0-9_-]+\.json$/.test(entry)).sort()) {
155
+ evaluations.push((await readJsonArtifact(projectPath, `${taskDirectory(taskId)}/evaluations/${name}`, "trajectory-evaluation", packageRoot)).value);
156
+ }
157
+ return { uri, taskId, data: { evaluations } };
158
+ }
159
+ if (uri === "project/capability-policy") {
160
+ return { uri, data: await loadCapabilityPolicy(projectPath, packageRoot) };
161
+ }
111
162
  if (uri === "task/status") {
112
163
  const result = await runStatus({ target: projectPath, packageRoot, taskId });
113
164
  return { uri, taskId, data: result };
@@ -16,6 +16,9 @@ export const NEXT_ACTIONS = Object.freeze({
16
16
  RECORD_DIAGNOSIS: "RECORD_DIAGNOSIS",
17
17
  CORRECT: "CORRECT",
18
18
  CHANGE_STRATEGY: "CHANGE_STRATEGY",
19
+ REQUIRE_NEW_DIAGNOSTIC_INFORMATION: "REQUIRE_NEW_DIAGNOSTIC_INFORMATION",
20
+ INTRODUCE_NEW_OBSERVATION: "INTRODUCE_NEW_OBSERVATION",
21
+ RECORD_INTERVENTION: "RECORD_INTERVENTION",
19
22
  ENTER_REVIEWING: "ENTER_REVIEWING",
20
23
  RECORD_TERMINAL_RESULT: "RECORD_TERMINAL_RESULT",
21
24
  PREPARE_COMPLETION: "PREPARE_COMPLETION",
@@ -32,13 +35,18 @@ export const NEXT_ACTIONS = Object.freeze({
32
35
  RESTORE_BASELINE: "RESTORE_BASELINE",
33
36
  CONTINUE_WITH_EXISTING_BASELINE: "CONTINUE_WITH_EXISTING_BASELINE",
34
37
  RECONCILE_CLOSURE: "RECONCILE_CLOSURE",
38
+ RECONCILE_ACTION: "RECONCILE_ACTION",
39
+ AUTHORIZE_ACTION: "AUTHORIZE_ACTION",
40
+ REQUEST_ACTION_APPROVAL: "REQUEST_ACTION_APPROVAL",
41
+ RESOLVE_ACTION_APPROVAL: "RESOLVE_ACTION_APPROVAL",
42
+ VERIFY_EXTERNAL_ACTION: "VERIFY_EXTERNAL_ACTION",
35
43
  RECOVER_TASK: "RECOVER_TASK",
36
44
  RESUME_RECOVERED_TASK: "RESUME_RECOVERED_TASK",
37
45
  RESOLVE_RECOVERY_INCONSISTENCY: "RESOLVE_RECOVERY_INCONSISTENCY",
38
46
  NONE: "NONE",
39
47
  });
40
48
 
41
- function directCommandSpec(commandId, taskId, requiredInputs = []) {
49
+ export function directCommandSpec(commandId, taskId, requiredInputs = []) {
42
50
  return {
43
51
  commandId,
44
52
  executable: "forgeloop",
@@ -104,6 +112,12 @@ export function result({
104
112
  requiredArtifacts = [],
105
113
  missingArtifacts = [],
106
114
  progress = undefined,
115
+ diagnosticGuidance = undefined,
116
+ authorityRequired = undefined,
117
+ hostActionRequired = undefined,
118
+ approvalRequired = undefined,
119
+ capabilityDecision = undefined,
120
+ reconciliationAuthorityRequired = undefined,
107
121
  }) {
108
122
  const normalizedReasons = reasons
109
123
  .map((reason) => {
@@ -136,6 +150,14 @@ export function result({
136
150
  requiredArtifacts: uniqueSorted(requiredArtifacts),
137
151
  missingArtifacts: uniqueSorted(missingArtifacts),
138
152
  ...(progress ? { progress: structuredClone(progress) } : {}),
153
+ ...(diagnosticGuidance ? { diagnosticGuidance: structuredClone(diagnosticGuidance) } : {}),
154
+ ...(authorityRequired ? { authorityRequired: structuredClone(authorityRequired) } : {}),
155
+ ...(hostActionRequired ? { hostActionRequired: structuredClone(hostActionRequired) } : {}),
156
+ ...(approvalRequired ? { approvalRequired: structuredClone(approvalRequired) } : {}),
157
+ ...(capabilityDecision ? { capabilityDecision: structuredClone(capabilityDecision) } : {}),
158
+ ...(reconciliationAuthorityRequired
159
+ ? { reconciliationAuthorityRequired: structuredClone(reconciliationAuthorityRequired) }
160
+ : {}),
139
161
  };
140
162
  }
141
163
 
@@ -186,6 +208,18 @@ export function recordDiagnosisCommandSpec() {
186
208
  };
187
209
  }
188
210
 
211
+ export function recordInterventionCommandSpec(taskId = null) {
212
+ return {
213
+ commandId: "record-intervention",
214
+ executable: "forgeloop",
215
+ subcommand: "record-intervention",
216
+ argv: ["record-intervention", ...(taskId ? [`--task=${taskId}`] : [])],
217
+ requiredInputs: [
218
+ { name: "file", option: "--file=<intervention-json-path>" },
219
+ ],
220
+ };
221
+ }
222
+
189
223
  export function recordTerminalResultCommandSpec(requirement) {
190
224
  const reqId = requirement.id ?? requirement;
191
225
  const type = requirement.type ?? "PUBLICATION";