@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
@@ -6,6 +6,7 @@ export const NEXT_ACTIONS = Object.freeze({
6
6
  CREATE_CONTRACT: "CREATE_CONTRACT",
7
7
  ROUTE: "ROUTE",
8
8
  SATISFY_GATES: "SATISFY_GATES",
9
+ REVALIDATE_GATES: "REVALIDATE_GATES",
9
10
  RUN_PREFLIGHT: "RUN_PREFLIGHT",
10
11
  PLAN: "PLAN",
11
12
  START_EXECUTION: "START_EXECUTION",
@@ -47,6 +48,9 @@ export const NEXT_ACTIONS = Object.freeze({
47
48
  RECOVER_TASK: "RECOVER_TASK",
48
49
  RESUME_RECOVERED_TASK: "RESUME_RECOVERED_TASK",
49
50
  RESOLVE_RECOVERY_INCONSISTENCY: "RESOLVE_RECOVERY_INCONSISTENCY",
51
+ REPAIR_CONTRACT_BOOTSTRAP: "REPAIR_CONTRACT_BOOTSTRAP",
52
+ MIGRATE_CONTRACT_BOOTSTRAP_REPAIR: "MIGRATE_CONTRACT_BOOTSTRAP_REPAIR",
53
+ REVALIDATE_CHECKPOINT: "REVALIDATE_CHECKPOINT",
50
54
  NONE: "NONE",
51
55
  });
52
56
 
@@ -60,6 +64,33 @@ export function directCommandSpec(commandId, taskId, requiredInputs = []) {
60
64
  };
61
65
  }
62
66
 
67
+ export function routeCommandSpec(taskId) {
68
+ return {
69
+ commandId: "route",
70
+ executable: "forgeloop",
71
+ subcommand: "route",
72
+ argv: ["route", `--task=${taskId}`, "--json"],
73
+ requiredInputs: [
74
+ { name: "workType", option: "--work=<type>" },
75
+ { name: "surface", option: "--surface=<value>", repeatable: true, optional: true },
76
+ { name: "risk", option: "--risk=<value>", repeatable: true, optional: true },
77
+ { name: "platform", option: "--platform=<value>", repeatable: true, optional: true },
78
+ ],
79
+ };
80
+ }
81
+
82
+ export function gateRevalidationCommandSpec(taskId, gate) {
83
+ return {
84
+ commandId: "gate-revalidate",
85
+ executable: "forgeloop",
86
+ subcommand: "gate-revalidate",
87
+ argv: ["gate-revalidate", `--task=${taskId}`, `--gate=${gate}`, "--acknowledge-stale", "--json"],
88
+ requiredInputs: [
89
+ { name: "acknowledgeStale", option: "--acknowledge-stale" },
90
+ ],
91
+ };
92
+ }
93
+
63
94
  export function recoveryGuidanceForClassification(classification, taskId) {
64
95
  if (classification === "RECOVERABLE") {
65
96
  return {
@@ -167,8 +198,35 @@ export function result({
167
198
  };
168
199
  }
169
200
 
201
+ export function contractBootstrapRepairGuidance(taskId, reason = null) {
202
+ const command = `forgeloop task-repair-contract-bootstrap --task ${taskId} --acknowledge-repair --json`;
203
+ return {
204
+ nextAction: NEXT_ACTIONS.REPAIR_CONTRACT_BOOTSTRAP,
205
+ commands: [command],
206
+ commandSpecs: [directCommandSpec("task-repair-contract-bootstrap", taskId, [
207
+ { name: "acknowledgeRepair", option: "--acknowledge-repair", description: "Explicit caller acknowledgement of the exact repair signature." },
208
+ ])],
209
+ reason: reason ?? { code: "E_CONTRACT_BOOTSTRAP_REPAIR_AVAILABLE", message: "The exact duplicate contract bootstrap defect is repairable through the official append-only command." },
210
+ };
211
+ }
212
+
213
+ export function contractBootstrapRepairMigrationGuidance(taskId, reason = null) {
214
+ const command = `forgeloop task-migrate-contract-bootstrap-repair --task ${taskId} --acknowledge-migration --json`;
215
+ return {
216
+ nextAction: NEXT_ACTIONS.MIGRATE_CONTRACT_BOOTSTRAP_REPAIR,
217
+ commands: [command],
218
+ commandSpecs: [directCommandSpec("task-migrate-contract-bootstrap-repair", taskId, [
219
+ { name: "acknowledgeMigration", option: "--acknowledge-migration", description: "Fresh explicit caller acknowledgement of the exact legacy marker migration." },
220
+ ])],
221
+ reason: reason ?? { code: "E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_AVAILABLE", message: "The exact legacy contract bootstrap repair marker can be migrated through the official append-only command." },
222
+ };
223
+ }
224
+
170
225
  export function commandFor(action) {
171
226
  return {
227
+ [NEXT_ACTIONS.ROUTE]: "forgeloop route --task <id> --work <type> --json",
228
+ [NEXT_ACTIONS.RESOLVE_STALE_ROUTE]: "forgeloop route --task <id> --work <type> --json",
229
+ [NEXT_ACTIONS.REVALIDATE_GATES]: "forgeloop gate-revalidate --task <id> --gate <name> --acknowledge-stale --json",
172
230
  [NEXT_ACTIONS.PLAN]: "forgeloop advance --to PLANNED",
173
231
  [NEXT_ACTIONS.RUN_PREFLIGHT]: "forgeloop preflight --json",
174
232
  [NEXT_ACTIONS.START_EXECUTION]: "forgeloop advance --to EXECUTING",
@@ -5,7 +5,7 @@ import { resolvePlannedPhase } from "./next-action-planned-phase.js";
5
5
  import { resolvePendingActionGuidance } from "./next-action-pending-actions.js";
6
6
  import { preExecutionRefreshGuidance } from "./next-action-refresh.js";
7
7
  import { ARTIFACT_PATHS, readJsonArtifact } from "./artifacts.js";
8
- import { taskArtifactPath } from "./task-paths.js";
8
+ import { taskArtifactPath, taskGatePath } from "./task-paths.js";
9
9
  import { completionIdentityErrors, evaluateCompletion } from "./completion.js";
10
10
  import { readContract } from "./contract.js";
11
11
  import { evaluatePreflight, validatePersistedPreflight } from "./preflight.js";
@@ -16,10 +16,16 @@ import { classifyLoadedWorkState } from "./work-state.js";
16
16
 
17
17
  import { evaluateStartExecutionPrerequisites } from "./execution-prerequisites.js";
18
18
 
19
- import { NEXT_ACTIONS, commandFor, decision, recoveryGuidanceForClassification, result, uniqueSorted } from "./next-action-model.js";
19
+ import { NEXT_ACTIONS, commandFor, decision, gateRevalidationCommandSpec, recoveryGuidanceForClassification, result, routeCommandSpec, uniqueSorted } from "./next-action-model.js";
20
20
  import { artifactError, freshnessReasons, loadArtifact, staleReasons } from "./next-action-artifacts.js";
21
21
 
22
22
  import { inspectTaskConflictState } from "./task-conflict-inspection.js";
23
+ import { validateEventLedger } from "./events.js";
24
+ import {
25
+ isContractBootstrapRepairCandidate,
26
+ isLegacyContractBootstrapRepairMigrationCandidate,
27
+ } from "./contract-bootstrap-recovery.js";
28
+ import { contractBootstrapRepairGuidance, contractBootstrapRepairMigrationGuidance } from "./next-action-model.js";
23
29
 
24
30
  import { evaluateContinuityNextAction } from "./next-action-continuity.js";
25
31
 
@@ -32,10 +38,85 @@ export const PHASES_REQUIRING_EXECUTION_CHRONOLOGY = new Set([
32
38
  "COMPLETE",
33
39
  ]);
34
40
 
41
+ const PHASES_ALLOWING_GATE_REVALIDATION = new Set([
42
+ "EXECUTING", "VERIFYING", "DIAGNOSING", "CORRECTING", "REVIEWING",
43
+ ]);
44
+
35
45
  export function phaseRequiresExecutionChronology(phase) {
36
46
  return PHASES_REQUIRING_EXECUTION_CHRONOLOGY.has(phase);
37
47
  }
38
48
 
49
+ async function contractBootstrapRepairGuidanceForExplicitTask({ target, packageRoot, explicitTaskId, context, eventsRel, stateRel }) {
50
+ const ledger = await validateEventLedger(target, packageRoot, { taskId: explicitTaskId });
51
+ const candidate = isContractBootstrapRepairCandidate(ledger.events, ledger.errors, explicitTaskId);
52
+ if (candidate) {
53
+ const guidance = contractBootstrapRepairGuidance(explicitTaskId);
54
+ return result({
55
+ ...context,
56
+ nextAction: guidance.nextAction,
57
+ commands: guidance.commands,
58
+ commandSpecs: guidance.commandSpecs,
59
+ reasons: [artifactError(guidance.reason.code, guidance.reason.message, [eventsRel, taskArtifactPath(explicitTaskId, "state")])],
60
+ requiredArtifacts: [eventsRel, taskArtifactPath(explicitTaskId, "contract")],
61
+ });
62
+ }
63
+ const tolerantLedger = await validateEventLedger(target, packageRoot, {
64
+ taskId: explicitTaskId,
65
+ allowUnmigratedLegacyContractBootstrapRepairMarkers: true,
66
+ });
67
+ if (!tolerantLedger.valid || !isLegacyContractBootstrapRepairMigrationCandidate(tolerantLedger.events, explicitTaskId)) {
68
+ return null;
69
+ }
70
+ const guidance = contractBootstrapRepairMigrationGuidance(explicitTaskId);
71
+ return result({
72
+ ...context,
73
+ nextAction: guidance.nextAction,
74
+ commands: guidance.commands,
75
+ commandSpecs: guidance.commandSpecs,
76
+ reasons: [artifactError(guidance.reason.code, guidance.reason.message, [eventsRel, stateRel])],
77
+ requiredArtifacts: [eventsRel, taskArtifactPath(explicitTaskId, "contract"), stateRel],
78
+ });
79
+ }
80
+
81
+ async function explicitTaskConflictGuidance({ target, packageRoot, explicitTaskId, context, eventsRel, stateRel, state }) {
82
+ try {
83
+ const migrationGuidance = await contractBootstrapRepairGuidanceForExplicitTask({
84
+ target, packageRoot, explicitTaskId, context, eventsRel, stateRel,
85
+ });
86
+ if (migrationGuidance) return migrationGuidance;
87
+ } catch {
88
+ // The regular conflict inspection below remains the fail-closed path.
89
+ }
90
+ let inspection;
91
+ try {
92
+ inspection = await inspectTaskConflictState(target, {
93
+ taskId: explicitTaskId,
94
+ packageRoot,
95
+ });
96
+ } catch (error) {
97
+ inspection = {
98
+ classification: "INCONSISTENT",
99
+ reasonCodes: [error.code ?? "E_TASK_RECOVERY_INCONSISTENT"],
100
+ };
101
+ }
102
+ if (["ACTIVE", "COMPLETE"].includes(inspection.classification)) return null;
103
+ if (PHASES_ALLOWING_GATE_REVALIDATION.has(state?.phase)
104
+ && inspection.reasonCodes?.includes("E_GATE_STALE")) return null;
105
+ const guidance = recoveryGuidanceForClassification(inspection.classification, explicitTaskId);
106
+ return result({
107
+ ...context,
108
+ nextAction: guidance.nextAction,
109
+ commands: guidance.commands,
110
+ commandSpecs: guidance.commandSpecs,
111
+ reasons: inspection.reasonCodes.map((code) => artifactError(
112
+ code,
113
+ `Task conflict state is ${inspection.classification}; follow the structured recovery guidance.`,
114
+ [stateRel, eventsRel, taskArtifactPath(explicitTaskId, "recovery")],
115
+ )),
116
+ requiredArtifacts: [stateRel, eventsRel],
117
+ });
118
+ }
119
+
39
120
  export async function resolveNextActionPhase({
40
121
  target,
41
122
  packageRoot,
@@ -55,33 +136,10 @@ export async function resolveNextActionPhase({
55
136
  } = {}) {
56
137
  const { policyRecoveryAction } = helpers;
57
138
  if (explicitTaskId) {
58
- let inspection;
59
- try {
60
- inspection = await inspectTaskConflictState(target, {
61
- taskId: explicitTaskId,
62
- packageRoot,
63
- });
64
- } catch (error) {
65
- inspection = {
66
- classification: "INCONSISTENT",
67
- reasonCodes: [error.code ?? "E_TASK_RECOVERY_INCONSISTENT"],
68
- };
69
- }
70
- if (!["ACTIVE", "COMPLETE"].includes(inspection.classification)) {
71
- const guidance = recoveryGuidanceForClassification(inspection.classification, explicitTaskId);
72
- return result({
73
- ...context,
74
- nextAction: guidance.nextAction,
75
- commands: guidance.commands,
76
- commandSpecs: guidance.commandSpecs,
77
- reasons: inspection.reasonCodes.map((code) => artifactError(
78
- code,
79
- `Task conflict state is ${inspection.classification}; follow the structured recovery guidance.`,
80
- [stateRel, eventsRel, taskArtifactPath(explicitTaskId, "recovery")],
81
- )),
82
- requiredArtifacts: [stateRel, eventsRel],
83
- });
84
- }
139
+ const taskConflictGuidance = await explicitTaskConflictGuidance({
140
+ target, packageRoot, explicitTaskId, context, eventsRel, stateRel, state,
141
+ });
142
+ if (taskConflictGuidance) return taskConflictGuidance;
85
143
  }
86
144
  const actionGuidance = await resolvePendingActionGuidance({ target, packageRoot, state, context, eventsRel, authorityContext, runtimeContext, helpers });
87
145
  if (actionGuidance) return actionGuidance;
@@ -137,11 +195,11 @@ export async function resolveNextActionPhase({
137
195
  contractFile: contractRel,
138
196
  });
139
197
  if (freshness.status === "REVALIDATION_REQUIRED") {
140
- const refreshGuidance = await preExecutionRefreshGuidance({ target, packageRoot, state, contract });
198
+ const refreshGuidance = await preExecutionRefreshGuidance({ target, packageRoot, state, contract, freshness });
141
199
  return result({
142
200
  ...context,
143
201
  ...refreshGuidance,
144
- nextAction: NEXT_ACTIONS.RESOLVE_BLOCKER,
202
+ nextAction: refreshGuidance.nextAction,
145
203
  reasons: freshnessReasons(state, freshness),
146
204
  requiredArtifacts: uniqueSorted([
147
205
  stateRel,
@@ -151,13 +209,15 @@ export async function resolveNextActionPhase({
151
209
  });
152
210
  }
153
211
  if (state.phase === "CONTRACT_READY") {
154
- return decision(
155
- context,
156
- NEXT_ACTIONS.ROUTE,
157
- artifactError("PHASE_CONTRACT_READY", "Persist deterministic routing for the validated contract"),
158
- contractArtifacts,
159
- [routeRel],
160
- );
212
+ return result({
213
+ ...context,
214
+ nextAction: NEXT_ACTIONS.ROUTE,
215
+ reasons: [artifactError("PHASE_CONTRACT_READY", "Persist deterministic routing for the validated contract")],
216
+ commands: [`forgeloop route --task ${explicitTaskId} --work <type> --json`],
217
+ commandSpecs: [routeCommandSpec(explicitTaskId)],
218
+ requiredArtifacts: contractArtifacts,
219
+ missingArtifacts: [routeRel],
220
+ });
161
221
  }
162
222
  const routeResult = await loadArtifact(
163
223
  () => readPersistedRoute(target, packageRoot, { taskId: explicitTaskId }),
@@ -187,6 +247,8 @@ export async function resolveNextActionPhase({
187
247
  ...context,
188
248
  nextAction: NEXT_ACTIONS.RESOLVE_STALE_ROUTE,
189
249
  reasons: stale,
250
+ commands: [`forgeloop route --task ${explicitTaskId} --work <type> --json`],
251
+ commandSpecs: [routeCommandSpec(explicitTaskId)],
190
252
  requiredArtifacts,
191
253
  });
192
254
  }
@@ -214,6 +276,20 @@ export async function resolveNextActionPhase({
214
276
  ],
215
277
  });
216
278
  }
279
+ const staleGateErrors = PHASES_ALLOWING_GATE_REVALIDATION.has(state.phase)
280
+ ? (executionPrerequisites.preflight?.errors ?? []).filter((error) => error.code === "E_GATE_STALE")
281
+ : [];
282
+ if (staleGateErrors.length > 0) {
283
+ const gates = [...new Set(staleGateErrors.map((error) => error.gate).filter(Boolean))].sort();
284
+ return result({
285
+ ...context,
286
+ nextAction: NEXT_ACTIONS.REVALIDATE_GATES,
287
+ reasons: staleGateErrors,
288
+ commands: gates.map((gate) => `forgeloop gate-revalidate --task ${explicitTaskId} --gate ${gate} --acknowledge-stale --json`),
289
+ commandSpecs: gates.map((gate) => gateRevalidationCommandSpec(explicitTaskId, gate)),
290
+ requiredArtifacts: executionPrerequisites.requiredArtifacts,
291
+ });
292
+ }
217
293
  if (executionPrerequisites.errors.length > 0) {
218
294
  return result({
219
295
  ...context,
@@ -238,16 +314,28 @@ export async function resolveNextActionPhase({
238
314
  : validatePersistedPreflight(preflightArtifact.value?.value, preflight);
239
315
 
240
316
  if (["ROUTED", "DESIGNING", "PLANNED"].includes(state.phase) && missingGates.length > 0) {
317
+ const missingGatePaths = missingGates.map((gate) => taskGatePath(state.taskId, gate));
318
+ const gateRecordSpec = {
319
+ commandId: "gate-record",
320
+ executable: "forgeloop",
321
+ subcommand: "gate-record",
322
+ argv: ["gate-record", `--task=${state.taskId}`, `--gate=${missingGates[0]}`, "--status=satisfied", "--json"],
323
+ requiredInputs: [
324
+ { name: "artifact", option: "--artifact=<project-relative-path>", repeatable: true },
325
+ { name: "decision", option: "--decision=<text>", repeatable: true },
326
+ ],
327
+ };
241
328
  return result({
242
329
  ...context,
243
330
  nextAction: NEXT_ACTIONS.SATISFY_GATES,
244
- reasons: missingGates.map((gate) => artifactError(
331
+ reasons: missingGates.map((gate, index) => artifactError(
245
332
  "E_GATE_UNVERIFIED",
246
333
  `Required gate is missing or unverified: ${gate}`,
247
- [`${ARTIFACT_PATHS.gates}/${gate}.json`],
334
+ [missingGatePaths[index]],
248
335
  )),
249
- requiredArtifacts: [...preflightArtifacts, ...missingGates.map((gate) => `${ARTIFACT_PATHS.gates}/${gate}.json`)],
250
- missingArtifacts: missingGates.map((gate) => `${ARTIFACT_PATHS.gates}/${gate}.json`),
336
+ requiredArtifacts: [...preflightArtifacts, ...missingGatePaths],
337
+ missingArtifacts: missingGatePaths,
338
+ commandSpecs: [gateRecordSpec],
251
339
  });
252
340
  }
253
341
  if (state.phase === "ROUTED") {
@@ -1,19 +1,53 @@
1
- import { validateEventLedger } from "./events.js";
1
+ import { validateEventLedger, validateStateLedgerCoherence } from "./events.js";
2
2
  import { readPersistedRoute } from "./route-artifact.js";
3
- import { directCommandSpec } from "./next-action-model.js";
3
+ import { directCommandSpec, NEXT_ACTIONS } from "./next-action-model.js";
4
+ import { resolveTaskClaimState } from "./task-claim-state.js";
5
+ import { sameCanonicalGuideList } from "./contract-bootstrap-recovery.js";
6
+ import { CHECKPOINT_REVALIDATION_PHASES } from "./checkpoint-revalidation.js";
4
7
 
5
- /** Offer checkpoint removal only before execution, with valid blocked chronology. */
6
- export async function preExecutionRefreshGuidance({ target, packageRoot, state, contract }) {
7
- if (!["ROUTED", "DESIGNING", "PLANNED"].includes(state.phase)) return {};
8
+ /**
9
+ * Select a safe pre-execution refresh mutation. Repository-only drift keeps
10
+ * the checkpoint and its route identity; the legacy clear-state path remains
11
+ * limited to a documented blocked-preflight recovery.
12
+ */
13
+ export async function preExecutionRefreshGuidance({ target, packageRoot, state, contract, freshness }) {
14
+ if (!["ROUTED", "DESIGNING", "PLANNED"].includes(state.phase)) return { nextAction: NEXT_ACTIONS.RESOLVE_BLOCKER };
8
15
  const ledger = await validateEventLedger(target, packageRoot, { taskId: state.taskId });
9
- if (!ledger.valid || ledger.events.some((entry) => entry.event === "EXECUTION_STARTED")) return {};
16
+ if (!ledger.valid || ledger.events.some((entry) => entry.event === "EXECUTION_STARTED")) {
17
+ return { nextAction: NEXT_ACTIONS.RESOLVE_BLOCKER };
18
+ }
19
+ if (CHECKPOINT_REVALIDATION_PHASES.includes(state.phase)
20
+ && freshness?.status === "REVALIDATION_REQUIRED"
21
+ && [...new Set(freshness.reasons ?? [])].sort().join(",") === "REPOSITORY_CHANGED"
22
+ && state.contractFingerprint === contract.fingerprint) {
23
+ try {
24
+ const route = await readPersistedRoute(target, packageRoot, { taskId: state.taskId });
25
+ const ownership = await resolveTaskClaimState(target, { taskId: state.taskId, packageRoot });
26
+ const coherenceErrors = validateStateLedgerCoherence(state, ledger.events);
27
+ const repositoryAvailable = typeof freshness.repository?.head === "string" && freshness.repository.head.length > 0;
28
+ const routeIdentityValid = route.value?.contractFingerprint === contract.fingerprint
29
+ && state.routeFingerprint === route.fingerprint
30
+ && sameCanonicalGuideList(state.selectedGuides, route.value.guides);
31
+ if (ownership.ownershipValid && ownership.mutationAllowed && repositoryAvailable
32
+ && coherenceErrors.length === 0 && routeIdentityValid) {
33
+ return {
34
+ nextAction: NEXT_ACTIONS.REVALIDATE_CHECKPOINT,
35
+ commands: [`forgeloop checkpoint-revalidate --task ${state.taskId} --json`],
36
+ commandSpecs: [directCommandSpec("checkpoint-revalidate", state.taskId)],
37
+ };
38
+ }
39
+ } catch {
40
+ // Unsupported or ambiguous identity stays on the fail-closed blocker.
41
+ }
42
+ }
10
43
  const lastPreflight = ledger.events.filter((entry) => ["PREFLIGHT_READY", "PREFLIGHT_BLOCKED"].includes(entry.event)).at(-1);
11
- if (lastPreflight?.event !== "PREFLIGHT_BLOCKED") return {};
44
+ if (lastPreflight?.event !== "PREFLIGHT_BLOCKED") return { nextAction: NEXT_ACTIONS.RESOLVE_BLOCKER };
12
45
  let route;
13
46
  try { route = await readPersistedRoute(target, packageRoot, { taskId: state.taskId }); }
14
- catch { return {}; }
15
- if (route.value.contractFingerprint !== contract.fingerprint) return {};
47
+ catch { return { nextAction: NEXT_ACTIONS.RESOLVE_BLOCKER }; }
48
+ if (route.value.contractFingerprint !== contract.fingerprint) return { nextAction: NEXT_ACTIONS.RESOLVE_BLOCKER };
16
49
  return {
50
+ nextAction: NEXT_ACTIONS.RESOLVE_BLOCKER,
17
51
  commands: [`forgeloop clear-state --task ${state.taskId}`, `forgeloop preflight --task ${state.taskId}`],
18
52
  commandSpecs: [directCommandSpec("clear-state", state.taskId), directCommandSpec("preflight", state.taskId)],
19
53
  };
@@ -2,7 +2,7 @@ import { readJsonArtifact } from "./artifacts.js";
2
2
  import { evaluateCompletion } from "./completion.js";
3
3
  import { validateReceipt } from "./receipt.js";
4
4
  import { completionRelationshipErrors } from "./completion-relationships.js";
5
- import { evaluateRequiredEvidence, classifyRequirement } from "./evidence-readiness.js";
5
+ import { evaluateRequiredEvidence, classifyRequirement, terminalRequirementsForContract } from "./evidence-readiness.js";
6
6
  import { validateEventLedger, validateCompletionRecoveryAuthorization } from "./events.js";
7
7
  import { NEXT_ACTIONS, commandFor, decision, recordTerminalResultCommandSpec, result } from "./next-action-model.js";
8
8
  import { artifactError, checkListReasons, loadArtifact, requirementsAndCoverage } from "./next-action-artifacts.js";
@@ -153,9 +153,14 @@ export async function resolveReviewingPhase({ state, context, requiredArtifacts,
153
153
  err.code === "E_PUBLICATION_REQUIREMENT_PENDING" || err.code === "E_PRODUCTION_REQUIREMENT_PENDING"
154
154
  ));
155
155
  if (terminalPendingErrors.length > 0 && terminalPendingErrors.length === completion.errors.length) {
156
+ const canonicalTerminalRequirements = terminalRequirementsForContract(contract.value, {
157
+ additionalEvidence: preflight.policy?.requiredEvidence ?? [],
158
+ });
156
159
  const terminalPendingReqs = terminalPendingErrors.map((err) => {
157
160
  const reqId = err.requirementId;
158
- const matchingReq = evidence.requirements.find((r) => r.id === reqId)
161
+ const matchingReq = canonicalTerminalRequirements.find((r) => r.id === reqId)
162
+ ?? canonicalTerminalRequirements.find((r) => r.text === reqId)
163
+ ?? evidence.requirements.find((r) => r.id === reqId)
159
164
  ?? evidence.requirements.find((r) => r.text === reqId)
160
165
  ?? classifyRequirement(reqId ?? err.message);
161
166
  return matchingReq;
@@ -287,13 +287,41 @@ async function computeNextAction(targetOrOptions = {}, packageRootOption) {
287
287
  });
288
288
  }
289
289
  }
290
- return decision(
291
- {},
292
- NEXT_ACTIONS.DISCOVER,
293
- artifactError("WORK_STATE_ABSENT", "No work-state checkpoint is present", [stateRel]),
294
- [stateRel],
295
- [stateRel],
296
- );
290
+ const events = await readEvents(target, packageRoot, { taskId: explicitTaskId });
291
+ if (events.some((event) => event.event === "DISCOVERY_STARTED")) {
292
+ return result({
293
+ taskId: explicitTaskId ?? "unknown",
294
+ currentPhase: "DISCOVERING",
295
+ nextAction: NEXT_ACTIONS.CREATE_CONTRACT,
296
+ reasons: [artifactError("PHASE_DISCOVERING", "Create and validate the task contract", [contractRel])],
297
+ commands: [`forgeloop contract-create --task ${explicitTaskId} --preset <documentation|bug|feature|release> --json`],
298
+ commandSpecs: [{
299
+ commandId: "contract-create",
300
+ executable: "forgeloop",
301
+ subcommand: "contract-create",
302
+ argv: ["contract-create", `--task=${explicitTaskId}`, "--json"],
303
+ requiredInputs: [{ name: "preset", option: "--preset=<documentation|bug|feature|release>" }],
304
+ }],
305
+ requiredArtifacts: [contractRel],
306
+ missingArtifacts: [contractRel, stateRel],
307
+ });
308
+ }
309
+ return result({
310
+ taskId: explicitTaskId ?? "unknown",
311
+ currentPhase: "RECEIVED",
312
+ nextAction: NEXT_ACTIONS.DISCOVER,
313
+ reasons: [artifactError("WORK_STATE_ABSENT", "No work-state checkpoint is present", [stateRel])],
314
+ commands: [`forgeloop discover --task ${explicitTaskId} --json`],
315
+ commandSpecs: [{
316
+ commandId: "discover",
317
+ executable: "forgeloop",
318
+ subcommand: "discover",
319
+ argv: ["discover", `--task=${explicitTaskId}`, "--json"],
320
+ requiredInputs: [],
321
+ }],
322
+ requiredArtifacts: [stateRel],
323
+ missingArtifacts: [stateRel],
324
+ });
297
325
  }
298
326
 
299
327
  return resolveNextActionPhase({
@@ -0,0 +1,63 @@
1
+ const MAX_EXPLANATION_TEXT = 240;
2
+ const MAX_EXPLANATION_REASON_CODE = 96;
3
+ const MAX_EXPLANATION_ARTIFACT_TEXT = 240;
4
+ const MAX_EXPLANATION_REASONS = 3;
5
+ const MAX_EXPLANATION_ARTIFACTS = 6;
6
+
7
+ const REQUIRED_CHANGES = Object.freeze({
8
+ E_CONTRACT_STALE: "Refresh the contract-bound checkpoint and route before continuing.",
9
+ E_ROUTE_STALE: "Re-run the canonical route for the current contract before continuing.",
10
+ E_REPOSITORY_CHANGED: "Revalidate the repository and record new evidence before continuing.",
11
+ E_RECEIPT_STALE: "Inspect the receipt boundary and obtain a fresh independently verifiable receipt.",
12
+ E_RECEIPT_MISSING: "Provide the required task-bound receipt or record the check as unavailable.",
13
+ E_CHECK_OBSERVATION_REQUIRED: "Run the required check through an observable verification boundary and record its result.",
14
+ E_TASK_SCOPE_CONFLICT: "Resolve the overlapping task ownership through the canonical task recovery or coordination path.",
15
+ E_VERIFICATION_EXECUTION_INVALID: "Repair or replace the verification execution boundary before recording evidence.",
16
+ E_PUBLICATION_REQUIREMENT_PENDING: "Obtain independently verifiable publication evidence; do not infer publication from local checks.",
17
+ });
18
+
19
+ function bounded(value, limit = MAX_EXPLANATION_TEXT) {
20
+ const text = String(value ?? "").trim();
21
+ return text.length <= limit ? text : `${text.slice(0, limit - 1)}…`;
22
+ }
23
+
24
+ function reasonExplanation(reason) {
25
+ const source = reason && typeof reason === "object" ? reason : {};
26
+ const artifacts = [...new Set((Array.isArray(source.artifacts) ? source.artifacts : [])
27
+ .filter((artifact) => typeof artifact === "string" && artifact.length > 0)
28
+ .map((artifact) => bounded(artifact, MAX_EXPLANATION_ARTIFACT_TEXT)))]
29
+ .slice(0, MAX_EXPLANATION_ARTIFACTS);
30
+ return {
31
+ code: bounded(source.code ?? "E_NEXT_ACTION_BLOCKED", MAX_EXPLANATION_REASON_CODE),
32
+ failedInvariant: bounded(source.message),
33
+ safeArtifacts: artifacts,
34
+ requiredChange: REQUIRED_CHANGES[source.code] ?? "Satisfy the canonical condition described by this reason before continuing.",
35
+ evidenceNeeded: artifacts.length > 0
36
+ ? `Inspect or update only the listed protocol artifacts, then re-run the canonical next action.`
37
+ : "Provide a fresh, scoped, independently observable result for the blocked condition.",
38
+ };
39
+ }
40
+
41
+ export function explainNextAction(result) {
42
+ const reasons = (Array.isArray(result?.reasons) ? result.reasons : [])
43
+ .slice(0, MAX_EXPLANATION_REASONS)
44
+ .map(reasonExplanation);
45
+ const mutating = (Array.isArray(result?.commandSpecs) ? result.commandSpecs : []).some((spec) => [
46
+ "advance",
47
+ "clear-state",
48
+ "complete",
49
+ "record-check",
50
+ "record-diagnosis",
51
+ "record-intervention",
52
+ "prepare-completion",
53
+ "route",
54
+ ].includes(spec.commandId));
55
+ return {
56
+ schemaVersion: 1,
57
+ summary: bounded(`Next action ${result?.nextAction ?? "UNKNOWN"} is required for phase ${result?.currentPhase ?? "UNKNOWN"}.`),
58
+ actionKind: mutating ? "MUTATING_COMMAND_MAY_BE_REQUIRED" : "READ_ONLY_OR_HOST_ACTION",
59
+ requiresExternalAuthority: Boolean(result?.authorityRequired || result?.hostActionRequired || result?.approvalRequired),
60
+ reasons,
61
+ bounded: true,
62
+ };
63
+ }