@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,403 @@
1
+ import { createLanguageProject, JAVA_SOURCE_EXTENSIONS, pathBelongsToRoot, portablePath } from "./multi-language-project.js";
2
+ import { maskBuildScript } from "./build-script.js";
3
+ import { parseXmlStructure } from "./xml-structure.js";
4
+
5
+ function invalidJava() {
6
+ return {
7
+ valid: false,
8
+ javaPlugin: false,
9
+ javaPlatform: false,
10
+ javaCompiler: false,
11
+ packaging: null,
12
+ modules: [],
13
+ includes: [],
14
+ gradleBuild: false,
15
+ gradleSettings: false,
16
+ gradleProperties: false,
17
+ };
18
+ }
19
+
20
+ function childNodes(node, name) {
21
+ return node.children.filter((child) => child.name.toLowerCase() === name.toLowerCase());
22
+ }
23
+
24
+ function nodesAtPath(root, names) {
25
+ return names.reduce((nodes, name) => nodes.flatMap((node) => childNodes(node, name)), [root]);
26
+ }
27
+
28
+ function nodeValues(nodes) {
29
+ return nodes
30
+ .filter((node) => node.children.length === 0 && node.text.length <= 256)
31
+ .map((node) => node.text.trim())
32
+ .filter(Boolean);
33
+ }
34
+
35
+ export function parseMavenPom(text) {
36
+ const document = parseXmlStructure(text);
37
+ if (!document || document.root.name.toLowerCase() !== "project") return invalidJava();
38
+ const root = document.root;
39
+ const packaging = nodeValues(childNodes(root, "packaging"))[0] ?? null;
40
+ const modules = nodeValues(nodesAtPath(root, ["modules", "module"]));
41
+ const compilerProperties = ["release", "source", "target"]
42
+ .some((name) => nodeValues(nodesAtPath(root, ["properties", `maven.compiler.${name}`])).length > 0);
43
+ const compilerPlugin = [
44
+ ["build", "plugins", "plugin", "artifactId"],
45
+ ["build", "pluginManagement", "plugins", "plugin", "artifactId"],
46
+ ].some((path) => nodeValues(nodesAtPath(root, path)).some((value) => value === "maven-compiler-plugin"));
47
+ const javaCompiler = compilerProperties || compilerPlugin;
48
+ const valid = nodeValues(childNodes(root, "artifactId")).length > 0
49
+ || packaging === "pom"
50
+ || modules.length > 0
51
+ || javaCompiler;
52
+ return { valid, javaPlugin: false, javaPlatform: false, javaCompiler, packaging, modules };
53
+ }
54
+
55
+ function maskGradleStringsAndComments(text) {
56
+ let result = "";
57
+ let state = "code";
58
+ let quote = null;
59
+ let escaped = false;
60
+ for (let index = 0; index < text.length; index += 1) {
61
+ const character = text[index];
62
+ const next = text[index + 1];
63
+ if (state === "string") {
64
+ if (escaped) escaped = false;
65
+ else if (character === "\\") escaped = true;
66
+ else if (character === quote) {
67
+ state = "code";
68
+ quote = null;
69
+ }
70
+ result += character === "\n" || character === "\r" ? character : " ";
71
+ } else if (state === "line-comment") {
72
+ if (character === "\n" || character === "\r") {
73
+ result += character;
74
+ state = "code";
75
+ } else result += " ";
76
+ } else if (state === "block-comment") {
77
+ if (character === "*" && next === "/") {
78
+ result += " ";
79
+ index += 1;
80
+ state = "code";
81
+ } else result += character === "\n" || character === "\r" ? character : " ";
82
+ } else if (character === "\"" || character === "'") {
83
+ result += " ";
84
+ quote = character;
85
+ state = "string";
86
+ } else if (character === "/" && next === "/") {
87
+ result += " ";
88
+ index += 1;
89
+ state = "line-comment";
90
+ } else if (character === "/" && next === "*") {
91
+ result += " ";
92
+ index += 1;
93
+ state = "block-comment";
94
+ } else {
95
+ result += character;
96
+ }
97
+ }
98
+ return state === "code" ? result : null;
99
+ }
100
+
101
+ function stripGradleComments(text) {
102
+ let result = "";
103
+ let state = "code";
104
+ let quote = null;
105
+ let escaped = false;
106
+ for (let index = 0; index < text.length; index += 1) {
107
+ const character = text[index];
108
+ const next = text[index + 1];
109
+ if (state === "string") {
110
+ result += character;
111
+ if (escaped) escaped = false;
112
+ else if (character === "\\") escaped = true;
113
+ else if (character === quote) {
114
+ state = "code";
115
+ quote = null;
116
+ }
117
+ } else if (state === "line-comment") {
118
+ if (character === "\n" || character === "\r") {
119
+ result += character;
120
+ state = "code";
121
+ } else result += " ";
122
+ } else if (state === "block-comment") {
123
+ if (character === "*" && next === "/") {
124
+ result += " ";
125
+ index += 1;
126
+ state = "code";
127
+ } else result += character === "\n" || character === "\r" ? character : " ";
128
+ } else if (character === "\"" || character === "'") {
129
+ result += character;
130
+ quote = character;
131
+ state = "string";
132
+ } else if (character === "/" && next === "/") {
133
+ result += " ";
134
+ index += 1;
135
+ state = "line-comment";
136
+ } else if (character === "/" && next === "*") {
137
+ result += " ";
138
+ index += 1;
139
+ state = "block-comment";
140
+ } else result += character;
141
+ }
142
+ return state === "code" ? result : null;
143
+ }
144
+
145
+ const JAVA_PLUGIN_IDS = new Set(["java", "java-library", "java-platform", "java-gradle-plugin", "application", "war"]);
146
+
147
+ function quotedPluginId(rawBlock, start) {
148
+ const plugin = rawBlock.slice(start).match(/^\s*(?:\(\s*)?["']([^"']+)["']/u)?.[1];
149
+ return JAVA_PLUGIN_IDS.has(plugin);
150
+ }
151
+
152
+ function pluginBlockHasJava(rawBlock) {
153
+ const masked = maskGradleStringsAndComments(rawBlock);
154
+ if (masked === null) return false;
155
+ for (const match of masked.matchAll(/\bid\b/gu)) {
156
+ if (quotedPluginId(rawBlock, match.index + match[0].length)) return true;
157
+ }
158
+ return /(?:^|[;{}\n])\s*(?:`(?:java|javaLibrary|javaPlatform|java-gradle-plugin|application|war)`|java|javaLibrary|javaPlatform|java-gradle-plugin|application|war)\s*(?:[;\n}]|$)/mu.test(masked);
159
+ }
160
+
161
+ function hasJavaPlugin(text) {
162
+ const masked = maskGradleStringsAndComments(text);
163
+ if (masked === null) return false;
164
+ for (const match of masked.matchAll(/\bplugins\s*\{([\s\S]*?)\}/gu)) {
165
+ const open = match[0].indexOf("{");
166
+ if (pluginBlockHasJava(text.slice(match.index + open + 1, match.index + match[0].length - 1))) return true;
167
+ }
168
+ for (const match of masked.matchAll(/\bapply\s+plugin\s*:\s*/gu)) {
169
+ const suffix = text.slice(match.index + match[0].length);
170
+ if (/^['"](?:java|java-library|java-platform|java-gradle-plugin|application|war)['"]/u.test(suffix)) return true;
171
+ }
172
+ return false;
173
+ }
174
+
175
+ function splitGradleArguments(text) {
176
+ if (text.trim() === "") return [];
177
+ const parts = [];
178
+ let start = 0;
179
+ let quote = null;
180
+ let escaped = false;
181
+ let depth = 0;
182
+ for (let index = 0; index < text.length; index += 1) {
183
+ const character = text[index];
184
+ if (quote) {
185
+ if (escaped) escaped = false;
186
+ else if (character === "\\") escaped = true;
187
+ else if (character === quote) quote = null;
188
+ continue;
189
+ }
190
+ if (character === "\"" || character === "'") {
191
+ quote = character;
192
+ } else if (character === "(" || character === "[" || character === "{") {
193
+ depth += 1;
194
+ } else if (character === ")" || character === "]" || character === "}") {
195
+ depth -= 1;
196
+ if (depth < 0) return null;
197
+ } else if (character === "," && depth === 0) {
198
+ parts.push(text.slice(start, index).trim());
199
+ start = index + 1;
200
+ }
201
+ }
202
+ if (quote || depth !== 0) return null;
203
+ parts.push(text.slice(start).trim());
204
+ return parts.filter(Boolean);
205
+ }
206
+
207
+ function gradleQuotedValue(value) {
208
+ const trimmed = value.trim();
209
+ if (trimmed.length < 2 || !["\"", "'"].includes(trimmed[0]) || trimmed.at(-1) !== trimmed[0]) return null;
210
+ const quote = trimmed[0];
211
+ if (trimmed.includes("$")) return null;
212
+ let result = "";
213
+ let escaped = false;
214
+ for (let index = 1; index < trimmed.length; index += 1) {
215
+ const character = trimmed[index];
216
+ if (escaped) {
217
+ result += character;
218
+ escaped = false;
219
+ } else if (character === "\\") {
220
+ escaped = true;
221
+ } else if (character === quote) {
222
+ return index === trimmed.length - 1 ? result : null;
223
+ } else {
224
+ result += character;
225
+ }
226
+ }
227
+ return null;
228
+ }
229
+
230
+ function gradleParenthesisEnd(source, open) {
231
+ let depth = 1;
232
+ for (let index = open + 1; index < source.length; index += 1) {
233
+ if (source[index] === "(") depth += 1;
234
+ else if (source[index] === ")") {
235
+ depth -= 1;
236
+ if (depth === 0) return index;
237
+ }
238
+ }
239
+ return -1;
240
+ }
241
+
242
+ function topLevelGradleIncludeOffsets(source) {
243
+ const offsets = [];
244
+ const depths = { braces: 0, parentheses: 0, brackets: 0 };
245
+ for (let index = 0; index < source.length; index += 1) {
246
+ const character = source[index];
247
+ if (character === "{") depths.braces += 1;
248
+ else if (character === "}") depths.braces = Math.max(0, depths.braces - 1);
249
+ else if (character === "(") depths.parentheses += 1;
250
+ else if (character === ")") depths.parentheses = Math.max(0, depths.parentheses - 1);
251
+ else if (character === "[") depths.brackets += 1;
252
+ else if (character === "]") depths.brackets = Math.max(0, depths.brackets - 1);
253
+ if (Object.values(depths).some((depth) => depth !== 0)
254
+ || !source.startsWith("include", index)
255
+ || /[A-Za-z0-9_]/u.test(source[index - 1] ?? "")
256
+ || /[A-Za-z0-9_]/u.test(source[index + 7] ?? "")) continue;
257
+ offsets.push(index);
258
+ }
259
+ return offsets;
260
+ }
261
+
262
+ function parseGradleIncludeAt(text, source, index) {
263
+ let cursor = index + 7;
264
+ while (/\s/u.test(text[cursor] ?? "")) cursor += 1;
265
+ let body;
266
+ if (text[cursor] === "(") {
267
+ const close = gradleParenthesisEnd(source, cursor);
268
+ if (close < 0) return null;
269
+ body = text.slice(cursor + 1, close);
270
+ } else {
271
+ const end = text.indexOf("\n", cursor);
272
+ body = text.slice(cursor, end < 0 ? text.length : end);
273
+ }
274
+ const commentFreeBody = stripGradleComments(body);
275
+ const values = commentFreeBody === null ? null : splitGradleArguments(commentFreeBody);
276
+ if (values === null) return null;
277
+ const literals = values.map((value) => gradleQuotedValue(value));
278
+ return literals.some((literal) => literal === null || literal.trim() === "") ? null : literals;
279
+ }
280
+
281
+ function isStandaloneGradleInclude(source, index) {
282
+ const statementStart = Math.max(
283
+ source.lastIndexOf("\n", index - 1),
284
+ source.lastIndexOf("\r", index - 1),
285
+ source.lastIndexOf(";", index - 1),
286
+ ) + 1;
287
+ return source.slice(statementStart, index).trim() === "";
288
+ }
289
+
290
+ function isUnbracedGradleControlBody(source, index) {
291
+ const lineStart = Math.max(source.lastIndexOf("\n", index - 1), source.lastIndexOf("\r", index - 1)) + 1;
292
+ let previousEnd = lineStart - 1;
293
+ while (previousEnd >= 0) {
294
+ const previousStart = Math.max(
295
+ source.lastIndexOf("\n", previousEnd - 1),
296
+ source.lastIndexOf("\r", previousEnd - 1),
297
+ ) + 1;
298
+ const previousLine = source.slice(previousStart, previousEnd).trim();
299
+ if (previousLine) {
300
+ if (previousLine.includes("{")) return false;
301
+ return /^(?:if|else|for|while|when|switch)\b/u.test(previousLine);
302
+ }
303
+ previousEnd = previousStart - 1;
304
+ }
305
+ return false;
306
+ }
307
+
308
+ export function parseGradleSettings(text) {
309
+ const invalid = {
310
+ valid: false,
311
+ javaPlugin: false,
312
+ javaPlatform: false,
313
+ javaCompiler: false,
314
+ packaging: null,
315
+ modules: [],
316
+ includes: [],
317
+ gradleBuild: false,
318
+ gradleSettings: true,
319
+ gradleProperties: false,
320
+ };
321
+ if (typeof text !== "string" || text.length > 1024 * 1024 || /\r(?!\n)/u.test(text)) return invalid;
322
+ const source = maskGradleStringsAndComments(text);
323
+ if (source === null) return invalid;
324
+ const includes = [];
325
+ for (const offset of topLevelGradleIncludeOffsets(source)) {
326
+ if (!isStandaloneGradleInclude(source, offset) || isUnbracedGradleControlBody(source, offset)) continue;
327
+ const literals = parseGradleIncludeAt(text, source, offset);
328
+ if (literals === null) return invalid;
329
+ includes.push(...literals);
330
+ }
331
+ return { ...invalid, valid: true, includes: [...new Set(includes)] };
332
+ }
333
+
334
+ export function parseGradleProperties(text) {
335
+ if (typeof text !== "string" || text.length > 1024 * 1024 || /\r(?!\n)/u.test(text)) return invalidJava();
336
+ return { ...invalidJava(), valid: true, gradleProperties: true };
337
+ }
338
+
339
+ export function parseGradleBuild(text) {
340
+ if (typeof text !== "string" || text.length > 1024 * 1024 || /\r(?!\n)/u.test(text)) return invalidJava();
341
+ const source = maskGradleStringsAndComments(text);
342
+ if (source === null) return invalidJava();
343
+ const javaPlugin = hasJavaPlugin(text);
344
+ const javaPlatform = javaPlugin && /(?:java-platform|javaPlatform)/u.test(text);
345
+ return {
346
+ valid: javaPlugin || javaPlatform,
347
+ javaPlugin,
348
+ javaPlatform,
349
+ javaCompiler: false,
350
+ packaging: null,
351
+ modules: [],
352
+ includes: [],
353
+ gradleBuild: true,
354
+ gradleSettings: false,
355
+ gradleProperties: false,
356
+ };
357
+ }
358
+
359
+ export function parseBazelJava(text) {
360
+ if (typeof text !== "string" || text.length > 1024 * 1024 || /\r(?!\n)/u.test(text)) return invalidJava();
361
+ const source = maskBuildScript(text);
362
+ const javaPlugin = source !== null && /\bjava_(?:binary|library|test|import|plugin)\s*\(/u.test(source);
363
+ return { valid: javaPlugin, javaPlugin, javaPlatform: false, javaCompiler: false, packaging: null, modules: [] };
364
+ }
365
+
366
+ function hasOwnedJavaSource(sourceFiles, targetRoot, projectRoot, projectRoots) {
367
+ return sourceFiles.some((file) => JAVA_SOURCE_EXTENSIONS.has(file.extension)
368
+ && pathBelongsToRoot(portablePath(targetRoot, file.path), projectRoot, projectRoots));
369
+ }
370
+
371
+ export function inspectJavaProject({ projectRoot, manifestRelative, manifestText, manifestName, targetRoot, sourceFiles, projectRoots }) {
372
+ const parsed = manifestName === "pom.xml"
373
+ ? parseMavenPom(manifestText)
374
+ : manifestName === "build" || manifestName === "build.bazel"
375
+ ? parseBazelJava(manifestText)
376
+ : manifestName === "settings.gradle" || manifestName === "settings.gradle.kts"
377
+ ? parseGradleSettings(manifestText)
378
+ : manifestName === "gradle.properties"
379
+ ? parseGradleProperties(manifestText)
380
+ : parseGradleBuild(manifestText);
381
+ const javaSource = hasOwnedJavaSource(sourceFiles, targetRoot, projectRoot, projectRoots);
382
+ const canProvideJavaEvidence = !parsed.gradleSettings && !parsed.gradleProperties;
383
+ const structurallyValid = parsed.valid || parsed.gradleBuild;
384
+ const primary = canProvideJavaEvidence && structurallyValid
385
+ && (javaSource || parsed.javaPlugin || parsed.javaPlatform || parsed.javaCompiler);
386
+ const primarySignals = primary
387
+ ? [`${manifestRelative}:${parsed.javaPlatform ? "java-platform" : manifestName === "pom.xml" ? "maven" : "build"}`]
388
+ : [];
389
+ const supportingSignals = [];
390
+ if (javaSource) supportingSignals.push(`${projectRoot === "." ? "source" : `${projectRoot}/source`}:java`);
391
+ for (const module of parsed.modules) supportingSignals.push(`${manifestRelative}:module=${module}`);
392
+ for (const include of parsed.includes ?? []) supportingSignals.push(`${manifestRelative}:include=${include}`);
393
+ return createLanguageProject({
394
+ kind: "java",
395
+ root: projectRoot,
396
+ manifest: manifestRelative,
397
+ frameworks: primary ? ["java"] : [],
398
+ primary,
399
+ primarySignals,
400
+ supportingSignals,
401
+ internal: { ...parsed, valid: parsed.valid },
402
+ });
403
+ }
@@ -0,0 +1,10 @@
1
+ import { DECISION_AUTHORITY, DECISION_EVIDENCE_AUTHORITY, PINNED_JEV_MODEL } from "../decision/constants.js";
2
+
3
+ export const MODEL_ROUTE_TIERS = Object.freeze(["NONE", "FAST", "STANDARD", "PRIMARY"]);
4
+ export const MODEL_ROUTE_POLICY_VERSION = 1;
5
+ export const MODEL_ROUTE_PROJECTION_AUTHORITY = DECISION_AUTHORITY;
6
+ export const MODEL_ROUTE_PROJECTION_EVIDENCE_AUTHORITY = DECISION_EVIDENCE_AUTHORITY;
7
+ export const MODEL_ROUTE_JEV_MODEL = PINNED_JEV_MODEL;
8
+
9
+ export const MODEL_ROUTE_RANK = Object.freeze({ NONE: 0, FAST: 1, STANDARD: 2, PRIMARY: 3 });
10
+
@@ -0,0 +1,103 @@
1
+ import { MODEL_ROUTE_RANK, MODEL_ROUTE_TIERS } from "./constants.js";
2
+
3
+ const GENERATION_WORK_TYPES = new Set([
4
+ "code", "bug", "refactor", "backend", "api", "infrastructure", "security-review", "release",
5
+ ]);
6
+ const PRIMARY_WORK_TYPES = new Set(["infrastructure", "security-review", "release"]);
7
+ const PRIMARY_RISKS = new Set(["secrets", "personal-data", "publication", "critical-path", "destructive", "irreversible", "migration", "credentials", "payment"]);
8
+ const PRIMARY_SURFACES = new Set(["auth", "critical-path", "database"]);
9
+
10
+ function text(value) {
11
+ return typeof value === "string" ? value.toLowerCase() : "";
12
+ }
13
+
14
+ function array(value) {
15
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
16
+ }
17
+
18
+ function contractText(contract) {
19
+ if (!contract || typeof contract !== "object" || Array.isArray(contract)) return "";
20
+ const values = [contract.objective, contract.summary, ...(contract.deliverables ?? []), ...(contract.successCriteria ?? []), ...(contract.risks ?? []), ...(contract.constraints ?? [])];
21
+ return values.map((value) => value && typeof value === "object" ? value.text : value).filter((value) => typeof value === "string").join(" ").toLowerCase();
22
+ }
23
+
24
+ function normalizedInput(input = {}) {
25
+ return {
26
+ workType: text(input.workType),
27
+ surfaces: array(input.surfaces).map(text),
28
+ risks: array(input.risks).map(text),
29
+ platforms: array(input.platforms).map(text),
30
+ behaviorChange: input.behaviorChange === true,
31
+ executableChange: input.executableChange === true,
32
+ generationRequired: input.generationRequired === true,
33
+ architectureChange: input.architectureChange === true,
34
+ ambiguity: input.ambiguity === true,
35
+ contract: input.contract,
36
+ };
37
+ }
38
+
39
+ function deterministicFloor(input) {
40
+ const normalized = normalizedInput(input);
41
+ const reasons = [];
42
+ const combined = `${contractText(normalized.contract)} ${normalized.workType} ${normalized.surfaces.join(" ")} ${normalized.risks.join(" ")}`;
43
+ const generationRequired = normalized.generationRequired
44
+ || normalized.behaviorChange
45
+ || normalized.executableChange
46
+ || GENERATION_WORK_TYPES.has(normalized.workType)
47
+ || /\b(implement|fix|refactor|change|build|generate|write|modify|code)\b/.test(combined);
48
+ const primary = normalized.architectureChange
49
+ || normalized.ambiguity
50
+ || PRIMARY_WORK_TYPES.has(normalized.workType)
51
+ || normalized.risks.some((risk) => PRIMARY_RISKS.has(risk))
52
+ || normalized.surfaces.some((surface) => PRIMARY_SURFACES.has(surface))
53
+ || /\b(architecture|architectural|security boundary|migration|ambiguous|unknown requirements)\b/.test(combined);
54
+
55
+ if (!generationRequired) {
56
+ reasons.push("NO_GENERATION_REQUIRED");
57
+ return { floor: "NONE", reasons };
58
+ }
59
+ reasons.push("GENERATION_REQUIRED");
60
+ if (primary) {
61
+ reasons.push("PRIMARY_COMPLEXITY_OR_RISK");
62
+ return { floor: "PRIMARY", reasons };
63
+ }
64
+ reasons.push("STANDARD_GENERATION_FLOOR");
65
+ return { floor: "STANDARD", reasons };
66
+ }
67
+
68
+ function normalizeRecommendation(recommendation) {
69
+ if (recommendation === null || recommendation === undefined) return null;
70
+ if (typeof recommendation === "string") {
71
+ return MODEL_ROUTE_TIERS.includes(recommendation) ? { tier: recommendation, confidence: null, requiresEscalation: false } : null;
72
+ }
73
+ if (!recommendation || typeof recommendation !== "object" || Array.isArray(recommendation)) return null;
74
+ const tier = typeof recommendation.tier === "string" && MODEL_ROUTE_TIERS.includes(recommendation.tier) ? recommendation.tier : null;
75
+ if (!tier) return null;
76
+ return {
77
+ tier,
78
+ confidence: typeof recommendation.confidence === "number" && Number.isFinite(recommendation.confidence) ? recommendation.confidence : null,
79
+ requiresEscalation: recommendation.requiresEscalation === true,
80
+ };
81
+ }
82
+
83
+ export function resolveModelRoute({ input = {}, semanticRecommendation = null } = {}) {
84
+ const deterministic = deterministicFloor(input);
85
+ const recommendation = normalizeRecommendation(semanticRecommendation);
86
+ let resolved = deterministic.floor;
87
+ const reasons = [...deterministic.reasons];
88
+ if (recommendation?.requiresEscalation) {
89
+ if (MODEL_ROUTE_RANK[resolved] < MODEL_ROUTE_RANK.PRIMARY) resolved = "PRIMARY";
90
+ reasons.push("JEV_LOW_CONFIDENCE_ESCALATION");
91
+ } else if (recommendation && MODEL_ROUTE_RANK[recommendation.tier] > MODEL_ROUTE_RANK[resolved]) {
92
+ resolved = recommendation.tier;
93
+ reasons.push("JEV_ESCALATION_ONLY");
94
+ }
95
+ return {
96
+ floor: deterministic.floor,
97
+ resolved,
98
+ reasons: [...new Set(reasons)],
99
+ semanticRecommendation: recommendation,
100
+ generationRequired: deterministic.floor !== "NONE",
101
+ };
102
+ }
103
+
@@ -0,0 +1,37 @@
1
+ import {
2
+ MODEL_ROUTE_JEV_MODEL,
3
+ MODEL_ROUTE_POLICY_VERSION,
4
+ MODEL_ROUTE_PROJECTION_AUTHORITY,
5
+ MODEL_ROUTE_PROJECTION_EVIDENCE_AUTHORITY,
6
+ MODEL_ROUTE_RANK,
7
+ MODEL_ROUTE_TIERS,
8
+ } from "./constants.js";
9
+ import { resolveModelRoute } from "./policy.js";
10
+
11
+ export function projectModelRoute({ input = {}, semanticRecommendation = null } = {}) {
12
+ const route = resolveModelRoute({ input, semanticRecommendation });
13
+ if (!MODEL_ROUTE_TIERS.includes(route.floor) || !MODEL_ROUTE_TIERS.includes(route.resolved)
14
+ || MODEL_ROUTE_RANK[route.resolved] < MODEL_ROUTE_RANK[route.floor]) {
15
+ const error = new Error("Model route violates its deterministic safety floor.");
16
+ error.code = "E_MODEL_ROUTE_SAFETY_FLOOR_INVALID";
17
+ throw error;
18
+ }
19
+ return {
20
+ schemaVersion: 1,
21
+ policyVersion: MODEL_ROUTE_POLICY_VERSION,
22
+ engine: "typesafe-jev",
23
+ model: MODEL_ROUTE_JEV_MODEL,
24
+ floor: route.floor,
25
+ resolved: route.resolved,
26
+ reasons: route.reasons,
27
+ generationRequired: route.generationRequired,
28
+ semanticRecommendation: route.semanticRecommendation,
29
+ authority: MODEL_ROUTE_PROJECTION_AUTHORITY,
30
+ evidenceAuthority: MODEL_ROUTE_PROJECTION_EVIDENCE_AUTHORITY,
31
+ lifecycleAuthority: false,
32
+ completionAuthority: false,
33
+ ownershipAuthority: false,
34
+ installationAuthority: false,
35
+ };
36
+ }
37
+
@@ -0,0 +1,117 @@
1
+ import path from "node:path";
2
+
3
+ export const LANGUAGE_PROJECT_FILE_KINDS = Object.freeze({
4
+ "go.mod": ["go"],
5
+ "go.work": ["go-work"],
6
+ "tsconfig.json": ["typescript"],
7
+ "jsconfig.json": ["javascript-config"],
8
+ "composer.json": ["php"],
9
+ "pom.xml": ["java-maven"],
10
+ "build.gradle": ["java-gradle"],
11
+ "build.gradle.kts": ["java-gradle"],
12
+ "settings.gradle": ["java-gradle-settings"],
13
+ "settings.gradle.kts": ["java-gradle-settings"],
14
+ "gradle.properties": ["java-gradle-properties"],
15
+ "build": ["java-bazel", "native-bazel"],
16
+ "build.bazel": ["java-bazel", "native-bazel"],
17
+ "cmakelists.txt": ["c-cpp-cmake", "swift-cmake"],
18
+ "meson.build": ["c-cpp-meson", "swift-meson"],
19
+ "meson_options.txt": ["c-cpp-meson-options"],
20
+ "makefile": ["c-cpp-make"],
21
+ "gnumakefile": ["c-cpp-make"],
22
+ "package.swift": ["swift-package"],
23
+ "project.pbxproj": ["swift-xcode"],
24
+ });
25
+
26
+ export const LANGUAGE_SOURCE_EXTENSIONS = Object.freeze(new Set([
27
+ ".c", ".cc", ".cpp", ".cxx", ".c++",
28
+ ".go", ".java", ".php", ".sql", ".psql", ".pgsql", ".swift",
29
+ ".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs",
30
+ ]));
31
+
32
+ export const SQL_SOURCE_EXTENSIONS = Object.freeze(new Set([".sql", ".psql", ".pgsql"]));
33
+ export const C_SOURCE_EXTENSIONS = Object.freeze(new Set([".c"]));
34
+ export const CPP_SOURCE_EXTENSIONS = Object.freeze(new Set([".cc", ".cpp", ".cxx", ".c++", ".C"]));
35
+ export const JAVA_SOURCE_EXTENSIONS = Object.freeze(new Set([".java"]));
36
+ export const PHP_SOURCE_EXTENSIONS = Object.freeze(new Set([".php", ".phtml", ".php3", ".php4", ".php5", ".php7", ".phps"]));
37
+ export const SWIFT_SOURCE_EXTENSIONS = Object.freeze(new Set([".swift"]));
38
+
39
+ export function languageProjectKinds(fileName) {
40
+ if (typeof fileName !== "string") return [];
41
+ const lowerName = fileName.toLowerCase();
42
+ if (/^tsconfig\.[^/]+\.json$/u.test(lowerName)) return ["typescript"];
43
+ if (/\.sqlproj$/u.test(lowerName)) return ["sql-project"];
44
+ return LANGUAGE_PROJECT_FILE_KINDS[lowerName] ?? [];
45
+ }
46
+
47
+ export function languageSourceExtension(fileName) {
48
+ if (typeof fileName !== "string") return null;
49
+ const extension = path.extname(fileName);
50
+ const normalized = extension === ".C" ? ".C" : extension.toLowerCase();
51
+ return LANGUAGE_SOURCE_EXTENSIONS.has(normalized.toLowerCase()) ? normalized : null;
52
+ }
53
+
54
+ export function portablePath(root, absolutePath) {
55
+ const relative = path.relative(root, absolutePath).split(path.sep).join("/");
56
+ return relative === "" ? "." : relative;
57
+ }
58
+
59
+ export function relativeProjectRoots(targetRoot, projectFiles = []) {
60
+ return [...new Set(projectFiles
61
+ .map((entry) => portablePath(targetRoot, path.dirname(entry.path)))
62
+ .filter((root) => root !== ".." && !root.startsWith("../")))].sort((left, right) => left.localeCompare(right));
63
+ }
64
+
65
+ export function pathBelongsToRoot(relativePath, root, projectRoots = []) {
66
+ const normalizedPath = relativePath.replaceAll("\\", "/").toLowerCase();
67
+ const normalizedRoot = root.replaceAll("\\", "/").toLowerCase();
68
+ if (normalizedRoot !== "."
69
+ && normalizedPath !== normalizedRoot
70
+ && !normalizedPath.startsWith(`${normalizedRoot}/`)) return false;
71
+ return !projectRoots.some((candidate) => {
72
+ const normalizedCandidate = candidate.replaceAll("\\", "/").toLowerCase();
73
+ const nested = normalizedRoot === "."
74
+ ? normalizedCandidate !== "."
75
+ : normalizedCandidate.startsWith(`${normalizedRoot}/`);
76
+ return nested
77
+ && (normalizedPath === normalizedCandidate || normalizedPath.startsWith(`${normalizedCandidate}/`));
78
+ });
79
+ }
80
+
81
+ export function sourceFilesUnderRoot(sourceFiles, targetRoot, root, extensions, projectRoots = []) {
82
+ return sourceFiles.filter((file) => {
83
+ if (!extensions.has(languageSourceExtension(file.name))) return false;
84
+ return pathBelongsToRoot(portablePath(targetRoot, file.path), root, projectRoots);
85
+ });
86
+ }
87
+
88
+ export function createLanguageProject({
89
+ kind,
90
+ root,
91
+ manifest,
92
+ frameworks = [],
93
+ primary = false,
94
+ primarySignals = [],
95
+ supportingSignals = [],
96
+ internal = null,
97
+ }) {
98
+ return {
99
+ kind,
100
+ root,
101
+ manifest,
102
+ validManifest: Boolean(internal?.valid ?? primary),
103
+ primary,
104
+ frameworks: [...new Set(frameworks)].sort((left, right) => left.localeCompare(right)),
105
+ primarySignals: [...primarySignals],
106
+ supportingSignals: [...supportingSignals],
107
+ internal,
108
+ };
109
+ }
110
+
111
+ export function isDirectSourceClaim(claim, extensions) {
112
+ return extensions.has(path.extname(claim).toLowerCase());
113
+ }
114
+
115
+ export function isConfigOnlyProject(project) {
116
+ return !project?.frameworks?.length && !project?.primary;
117
+ }