@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
@@ -0,0 +1,124 @@
1
+ import { canonicalFingerprint } from "./artifacts.js";
2
+
3
+ export const GATE_SATISFIED_EVENT = "GATE_SATISFIED";
4
+ export const GATE_REVALIDATED_EVENT = "GATE_REVALIDATED";
5
+ const CONTRACT_REVISED_EVENT = "CONTRACT_REVISED";
6
+ const TRANSACTION_COMMITTED_EVENT = "TRANSACTION_COMMITTED";
7
+
8
+ function eventHash(event) {
9
+ const { hash, ...body } = event ?? {};
10
+ return canonicalFingerprint(body);
11
+ }
12
+
13
+ export function assertGateSatisfiedDetails(details) {
14
+ if (!details || typeof details !== "object" || Array.isArray(details)
15
+ || Object.keys(details).length !== 1
16
+ || typeof details.gate !== "string"
17
+ || details.gate.trim().length === 0) {
18
+ const error = new Error("GATE_SATISFIED requires exactly one non-empty gate detail");
19
+ error.code = "E_EVENT_INVALID";
20
+ throw error;
21
+ }
22
+ return details;
23
+ }
24
+
25
+ export function assertGateRevalidatedDetails(details) {
26
+ const keys = ["gate", "previousGateFingerprint", "gateFingerprint", "stalePaths"];
27
+ if (!details || typeof details !== "object" || Array.isArray(details)
28
+ || Object.keys(details).length !== keys.length
29
+ || keys.some((key) => !Object.prototype.hasOwnProperty.call(details, key))
30
+ || typeof details.gate !== "string"
31
+ || details.gate.trim().length === 0
32
+ || !/^[a-z0-9][a-z0-9-]*$/.test(details.gate)
33
+ || !/^[a-f0-9]{64}$/.test(details.previousGateFingerprint)
34
+ || !/^[a-f0-9]{64}$/.test(details.gateFingerprint)
35
+ || !Array.isArray(details.stalePaths)
36
+ || details.stalePaths.length === 0
37
+ || details.stalePaths.some((value) => typeof value !== "string" || value.trim().length === 0)
38
+ || [...details.stalePaths].sort().join("\0") !== details.stalePaths.join("\0")
39
+ || new Set(details.stalePaths).size !== details.stalePaths.length) {
40
+ const error = new Error("GATE_REVALIDATED requires a gate, two fingerprints, and sorted stale paths");
41
+ error.code = "E_GATE_REVALIDATION_INVALID";
42
+ throw error;
43
+ }
44
+ return details;
45
+ }
46
+
47
+ function gateIndex(events, gateEvent, eventName) {
48
+ return events.findIndex((event) => event === gateEvent
49
+ || (event?.seq === gateEvent?.seq
50
+ && event?.taskId === gateEvent?.taskId
51
+ && event?.event === eventName));
52
+ }
53
+
54
+ function isValidGateCommit(gateEvent, commitEvent, operation) {
55
+ return commitEvent?.event === TRANSACTION_COMMITTED_EVENT
56
+ && commitEvent.taskId === gateEvent.taskId
57
+ && commitEvent.seq === gateEvent.seq + 1
58
+ && commitEvent.previousHash === gateEvent.hash
59
+ && gateEvent.hash === eventHash(gateEvent)
60
+ && commitEvent.hash === eventHash(commitEvent)
61
+ && commitEvent.details
62
+ && typeof commitEvent.details === "object"
63
+ && !Array.isArray(commitEvent.details)
64
+ && Object.keys(commitEvent.details).length === 2
65
+ && typeof commitEvent.details.transactionId === "string"
66
+ && commitEvent.details.transactionId.length > 0
67
+ && commitEvent.details.operation === operation;
68
+ }
69
+
70
+ export function resolveGateRecordBoundary(events, gateEvent) {
71
+ if (!Array.isArray(events) || !gateEvent || gateEvent.event !== GATE_SATISFIED_EVENT) return null;
72
+ const index = gateIndex(events, gateEvent, GATE_SATISFIED_EVENT);
73
+ if (index < 0) return null;
74
+ const commitEvent = events[index + 1];
75
+ return isValidGateCommit(gateEvent, commitEvent, "gate-record")
76
+ ? { gateEvent, transactionCommitEvent: commitEvent }
77
+ : null;
78
+ }
79
+
80
+ export function resolveGateRevalidationBoundary(events, gateEvent) {
81
+ if (!Array.isArray(events) || !gateEvent || gateEvent.event !== GATE_REVALIDATED_EVENT) return null;
82
+ const index = gateIndex(events, gateEvent, GATE_REVALIDATED_EVENT);
83
+ if (index < 0) return null;
84
+ const commitEvent = events[index + 1];
85
+ return isValidGateCommit(gateEvent, commitEvent, "gate-revalidate")
86
+ ? { gateEvent, transactionCommitEvent: commitEvent }
87
+ : null;
88
+ }
89
+
90
+ export function hasCurrentGateEvidence(events, taskId, gate, latestContractRevisionSeq = 0) {
91
+ return events.some((event) => [GATE_SATISFIED_EVENT, GATE_REVALIDATED_EVENT].includes(event.event)
92
+ && event.taskId === taskId
93
+ && event.details?.gate === gate
94
+ && event.seq > latestContractRevisionSeq);
95
+ }
96
+
97
+ export function validateGateSatisfactionBindings(events = []) {
98
+ const errors = [];
99
+ for (const [index, event] of events.entries()) {
100
+ if (![GATE_SATISFIED_EVENT, GATE_REVALIDATED_EVENT].includes(event.event)) continue;
101
+ const revalidated = event.event === GATE_REVALIDATED_EVENT;
102
+ try {
103
+ if (revalidated) assertGateRevalidatedDetails(event.details);
104
+ else assertGateSatisfiedDetails(event.details);
105
+ } catch (error) {
106
+ errors.push({ code: error.code ?? "E_EVENT_INVALID", message: `event ${event.seq} (${event.event}): ${error.message}` });
107
+ continue;
108
+ }
109
+ const revisedBeforeGate = events.slice(0, index).findLast((candidate) => candidate.taskId === event.taskId
110
+ && candidate.event === CONTRACT_REVISED_EVENT);
111
+ const boundary = revalidated
112
+ ? resolveGateRevalidationBoundary(events, event)
113
+ : resolveGateRecordBoundary(events, event);
114
+ if ((revalidated || revisedBeforeGate) && !boundary) {
115
+ errors.push({
116
+ code: revalidated ? "E_GATE_REVALIDATION_INVALID" : "E_EVENT_INVALID",
117
+ message: revalidated
118
+ ? `event ${event.seq} must be immediately followed by its gate-revalidate transaction commit`
119
+ : `event ${event.seq} must be immediately followed by its gate-record transaction commit after contract revision`,
120
+ });
121
+ }
122
+ }
123
+ return errors;
124
+ }
@@ -0,0 +1,206 @@
1
+ import path from "node:path";
2
+
3
+ import { createLanguageProject, portablePath } from "./multi-language-project.js";
4
+
5
+ const GO_DIRECTIVE = /^(module|go|toolchain|godebug|require|replace|exclude|retract|tool|ignore)\b/u;
6
+ const GO_WORK_DIRECTIVES = new Set(["go", "toolchain", "godebug", "use", "replace", "tool"]);
7
+
8
+ function invalidGoResult() {
9
+ return {
10
+ valid: false,
11
+ module: null,
12
+ goVersion: null,
13
+ toolchain: null,
14
+ uses: [],
15
+ localReplacements: [],
16
+ ignorePaths: [],
17
+ };
18
+ }
19
+
20
+ function stripGoComment(line) {
21
+ const index = line.indexOf("//");
22
+ return (index < 0 ? line : line.slice(0, index)).trim();
23
+ }
24
+
25
+ function parseGoDirective(line) {
26
+ const block = line.match(/^(module|go|toolchain|godebug|require|replace|exclude|retract|use|tool|ignore)\b\s*\(\s*$/u);
27
+ const match = line.match(/^(module|go|toolchain|godebug|require|replace|exclude|retract|use|tool|ignore)\b\s*(?:\(([^)]*)\)|(.+))$/u);
28
+ if (block) return { name: block[1], value: "" };
29
+ if (!match) return null;
30
+ return {
31
+ name: match[1],
32
+ value: (match[2] ?? match[3] ?? "").trim(),
33
+ };
34
+ }
35
+
36
+ function collectGoBlock(lines, start) {
37
+ const values = [];
38
+ for (let index = start + 1; index < lines.length; index += 1) {
39
+ const line = stripGoComment(lines[index]);
40
+ if (line === ")") return { values, end: index, valid: true };
41
+ if (line !== "") values.push(line);
42
+ }
43
+ return { values, end: lines.length, valid: false };
44
+ }
45
+
46
+ function validGoDirectiveValue(name, value) {
47
+ if (!value || /\s{2,}/u.test(value)) return false;
48
+ if (name === "ignore") {
49
+ const portable = value.replaceAll("\\", "/");
50
+ const segments = portable.split("/");
51
+ return !value.includes("\\")
52
+ && !portable.startsWith("/")
53
+ && !/^[A-Za-z]:\//u.test(portable)
54
+ && !segments.includes("..")
55
+ && !value.includes("=>");
56
+ }
57
+ if (["require", "exclude"].includes(name)) return /^\S+\s+v\S+$/u.test(value);
58
+ if (name === "replace") return /^\S+(?:\s+v\S+)?\s+=>\s+\S+(?:\s+v\S+)?$/u.test(value);
59
+ if (name === "retract") return /^\S+(?:\s*,\s*\S+)?$/u.test(value);
60
+ if (name === "godebug") return /^\S+=\S+$/u.test(value);
61
+ if (name === "tool") return /^\S+(?:\s+v\S+)?$/u.test(value);
62
+ return true;
63
+ }
64
+
65
+ function parseGoModMetadataDirective(state, directive) {
66
+ if (directive.name === "module") {
67
+ if (state.module !== null || directive.value === "" || /\s/u.test(directive.value)) return false;
68
+ state.module = directive.value;
69
+ return true;
70
+ }
71
+ if (directive.name === "go") {
72
+ if (state.goVersion !== null || !/^\d+(?:\.\d+){1,2}$/u.test(directive.value)) return false;
73
+ state.goVersion = directive.value;
74
+ return true;
75
+ }
76
+ if (directive.name === "toolchain") {
77
+ if (state.toolchain !== null || !/^\S+$/u.test(directive.value)) return false;
78
+ state.toolchain = directive.value;
79
+ return true;
80
+ }
81
+ return null;
82
+ }
83
+
84
+ function parseGoModBlockDirective(lines, index, directive) {
85
+ const block = collectGoBlock(lines, index);
86
+ const valid = block.valid && block.values.every((value) => validGoDirectiveValue(directive.name, value));
87
+ return {
88
+ valid,
89
+ end: block.end,
90
+ ignorePaths: valid && directive.name === "ignore" ? block.values : [],
91
+ };
92
+ }
93
+
94
+ function parseGoModLine(lines, index, state) {
95
+ const line = stripGoComment(lines[index]);
96
+ if (line === "" || line.startsWith("//")) return { valid: true, end: index, ignorePaths: [] };
97
+ const directive = parseGoDirective(line);
98
+ if (!directive || !GO_DIRECTIVE.test(line)) return { valid: false, end: index, ignorePaths: [] };
99
+ const metadataValid = parseGoModMetadataDirective(state, directive);
100
+ if (metadataValid !== null) return { valid: metadataValid, end: index, ignorePaths: [] };
101
+ if (line.endsWith("(")) return parseGoModBlockDirective(lines, index, directive);
102
+ const valid = validGoDirectiveValue(directive.name, directive.value);
103
+ return {
104
+ valid,
105
+ end: index,
106
+ ignorePaths: valid && directive.name === "ignore" ? [directive.value] : [],
107
+ };
108
+ }
109
+
110
+ export function parseGoMod(text) {
111
+ if (typeof text !== "string" || text.length > 1024 * 1024 || /\r(?!\n)/u.test(text)) return invalidGoResult();
112
+ const lines = text.replaceAll("\r\n", "\n").split("\n");
113
+ const state = { module: null, goVersion: null, toolchain: null, valid: true };
114
+ const ignorePaths = [];
115
+ for (let index = 0; index < lines.length; index += 1) {
116
+ const parsed = parseGoModLine(lines, index, state);
117
+ state.valid &&= parsed.valid;
118
+ ignorePaths.push(...parsed.ignorePaths);
119
+ index = parsed.end;
120
+ }
121
+ return {
122
+ valid: state.valid && state.module !== null,
123
+ module: state.module,
124
+ goVersion: state.goVersion,
125
+ toolchain: state.toolchain,
126
+ uses: [],
127
+ localReplacements: [],
128
+ ignorePaths: [...new Set(ignorePaths)],
129
+ };
130
+ }
131
+
132
+ export function parseGoWork(text) {
133
+ if (typeof text !== "string" || text.length > 1024 * 1024 || /\r(?!\n)/u.test(text)) {
134
+ return { valid: false, goVersion: null, uses: [] };
135
+ }
136
+ const lines = text.replaceAll("\r\n", "\n").split("\n");
137
+ let goVersion = null;
138
+ const uses = [];
139
+ let valid = true;
140
+ for (let index = 0; index < lines.length; index += 1) {
141
+ const line = stripGoComment(lines[index]);
142
+ if (line === "") continue;
143
+ const directive = parseGoDirective(line);
144
+ if (!directive) {
145
+ valid = false;
146
+ continue;
147
+ }
148
+ if (directive.name === "go") {
149
+ if (goVersion !== null || !/^\d+(?:\.\d+){1,2}$/u.test(directive.value)) valid = false;
150
+ else goVersion = directive.value;
151
+ continue;
152
+ }
153
+ if (directive.name === "toolchain") {
154
+ if (!/^\S+$/u.test(directive.value)) valid = false;
155
+ continue;
156
+ }
157
+ if (!GO_WORK_DIRECTIVES.has(directive.name)) {
158
+ valid = false;
159
+ continue;
160
+ }
161
+ if (directive.name !== "use") {
162
+ if (line.endsWith("(")) {
163
+ const block = collectGoBlock(lines, index);
164
+ if (!block.valid || !block.values.every((value) => validGoDirectiveValue(directive.name, value))) valid = false;
165
+ index = block.end;
166
+ }
167
+ continue;
168
+ }
169
+ const values = line.endsWith("(") ? collectGoBlock(lines, index) : { values: [directive.value], end: index, valid: true };
170
+ if (!values.valid || values.values.some((value) => !validGoDirectiveValue("use", value)
171
+ || value.startsWith("/") || /^[A-Za-z]:/u.test(value))) {
172
+ valid = false;
173
+ } else {
174
+ uses.push(...values.values);
175
+ }
176
+ index = values.end;
177
+ }
178
+ return { valid: valid && goVersion !== null && uses.length > 0, goVersion, uses: [...new Set(uses)] };
179
+ }
180
+
181
+ export function inspectGoProject({ projectRoot, manifestRelative, manifestText, manifestName, targetRoot, projectFiles }) {
182
+ const parsed = manifestName === "go.work" ? parseGoWork(manifestText) : parseGoMod(manifestText);
183
+ const localUses = manifestName === "go.work"
184
+ ? parsed.uses.filter((use) => projectFiles.some((file) => file.kind === "go"
185
+ && parseGoMod(file.text).valid
186
+ && portablePath(targetRoot, path.dirname(file.path)) === path.posix.normalize(path.posix.join(projectRoot, use))))
187
+ : [];
188
+ const primary = parsed.valid && (manifestName === "go.mod" || localUses.length > 0);
189
+ const primarySignals = primary
190
+ ? [manifestName === "go.mod" ? `${manifestRelative}:module` : `${manifestRelative}:workspace`]
191
+ : [];
192
+ const supportingSignals = [];
193
+ if (parsed.goVersion) supportingSignals.push(`${manifestRelative}:go=${parsed.goVersion}`);
194
+ if (parsed.toolchain) supportingSignals.push(`${manifestRelative}:toolchain=${parsed.toolchain}`);
195
+ for (const use of localUses) supportingSignals.push(`${manifestRelative}:use=${use}`);
196
+ return createLanguageProject({
197
+ kind: "go",
198
+ root: projectRoot,
199
+ manifest: manifestRelative,
200
+ frameworks: primary ? ["go"] : [],
201
+ primary,
202
+ primarySignals,
203
+ supportingSignals,
204
+ internal: { ...parsed, valid: parsed.valid, manifestName, localUses },
205
+ });
206
+ }
@@ -24,7 +24,7 @@ export const INTEGRATION_RISK_CLASSES = Object.freeze({
24
24
  });
