@cassiomc1/forgeloop 1.12.0 → 1.14.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.
Files changed (249) hide show
  1. package/.github/copilot-instructions.md +1 -1
  2. package/AGENTS.md +1 -1
  3. package/AGENT_COMPATIBILITY.md +8 -0
  4. package/CLAUDE.md +1 -1
  5. package/CONTRIBUTING.md +90 -0
  6. package/DOCS_INDEX.md +46 -12
  7. package/ENG/c-development-eng.md +112 -0
  8. package/ENG/cpp-development-eng.md +109 -0
  9. package/ENG/dotnet-aspnetcore-development-eng.md +401 -0
  10. package/ENG/go-development-eng.md +103 -0
  11. package/ENG/java-development-eng.md +125 -0
  12. package/ENG/nodejs-backend-development-eng.md +605 -0
  13. package/ENG/php-development-eng.md +104 -0
  14. package/ENG/rust-development-eng.md +422 -0
  15. package/ENG/sec-code-eng.md +7 -7
  16. package/ENG/sql-development-eng.md +108 -0
  17. package/ENG/swift-development-eng.md +111 -0
  18. package/ENG/typescript-development-eng.md +108 -0
  19. package/EXECUTION_STATE.md +12 -0
  20. package/GUIDE_ROUTER.md +418 -9
  21. package/LOOP_ENGINEERING.md +28 -2
  22. package/ORCHESTRATOR_INTEGRATION.md +9 -5
  23. package/PROTOCOL_INTEGRATION.md +55 -2
  24. package/QUALITY_SCORECARD.md +1 -0
  25. package/README.md +78 -52
  26. package/TERMINOLOGY.md +2 -0
  27. package/THIRD_PARTY_NOTICES.md +19 -7
  28. package/THREAT_MODEL.md +140 -1
  29. package/completions/_forgeloop +22 -4
  30. package/completions/forgeloop.bash +40 -4
  31. package/completions/forgeloop.fish +130 -1
  32. package/docs/ADVISORY_CONTEXT.md +25 -0
  33. package/docs/AGENT_BROWSER_ADAPTER.md +81 -0
  34. package/docs/AGENT_BROWSER_VERIFICATION.md +6 -0
  35. package/docs/AGENT_PROTOCOL_SUMMARY.md +81 -3
  36. package/docs/AGENT_SKILL.md +66 -0
  37. package/docs/ARTIFACT_REFERENCE.md +123 -0
  38. package/docs/AUDIT_UX.md +46 -0
  39. package/docs/BROWSER_VERIFICATION.md +136 -0
  40. package/docs/CLI_REFERENCE.md +392 -10
  41. package/docs/CODE_ATTESTATION.md +2 -2
  42. package/docs/DOCUMENTATION_GUIDE.md +34 -12
  43. package/docs/GETTING_STARTED.md +59 -0
  44. package/docs/JEV_BENCHMARKS.md +31 -0
  45. package/docs/MODEL_ROUTING.md +37 -0
  46. package/docs/OPENSRC_ADAPTER.md +241 -0
  47. package/docs/PACKAGE_CONTENTS.md +60 -19
  48. package/docs/PROVIDERS.md +126 -0
  49. package/docs/PROVIDER_ARCHITECTURE.md +199 -0
  50. package/docs/RECIPES.md +32 -0
  51. package/docs/RELEASE_CHECKLIST.md +66 -5
  52. package/docs/SECURITY_REVIEW.md +71 -0
  53. package/docs/SEMANTIC_DECISION_PLANE.md +71 -0
  54. package/docs/TEST_INTELLIGENCE.md +29 -0
  55. package/docs/TEST_PRUNING.md +14 -0
  56. package/docs/TROUBLESHOOTING.md +298 -3
  57. package/docs/UNIVERSAL_INTEGRATION.md +31 -0
  58. package/docs/assets/diagrams/forgeloop-code-attestation-flow.html +2 -2
  59. package/docs/assets/diagrams/forgeloop-code-attestation-flow.receipt.json +5 -5
  60. package/docs/assets/diagrams/forgeloop-code-attestation-flow.svg +1 -1
  61. package/docs/assets/diagrams/forgeloop-engineering-flow.html +39 -26
  62. package/docs/assets/diagrams/forgeloop-engineering-flow.receipt.json +6 -6
  63. package/docs/assets/diagrams/forgeloop-engineering-flow.svg +26 -26
  64. package/docs/assets/diagrams/forgeloop-verification-trust-flow.html +2 -1
  65. package/docs/assets/diagrams/forgeloop-verification-trust-flow.receipt.json +5 -5
  66. package/docs/assets/diagrams/forgeloop-verification-trust-flow.svg +1 -1
  67. package/docs/diagrams/README.md +13 -9
  68. package/docs/diagrams/forgeloop-code-attestation-flow.workflow.json +1 -1
  69. package/docs/diagrams/forgeloop-engineering-flow.workflow.json +24 -19
  70. package/docs/diagrams/forgeloop-verification-trust-flow.workflow.json +1 -0
  71. package/docs/diagrams/reviews/forgeloop-code-attestation-flow.review.json +4 -4
  72. package/docs/diagrams/reviews/forgeloop-engineering-flow.review.json +4 -4
  73. package/docs/diagrams/reviews/forgeloop-verification-trust-flow.review.json +4 -4
  74. package/docs/documentation-manifest.json +1397 -0
  75. package/docs/protocol-requirements.json +101 -0
  76. package/package.json +46 -4
  77. package/schemas/config.schema.json +14 -0
  78. package/schemas/context-plan.schema.json +18 -0
  79. package/schemas/routing-input.schema.json +1 -1
  80. package/schemas/semantic-decision.schema.json +46 -0
  81. package/schemas/test-utility.schema.json +44 -0
  82. package/scripts/CI_VALIDATORS.md +84 -11
  83. package/scripts/benchmark-jev.mjs +5 -0
  84. package/scripts/benchmark-test-intelligence.mjs +4 -0
  85. package/scripts/generate-agent-protocol-summary.mjs +40 -1
  86. package/scripts/generate-forgeloop-skill.mjs +133 -0
  87. package/scripts/jev-smoke.mjs +19 -0
  88. package/skills/forgeloop/README.md +9 -0
  89. package/skills/forgeloop/SKILL.md +77 -0
  90. package/skills/forgeloop/references/lifecycle.md +9 -0
  91. package/skills/forgeloop/references/recovery.md +7 -0
  92. package/skills/forgeloop/references/verification.md +7 -0
  93. package/src/adapters/agent-browser/assertions.js +47 -0
  94. package/src/adapters/agent-browser/commands.js +54 -0
  95. package/src/adapters/agent-browser/index.js +3 -0
  96. package/src/adapters/agent-browser/locator.js +40 -0
  97. package/src/adapters/agent-browser/process.js +215 -0
  98. package/src/adapters/agent-browser/provider.js +313 -0
  99. package/src/adapters/emulated-services/constants.js +24 -0
  100. package/src/adapters/emulated-services/index.js +7 -0
  101. package/src/adapters/emulated-services/process.js +162 -0
  102. package/src/adapters/emulated-services/provider.js +282 -0
  103. package/src/adapters/opensrc/normalize.js +90 -0
  104. package/src/adapters/opensrc/process.js +248 -0
  105. package/src/adapters/opensrc/provider.js +338 -0
  106. package/src/adapters/opensrc/search.js +264 -0
  107. package/src/adapters/typesafe/client.js +28 -0
  108. package/src/adapters/typesafe/engine.js +63 -0
  109. package/src/adapters/typesafe/normalize.js +41 -0
  110. package/src/cli.js +108 -0
  111. package/src/commands/checkpoint-revalidate.js +176 -0
  112. package/src/commands/context-plan.js +38 -0
  113. package/src/commands/contract-create.js +264 -0
  114. package/src/commands/contract-revise.js +236 -0
  115. package/src/commands/decision-show.js +14 -0
  116. package/src/commands/decision-status.js +22 -0
  117. package/src/commands/discover.js +41 -0
  118. package/src/commands/doctor.js +15 -0
  119. package/src/commands/gate-record.js +205 -0
  120. package/src/commands/gate-revalidate.js +137 -0
  121. package/src/commands/model-route.js +32 -0
  122. package/src/commands/next.js +19 -7
  123. package/src/commands/route.js +146 -18
  124. package/src/commands/semantic-plan.js +17 -0
  125. package/src/commands/task-abandon.js +224 -0
  126. package/src/commands/task-create.js +84 -25
  127. package/src/commands/task-list.js +22 -2
  128. package/src/commands/task-migrate-contract-bootstrap-repair.js +288 -0
  129. package/src/commands/task-repair-contract-bootstrap.js +263 -0
  130. package/src/commands/test-inventory.js +5 -0
  131. package/src/commands/test-prune-plan.js +5 -0
  132. package/src/commands/test-prune-probe.js +5 -0
  133. package/src/commands/test-utility.js +5 -0
  134. package/src/commands/validate-protocol.js +10 -1
  135. package/src/config/guides.json +44 -0
  136. package/src/core/artifact-registry.js +24 -0
  137. package/src/core/audit-ux.js +514 -0
  138. package/src/core/browser-verification/constants.js +149 -0
  139. package/src/core/browser-verification/normalize.js +254 -0
  140. package/src/core/browser-verification/provider.js +519 -0
  141. package/src/core/browser-verification/service.js +115 -0
  142. package/src/core/build-script.js +151 -0
  143. package/src/core/c-cpp-project.js +143 -0
  144. package/src/core/checkpoint-revalidation.js +319 -0
  145. package/src/core/cli-command-definitions.js +249 -1
  146. package/src/core/command-executors.js +115 -3
  147. package/src/core/command-input.js +212 -102
  148. package/src/core/completion-artifacts.js +14 -5
  149. package/src/core/completion.js +4 -6
  150. package/src/core/config.js +3 -0
  151. package/src/core/context-compiler/budget.js +9 -0
  152. package/src/core/context-compiler/candidates.js +39 -0
  153. package/src/core/context-compiler/compiler.js +63 -0
  154. package/src/core/context-compiler/fingerprint.js +11 -0
  155. package/src/core/context-compiler/policy.js +13 -0
  156. package/src/core/context-compiler/result.js +23 -0
  157. package/src/core/contract-bootstrap-recovery.js +655 -0
  158. package/src/core/contract-presets.js +82 -0
  159. package/src/core/contract-revision.js +210 -0
  160. package/src/core/decision/artifact.js +69 -0
  161. package/src/core/decision/benchmarks.js +103 -0
  162. package/src/core/decision/cache.js +27 -0
  163. package/src/core/decision/constants.js +58 -0
  164. package/src/core/decision/cutover.js +34 -0
  165. package/src/core/decision/engine.js +22 -0
  166. package/src/core/decision/errors.js +68 -0
  167. package/src/core/decision/events.js +101 -0
  168. package/src/core/decision/freshness.js +19 -0
  169. package/src/core/decision/normalizers/index.js +115 -0
  170. package/src/core/decision/policy.js +18 -0
  171. package/src/core/decision/projection.js +16 -0
  172. package/src/core/decision/question-registry.js +201 -0
  173. package/src/core/decision/request.js +26 -0
  174. package/src/core/decision/resolver.js +130 -0
  175. package/src/core/decision/result.js +58 -0
  176. package/src/core/decision/service.js +156 -0
  177. package/src/core/decision/state-builder.js +65 -0
  178. package/src/core/decision/task-bindings.js +30 -0
  179. package/src/core/decision/test-provider.js +32 -0
  180. package/src/core/decision/thresholds.js +15 -0
  181. package/src/core/error-codes.js +281 -3
  182. package/src/core/events.js +226 -57
  183. package/src/core/evidence-readiness.js +9 -0
  184. package/src/core/execution-prerequisites.js +14 -0
  185. package/src/core/execution-profile.js +63 -38
  186. package/src/core/filesystem.js +1 -10
  187. package/src/core/gate-provenance.js +124 -0
  188. package/src/core/go-project.js +206 -0
  189. package/src/core/integration-invocation-policy.js +27 -4
  190. package/src/core/integration-resources.js +86 -61
  191. package/src/core/java-project.js +403 -0
  192. package/src/core/model-router/constants.js +10 -0
  193. package/src/core/model-router/policy.js +103 -0
  194. package/src/core/model-router/router.js +37 -0
  195. package/src/core/multi-language-project.js +117 -0
  196. package/src/core/next-action-model.js +58 -0
  197. package/src/core/next-action-phases.js +130 -42
  198. package/src/core/next-action-refresh.js +43 -9
  199. package/src/core/next-action-review-phase.js +7 -2
  200. package/src/core/next-action.js +35 -7
  201. package/src/core/next-explanation.js +63 -0
  202. package/src/core/phase.js +128 -10
  203. package/src/core/php-project.js +85 -0
  204. package/src/core/preflight-consistency.js +23 -9
  205. package/src/core/preflight-loaders.js +37 -5
  206. package/src/core/project-detection.js +1760 -52
  207. package/src/core/protocol-info.js +65 -0
  208. package/src/core/protocol.js +20 -0
  209. package/src/core/reconcile-closure.js +132 -53
  210. package/src/core/recovery-history.js +1 -0
  211. package/src/core/resumability.js +154 -44
  212. package/src/core/route-artifact.js +15 -1
  213. package/src/core/router.js +223 -4
  214. package/src/core/runtime-context.js +118 -61
  215. package/src/core/rust-project.js +400 -0
  216. package/src/core/schema-validation.js +3 -0
  217. package/src/core/security-review/constants.js +64 -0
  218. package/src/core/security-review/normalize.js +245 -0
  219. package/src/core/security-review/provider.js +204 -0
  220. package/src/core/security-review/service.js +134 -0
  221. package/src/core/semantic-planning/constants.js +19 -0
  222. package/src/core/semantic-planning/projection.js +94 -0
  223. package/src/core/semantic-planning/service.js +15 -0
  224. package/src/core/sources.js +37 -0
  225. package/src/core/sql-project.js +141 -0
  226. package/src/core/swift-project.js +200 -0
  227. package/src/core/task-claim-state.js +201 -1
  228. package/src/core/task-conflict-inspection.js +31 -5
  229. package/src/core/task-paths.js +13 -0
  230. package/src/core/task-recovery.js +1 -0
  231. package/src/core/templates.js +3 -0
  232. package/src/core/test-intelligence/benchmarks.js +68 -0
  233. package/src/core/test-intelligence/inventory.js +73 -0
  234. package/src/core/test-intelligence/prune.js +90 -0
  235. package/src/core/test-intelligence/semantic-state.js +15 -0
  236. package/src/core/test-intelligence/service.js +40 -0
  237. package/src/core/test-intelligence/utility.js +50 -0
  238. package/src/core/trace.js +11 -7
  239. package/src/core/transaction.js +1 -0
  240. package/src/core/typescript-project.js +349 -0
  241. package/src/core/xml-structure.js +123 -0
  242. package/src/integration.d.ts +492 -0
  243. package/src/integration.js +54 -0
  244. package/src/providers/README.md +47 -0
  245. package/src/providers/capabilities.js +46 -0
  246. package/src/providers/errors.js +15 -0
  247. package/src/providers/index.js +29 -0
  248. package/src/providers/json-snapshot.js +105 -0
  249. package/src/providers/registry.js +152 -0
@@ -14,6 +14,195 @@ function validateSearchCommandInput(command, options, help) {
14
14
  }
15
15
  }
16
16
 
17
+ function validatePolicyInput(command, options) {
18
+ if (command === "policy" && !options.policy) throw inputError("policy requires a name");
19
+ if (command !== "policy" && options.policy) throw inputError(`Policy name is not valid for ${command}`);
20
+ }
21
+
22
+ function validateContractBootstrapRepairMigrationInput(command, options) {
23
+ if (command !== "task-migrate-contract-bootstrap-repair") return;
24
+ if (!options.taskId) throw inputError("task-migrate-contract-bootstrap-repair requires --task");
25
+ if (!options.acknowledgeMigration) throw inputError("task-migrate-contract-bootstrap-repair requires --acknowledge-migration");
26
+ }
27
+
28
+ function validateTaskCreationInput(command, options, help = false) {
29
+ if (help) return;
30
+ if (command === "bundle" && !options.taskId) throw inputError("bundle requires --task");
31
+ if (command === "task-create" && !options.taskId) throw inputError("task-create requires --task");
32
+ if (!["task-create", "contract-create", "contract-revise"].includes(command) && (options.preset || options.preview)) {
33
+ throw inputError(`preset/preview options are not valid for ${command}`);
34
+ }
35
+ if (command === "task-create" && options.preview && !options.preset && !options.contractFile) {
36
+ throw inputError("task-create --preview requires --preset or --contract-file");
37
+ }
38
+ if (command === "task-create" && options.preset && options.contractFile) {
39
+ throw inputError("task-create accepts either --preset or --contract-file, not both");
40
+ }
41
+ if (command === "contract-create" && !options.taskId) throw inputError("contract-create requires --task");
42
+ if (command === "contract-create" && !options.preset && !options.contractFile) throw inputError("contract-create requires --preset or --contract-file");
43
+ if (command === "contract-create" && options.preset && options.contractFile) throw inputError("contract-create accepts either --preset or --contract-file, not both");
44
+ if (command === "contract-revise" && !options.taskId) throw inputError("contract-revise requires --task");
45
+ if (command === "contract-revise" && !options.preset && !options.contractFile) throw inputError("contract-revise requires --preset or --contract-file");
46
+ if (command === "contract-revise" && options.preset && options.contractFile) throw inputError("contract-revise accepts either --preset or --contract-file, not both");
47
+ if (command === "task-repair-contract-bootstrap" && !options.taskId) throw inputError("task-repair-contract-bootstrap requires --task");
48
+ if (command === "task-repair-contract-bootstrap" && !options.acknowledgeRepair) throw inputError("task-repair-contract-bootstrap requires --acknowledge-repair");
49
+ validateContractBootstrapRepairMigrationInput(command, options);
50
+ const hasTaskListFilter = Boolean(
51
+ options.phase
52
+ || options.active
53
+ || (options.limit !== undefined && options.limit !== null)
54
+ || (options.offset !== undefined && options.offset !== null && options.offset !== 0),
55
+ );
56
+ if (command !== "task-list" && hasTaskListFilter) {
57
+ throw inputError(`task-list filters are not valid for ${command}`);
58
+ }
59
+ }
60
+
61
+ function validateGateRecordInput(command, options, help) {
62
+ if (command !== "gate-record" || help) return;
63
+ if (!options.taskId) throw inputError("gate-record requires --task");
64
+ if (!options.gate) throw inputError("gate-record requires --gate");
65
+ if (!["satisfied", "unverified", "blocked"].includes(options.gateStatus)) {
66
+ throw inputError("gate-record --status must be satisfied, unverified, or blocked");
67
+ }
68
+ }
69
+
70
+ function validateExecutionProfileInput(command, options) {
71
+ if (options.executionProfile !== null && options.executionProfile !== undefined) {
72
+ if (command !== "route") throw inputError(`executionProfile is not valid for ${command}`);
73
+ if (!EXECUTION_PROFILE_REQUESTS.includes(options.executionProfile)) {
74
+ throw inputError(`route --execution-profile must be one of ${EXECUTION_PROFILE_REQUESTS.join(", ")}`);
75
+ }
76
+ }
77
+ }
78
+
79
+ function validateUsageInput(command, options, help) {
80
+ if (command === "usage-record" && !help) {
81
+ if (!options.taskId) throw inputError("usage-record requires --task");
82
+ if ((options.usageSource ?? "ACTOR_REPORTED") !== "ACTOR_REPORTED") {
83
+ throw inputError("usage-record accepts only --source ACTOR_REPORTED");
84
+ }
85
+ }
86
+ if (command === "efficiency" && !help && !options.taskId) {
87
+ throw inputError("efficiency requires --task");
88
+ }
89
+ }
90
+
91
+ function validateQualityInput(command, options, help) {
92
+ if (["quality-baseline", "quality-verify", "quality-status"].includes(command) && !help && !options.taskId) {
93
+ throw inputError(`${command} requires --task`);
94
+ }
95
+ if (["quality-baseline", "quality-verify"].includes(command) && !help
96
+ && options.timeoutMs !== null && options.timeoutMs !== undefined
97
+ && (!Number.isInteger(options.timeoutMs) || options.timeoutMs < 0 || options.timeoutMs > 300000)) {
98
+ throw inputError(`${command} --timeout-ms must be between 0 and 300000`);
99
+ }
100
+ }
101
+
102
+ function validateProfileAndUsageInput(command, options, help) {
103
+ validateExecutionProfileInput(command, options);
104
+ validateUsageInput(command, options, help);
105
+ validateQualityInput(command, options, help);
106
+ if (command !== "quality-baseline" && options.replace === true) throw inputError("--replace is only valid for quality-baseline");
107
+ if (command !== "usage-record" && options.usageSource !== undefined && options.usageSource !== "ACTOR_REPORTED") {
108
+ throw inputError(`usageSource is not valid for ${command}`);
109
+ }
110
+ }
111
+
112
+ function validateOutputInput(command, options) {
113
+ if (options.compact === true && !["next", "task-show"].includes(command)) {
114
+ throw inputError(`compact output is not valid for ${command}`);
115
+ }
116
+ if (command !== "next" && options.explain === true) throw inputError(`explain output is not valid for ${command}`);
117
+ }
118
+
119
+ const TASK_SELECTOR_COMMANDS = new Set([
120
+ "context-plan",
121
+ "model-route",
122
+ "semantic-plan",
123
+ "test-utility",
124
+ "checkpoint-revalidate",
125
+ "workspace-bind",
126
+ "workspace-status",
127
+ "handoff-create",
128
+ "handoff-list",
129
+ "handoff-show",
130
+ "handoff-accept",
131
+ "responsibility-set",
132
+ "responsibility-status",
133
+ "verify-scope",
134
+ "attestation-create",
135
+ "attestation-status",
136
+ "attestation-verify",
137
+ ]);
138
+
139
+ function validateRequiredTaskSelector(command, options, help) {
140
+ if (TASK_SELECTOR_COMMANDS.has(command) && !help && !options.taskId) {
141
+ throw inputError(`${command} requires --task`);
142
+ }
143
+ }
144
+
145
+ function validateHandoffShowInput(command, options, help) {
146
+ if (command === "handoff-show" && !help && !options.handoffId) throw inputError("handoff-show requires --id");
147
+ }
148
+
149
+ function validateHandoffAcceptInput(command, options, help) {
150
+ if (command !== "handoff-accept" || help) return;
151
+ if (!options.handoffId) throw inputError("handoff-accept requires --handoff");
152
+ if (!options.consumerId) throw inputError("handoff-accept requires --consumer-id");
153
+ }
154
+
155
+ function validateResponsibilityInput(command, options, help) {
156
+ if (command === "responsibility-set" && !help && !options.responsibilityLabel) {
157
+ throw inputError("responsibility-set requires --label");
158
+ }
159
+ }
160
+
161
+ function validateVerificationScopeInput(command, options, help) {
162
+ if (command !== "verify-scope" || help) return;
163
+ const mode = String(options.verificationScopeMode ?? "AUTO").toUpperCase();
164
+ if (!["AUTO", "CHANGED", "CLAIMED", "FULL"].includes(mode)) throw inputError("verify-scope --mode must be AUTO, CHANGED, CLAIMED, or FULL");
165
+ }
166
+
167
+ function validateAttestationRangeInput(command, options, help) {
168
+ if (command !== "attestation-verify-range" || help) return;
169
+ if (!options.baseRevision) throw inputError("attestation-verify-range requires --base");
170
+ if (!options.headRevision) throw inputError("attestation-verify-range requires --head");
171
+ }
172
+
173
+ function validateTaskSelectorsInput(command, options, help) {
174
+ validateRequiredTaskSelector(command, options, help);
175
+ validateHandoffShowInput(command, options, help);
176
+ validateHandoffAcceptInput(command, options, help);
177
+ validateResponsibilityInput(command, options, help);
178
+ validateVerificationScopeInput(command, options, help);
179
+ validateAttestationRangeInput(command, options, help);
180
+ }
181
+
182
+ function validateRecordCheckInput(options) {
183
+ if (!options.checkId) throw inputError("record-check requires --id");
184
+ if (!options.checkRequirement) throw inputError("record-check requires --requirement");
185
+ if (!options.checkStatus) throw inputError("record-check requires --status");
186
+ if (!options.checkEvidenceKind) throw inputError("record-check requires --evidence-kind");
187
+ if (!options.checkCommand && !options.checkResult) throw inputError("record-check requires --command or --result");
188
+ }
189
+
190
+ function validateCommandCheckInput(options, command) {
191
+ if (!options.checkId) throw inputError(`${command} requires --id`);
192
+ if (!options.checkRequirement) throw inputError(`${command} requires --requirement`);
193
+ if (options.checkKind || options.checkStatus || options.checkEvidenceKind || options.checkCommand
194
+ || options.checkResult || options.checkExitCode !== null || options.checkExecutionRef || options.checkProvenance) {
195
+ throw inputError(`${command} accepts only --id, --requirement, --details,${command === "run-check" ? " --timeout-ms, --scope-ref," : ""} and -- <argv>`);
196
+ }
197
+ if (!Array.isArray(options.commandArgv) || options.commandArgv.length === 0) throw inputError(`${command} requires -- followed by an exact command argv`);
198
+ }
199
+
200
+ function validateCheckInput(command, options, help) {
201
+ if (help) return;
202
+ if (command === "record-check") validateRecordCheckInput(options);
203
+ if (command === "run-check" || command === "reconcile-closure") validateCommandCheckInput(options, command);
204
+ }
205
+
17
206
  /**
18
207
  * Transport-neutral option defaults shared by the CLI parser and the
19
208
  * programmatic command runtime so every executor observes the same
@@ -25,6 +214,7 @@ export function defaultCommandInputValues() {
25
214
  dryRun: false,
26
215
  json: false,
27
216
  compact: false,
217
+ explain: false,
28
218
  strict: false,
29
219
  fix: false,
30
220
  adopt: [],
@@ -44,6 +234,8 @@ export function defaultCommandInputValues() {
44
234
  assetPath: null,
45
235
  binaryPath: null,
46
236
  force: false,
237
+ acknowledgeRepair: false,
238
+ acknowledgeMigration: false,
47
239
  workType: null,
48
240
  surfaces: [],
49
241
  risks: [],
@@ -64,6 +256,19 @@ export function defaultCommandInputValues() {
64
256
  to: null,
65
257
  file: null,
66
258
  contractFile: null,
259
+ preset: null,
260
+ gate: null,
261
+ gateStatus: null,
262
+ gateArtifacts: [],
263
+ gateDecisions: [],
264
+ gateUnknowns: [],
265
+ gateAssumptions: [],
266
+ gateEvidenceFile: null,
267
+ preview: false,
268
+ phase: null,
269
+ active: false,
270
+ limit: null,
271
+ offset: 0,
67
272
  routeFile: null,
68
273
  stateFile: null,
69
274
  receiptFile: null,
@@ -128,110 +333,15 @@ export function validateForgeLoopCommandInput({ command, input, help = false } =
128
333
  const options = input ?? {};
129
334
  if (!command) return;
130
335
 
131
- if (command === "policy" && !options.policy) {
132
- throw inputError("policy requires a name");
133
- }
134
- if (command !== "policy" && options.policy) {
135
- throw inputError(`Policy name is not valid for ${command}`);
136
- }
137
- if (command === "bundle" && !options.taskId) {
138
- throw inputError("bundle requires --task");
139
- }
140
- if (command === "task-create" && !options.taskId) {
141
- throw inputError("task-create requires --task");
142
- }
336
+ validatePolicyInput(command, options);
337
+ validateTaskCreationInput(command, options, help);
338
+ validateGateRecordInput(command, options, help);
143
339
  validateSearchCommandInput(command, options, help);
144
- if (options.executionProfile !== null && options.executionProfile !== undefined) {
145
- if (command !== "route") throw inputError(`executionProfile is not valid for ${command}`);
146
- if (!EXECUTION_PROFILE_REQUESTS.includes(options.executionProfile)) {
147
- throw inputError(`route --execution-profile must be one of ${EXECUTION_PROFILE_REQUESTS.join(", ")}`);
148
- }
149
- }
150
- if (command === "usage-record" && !help) {
151
- if (!options.taskId) throw inputError("usage-record requires --task");
152
- if ((options.usageSource ?? "ACTOR_REPORTED") !== "ACTOR_REPORTED") {
153
- throw inputError("usage-record accepts only --source ACTOR_REPORTED");
154
- }
155
- }
156
- if (command === "efficiency" && !help && !options.taskId) {
157
- throw inputError("efficiency requires --task");
158
- }
159
- if (["quality-baseline", "quality-verify", "quality-status"].includes(command) && !help && !options.taskId) {
160
- throw inputError(`${command} requires --task`);
161
- }
162
- if (["quality-baseline", "quality-verify"].includes(command) && !help
163
- && options.timeoutMs !== null && options.timeoutMs !== undefined
164
- && (!Number.isInteger(options.timeoutMs) || options.timeoutMs < 0 || options.timeoutMs > 300000)) {
165
- throw inputError(`${command} --timeout-ms must be between 0 and 300000`);
166
- }
167
- if (command !== "quality-baseline" && options.replace === true) {
168
- throw inputError(`--replace is only valid for quality-baseline`);
169
- }
170
- if (command !== "usage-record" && options.usageSource !== undefined && options.usageSource !== "ACTOR_REPORTED") {
171
- throw inputError(`usageSource is not valid for ${command}`);
172
- }
173
- if (options.compact === true && !["next", "task-show"].includes(command)) {
174
- throw inputError(`compact output is not valid for ${command}`);
175
- }
176
- if (["workspace-bind", "workspace-status", "handoff-create", "handoff-list", "handoff-show", "handoff-accept", "responsibility-set", "responsibility-status", "verify-scope", "attestation-create", "attestation-status", "attestation-verify"].includes(command)
177
- && !options.taskId) {
178
- throw inputError(`${command} requires --task`);
179
- }
180
- if (command === "handoff-show" && !help && !options.handoffId) {
181
- throw inputError("handoff-show requires --id");
182
- }
183
- if (command === "handoff-accept" && !help) {
184
- if (!options.handoffId) {
185
- throw inputError("handoff-accept requires --handoff");
186
- }
187
- if (!options.consumerId) {
188
- throw inputError("handoff-accept requires --consumer-id");
189
- }
190
- }
191
- if (command === "responsibility-set" && !help && !options.responsibilityLabel) {
192
- throw inputError("responsibility-set requires --label");
193
- }
194
- if (command === "verify-scope" && !help) {
195
- const mode = String(options.verificationScopeMode ?? "AUTO").toUpperCase();
196
- if (!["AUTO", "CHANGED", "CLAIMED", "FULL"].includes(mode)) {
197
- throw inputError("verify-scope --mode must be AUTO, CHANGED, CLAIMED, or FULL");
198
- }
199
- }
200
- if (command === "attestation-verify-range" && !help) {
201
- if (!options.baseRevision) throw inputError("attestation-verify-range requires --base");
202
- if (!options.headRevision) throw inputError("attestation-verify-range requires --head");
203
- }
340
+ validateProfileAndUsageInput(command, options, help);
341
+ validateOutputInput(command, options);
342
+ validateTaskSelectorsInput(command, options, help);
204
343
 
205
344
  validateContinuityOptions(command, options);
206
345
 
207
- if (command === "record-check" && !help) {
208
- if (!options.checkId) throw inputError("record-check requires --id");
209
- if (!options.checkRequirement) throw inputError("record-check requires --requirement");
210
- if (!options.checkStatus) throw inputError("record-check requires --status");
211
- if (!options.checkEvidenceKind) throw inputError("record-check requires --evidence-kind");
212
- if (!options.checkCommand && !options.checkResult) throw inputError("record-check requires --command or --result");
213
- }
214
- if (command === "run-check" && !help) {
215
- if (!options.checkId) throw inputError("run-check requires --id");
216
- if (!options.checkRequirement) throw inputError("run-check requires --requirement");
217
- if (options.checkKind || options.checkStatus || options.checkEvidenceKind || options.checkCommand
218
- || options.checkResult || options.checkExitCode !== null || options.checkExecutionRef || options.checkProvenance) {
219
- throw inputError("run-check accepts only --id, --requirement, --details, --timeout-ms, --scope-ref, and -- <argv>");
220
- }
221
- if (!Array.isArray(options.commandArgv) || options.commandArgv.length === 0) {
222
- throw inputError("run-check requires -- followed by an exact command argv");
223
- }
224
- }
225
- if (command === "reconcile-closure" && !help) {
226
- if (!options.taskId) throw inputError("reconcile-closure requires --task");
227
- if (!options.checkId) throw inputError("reconcile-closure requires --id");
228
- if (!options.checkRequirement) throw inputError("reconcile-closure requires --requirement");
229
- if (options.checkKind || options.checkStatus || options.checkEvidenceKind || options.checkCommand
230
- || options.checkResult || options.checkExitCode !== null || options.checkExecutionRef || options.checkProvenance) {
231
- throw inputError("reconcile-closure accepts only --id, --requirement, --details, and -- <argv>");
232
- }
233
- if (!Array.isArray(options.commandArgv) || options.commandArgv.length === 0) {
234
- throw inputError("reconcile-closure requires -- followed by an exact command argv");
235
- }
236
- }
346
+ validateCheckInput(command, options, help);
237
347
  }
@@ -18,7 +18,7 @@ import { readPersistedRoute } from "./route-artifact.js";
18
18
  import { createReceipt, validateReceipt } from "./receipt.js";
19
19
  import { readWorkState, mutateWorkState } from "./work-state.js";
20
20
  import { assertExecutionPrerequisites, hasExecutionStarted } from "./execution-prerequisites.js";
21
- import { normalizeRequirements } from "./evidence-readiness.js";
21
+ import { normalizeRequirements, terminalRequirementsForContract } from "./evidence-readiness.js";
22
22
  import { classifyCommandResolution, validateVerificationAuthority } from "./verification-capability.js";
23
23
  import { readExecutionArtifact, validateExecutionBinding } from "./execution.js";
24
24
  import { taskArtifactPath, taskExecutionPath } from "./task-paths.js";
@@ -478,10 +478,15 @@ export async function assertRecordCheckPrerequisites({
478
478
  const requested = normalizeRequirements(requiredEvidence).find((item) => (
479
479
  item.id === requirement || item.text === requirement
480
480
  ));
481
- if (requested?.terminalOwned && status === "passed" && evidenceKind === "OBSERVED") {
481
+ const terminalRequested = terminalRequirementsForContract(contract.value, {
482
+ additionalEvidence: preflight.policy?.requiredEvidence ?? [],
483
+ }).find((item) => (
484
+ item.id === requirement || item.text === requirement
485
+ ));
486
+ if ((requested?.terminalOwned || terminalRequested) && status === "passed" && evidenceKind === "OBSERVED") {
482
487
  throw artifactError(
483
488
  "E_FUTURE_LIFECYCLE_EVIDENCE",
484
- `Terminal-owned requirement cannot be recorded before its authoritative result: ${requested.text}`,
489
+ `Terminal-owned requirement cannot be recorded before its authoritative result: ${(requested ?? terminalRequested).text}`,
485
490
  [stateRel, eventsRel],
486
491
  );
487
492
  }
@@ -827,8 +832,12 @@ export async function recordTerminalResult({
827
832
  additionalEvidence: preflight.policy?.requiredEvidence ?? [],
828
833
  });
829
834
 
830
- const normalized = normalizeRequirements(requiredEvidence);
831
- const requested = normalized.find((item) => (
835
+ const terminalRequirements = terminalRequirementsForContract(contract.value, {
836
+ additionalEvidence: preflight.policy?.requiredEvidence ?? [],
837
+ });
838
+ const requested = terminalRequirements.find((item) => (
839
+ item.id === requirement || item.text === requirement
840
+ )) ?? normalizeRequirements(requiredEvidence).find((item) => (
832
841
  item.id === requirement || item.text === requirement
833
842
  ));
834
843
 
@@ -10,7 +10,7 @@ import { completionRelationshipErrors } from "./completion-relationships.js";
10
10
  import { assertSafePath, ensureWithin, fileExists } from "./filesystem.js";
11
11
  import { evaluateStartExecutionPrerequisites, hasExecutionStarted } from "./execution-prerequisites.js";
12
12
  import { isRecoverableCompletionEvidenceCode } from "./completion-recovery.js";
13
- import { evaluateTerminalRequirements } from "./evidence-readiness.js";
13
+ import { evaluateTerminalRequirements, terminalRequirementsForContract } from "./evidence-readiness.js";
14
14
  import { PROJECT_ARTIFACT_PATHS, taskArtifactPath, taskCodeManifestPath } from "./task-paths.js";
15
15
  import { detectPolicyCapability, evaluateTargetPolicy } from "./policy-engine.js";
16
16
  import { listActions } from "./actions.js";
@@ -500,12 +500,10 @@ export async function evaluateCompletion({
500
500
  const publication = receiptValue ? publicationStatus(receiptValue) : "not-published";
501
501
 
502
502
  if (contract) {
503
- const allContractReqs = [
504
- ...(contract.value.verification ?? []),
505
- ...(contract.value.successCriteria ?? []),
506
- ];
507
503
  const terminalEval = evaluateTerminalRequirements({
508
- requirements: allContractReqs,
504
+ requirements: terminalRequirementsForContract(contract.value, {
505
+ additionalEvidence: preflight.policy?.requiredEvidence ?? [],
506
+ }),
509
507
  receipt: receiptValue,
510
508
  });
511
509
  for (const termErr of terminalEval.errors) {
@@ -4,6 +4,7 @@ import { E_ATTESTATION_CONFIGURATION_INVALID } from "./error-codes.js";
4
4
  import { normalizeVerificationConfiguration } from "./verification-scope-capability.js";
5
5
  import { EXECUTION_PROFILE_REQUESTS } from "./execution-profile.js";
6
6
  import { normalizeStructuralQualityConfig } from "./structural-quality/policy.js";
7
+ import { normalizeDecisionPolicy } from "./decision/policy.js";
7
8
 
8
9
  export const CONFIG_SCHEMA_VERSION = 1;
9
10
  export const COMPLIANCE_MODES = Object.freeze(["advisory", "standard", "strict"]);
@@ -82,6 +83,7 @@ export function createConfig(input = {}) {
82
83
  executionProfile = input.executionProfile;
83
84
  }
84
85
  const structuralQuality = normalizeStructuralQualityConfig(input.structuralQuality);
86
+ const decisionEngine = normalizeDecisionPolicy(input.decisionEngine);
85
87
  return {
86
88
  schemaVersion: CONFIG_SCHEMA_VERSION,
87
89
  protocolVersion: PROTOCOL_VERSION,
@@ -93,6 +95,7 @@ export function createConfig(input = {}) {
93
95
  ...(attestation ? { attestation } : {}),
94
96
  ...(executionProfile ? { executionProfile } : {}),
95
97
  ...(structuralQuality !== undefined ? { structuralQuality } : {}),
98
+ decisionEngine,
96
99
  };
97
100
  }
98
101
 
@@ -0,0 +1,9 @@
1
+ export const CONTEXT_BUDGETS = Object.freeze({
2
+ light: Object.freeze({ maxItems: 12, maxChars: 24_000 }),
3
+ balanced: Object.freeze({ maxItems: 32, maxChars: 64_000 }),
4
+ full: Object.freeze({ maxItems: 96, maxChars: 160_000 }),
5
+ });
6
+
7
+ export function contextBudget(profile = "balanced") {
8
+ return CONTEXT_BUDGETS[profile] ?? CONTEXT_BUDGETS.balanced;
9
+ }
@@ -0,0 +1,39 @@
1
+ import { canonicalFingerprint } from "../artifacts.js";
2
+ import { buildDecisionState } from "../decision/state-builder.js";
3
+
4
+ const INJECTION_PATTERN = /(ignore|disregard|override)\s+(?:all|previous|forge|system|developer)|exfiltrat|reveal\s+(?:the|all)\s+(?:secret|token|key)|execute\s+(?:this|the)\s+command/i;
5
+
6
+ export function normalizeContextCandidate(input = {}) {
7
+ const safe = buildDecisionState({
8
+ id: input.id ?? null,
9
+ sourceRef: input.sourceRef ?? null,
10
+ kind: input.kind ?? "UNKNOWN",
11
+ summary: input.summary ?? "",
12
+ required: input.required === true,
13
+ mandatory: input.mandatory === true,
14
+ risk: input.risk ?? "NORMAL",
15
+ trust: input.trust ?? "UNTRUSTED",
16
+ });
17
+ const summary = typeof safe.summary === "string" ? safe.summary : "";
18
+ const id = typeof safe.id === "string" && safe.id ? safe.id : `candidate-${canonicalFingerprint(safe).slice(0, 16)}`;
19
+ return {
20
+ ...safe,
21
+ id,
22
+ summary,
23
+ chars: summary.length,
24
+ promptInjection: INJECTION_PATTERN.test(summary),
25
+ };
26
+ }
27
+
28
+ export function normalizeContextCandidates(candidates = []) {
29
+ const seen = new Set();
30
+ const normalized = [];
31
+ for (const candidate of candidates) {
32
+ const value = normalizeContextCandidate(candidate);
33
+ const identity = `${value.id}:${value.sourceRef ?? ""}`;
34
+ if (seen.has(identity)) continue;
35
+ seen.add(identity);
36
+ normalized.push(value);
37
+ }
38
+ return normalized;
39
+ }
@@ -0,0 +1,63 @@
1
+ import { contextBudget } from "./budget.js";
2
+ import { normalizeContextCandidates } from "./candidates.js";
3
+ import { contextPlanFingerprint } from "./fingerprint.js";
4
+ import { canonicalFingerprint } from "../artifacts.js";
5
+ import { assertContextCompilerPolicy, candidatePriority } from "./policy.js";
6
+ import { contextPlanResult } from "./result.js";
7
+
8
+ function orderCandidates(candidates, rankedIds = []) {
9
+ const ranks = new Map(rankedIds.map((id, index) => [id, rankedIds.length - index]));
10
+ return [...candidates].sort((a, b) => (b.mandatory - a.mandatory)
11
+ || (b.required - a.required)
12
+ || ((ranks.get(b.id) ?? 0) - (ranks.get(a.id) ?? 0))
13
+ || (candidatePriority(b) - candidatePriority(a))
14
+ || a.id.localeCompare(b.id));
15
+ }
16
+
17
+ export function compileContext({ candidates = [], profile = "balanced", mandatoryIds = [], semanticRanker = null } = {}) {
18
+ const policy = assertContextCompilerPolicy({ profile, mandatoryIds });
19
+ const normalized = normalizeContextCandidates(candidates);
20
+ const candidateSetFingerprint = canonicalFingerprint(normalized.map((candidate) => ({
21
+ id: candidate.id,
22
+ sourceRef: candidate.sourceRef,
23
+ summary: candidate.summary,
24
+ required: candidate.required,
25
+ mandatory: candidate.mandatory,
26
+ })));
27
+ const mandatory = new Set(policy.mandatoryIds);
28
+ const marked = normalized.map((candidate) => ({ ...candidate, mandatory: candidate.mandatory || mandatory.has(candidate.id) }));
29
+ const semantic = typeof semanticRanker === "function" ? semanticRanker(marked.filter((item) => !item.promptInjection).map((item) => ({ id: item.id, summary: item.summary }))) : [];
30
+ const rankedIds = Array.isArray(semantic) ? semantic : (semantic?.rankedIds ?? []);
31
+ const excludedIds = new Set(Array.isArray(semantic) ? [] : (semantic?.excludedIds ?? []));
32
+ const ordered = orderCandidates(marked, Array.isArray(rankedIds) ? rankedIds : []);
33
+ const budget = contextBudget(profile);
34
+ const selected = [];
35
+ const omitted = [];
36
+ let chars = 0;
37
+ for (const candidate of ordered) {
38
+ const mustKeep = candidate.mandatory || candidate.required;
39
+ if (excludedIds.has(candidate.id) && !mustKeep) {
40
+ omitted.push(candidate);
41
+ continue;
42
+ }
43
+ const fits = selected.length < budget.maxItems && chars + candidate.chars <= budget.maxChars;
44
+ if (fits || mustKeep) {
45
+ selected.push(candidate);
46
+ chars += candidate.chars;
47
+ } else omitted.push(candidate);
48
+ }
49
+ const promptInjectionIds = marked.filter((item) => item.promptInjection).map((item) => item.id).sort();
50
+ const result = contextPlanResult({
51
+ profile,
52
+ candidates: marked,
53
+ selected,
54
+ omitted,
55
+ mandatoryIds: [...new Set([...policy.mandatoryIds, ...marked.filter((item) => item.mandatory).map((item) => item.id)])].sort(),
56
+ promptInjectionIds,
57
+ fingerprint: null,
58
+ budget,
59
+ candidateSetFingerprint,
60
+ });
61
+ result.decisionFingerprint = contextPlanFingerprint(result);
62
+ return result;
63
+ }
@@ -0,0 +1,11 @@
1
+ import { canonicalFingerprint } from "../artifacts.js";
2
+
3
+ export function contextPlanFingerprint(plan) {
4
+ return canonicalFingerprint({
5
+ profile: plan.profile,
6
+ selectedIds: plan.selected.map((item) => item.id),
7
+ omittedIds: plan.omitted.map((item) => item.id),
8
+ mandatoryIds: plan.mandatoryIds,
9
+ promptInjectionIds: plan.promptInjectionIds,
10
+ });
11
+ }
@@ -0,0 +1,13 @@
1
+ import { decisionError, DECISION_ERROR_CODES } from "../decision/errors.js";
2
+
3
+ const RISK_ORDER = Object.freeze({ CRITICAL: 4, HIGH: 3, NORMAL: 2, LOW: 1 });
4
+
5
+ export function assertContextCompilerPolicy({ profile = "balanced", mandatoryIds = [] } = {}) {
6
+ if (!["light", "balanced", "full"].includes(profile)) throw decisionError(DECISION_ERROR_CODES.POLICY_INVALID, "Context compiler profile is invalid.");
7
+ if (!Array.isArray(mandatoryIds) || mandatoryIds.some((id) => typeof id !== "string" || !id)) throw decisionError(DECISION_ERROR_CODES.POLICY_INVALID, "Context compiler mandatory IDs are invalid.");
8
+ return { profile, mandatoryIds: [...new Set(mandatoryIds)] };
9
+ }
10
+
11
+ export function candidatePriority(candidate) {
12
+ return (candidate.mandatory || candidate.required ? 100 : 0) + (candidate.promptInjection ? -50 : 0) + (RISK_ORDER[candidate.risk] ?? 0);
13
+ }
@@ -0,0 +1,23 @@
1
+ export function contextPlanResult({ profile, candidates, selected, omitted, mandatoryIds, promptInjectionIds, fingerprint, budget, candidateSetFingerprint }) {
2
+ return {
3
+ schemaVersion: 1,
4
+ protocolVersion: 1,
5
+ profile,
6
+ budget,
7
+ candidateItems: candidates.length,
8
+ selectedItems: selected.length,
9
+ candidateChars: candidates.reduce((total, item) => total + item.chars, 0),
10
+ selectedChars: selected.reduce((total, item) => total + item.chars, 0),
11
+ selected,
12
+ omitted,
13
+ mandatoryIds,
14
+ promptInjectionIds,
15
+ candidateSetFingerprint,
16
+ tokenUsage: { inputTokens: null, outputTokens: null, reportedBy: "UNKNOWN" },
17
+ decisionFingerprint: fingerprint,
18
+ authority: "SEMANTIC_DECISION",
19
+ evidenceAuthority: "NONE",
20
+ lifecycleAuthority: false,
21
+ completionAuthority: false,
22
+ };
23
+ }