25
25
 
26
26
  const READ_ONLY_COMMANDS = Object.freeze(new Set([
27
- "protocol-info", "status", "next", "continuity", "reconcile-continuity",
27
+ "protocol-info", "decision-status", "decision-show", "context-plan", "model-route", "semantic-plan", "test-inventory", "status", "next", "continuity", "reconcile-continuity",
28
28
  "task-list", "task-show", "task-lock-status", "progress", "audit", "report",
29
29
  "inspect", "validate-state", "validate-protocol", "validate-receipt",
30
30
  "policy-status", "policy-diff", "rule-verify", "policy",
@@ -37,7 +37,7 @@ const READ_ONLY_COMMANDS = Object.freeze(new Set([
37
37
  ]));
38
38
 
39
39
  const LOOP_MUTATION_COMMANDS = Object.freeze(new Set([
40
- "route", "preflight", "advance", "task-create", "task-scope",
40
+ "discover", "contract-create", "route", "preflight", "advance", "task-create", "task-scope",
41
41
  "record-continuity", "clear-continuity", "prepare-completion",
42
42
  "record-check", "record-diagnosis", "record-decision-criterion",
43
43
  "record-terminal-result", "complete",
@@ -86,7 +86,17 @@ const STATIC_RISK_CLASSES = Object.freeze({
86
86
  bundle: INTEGRATION_RISK_CLASSES.MAINTENANCE,
87
87
  "profile-interview": INTEGRATION_RISK_CLASSES.MAINTENANCE,
88
88
  "task-recover": INTEGRATION_RISK_CLASSES.CLAIM_RELEASE_RECOVERY,
89
+ "task-abandon": INTEGRATION_RISK_CLASSES.CLAIM_RELEASE_RECOVERY,
89
90
  "task-repair-legacy-recovery": INTEGRATION_RISK_CLASSES.LEGACY_MIGRATION,
91
+ "gate-record": INTEGRATION_RISK_CLASSES.MAINTENANCE,
92
+ "gate-revalidate": INTEGRATION_RISK_CLASSES.MAINTENANCE,
93
+ "test-utility": INTEGRATION_RISK_CLASSES.MAINTENANCE,
94
+ "test-prune-plan": INTEGRATION_RISK_CLASSES.READ_ONLY,
95
+ "test-prune-probe": INTEGRATION_RISK_CLASSES.EXTERNAL_EXECUTION,
96
+ "task-repair-contract-bootstrap": INTEGRATION_RISK_CLASSES.LEGACY_MIGRATION,
97
+ "task-migrate-contract-bootstrap-repair": INTEGRATION_RISK_CLASSES.LEGACY_MIGRATION,
98
+ "checkpoint-revalidate": INTEGRATION_RISK_CLASSES.MAINTENANCE,
99
+ "contract-revise": INTEGRATION_RISK_CLASSES.MAINTENANCE,
90
100
  });
91
101
 
92
102
  // Sparse input-dependent refinements over the static table.
@@ -169,6 +179,18 @@ export function getForgeLoopCapabilities({ packageVersion = null } = {}) {
169
179
  commands: ["next", "task-show"],
170
180
  preservesDefaultOutput: true,
171
181
  },
182
+ auditUx: {
183
+ version: 1,
184
+ supported: true,
185
+ readOnly: true,
186
+ resource: "task/audit-view",
187
+ timeline: true,
188
+ lifecycleAuthority: false,
189
+ evidenceAuthority: false,
190
+ completionAuthority: false,
191
+ mutationAuthority: false,
192
+ externalExecution: false,
193
+ },
172
194
  usageTelemetry: {
173
195
  version: 1,
174
196
  supported: true,
@@ -295,6 +317,7 @@ export function getForgeLoopCapabilities({ packageVersion = null } = {}) {
295
317
  { name: "task/approvals", scope: "TASK" },
296
318
  { name: "task/metrics", scope: "TASK" },
297
319
  { name: "task/context", scope: "TASK" },
320
+ { name: "task/audit-view", scope: "TASK" },
298
321
  { name: "task/evaluations", scope: "TASK" },
299
322
  { name: "project/capability-policy", scope: "PROJECT" },
300
323
  { name: "repository/index-status", scope: "PROJECT" },
@@ -352,8 +375,8 @@ export function classifyForgeLoopInvocation(command, input = {}) {
352
375
  removesArtifacts: definition.removes.length > 0,
353
376
  executesExternalProcess: definition.mayExecuteExternalProcess === true,
354
377
  affectsClaimAuthority: [
355
- "task-resume", "task-recover", "task-repair-legacy-recovery",
356
- "task-create", "task-scope", "complete",
378
+ "task-resume", "task-recover", "task-repair-legacy-recovery", "task-repair-contract-bootstrap",
379
+ "task-create", "task-scope", "complete", "task-migrate-contract-bootstrap-repair",
357
380
  ].includes(command),
358
381
  destructive: [
359
382
  INTEGRATION_RISK_CLASSES.AUTHORITY_MUTATION,
@@ -21,6 +21,12 @@ import { resolveAttestationStatus } from "./attestation.js";
21
21
  import { buildExecutionProfileContext } from "./execution-profile-context.js";
22
22
  import { projectStructuralQualityStatus } from "./structural-quality/service.js";
23
23
  import { getRepositoryIndexStatus } from "../repository-index/status.js";
24
+ import { buildAuditUxView } from "./audit-ux.js";
25
+ import { runContextPlan } from "../commands/context-plan.js";
26
+ import { runModelRoute } from "../commands/model-route.js";
27
+ import { readPersistedRoute } from "./route-artifact.js";
28
+ import { readWorkState } from "./work-state.js";
29
+ import { readTestUtility } from "./test-intelligence/service.js";
24
30
 
25
31
  /**
26
32
  * Canonical integration resource allowlist.
@@ -86,11 +92,18 @@ export const INTEGRATION_RESOURCE_DEFINITIONS = Object.freeze({
86
92
  "task/approvals": Object.freeze({ scope: "TASK", description: "Durable approval artifacts for one task." }),
87
93
  "task/metrics": Object.freeze({ scope: "TASK", description: "Read-only trajectory metrics for one task." }),
88
94
  "task/context": Object.freeze({ scope: "TASK", description: "Read-only profile-aware task context with bounded presentation policy." }),
95
+ "task/audit-view": Object.freeze({ scope: "TASK", description: "Bounded read-only Audit UX projection composed from canonical task resolvers." }),
89
96
  "task/evaluations": Object.freeze({ scope: "TASK", description: "Persisted trajectory evaluations for one task." }),
97
+ "task/decisions": Object.freeze({ scope: "TASK", description: "Persisted, fingerprint-bound semantic decisions without lifecycle or evidence authority." }),
98
+ "task/context-plan": Object.freeze({ scope: "TASK", description: "Bounded non-authoritative semantic context-plan projection." }),
99
+ "task/model-route": Object.freeze({ scope: "TASK", description: "Deterministic model-routing floor with advisory semantic escalation." }),
100
+ "task/test-utility": Object.freeze({ scope: "TASK", description: "Non-evidence test inventory and utility analysis; never deletion authority." }),
90
101
  "project/capability-policy": Object.freeze({ scope: "PROJECT", description: "Project capability policy, never host authority." }),
91
102
  "repository/index-status": Object.freeze({ scope: "PROJECT", description: "Provider-neutral repository-index health and owned-server status." }),
92
103
  });
93
104
 
105
+ const TASK_RESOURCE_URIS = new Set(Object.keys(INTEGRATION_RESOURCE_DEFINITIONS).filter((uri) => uri.startsWith("task/")));
106
+
94
107
  function ownershipProjection(projection) {
95
108
  return {
96
109
  taskId: projection.taskId,
@@ -120,67 +133,20 @@ async function readForgeLoopIntegrationResourceCore(uri, {
120
133
  throw error;
121
134
  }
122
135
 
123
- switch (uri) {
124
- case "protocol/info": {
125
- return { uri, data: await runProtocolInfo({ packageVersion }) };
126
- }
127
- case "project/tasks": {
128
- const tasks = await discoverTasks(projectPath, packageRoot);
129
- return {
130
- uri,
131
- data: {
132
- count: tasks.length,
133
- tasks: tasks.map((task) => ({
134
- taskId: task.taskId,
135
- healthy: task.healthy !== false,
136
- phase: task.phase ?? null,
137
- mutationAllowed: task.mutationAllowed !== false,
138
- })),
139
- },
140
- };
141
- }
142
- case "task/status":
143
- case "task/ownership":
144
- case "task/contract":
145
- case "task/continuity": {
146
- if (typeof taskId !== "string" || !taskId) {
147
- const error = new Error(`Resource ${uri} requires a taskId`);
148
- error.code = "E_TASK_REQUIRED";
149
- throw error;
150
- }
151
- break;
152
- }
153
- case "task/workspace-binding":
154
- case "task/handoffs":
155
- case "task/responsibility":
156
- case "task/verification-scope":
157
- case "task/attestation": {
158
- if (typeof taskId !== "string" || !taskId) {
159
- const error = new Error(`Resource ${uri} requires a taskId`);
160
- error.code = "E_TASK_REQUIRED";
161
- throw error;
162
- }
163
- break;
164
- }
165
- case "task/structural-quality": {
166
- if (typeof taskId !== "string" || !taskId) {
167
- const error = new Error(`Resource ${uri} requires a taskId`);
168
- error.code = "E_TASK_REQUIRED";
169
- throw error;
170
- }
171
- break;
172
- }
173
- case "task/actions":
174
- case "task/action":
175
- case "task/approvals":
176
- case "task/metrics":
177
- case "task/context":
178
- case "task/evaluations": {
179
- if (typeof taskId !== "string" || !taskId) {
180
- const error = new Error(`Resource ${uri} requires a taskId`); error.code = "E_TASK_REQUIRED"; throw error;
181
- }
182
- break;
183
- }
136
+ if (uri === "protocol/info") return { uri, data: await runProtocolInfo({ packageVersion }) };
137
+ if (uri === "project/tasks") {
138
+ const tasks = await discoverTasks(projectPath, packageRoot);
139
+ return { uri, data: { count: tasks.length, tasks: tasks.map((task) => ({
140
+ taskId: task.taskId,
141
+ healthy: task.healthy !== false,
142
+ phase: task.phase ?? null,
143
+ mutationAllowed: task.mutationAllowed !== false,
144
+ })) } };
145
+ }
146
+ if (TASK_RESOURCE_URIS.has(uri) && (typeof taskId !== "string" || !taskId)) {
147
+ const error = new Error(`Resource ${uri} requires a taskId`);
148
+ error.code = "E_TASK_REQUIRED";
149
+ throw error;
184
150
  }
185
151
 
186
152
  if (uri === "task/ownership") {
@@ -270,6 +236,45 @@ async function readForgeLoopIntegrationResourceCore(uri, {
270
236
  }
271
237
  return { uri, taskId, data: { evaluations } };
272
238
  }
239
+ if (uri === "task/decisions") {
240
+ const dir = path.join(projectPath, taskDirectory(taskId), "decisions");
241
+ let names = []; try { names = await readdir(dir); } catch { /* absent is an empty projection */ }
242
+ const decisions = [];
243
+ for (const name of names.filter((entry) => /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}\.json$/.test(entry)).sort()) {
244
+ decisions.push((await readJsonArtifact(projectPath, `${taskDirectory(taskId)}/decisions/${name}`, "semantic-decision", packageRoot)).value);
245
+ }
246
+ return { uri, taskId, data: { taskId, decisions } };
247
+ }
248
+ if (uri === "task/context-plan") {
249
+ return { uri, taskId, data: await runContextPlan({ target: projectPath, packageRoot, taskId, profile: runtimeContext?.contextProfile ?? "balanced", readOnly: true }) };
250
+ }
251
+ if (uri === "task/model-route") {
252
+ const state = await readWorkState(projectPath, { packageRoot, taskId });
253
+ const route = await readPersistedRoute(projectPath, packageRoot, { taskId });
254
+ return {
255
+ uri,
256
+ taskId,
257
+ data: await runModelRoute({
258
+ target: projectPath,
259
+ packageRoot,
260
+ taskId,
261
+ workType: route.value.input?.workType,
262
+ surfaces: route.value.input?.surfaces,
263
+ risks: route.value.input?.risks,
264
+ platforms: route.value.input?.platforms,
265
+ behaviorChange: route.value.input?.behaviorChange,
266
+ executableChange: route.value.input?.executableChange,
267
+ generationRequired: route.value.input?.generationRequired,
268
+ architectureChange: route.value.input?.architectureChange,
269
+ ambiguity: route.value.input?.ambiguity,
270
+ contract: { phase: state.phase },
271
+ }),
272
+ };
273
+ }
274
+ if (uri === "task/test-utility") {
275
+ const artifact = await readTestUtility({ target: projectPath, packageRoot, taskId });
276
+ return { uri, taskId, data: artifact.value };
277
+ }
273
278
  if (uri === "project/capability-policy") {
274
279
  return { uri, data: await loadCapabilityPolicy(projectPath, packageRoot) };
275
280
  }
@@ -292,6 +297,26 @@ async function readForgeLoopIntegrationResourceCore(uri, {
292
297
  }
293
298
 
294
299
  export async function readForgeLoopIntegrationResource(uri, options = {}) {
300
+ if (uri === "task/audit-view") {
301
+ if (typeof options.taskId !== "string" || !options.taskId) {
302
+ const error = new Error("Resource task/audit-view requires a taskId"); error.code = "E_TASK_REQUIRED"; throw error;
303
+ }
304
+ return {
305
+ uri,
306
+ taskId: options.taskId,
307
+ data: await buildAuditUxView({
308
+ target: options.projectPath ?? ".",
309
+ packageRoot: options.packageRoot,
310
+ taskId: options.taskId,
311
+ limit: options.limit,
312
+ beforeSequence: options.beforeSequence,
313
+ afterSequence: options.afterSequence,
314
+ categories: options.categories,
315
+ authorityContext: options.runtimeContext?.authorityContext,
316
+ runtimeContext: options.runtimeContext,
317
+ }),
318
+ };
319
+ }
295
320
  if (uri === "repository/index-status") {
296
321
  return {
297
322
  uri,