@intentius/chant 0.79.0 → 0.81.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 (462) hide show
  1. package/dist/audit/core.d.ts.map +1 -1
  2. package/dist/audit/discover.d.ts +30 -0
  3. package/dist/audit/discover.d.ts.map +1 -1
  4. package/dist/audit/report-html.d.ts +5 -0
  5. package/dist/audit/report-html.d.ts.map +1 -1
  6. package/dist/audit/report-model.d.ts +13 -0
  7. package/dist/audit/report-model.d.ts.map +1 -1
  8. package/dist/audit/terraform-state.d.ts +13 -0
  9. package/dist/audit/terraform-state.d.ts.map +1 -1
  10. package/dist/cli/command-group.d.ts +6 -0
  11. package/dist/cli/command-group.d.ts.map +1 -1
  12. package/dist/cli/commands/audit.d.ts +44 -3
  13. package/dist/cli/commands/audit.d.ts.map +1 -1
  14. package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -1
  15. package/dist/cli/commands/check-lexicon.d.ts +8 -0
  16. package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
  17. package/dist/cli/commands/doctor.d.ts.map +1 -1
  18. package/dist/cli/commands/import-agents.d.ts.map +1 -1
  19. package/dist/cli/commands/init.d.ts +6 -0
  20. package/dist/cli/commands/init.d.ts.map +1 -1
  21. package/dist/cli/commands/lint.d.ts.map +1 -1
  22. package/dist/cli/commands/onboard.d.ts.map +1 -1
  23. package/dist/cli/commands/update.d.ts +6 -0
  24. package/dist/cli/commands/update.d.ts.map +1 -1
  25. package/dist/cli/commands/vendor.d.ts +5 -0
  26. package/dist/cli/commands/vendor.d.ts.map +1 -1
  27. package/dist/cli/conflict-check.d.ts +6 -0
  28. package/dist/cli/conflict-check.d.ts.map +1 -1
  29. package/dist/cli/handlers/build.d.ts.map +1 -1
  30. package/dist/cli/handlers/components.d.ts.map +1 -1
  31. package/dist/cli/handlers/dev.d.ts.map +1 -1
  32. package/dist/cli/handlers/fan-out.d.ts.map +1 -1
  33. package/dist/cli/handlers/init.d.ts.map +1 -1
  34. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  35. package/dist/cli/handlers/misc.d.ts.map +1 -1
  36. package/dist/cli/handlers/operator.d.ts +7 -0
  37. package/dist/cli/handlers/operator.d.ts.map +1 -1
  38. package/dist/cli/handlers/promote.d.ts +20 -0
  39. package/dist/cli/handlers/promote.d.ts.map +1 -0
  40. package/dist/cli/handlers/run-generate.d.ts +39 -0
  41. package/dist/cli/handlers/run-generate.d.ts.map +1 -0
  42. package/dist/cli/handlers/run.d.ts +4 -1
  43. package/dist/cli/handlers/run.d.ts.map +1 -1
  44. package/dist/cli/handlers/vendor.d.ts +8 -3
  45. package/dist/cli/handlers/vendor.d.ts.map +1 -1
  46. package/dist/cli/main.d.ts +12 -0
  47. package/dist/cli/main.d.ts.map +1 -1
  48. package/dist/cli/mcp/op-tools.d.ts.map +1 -1
  49. package/dist/cli/plugins.d.ts +14 -0
  50. package/dist/cli/plugins.d.ts.map +1 -1
  51. package/dist/cli/registry.d.ts +46 -1
  52. package/dist/cli/registry.d.ts.map +1 -1
  53. package/dist/codegen/fetch.d.ts +10 -0
  54. package/dist/codegen/fetch.d.ts.map +1 -1
  55. package/dist/components/capability-plugin-loader.d.ts.map +1 -1
  56. package/dist/components/cli-support.d.ts +3 -1
  57. package/dist/components/cli-support.d.ts.map +1 -1
  58. package/dist/components/discover.d.ts.map +1 -1
  59. package/dist/components/driver.d.ts +16 -2
  60. package/dist/components/driver.d.ts.map +1 -1
  61. package/dist/components/fan-out-support.d.ts.map +1 -1
  62. package/dist/components/gate-plan.d.ts +40 -0
  63. package/dist/components/gate-plan.d.ts.map +1 -0
  64. package/dist/components/pilots/alb-ecs.pilot.d.ts +7 -1
  65. package/dist/components/pilots/alb-ecs.pilot.d.ts.map +1 -1
  66. package/dist/components/presets/ecs-fargate.d.ts +10 -4
  67. package/dist/components/presets/ecs-fargate.d.ts.map +1 -1
  68. package/dist/components/promote.d.ts +264 -0
  69. package/dist/components/promote.d.ts.map +1 -0
  70. package/dist/components/sandbox/driver.d.ts.map +1 -1
  71. package/dist/components/sandbox/run.d.ts.map +1 -1
  72. package/dist/components/verbs/build-archive.d.ts +22 -11
  73. package/dist/components/verbs/build-archive.d.ts.map +1 -1
  74. package/dist/components/verbs/run-agent.d.ts.map +1 -1
  75. package/dist/config-static.d.ts +59 -0
  76. package/dist/config-static.d.ts.map +1 -0
  77. package/dist/config.d.ts +104 -5
  78. package/dist/config.d.ts.map +1 -1
  79. package/dist/content-digest.d.ts +17 -0
  80. package/dist/content-digest.d.ts.map +1 -0
  81. package/dist/discovery/files.d.ts +48 -4
  82. package/dist/discovery/files.d.ts.map +1 -1
  83. package/dist/discovery/fold-import.d.ts +32 -1
  84. package/dist/discovery/fold-import.d.ts.map +1 -1
  85. package/dist/discovery/index.d.ts.map +1 -1
  86. package/dist/discovery/sandbox/driver.d.ts +20 -0
  87. package/dist/discovery/sandbox/driver.d.ts.map +1 -1
  88. package/dist/discovery/sandbox/fork.d.ts +20 -0
  89. package/dist/discovery/sandbox/fork.d.ts.map +1 -1
  90. package/dist/discovery/walk.d.ts +119 -0
  91. package/dist/discovery/walk.d.ts.map +1 -0
  92. package/dist/graph-ir.d.ts +17 -1
  93. package/dist/graph-ir.d.ts.map +1 -1
  94. package/dist/join-key.d.ts +31 -0
  95. package/dist/join-key.d.ts.map +1 -0
  96. package/dist/lexicon-module.d.ts +110 -0
  97. package/dist/lexicon-module.d.ts.map +1 -0
  98. package/dist/lexicon.d.ts +82 -1
  99. package/dist/lexicon.d.ts.map +1 -1
  100. package/dist/lifecycle/build-ledger-store.d.ts.map +1 -1
  101. package/dist/lifecycle/digest.d.ts +5 -1
  102. package/dist/lifecycle/digest.d.ts.map +1 -1
  103. package/dist/lifecycle/gate-ledger.d.ts +20 -5
  104. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  105. package/dist/lifecycle/git.d.ts +64 -0
  106. package/dist/lifecycle/git.d.ts.map +1 -1
  107. package/dist/lifecycle/lease.d.ts +8 -1
  108. package/dist/lifecycle/lease.d.ts.map +1 -1
  109. package/dist/lifecycle/legacy-digest.d.ts +59 -0
  110. package/dist/lifecycle/legacy-digest.d.ts.map +1 -0
  111. package/dist/lifecycle/member-ledger.d.ts +67 -0
  112. package/dist/lifecycle/member-ledger.d.ts.map +1 -0
  113. package/dist/lifecycle/plan-digest.d.ts +4 -3
  114. package/dist/lifecycle/plan-digest.d.ts.map +1 -1
  115. package/dist/lifecycle/release-ledger.d.ts +42 -1
  116. package/dist/lifecycle/release-ledger.d.ts.map +1 -1
  117. package/dist/lifecycle/status.d.ts +90 -5
  118. package/dist/lifecycle/status.d.ts.map +1 -1
  119. package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts +2 -3
  120. package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts.map +1 -1
  121. package/dist/op/activities/activity-contracts.d.ts +32 -0
  122. package/dist/op/activities/activity-contracts.d.ts.map +1 -1
  123. package/dist/op/activities/index.d.ts +2 -0
  124. package/dist/op/activities/index.d.ts.map +1 -1
  125. package/dist/op/activities/propose-upgrade.d.ts +75 -0
  126. package/dist/op/activities/propose-upgrade.d.ts.map +1 -0
  127. package/dist/op/activity-contract-registry.d.ts.map +1 -1
  128. package/dist/op/activity-registry.d.ts.map +1 -1
  129. package/dist/op/discover.d.ts.map +1 -1
  130. package/dist/op/gate-approval.d.ts.map +1 -1
  131. package/dist/op/gate-name.d.ts +7 -0
  132. package/dist/op/gate-name.d.ts.map +1 -1
  133. package/dist/op/gate-summary.d.ts +16 -0
  134. package/dist/op/gate-summary.d.ts.map +1 -1
  135. package/dist/op/gate.d.ts +16 -2
  136. package/dist/op/gate.d.ts.map +1 -1
  137. package/dist/op/generate-pipeline.d.ts.map +1 -1
  138. package/dist/op/runtimes/local.d.ts.map +1 -1
  139. package/dist/project-root.d.ts +26 -0
  140. package/dist/project-root.d.ts.map +1 -1
  141. package/dist/workspace/__fixtures__/contract-repo.d.ts +37 -0
  142. package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -0
  143. package/dist/workspace/__fixtures__/two-member-workspace.d.ts +11 -0
  144. package/dist/workspace/__fixtures__/two-member-workspace.d.ts.map +1 -0
  145. package/dist/workspace/checks/generated.d.ts +156 -0
  146. package/dist/workspace/checks/generated.d.ts.map +1 -0
  147. package/dist/workspace/checks/ledgers.d.ts +78 -0
  148. package/dist/workspace/checks/ledgers.d.ts.map +1 -0
  149. package/dist/workspace/checks/links.d.ts +25 -0
  150. package/dist/workspace/checks/links.d.ts.map +1 -0
  151. package/dist/workspace/checks/pipelines.d.ts +76 -0
  152. package/dist/workspace/checks/pipelines.d.ts.map +1 -0
  153. package/dist/workspace/checks.d.ts +161 -0
  154. package/dist/workspace/checks.d.ts.map +1 -0
  155. package/dist/workspace/compose-graph.d.ts +115 -0
  156. package/dist/workspace/compose-graph.d.ts.map +1 -0
  157. package/dist/workspace/compose-reports.d.ts +68 -0
  158. package/dist/workspace/compose-reports.d.ts.map +1 -0
  159. package/dist/workspace/declaration.d.ts +220 -0
  160. package/dist/workspace/declaration.d.ts.map +1 -0
  161. package/dist/workspace/declaration.schema.json +434 -0
  162. package/dist/workspace/generated-files.d.ts +65 -0
  163. package/dist/workspace/generated-files.d.ts.map +1 -0
  164. package/dist/workspace/glob.d.ts +13 -0
  165. package/dist/workspace/glob.d.ts.map +1 -0
  166. package/dist/workspace/graph-cli.d.ts +75 -0
  167. package/dist/workspace/graph-cli.d.ts.map +1 -0
  168. package/dist/workspace/init.d.ts +96 -0
  169. package/dist/workspace/init.d.ts.map +1 -0
  170. package/dist/workspace/jsonc.d.ts +38 -0
  171. package/dist/workspace/jsonc.d.ts.map +1 -0
  172. package/dist/workspace/kinds.d.ts +192 -0
  173. package/dist/workspace/kinds.d.ts.map +1 -0
  174. package/dist/workspace/lineage-check.d.ts +84 -0
  175. package/dist/workspace/lineage-check.d.ts.map +1 -0
  176. package/dist/workspace/lineage-cli.d.ts +31 -0
  177. package/dist/workspace/lineage-cli.d.ts.map +1 -0
  178. package/dist/workspace/lineage-init.d.ts +101 -0
  179. package/dist/workspace/lineage-init.d.ts.map +1 -0
  180. package/dist/workspace/lineage-lock.d.ts +258 -0
  181. package/dist/workspace/lineage-lock.d.ts.map +1 -0
  182. package/dist/workspace/lineage-merge.d.ts +14 -0
  183. package/dist/workspace/lineage-merge.d.ts.map +1 -0
  184. package/dist/workspace/lineage-migrations.d.ts +178 -0
  185. package/dist/workspace/lineage-migrations.d.ts.map +1 -0
  186. package/dist/workspace/lineage-update.d.ts +59 -0
  187. package/dist/workspace/lineage-update.d.ts.map +1 -0
  188. package/dist/workspace/lineage-upgrade-cli.d.ts +40 -0
  189. package/dist/workspace/lineage-upgrade-cli.d.ts.map +1 -0
  190. package/dist/workspace/lineage-upgrade.d.ts +146 -0
  191. package/dist/workspace/lineage-upgrade.d.ts.map +1 -0
  192. package/dist/workspace/lineage-vendor.d.ts +63 -0
  193. package/dist/workspace/lineage-vendor.d.ts.map +1 -0
  194. package/dist/workspace/lineage-version.d.ts +32 -0
  195. package/dist/workspace/lineage-version.d.ts.map +1 -0
  196. package/dist/workspace/links.d.ts +150 -0
  197. package/dist/workspace/links.d.ts.map +1 -0
  198. package/dist/workspace/ls.d.ts +99 -0
  199. package/dist/workspace/ls.d.ts.map +1 -0
  200. package/dist/workspace/member-commands.d.ts +139 -0
  201. package/dist/workspace/member-commands.d.ts.map +1 -0
  202. package/dist/workspace/member-handles.d.ts +54 -0
  203. package/dist/workspace/member-handles.d.ts.map +1 -0
  204. package/dist/workspace/member-pipeline.d.ts +143 -0
  205. package/dist/workspace/member-pipeline.d.ts.map +1 -0
  206. package/dist/workspace/member-run.d.ts +67 -0
  207. package/dist/workspace/member-run.d.ts.map +1 -0
  208. package/dist/workspace/reason-codes.d.ts +65 -0
  209. package/dist/workspace/reason-codes.d.ts.map +1 -0
  210. package/dist/workspace/record-source.d.ts +22 -0
  211. package/dist/workspace/record-source.d.ts.map +1 -0
  212. package/dist/workspace/records-cli.d.ts +81 -0
  213. package/dist/workspace/records-cli.d.ts.map +1 -0
  214. package/dist/workspace/records.d.ts +118 -0
  215. package/dist/workspace/records.d.ts.map +1 -0
  216. package/dist/workspace/root-refusal.d.ts +53 -0
  217. package/dist/workspace/root-refusal.d.ts.map +1 -0
  218. package/dist/workspace/status.d.ts +150 -0
  219. package/dist/workspace/status.d.ts.map +1 -0
  220. package/dist/workspace/template-manifest.d.ts +90 -0
  221. package/dist/workspace/template-manifest.d.ts.map +1 -0
  222. package/dist/workspace/tree.d.ts +36 -0
  223. package/dist/workspace/tree.d.ts.map +1 -0
  224. package/dist/workspace/trust/attestor.d.ts +67 -0
  225. package/dist/workspace/trust/attestor.d.ts.map +1 -0
  226. package/dist/workspace/trust/policy.d.ts +111 -0
  227. package/dist/workspace/trust/policy.d.ts.map +1 -0
  228. package/dist/workspace/trust/provenance.d.ts +67 -0
  229. package/dist/workspace/trust/provenance.d.ts.map +1 -0
  230. package/dist/workspace/trust/ssh-commit.d.ts +43 -0
  231. package/dist/workspace/trust/ssh-commit.d.ts.map +1 -0
  232. package/dist/workspace/trust/test-repo.d.ts +42 -0
  233. package/dist/workspace/trust/test-repo.d.ts.map +1 -0
  234. package/dist/workspace/trust/verify-cli.d.ts +9 -0
  235. package/dist/workspace/trust/verify-cli.d.ts.map +1 -0
  236. package/dist/workspace/trust/verify.d.ts +66 -0
  237. package/dist/workspace/trust/verify.d.ts.map +1 -0
  238. package/dist/workspace/which-chant.d.ts +58 -0
  239. package/dist/workspace/which-chant.d.ts.map +1 -0
  240. package/dist/workspace/workspace-kinds.schema.json +133 -0
  241. package/package.json +4 -3
  242. package/src/audit/core.ts +13 -2
  243. package/src/audit/discover.ts +102 -36
  244. package/src/audit/report-html.ts +3 -1
  245. package/src/audit/report-model.ts +14 -1
  246. package/src/audit/terraform-state.test.ts +81 -1
  247. package/src/audit/terraform-state.ts +25 -0
  248. package/src/cli/command-group.ts +7 -0
  249. package/src/cli/commands/audit-truncation.test.ts +167 -0
  250. package/src/cli/commands/audit.ts +132 -29
  251. package/src/cli/commands/check-lexicon-examples.ts +2 -1
  252. package/src/cli/commands/check-lexicon.ts +24 -0
  253. package/src/cli/commands/doctor.test.ts +30 -0
  254. package/src/cli/commands/doctor.ts +21 -0
  255. package/src/cli/commands/import-agents.ts +15 -4
  256. package/src/cli/commands/init.ts +76 -25
  257. package/src/cli/commands/lint.test.ts +52 -1
  258. package/src/cli/commands/lint.ts +41 -71
  259. package/src/cli/commands/onboard.ts +15 -0
  260. package/src/cli/commands/update.ts +18 -5
  261. package/src/cli/commands/vendor.ts +11 -6
  262. package/src/cli/conflict-check.test.ts +18 -1
  263. package/src/cli/conflict-check.ts +13 -0
  264. package/src/cli/discovery-skip.test.ts +168 -0
  265. package/src/cli/handlers/__snapshots__/build-member.test.ts.snap +38 -0
  266. package/src/cli/handlers/build-member.test.ts +127 -0
  267. package/src/cli/handlers/build.test.ts +11 -0
  268. package/src/cli/handlers/build.ts +35 -1
  269. package/src/cli/handlers/components.test.ts +127 -0
  270. package/src/cli/handlers/components.ts +70 -8
  271. package/src/cli/handlers/dev.ts +6 -1
  272. package/src/cli/handlers/fan-out.ts +9 -2
  273. package/src/cli/handlers/graph.test.ts +20 -0
  274. package/src/cli/handlers/graph.ts +4 -2
  275. package/src/cli/handlers/init.ts +54 -1
  276. package/src/cli/handlers/lifecycle.ts +2 -1
  277. package/src/cli/handlers/misc.ts +16 -0
  278. package/src/cli/handlers/operator.test.ts +68 -0
  279. package/src/cli/handlers/operator.ts +39 -10
  280. package/src/cli/handlers/promote.test.ts +377 -0
  281. package/src/cli/handlers/promote.ts +391 -0
  282. package/src/cli/handlers/run-generate-member.test.ts +83 -0
  283. package/src/cli/handlers/run-generate.test.ts +158 -0
  284. package/src/cli/handlers/run-generate.ts +168 -0
  285. package/src/cli/handlers/run.test.ts +47 -0
  286. package/src/cli/handlers/run.ts +41 -6
  287. package/src/cli/handlers/vendor.ts +147 -43
  288. package/src/cli/main.test.ts +137 -1
  289. package/src/cli/main.ts +290 -24
  290. package/src/cli/mcp/op-tools.ts +2 -1
  291. package/src/cli/path-lexicon-messages.test.ts +152 -0
  292. package/src/cli/plugins.ts +43 -5
  293. package/src/cli/registry.ts +46 -1
  294. package/src/cli/static-config-read.test.ts +208 -0
  295. package/src/codegen/fetch.test.ts +63 -1
  296. package/src/codegen/fetch.ts +30 -1
  297. package/src/components/__fixtures__/alb-ecs-service.json +0 -8
  298. package/src/components/capability-plugin-loader.ts +3 -1
  299. package/src/components/cli-support.ts +9 -3
  300. package/src/components/discover.test.ts +27 -57
  301. package/src/components/discover.ts +20 -51
  302. package/src/components/driver.test.ts +90 -12
  303. package/src/components/driver.ts +43 -5
  304. package/src/components/fan-out-support.ts +2 -1
  305. package/src/components/gate-plan.ts +53 -0
  306. package/src/components/pilots/README.md +1 -1
  307. package/src/components/pilots/alb-ecs.pilot.ts +7 -7
  308. package/src/components/presets/ecs-fargate.ts +10 -5
  309. package/src/components/promote.test.ts +645 -0
  310. package/src/components/promote.ts +641 -0
  311. package/src/components/sandbox/driver.ts +8 -8
  312. package/src/components/sandbox/run.ts +4 -1
  313. package/src/components/verbs/build-archive.ts +27 -21
  314. package/src/components/verbs/run-agent.ts +3 -5
  315. package/src/config-static.test.ts +136 -0
  316. package/src/config-static.ts +232 -0
  317. package/src/config.test.ts +40 -0
  318. package/src/config.ts +161 -11
  319. package/src/content-digest.test.ts +120 -0
  320. package/src/content-digest.ts +21 -0
  321. package/src/discovery/files.test.ts +127 -1
  322. package/src/discovery/files.ts +91 -59
  323. package/src/discovery/fold-import.ts +127 -9
  324. package/src/discovery/index.ts +1 -0
  325. package/src/discovery/sandbox/driver.ts +73 -35
  326. package/src/discovery/sandbox/fork-diagnostic.test.ts +136 -13
  327. package/src/discovery/sandbox/fork.ts +83 -43
  328. package/src/discovery/sandbox/path-lexicon-multifile.test.ts +285 -0
  329. package/src/discovery/sandbox/path-lexicon-parity.test.ts +162 -0
  330. package/src/discovery/walk.test.ts +263 -0
  331. package/src/discovery/walk.ts +334 -0
  332. package/src/effect-receipt.ts +2 -2
  333. package/src/graph-ir.ts +18 -1
  334. package/src/join-key.test.ts +25 -0
  335. package/src/join-key.ts +39 -0
  336. package/src/lexicon-module.test.ts +203 -0
  337. package/src/lexicon-module.ts +215 -0
  338. package/src/lexicon.ts +105 -1
  339. package/src/lifecycle/build-ledger-store.ts +13 -2
  340. package/src/lifecycle/digest.ts +9 -5
  341. package/src/lifecycle/gate-ledger.test.ts +20 -0
  342. package/src/lifecycle/gate-ledger.ts +28 -6
  343. package/src/lifecycle/git.test.ts +13 -1
  344. package/src/lifecycle/git.ts +221 -176
  345. package/src/lifecycle/lease.ts +25 -10
  346. package/src/lifecycle/legacy-digest.test.ts +206 -0
  347. package/src/lifecycle/legacy-digest.ts +103 -0
  348. package/src/lifecycle/member-ledger.test.ts +319 -0
  349. package/src/lifecycle/member-ledger.ts +145 -0
  350. package/src/lifecycle/plan-digest.ts +7 -7
  351. package/src/lifecycle/release-ledger.test.ts +20 -0
  352. package/src/lifecycle/release-ledger.ts +53 -3
  353. package/src/lifecycle/status.test.ts +151 -1
  354. package/src/lifecycle/status.ts +228 -7
  355. package/src/lint/rules/comp/comp003-mutating-no-rollback.ts +2 -3
  356. package/src/op/activities/activity-contracts.ts +37 -0
  357. package/src/op/activities/index.ts +10 -0
  358. package/src/op/activities/propose-upgrade.ts +206 -0
  359. package/src/op/activity-contract-registry.ts +18 -4
  360. package/src/op/activity-registry.ts +26 -0
  361. package/src/op/discover.test.ts +14 -4
  362. package/src/op/discover.ts +19 -23
  363. package/src/op/gate-approval.ts +3 -1
  364. package/src/op/gate-name.ts +8 -0
  365. package/src/op/gate-summary.test.ts +14 -0
  366. package/src/op/gate-summary.ts +25 -2
  367. package/src/op/gate.ts +36 -7
  368. package/src/op/generate-pipeline.test.ts +14 -5
  369. package/src/op/generate-pipeline.ts +2 -1
  370. package/src/op/runtimes/local.ts +2 -1
  371. package/src/project-root.test.ts +66 -1
  372. package/src/project-root.ts +48 -0
  373. package/src/workspace/__fixtures__/contract-repo.ts +100 -0
  374. package/src/workspace/__fixtures__/two-member-workspace.ts +35 -0
  375. package/src/workspace/behold-kinds.test.ts +162 -0
  376. package/src/workspace/check-contract.test.ts +127 -0
  377. package/src/workspace/check.schema.json +347 -0
  378. package/src/workspace/checks/generated.test.ts +287 -0
  379. package/src/workspace/checks/generated.ts +475 -0
  380. package/src/workspace/checks/ledgers.test.ts +139 -0
  381. package/src/workspace/checks/ledgers.ts +246 -0
  382. package/src/workspace/checks/links.ts +183 -0
  383. package/src/workspace/checks/pipelines.test.ts +133 -0
  384. package/src/workspace/checks/pipelines.ts +225 -0
  385. package/src/workspace/checks-repo.test.ts +71 -0
  386. package/src/workspace/checks.test.ts +450 -0
  387. package/src/workspace/checks.ts +461 -0
  388. package/src/workspace/compose-graph.test.ts +146 -0
  389. package/src/workspace/compose-graph.ts +228 -0
  390. package/src/workspace/compose-reports.test.ts +109 -0
  391. package/src/workspace/compose-reports.ts +186 -0
  392. package/src/workspace/declaration.schema.json +434 -0
  393. package/src/workspace/declaration.test.ts +347 -0
  394. package/src/workspace/declaration.ts +623 -0
  395. package/src/workspace/generated-files.test.ts +114 -0
  396. package/src/workspace/generated-files.ts +94 -0
  397. package/src/workspace/glob.ts +49 -0
  398. package/src/workspace/graph-cli.ts +220 -0
  399. package/src/workspace/graph-contract.test.ts +204 -0
  400. package/src/workspace/graph.schema.json +662 -0
  401. package/src/workspace/init.test.ts +223 -0
  402. package/src/workspace/init.ts +585 -0
  403. package/src/workspace/jsonc.test.ts +65 -0
  404. package/src/workspace/jsonc.ts +231 -0
  405. package/src/workspace/kinds.test.ts +288 -0
  406. package/src/workspace/kinds.ts +506 -0
  407. package/src/workspace/lineage-check.ts +264 -0
  408. package/src/workspace/lineage-cli.ts +114 -0
  409. package/src/workspace/lineage-init.test.ts +231 -0
  410. package/src/workspace/lineage-init.ts +365 -0
  411. package/src/workspace/lineage-lock.test.ts +224 -0
  412. package/src/workspace/lineage-lock.ts +357 -0
  413. package/src/workspace/lineage-merge.ts +44 -0
  414. package/src/workspace/lineage-migrations.test.ts +178 -0
  415. package/src/workspace/lineage-migrations.ts +343 -0
  416. package/src/workspace/lineage-update.ts +160 -0
  417. package/src/workspace/lineage-upgrade-cli.ts +151 -0
  418. package/src/workspace/lineage-upgrade.test.ts +474 -0
  419. package/src/workspace/lineage-upgrade.ts +624 -0
  420. package/src/workspace/lineage-vendor.test.ts +126 -0
  421. package/src/workspace/lineage-vendor.ts +215 -0
  422. package/src/workspace/lineage-version.ts +127 -0
  423. package/src/workspace/links.test.ts +344 -0
  424. package/src/workspace/links.ts +331 -0
  425. package/src/workspace/ls-contract.test.ts +200 -0
  426. package/src/workspace/ls.schema.json +190 -0
  427. package/src/workspace/ls.ts +254 -0
  428. package/src/workspace/member-commands.e2e.test.ts +244 -0
  429. package/src/workspace/member-commands.test.ts +217 -0
  430. package/src/workspace/member-commands.ts +540 -0
  431. package/src/workspace/member-handles.test.ts +76 -0
  432. package/src/workspace/member-handles.ts +228 -0
  433. package/src/workspace/member-pipeline.test.ts +135 -0
  434. package/src/workspace/member-pipeline.ts +317 -0
  435. package/src/workspace/member-run.ts +146 -0
  436. package/src/workspace/read-contract.test.ts +129 -0
  437. package/src/workspace/reason-codes.test.ts +113 -0
  438. package/src/workspace/reason-codes.ts +81 -0
  439. package/src/workspace/record-source.ts +117 -0
  440. package/src/workspace/records-cli.ts +198 -0
  441. package/src/workspace/records-contract.test.ts +93 -0
  442. package/src/workspace/records.schema.json +150 -0
  443. package/src/workspace/records.test.ts +243 -0
  444. package/src/workspace/records.ts +384 -0
  445. package/src/workspace/root-refusal.ts +97 -0
  446. package/src/workspace/status-contract.test.ts +308 -0
  447. package/src/workspace/status.schema.json +210 -0
  448. package/src/workspace/status.ts +412 -0
  449. package/src/workspace/template-manifest.test.ts +79 -0
  450. package/src/workspace/template-manifest.ts +221 -0
  451. package/src/workspace/tree.ts +132 -0
  452. package/src/workspace/trust/attestor.ts +93 -0
  453. package/src/workspace/trust/policy.ts +306 -0
  454. package/src/workspace/trust/provenance.ts +181 -0
  455. package/src/workspace/trust/ssh-commit.ts +138 -0
  456. package/src/workspace/trust/test-repo.ts +138 -0
  457. package/src/workspace/trust/trust.test.ts +397 -0
  458. package/src/workspace/trust/verify-cli.ts +56 -0
  459. package/src/workspace/trust/verify.ts +194 -0
  460. package/src/workspace/which-chant.test.ts +117 -0
  461. package/src/workspace/which-chant.ts +151 -0
  462. package/src/workspace/workspace-kinds.schema.json +133 -0
@@ -1,6 +1,9 @@
1
- import { readdir, stat, open } from "node:fs/promises";
2
- import { existsSync } from "node:fs";
3
- import { join, relative } from "node:path";
1
+ import { open } from "node:fs/promises";
2
+ import { relative, sep, isAbsolute } from "node:path";
3
+ // @ts-ignore — picomatch has no types declaration
4
+ import picomatch from "picomatch";
5
+ import { resolveDiscoveryGlobs, type DiscoveryGlobs } from "../config";
6
+ import { readHead, walkDiscovery, workspaceMemberDirs } from "./walk";
4
7
 
5
8
  /**
6
9
  * Marker chant writes at the top of files it generates (a hosting lexicon's
@@ -12,13 +15,38 @@ import { join, relative } from "node:path";
12
15
  */
13
16
  export const GENERATED_MARKER = "Generated by chant — do not edit directly";
14
17
 
15
- /** True when a file's header carries the {@link GENERATED_MARKER}. Reads only the head. */
16
- async function isGeneratedFile(file: string): Promise<boolean> {
18
+ /**
19
+ * Comment a hand-written file carries to keep discovery out of it (#2519): a
20
+ * runnable script with top-level side effects, say, that lives next to the
21
+ * project's declarations. It must be a comment of its own (`// chant-discovery-skip`
22
+ * or inside a block comment) within the first {@link SKIP_MARKER_HEAD_BYTES}
23
+ * bytes of the file. See {@link hasSkipMarker}.
24
+ */
25
+ export const SKIP_MARKER = "chant-discovery-skip";
26
+
27
+ /** How much of a file's head {@link hasSkipMarker} reads: room for a shebang and a license header. */
28
+ const SKIP_MARKER_HEAD_BYTES = 1024;
29
+
30
+ /** {@link SKIP_MARKER} opening a `//`, `/*` or ` * ` comment line, as a whole word. */
31
+ const SKIP_MARKER_PATTERN = /^[ \t]*(?:\/\/|\/\*+|\*)[ \t]*chant-discovery-skip(?![\w-])/m;
32
+
33
+ /** True when `head`, the start of a file, carries the {@link SKIP_MARKER} comment. */
34
+ export function hasSkipMarker(head: string): boolean {
35
+ return SKIP_MARKER_PATTERN.test(head.slice(0, SKIP_MARKER_HEAD_BYTES));
36
+ }
37
+
38
+ /**
39
+ * Whether a file's head carries the {@link GENERATED_MARKER} or the
40
+ * {@link SKIP_MARKER}. Reads only the head. The generated marker is looked for
41
+ * in the first 256 bytes, as it always was.
42
+ */
43
+ export async function hasDiscoveryMarker(file: string): Promise<boolean> {
17
44
  let fh;
18
45
  try {
19
46
  fh = await open(file, "r");
20
- const { buffer, bytesRead } = await fh.read({ buffer: Buffer.alloc(256), position: 0 });
21
- return buffer.toString("utf8", 0, bytesRead).includes(GENERATED_MARKER);
47
+ const { buffer, bytesRead } = await fh.read({ buffer: Buffer.alloc(SKIP_MARKER_HEAD_BYTES), position: 0 });
48
+ if (buffer.toString("utf8", 0, Math.min(bytesRead, 256)).includes(GENERATED_MARKER)) return true;
49
+ return hasSkipMarker(buffer.toString("utf8", 0, bytesRead));
22
50
  } catch {
23
51
  return false;
24
52
  } finally {
@@ -26,61 +54,65 @@ async function isGeneratedFile(file: string): Promise<boolean> {
26
54
  }
27
55
  }
28
56
 
57
+ /** Whether a file's head carries the {@link GENERATED_MARKER} or the {@link SKIP_MARKER}. The synchronous form of {@link hasDiscoveryMarker}. */
58
+ export function hasDiscoveryMarkerSync(file: string): boolean {
59
+ const head = readHead(file, SKIP_MARKER_HEAD_BYTES);
60
+ return head.slice(0, 256).includes(GENERATED_MARKER) || hasSkipMarker(head);
61
+ }
62
+
29
63
  /**
30
- * Recursively find all TypeScript infrastructure files in a directory
31
- * @param path - The directory path to search
32
- * @returns Array of file paths to .ts files (excluding test files and chant-generated files)
64
+ * Compile a project's `exclude`/`include` globs (#2519) into a predicate that
65
+ * is true for a file discovery must skip, or `undefined` when there is nothing
66
+ * to skip.
67
+ *
68
+ * Each pattern is matched, with picomatch and dot files included, against the
69
+ * file's path relative to `globs.root` (forward slashes) and against the path
70
+ * of every directory above it, so a pattern naming a directory covers
71
+ * everything under it. A file is skipped when some `exclude` pattern matches
72
+ * and no `include` pattern does: `include` always wins. Files outside
73
+ * `globs.root` are never skipped. The discovery walk (`./walk.ts`) applies the
74
+ * same rule, and also lets `include` re-admit what its default rules skip.
33
75
  */
34
- export async function findInfraFiles(path: string): Promise<string[]> {
35
- const files: string[] = [];
36
- let sourceRoot: string | null = null;
37
-
38
- async function scanDirectory(dir: string): Promise<void> {
39
- let entries;
40
-
41
- try {
42
- entries = await readdir(dir, { withFileTypes: true });
43
- } catch (error) {
44
- // Skip directories we can't read
45
- return;
46
- }
47
-
48
- for (const entry of entries) {
49
- const fullPath = join(dir, entry.name);
76
+ export function compileDiscoveryFilter(globs: DiscoveryGlobs | undefined): ((file: string) => boolean) | undefined {
77
+ if (!globs || globs.exclude.length === 0) return undefined;
78
+ const excluded = picomatch(globs.exclude, { dot: true });
79
+ const included = globs.include.length > 0 ? picomatch(globs.include, { dot: true }) : () => false;
80
+ return (file: string): boolean => {
81
+ const rel = relative(globs.root, file);
82
+ if (rel === "" || rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel)) return false;
83
+ const parts = rel.split(sep);
84
+ const candidates = parts.map((_, i) => parts.slice(0, i + 1).join("/"));
85
+ return candidates.some((c) => excluded(c)) && !candidates.some((c) => included(c));
86
+ };
87
+ }
50
88
 
51
- // Skip node_modules
52
- if (entry.isDirectory() && entry.name === "node_modules") {
53
- continue;
54
- }
89
+ /** Options for {@link findInfraFiles}. */
90
+ export interface FindInfraFilesOptions {
91
+ /**
92
+ * The project's discovery globs. Omitted, they are read from the project
93
+ * config `path` belongs to ({@link resolveDiscoveryGlobs}); `null` applies none.
94
+ */
95
+ globs?: DiscoveryGlobs | null;
96
+ }
55
97
 
56
- if (entry.isDirectory()) {
57
- // Child project boundary — a directory with its own chant.config.ts
58
- // is a separate scope, but only if we've already found the project's
59
- // own source root. The first config directory is the source root, not
60
- // a child project.
61
- const configPath = join(fullPath, "chant.config.ts");
62
- if (existsSync(configPath)) {
63
- if (sourceRoot === null) {
64
- sourceRoot = fullPath;
65
- } else {
66
- continue;
67
- }
68
- }
69
- await scanDirectory(fullPath);
70
- } else if (entry.isFile()) {
71
- // Include only .ts files, exclude test files and chant-generated files
72
- if (
73
- entry.name.endsWith(".ts") &&
74
- !entry.name.endsWith(".test.ts") &&
75
- !entry.name.endsWith(".spec.ts") &&
76
- !(await isGeneratedFile(fullPath))
77
- ) {
78
- files.push(fullPath);
79
- }
80
- }
81
- }
82
- }
98
+ /** A TypeScript source file by name: `.ts`, not a test or spec. */
99
+ export function isSourceFileName(name: string): boolean {
100
+ return name.endsWith(".ts") && !name.endsWith(".test.ts") && !name.endsWith(".spec.ts");
101
+ }
83
102
 
84
- await scanDirectory(path);
85
- return files;
103
+ /**
104
+ * Find every TypeScript infrastructure file under `path`, through the one
105
+ * discovery walk (`./walk.ts`, #2527): test and spec files, chant-generated
106
+ * files, skip-marked files and whatever the walk's rules and the project's
107
+ * globs skip are left out.
108
+ */
109
+ export async function findInfraFiles(path: string, options?: FindInfraFilesOptions): Promise<string[]> {
110
+ const globs = options?.globs === null ? undefined : (options?.globs ?? (await resolveDiscoveryGlobs(path)));
111
+ return walkDiscovery({
112
+ walker: "source",
113
+ root: path,
114
+ globs,
115
+ excludeDirs: await workspaceMemberDirs(path),
116
+ accept: (name, full) => isSourceFileName(name) && !hasDiscoveryMarkerSync(full),
117
+ });
86
118
  }
@@ -27,6 +27,7 @@ import {
27
27
  type FoldedCompositeStepCall,
28
28
  } from "../fold/fold";
29
29
  import { isChantOwnedSpecifier, isFoldableHelperName } from "../fold/foldable-helpers";
30
+ import { lexiconModulePath, lexiconModuleRoot } from "../lexicon-module";
30
31
  import {
31
32
  briefNodeText,
32
33
  callExpressionMessage,
@@ -247,6 +248,32 @@ export interface FoldSession {
247
248
  * established one.
248
249
  */
249
250
  readonly lexiconPackages: ReadonlySet<string>;
251
+ /**
252
+ * chant#2577 — the absolute module paths of this build's lexicons that
253
+ * `chant.config.ts` declares by path (#2520), each resolved to the file an
254
+ * import of it reaches. Such a lexicon has no package specifier, so a
255
+ * project imports it by a relative or absolute path. A binding whose path
256
+ * resolves to one of these is treated exactly like a binding of a
257
+ * {@link lexiconPackages} entry: its data exports resolve as values, and
258
+ * under `--sandbox` it is on {@link isTrustedExecutableBinding}'s allowlist,
259
+ * because `loadPlugins` has already imported and run it in this process.
260
+ *
261
+ * Only the declared module itself is in the set. The other files of a
262
+ * multi-file path lexicon are covered by {@link lexiconRoots}.
263
+ */
264
+ readonly lexiconModules: ReadonlySet<string>;
265
+ /**
266
+ * chant#2590 — the directory trusted for each path-declared lexicon that has
267
+ * one (`../lexicon-module.ts`'s `pathLexiconRoot`: the declared `root`, or
268
+ * the module's own directory, inside the project and not containing its
269
+ * source directory). A file under one of these gets what a subpath of an
270
+ * active lexicon package gets: a place on the `--sandbox` trust list, and
271
+ * no fold as a project file. Its data exports are not read as values,
272
+ * which is also how a package subpath is treated. A directory that holds
273
+ * any of the build's own source files is dropped, so the build's source is
274
+ * never trusted this way.
275
+ */
276
+ readonly lexiconRoots: readonly string[];
250
277
  /**
251
278
  * chant #1093 — this build asked for the #1045 sandbox
252
279
  * (`DiscoveryOptions.sandbox`, `chant build --sandbox`), so fold must not
@@ -326,7 +353,24 @@ export function createFoldSession(
326
353
  lexiconPackages: readonly string[] = [],
327
354
  /** chant#2455 — `ι = executing`. Mutually exclusive with `sandbox`. */
328
355
  executing = false,
356
+ /**
357
+ * chant#2590 — the build's own source files. A path lexicon's trusted
358
+ * directory that holds any of them is dropped (see {@link FoldSession.lexiconRoots}).
359
+ */
360
+ sourceFiles: readonly string[] = [],
329
361
  ): FoldSession {
362
+ // chant#2577 — a lexicon declared by path is that module and no package, so
363
+ // its name contributes the module path and not `@intentius/chant-lexicon-<name>`.
364
+ const packages = new Set(lexiconPackages);
365
+ const modules = new Set<string>();
366
+ const roots: string[] = [];
367
+ for (const name of lexicons) {
368
+ const modulePath = lexiconModulePath(name);
369
+ if (modulePath === undefined) packages.add(lexiconPackageName(name));
370
+ else modules.add(resolveModulePath(modulePath, modulePath));
371
+ const root = lexiconModuleRoot(name);
372
+ if (root !== undefined && !sourceFiles.some((file) => isUnderDirectory(resolvePath(file), root))) roots.push(root);
373
+ }
330
374
  return {
331
375
  intrinsics,
332
376
  cache: new Map(),
@@ -334,7 +378,9 @@ export function createFoldSession(
334
378
  importCache: new Map(),
335
379
  resolvePathCache: new Map(),
336
380
  buildParams,
337
- lexiconPackages: new Set([...lexicons.map(lexiconPackageName), ...lexiconPackages]),
381
+ lexiconPackages: packages,
382
+ lexiconModules: modules,
383
+ lexiconRoots: roots,
338
384
  sandbox,
339
385
  executing,
340
386
  factoryModules: new Map(),
@@ -1104,6 +1150,39 @@ function activeLexiconPackage(specifier: string, lexiconPackages: ReadonlySet<st
1104
1150
  return lexiconPackages.has(specifier) ? specifier : undefined;
1105
1151
  }
1106
1152
 
1153
+ /**
1154
+ * chant#2577 — the path-declared counterpart of {@link activeLexiconPackage}:
1155
+ * the resolved module path when a relative or absolute `specifier` reaches
1156
+ * one of this build's {@link FoldSession.lexiconModules}, `undefined`
1157
+ * otherwise. A bare specifier is never resolved here, and a build with no
1158
+ * path-declared lexicon resolves nothing.
1159
+ */
1160
+ function activeLexiconModule(
1161
+ specifier: string,
1162
+ fromFile: string,
1163
+ lexiconModules: ReadonlySet<string>,
1164
+ resolvePathCache: Map<string, string>,
1165
+ ): string | undefined {
1166
+ if (lexiconModules.size === 0 || !isProjectFileSpecifier(specifier)) return undefined;
1167
+ let modulePath: string;
1168
+ try {
1169
+ modulePath = resolveModulePathMemoized(specifier, fromFile, resolvePathCache);
1170
+ } catch {
1171
+ return undefined;
1172
+ }
1173
+ return lexiconModules.has(modulePath) ? modulePath : undefined;
1174
+ }
1175
+
1176
+ /** chant#2590 — true when absolute `file` lies under absolute directory `dir`. */
1177
+ function isUnderDirectory(file: string, dir: string): boolean {
1178
+ return file.startsWith(dir.endsWith(sep) ? dir : dir + sep);
1179
+ }
1180
+
1181
+ /** chant#2590 — true when `path` lies under one of the build's trusted path-lexicon directories. */
1182
+ function inPathLexiconRoot(path: string, roots: readonly string[]): boolean {
1183
+ return roots.some((root) => isUnderDirectory(path, root));
1184
+ }
1185
+
1107
1186
  /**
1108
1187
  * chant #1995 — the package-ROOT portion of a bare subpath specifier
1109
1188
  * (`@intentius/chant-lexicon-azure/generated/index` ->
@@ -1168,13 +1247,17 @@ async function resolveActiveLexiconExport(
1168
1247
  fromFile: string,
1169
1248
  session: FoldSession,
1170
1249
  ): Promise<{ value: unknown } | undefined> {
1171
- if (!activeLexiconPackage(binding.specifier, session.lexiconPackages)) return undefined;
1172
-
1173
- let modulePath: string;
1174
- try {
1175
- modulePath = resolveModulePathMemoized(binding.specifier, fromFile, session.resolvePathCache);
1176
- } catch {
1177
- return undefined;
1250
+ let modulePath: string | undefined;
1251
+ if (activeLexiconPackage(binding.specifier, session.lexiconPackages)) {
1252
+ try {
1253
+ modulePath = resolveModulePathMemoized(binding.specifier, fromFile, session.resolvePathCache);
1254
+ } catch {
1255
+ return undefined;
1256
+ }
1257
+ } else {
1258
+ // chant#2577 — a lexicon declared by path, imported by its path.
1259
+ modulePath = activeLexiconModule(binding.specifier, fromFile, session.lexiconModules, session.resolvePathCache);
1260
+ if (modulePath === undefined) return undefined;
1178
1261
  }
1179
1262
 
1180
1263
  let mod: Record<string, unknown>;
@@ -1278,6 +1361,10 @@ interface ResolveCtx {
1278
1361
  * site that imports and executes a module.
1279
1362
  */
1280
1363
  lexiconPackages: ReadonlySet<string>;
1364
+ /** chant#2577 — see {@link FoldSession.lexiconModules}. */
1365
+ lexiconModules: ReadonlySet<string>;
1366
+ /** chant#2590 — see {@link FoldSession.lexiconRoots}. */
1367
+ lexiconRoots: readonly string[];
1281
1368
  /** chant #1093 — see {@link FoldSession.sandbox}. */
1282
1369
  sandbox: boolean;
1283
1370
  /** chant#2455 — see {@link FoldSession.executing}. */
@@ -2053,7 +2140,10 @@ function findCompositeDefinition(
2053
2140
  // chant build the set holds only `@intentius/chant-lexicon-*`, and no
2054
2141
  // lexicon exports a `Composite`, so nothing about a chant project moves.
2055
2142
  const chantOwned = isChantOwnedHelperBinding(compositeBinding, { ...ctx, file: scope.file });
2056
- const hostOwned = activeLexiconPackage(compositeBinding.specifier, ctx.lexiconPackages) !== undefined;
2143
+ const hostOwned =
2144
+ activeLexiconPackage(compositeBinding.specifier, ctx.lexiconPackages) !== undefined ||
2145
+ activeLexiconModule(compositeBinding.specifier, scope.file, ctx.lexiconModules, ctx.resolvePathCache) !==
2146
+ undefined;
2057
2147
  if (!chantOwned && !hostOwned) return undefined;
2058
2148
 
2059
2149
  const [fnArg, nameArg] = init.arguments;
@@ -2425,6 +2515,8 @@ async function interpretCompositeFactory(
2425
2515
  importCache: ctx.importCache,
2426
2516
  resolvePathCache: ctx.resolvePathCache,
2427
2517
  lexiconPackages: ctx.lexiconPackages,
2518
+ lexiconModules: ctx.lexiconModules,
2519
+ lexiconRoots: ctx.lexiconRoots,
2428
2520
  sandbox: ctx.sandbox,
2429
2521
  executing: ctx.executing,
2430
2522
  session: ctx.session,
@@ -3048,6 +3140,12 @@ function isTrustedExecutableBinding(binding: ImportBinding, ctx: ResolveCtx): bo
3048
3140
  } catch {
3049
3141
  return false;
3050
3142
  }
3143
+ // chant#2577 — arm 1 for a lexicon declared by path: the module
3144
+ // `loadPlugins` already imported, reached by the path the project imports it by.
3145
+ if (ctx.lexiconModules.has(targetPath)) return true;
3146
+ // chant#2590 — the subpath case of the same arm: another file of a
3147
+ // multi-file path lexicon, under its trusted directory.
3148
+ if (inPathLexiconRoot(targetPath, ctx.lexiconRoots)) return true;
3051
3149
  const root = chantCoreRoot();
3052
3150
  return targetPath === root || targetPath.startsWith(root + sep);
3053
3151
  }
@@ -3607,6 +3705,20 @@ async function buildExternals(
3607
3705
  } catch {
3608
3706
  continue;
3609
3707
  }
3708
+ // chant#2577 — a path-declared lexicon is a lexicon, not a project file:
3709
+ // resolve its data export the way the bare-specifier branch above does,
3710
+ // with no `liveSources` edge for the same reason.
3711
+ if (session.lexiconModules.has(targetPath)) {
3712
+ const lexiconExport = await resolveActiveLexiconExport(binding, file, session);
3713
+ if (lexiconExport) externals.set(localName, lexiconExport.value);
3714
+ continue;
3715
+ }
3716
+ // chant#2590 — another file of a multi-file path lexicon is a lexicon
3717
+ // subpath, not a project file: it is not folded, and like a package
3718
+ // subpath (see the bare-specifier branch above) its data exports are not
3719
+ // read as values. A constructor or composite from it resolves through the
3720
+ // import path, where `isTrustedExecutableBinding` admits it.
3721
+ if (inPathLexiconRoot(targetPath, session.lexiconRoots)) continue;
3610
3722
  const result = await foldFileMemoized(targetPath, session);
3611
3723
  if (!result.ok) {
3612
3724
  failures.set(localName, locatedMessage(binding.specifierNode, result.reason));
@@ -3632,6 +3744,9 @@ async function buildExternals(
3632
3744
  } catch {
3633
3745
  continue;
3634
3746
  }
3747
+ // chant#2577 — a namespace import of a lexicon is left alone, as the
3748
+ // bare-specifier check above leaves a package's alone.
3749
+ if (session.lexiconModules.has(targetPath) || inPathLexiconRoot(targetPath, session.lexiconRoots)) continue;
3635
3750
  const result = await foldFileMemoized(targetPath, session);
3636
3751
  if (!result.ok) {
3637
3752
  failures.set(localName, locatedMessage(binding.specifierNode, result.reason));
@@ -3738,6 +3853,8 @@ async function tryFoldFileCore(file: string, session: FoldSession): Promise<Fold
3738
3853
  importCache: session.importCache,
3739
3854
  resolvePathCache: session.resolvePathCache,
3740
3855
  lexiconPackages: session.lexiconPackages,
3856
+ lexiconModules: session.lexiconModules,
3857
+ lexiconRoots: session.lexiconRoots,
3741
3858
  sandbox: session.sandbox,
3742
3859
  executing: session.executing,
3743
3860
  session,
@@ -4267,6 +4384,7 @@ export async function foldProject(
4267
4384
  options.sandbox ?? false,
4268
4385
  options.lexiconPackages ?? [],
4269
4386
  options.executing ?? false,
4387
+ files,
4270
4388
  );
4271
4389
  const attempts = new Map<string, FoldFileResult>();
4272
4390
  for (const file of files) attempts.set(file, await tryFoldFile(file, intrinsics, session));
@@ -291,6 +291,7 @@ export async function discover(path: string, options?: DiscoveryOptions): Promis
291
291
  options.sandbox === true,
292
292
  [],
293
293
  options.executing === true,
294
+ files,
294
295
  )
295
296
  : undefined;
296
297
  if (options?.fold) {
@@ -70,6 +70,49 @@ function lit(value: unknown): string {
70
70
  return JSON.stringify(value);
71
71
  }
72
72
 
73
+ /**
74
+ * chant#2461 — the `send` every driver uses, as generated source lines.
75
+ *
76
+ * `process.send` is asynchronous: it queues the message on the IPC channel and
77
+ * returns. Node's contract is that the optional callback runs once the message
78
+ * has been written, or with an error if it could not be. Without the callback a
79
+ * failed send is not reported to the caller at all, so the driver could return
80
+ * from `main()` believing it had answered.
81
+ *
82
+ * So `send` returns a promise that settles only when the write has completed,
83
+ * and every call site awaits it before `main()` returns. A send that fails, or
84
+ * throws synchronously (a payload that cannot be serialized), writes the reason
85
+ * to stderr with a synchronous `writeSync` and exits 1. That way a result that
86
+ * never left the child cannot end as exit 0 with empty stderr, which is the
87
+ * signature that looks like success went missing.
88
+ *
89
+ * `before` is spliced in at the top of the function body (the run driver marks
90
+ * itself `reported` there).
91
+ */
92
+ export function sendFunctionSource(before: readonly string[] = []): string[] {
93
+ return [
94
+ `function send(payload) {`,
95
+ ...before.map((line) => ` ${line}`),
96
+ ` if (typeof process.send !== "function") {`,
97
+ ` console.log(JSON.stringify(payload));`,
98
+ ` return Promise.resolve();`,
99
+ ` }`,
100
+ ` return new Promise((resolve) => {`,
101
+ ` const lost = (err) => {`,
102
+ ` writeSync(2, "chant: the sandboxed child could not hand its result to the parent: " +`,
103
+ ` (err && err.message ? err.message : String(err)) + " (chant#2461)\\n");`,
104
+ ` process.exit(1);`,
105
+ ` };`,
106
+ ` try {`,
107
+ ` process.send(payload, (err) => (err ? lost(err) : resolve()));`,
108
+ ` } catch (err) {`,
109
+ ` lost(err);`,
110
+ ` }`,
111
+ ` });`,
112
+ `}`,
113
+ ];
114
+ }
115
+
73
116
  /**
74
117
  * Generate the driver module's full TypeScript source. Written to a tmp file
75
118
  * and bundled (see `./bundle.ts`) before being handed to a sandboxed child —
@@ -117,11 +160,7 @@ export function generateDriverSource(options: GenerateDriverOptions): string {
117
160
  ` "Nothing is thrown, so nothing else can name it (chant#2461).\\n");`,
118
161
  `});`,
119
162
  ``,
120
- `function send(payload) {`,
121
- ` reported = true;`,
122
- ` if (typeof process.send === "function") process.send(payload);`,
123
- ` else console.log(JSON.stringify(payload));`,
124
- `}`,
163
+ ...sendFunctionSource([`reported = true;`]),
125
164
  ``,
126
165
  // collectEntities (bundled, real DiscoveryError instances) already names
127
166
  // the exact offending file on a same-directory duplicate — reuse that
@@ -156,7 +195,7 @@ export function generateDriverSource(options: GenerateDriverOptions): string {
156
195
  ` entities = collectEntities(modules, BUILD_ROOT);`,
157
196
  ` } catch (err) {`,
158
197
  ` errors.push(classifyChildError(errFile(err), err, "resolution").toJSON());`,
159
- ` send({ entitySet: { entities: [] }, errors, provenanceByName: {} });`,
198
+ ` await send({ entitySet: { entities: [] }, errors, provenanceByName: {} });`,
160
199
  ` return;`,
161
200
  ` }`,
162
201
  ``,
@@ -178,18 +217,20 @@ export function generateDriverSource(options: GenerateDriverOptions): string {
178
217
  ` errors.push(classifyChildError("", err, "resolution").toJSON());`,
179
218
  ` }`,
180
219
  ``,
220
+ ` let entitySet;`,
181
221
  ` try {`,
182
- ` const entitySet = encodeEntitySet(entities);`,
183
- ` send({ entitySet, errors, provenanceByName });`,
222
+ ` entitySet = encodeEntitySet(entities);`,
184
223
  ` } catch (err) {`,
185
224
  ` errors.push(classifyChildError("", err, "resolution").toJSON());`,
186
- ` send({ entitySet: { entities: [] }, errors, provenanceByName });`,
225
+ ` await send({ entitySet: { entities: [] }, errors, provenanceByName });`,
226
+ ` return;`,
187
227
  ` }`,
228
+ ` await send({ entitySet, errors, provenanceByName });`,
188
229
  `}`,
189
230
  ``,
190
- `main().catch((err) => {`,
191
- ` send({ entitySet: { entities: [] }, errors: [classifyChildError("", err).toJSON()], provenanceByName: {}, fatal: true });`,
192
- `});`,
231
+ `main().catch((err) =>`,
232
+ ` send({ entitySet: { entities: [] }, errors: [classifyChildError("", err).toJSON()], provenanceByName: {}, fatal: true }),`,
233
+ `);`,
193
234
  );
194
235
 
195
236
  return lines.join("\n");
@@ -218,18 +259,16 @@ export function generateConfigDriverSource(configPath: string): string {
218
259
  return [
219
260
  `import { classifyChildError } from ${lit(CHILD_ERRORS_MODULE)};`,
220
261
  `import { scanConfigWireSafety } from ${lit(CONFIG_WIRE_MODULE)};`,
262
+ `import { writeSync } from "node:fs";`,
221
263
  ``,
222
- `function send(payload) {`,
223
- ` if (typeof process.send === "function") process.send(payload);`,
224
- ` else console.log(JSON.stringify(payload));`,
225
- `}`,
264
+ ...sendFunctionSource(),
226
265
  ``,
227
266
  `async function main() {`,
228
267
  ` let namespace;`,
229
268
  ` try {`,
230
269
  ` namespace = await import(${lit(configPath)});`,
231
270
  ` } catch (err) {`,
232
- ` send({ kind: "chant-config", ok: false, error: classifyChildError(${lit(configPath)}, err).toJSON() });`,
271
+ ` await send({ kind: "chant-config", ok: false, error: classifyChildError(${lit(configPath)}, err).toJSON() });`,
233
272
  ` return;`,
234
273
  ` }`,
235
274
  ``,
@@ -240,24 +279,26 @@ export function generateConfigDriverSource(configPath: string): string {
240
279
  ``,
241
280
  ` const offenders = scanConfigWireSafety(selected);`,
242
281
  ` if (offenders.length > 0) {`,
243
- ` send({ kind: "chant-config", ok: false, offenders });`,
282
+ ` await send({ kind: "chant-config", ok: false, offenders });`,
244
283
  ` return;`,
245
284
  ` }`,
246
285
  ``,
286
+ ` let config;`,
247
287
  ` try {`,
248
288
  // Round-trip here, not just at the IPC boundary: this is what proves the
249
289
  // payload really is JSON before it leaves the child, and turns anything
250
290
  // the scan somehow missed into a named error instead of a quiet drop.
251
- ` const config = JSON.parse(JSON.stringify(selected ?? {}));`,
252
- ` send({ kind: "chant-config", ok: true, config });`,
291
+ ` config = JSON.parse(JSON.stringify(selected ?? {}));`,
253
292
  ` } catch (err) {`,
254
- ` send({ kind: "chant-config", ok: false, error: classifyChildError(${lit(configPath)}, err, "resolution").toJSON() });`,
293
+ ` await send({ kind: "chant-config", ok: false, error: classifyChildError(${lit(configPath)}, err, "resolution").toJSON() });`,
294
+ ` return;`,
255
295
  ` }`,
296
+ ` await send({ kind: "chant-config", ok: true, config });`,
256
297
  `}`,
257
298
  ``,
258
- `main().catch((err) => {`,
259
- ` send({ kind: "chant-config", ok: false, error: classifyChildError(${lit(configPath)}, err).toJSON() });`,
260
- `});`,
299
+ `main().catch((err) =>`,
300
+ ` send({ kind: "chant-config", ok: false, error: classifyChildError(${lit(configPath)}, err).toJSON() }),`,
301
+ `);`,
261
302
  ].join("\n");
262
303
  }
263
304
 
@@ -294,18 +335,15 @@ export function generatePolicyDriverSource(policyPaths: readonly string[]): stri
294
335
  `import { classifyChildError } from ${lit(CHILD_ERRORS_MODULE)};`,
295
336
  `import { decodePolicyBuildResult, scanPolicyDiagnostics, PolicyWireError } from ${lit(POLICY_WIRE_MODULE)};`,
296
337
  `import { runPostSynthChecks, isPostSynthCheck } from ${lit(POST_SYNTH_MODULE)};`,
338
+ `import { writeSync } from "node:fs";`,
297
339
  ``,
298
- `function send(payload) {`,
299
- ` if (typeof process.send === "function") process.send(payload);`,
300
- ` else console.log(JSON.stringify(payload));`,
301
- `}`,
340
+ ...sendFunctionSource(),
302
341
  ``,
303
342
  `function fail(file, err, type) {`,
304
343
  ` if (err instanceof PolicyWireError) {`,
305
- ` send({ kind: "chant-policy", ok: false, offenders: err.offenders });`,
306
- ` return;`,
344
+ ` return send({ kind: "chant-policy", ok: false, offenders: err.offenders });`,
307
345
  ` }`,
308
- ` send({ kind: "chant-policy", ok: false, error: classifyChildError(file, err, type).toJSON() });`,
346
+ ` return send({ kind: "chant-policy", ok: false, error: classifyChildError(file, err, type).toJSON() });`,
309
347
  `}`,
310
348
  ``,
311
349
  // The wrapper described in the doc above: same id/description so any
@@ -329,7 +367,7 @@ export function generatePolicyDriverSource(policyPaths: readonly string[]): stri
329
367
  ` try {`,
330
368
  ` buildResult = decodePolicyBuildResult(request.buildResult);`,
331
369
  ` } catch (err) {`,
332
- ` fail("", err, "resolution");`,
370
+ ` await fail("", err, "resolution");`,
333
371
  ` return;`,
334
372
  ` }`,
335
373
  ``,
@@ -347,7 +385,7 @@ export function generatePolicyDriverSource(policyPaths: readonly string[]): stri
347
385
  ` if (isPostSynthCheck(value)) checks.push(guard(value, ${lit(policyPath)}));`,
348
386
  ` }`,
349
387
  ` } catch (err) {`,
350
- ` fail(${lit(policyPath)}, err, "import");`,
388
+ ` await fail(${lit(policyPath)}, err, "import");`,
351
389
  ` return;`,
352
390
  ` }`,
353
391
  );
@@ -359,11 +397,11 @@ export function generatePolicyDriverSource(policyPaths: readonly string[]): stri
359
397
  ` try {`,
360
398
  ` diagnostics = runPostSynthChecks(checks, buildResult, request.env ?? undefined);`,
361
399
  ` } catch (err) {`,
362
- ` fail("", err, "resolution");`,
400
+ ` await fail("", err, "resolution");`,
363
401
  ` return;`,
364
402
  ` }`,
365
403
  ``,
366
- ` send({ kind: "chant-policy", ok: true, diagnostics });`,
404
+ ` await send({ kind: "chant-policy", ok: true, diagnostics });`,
367
405
  `}`,
368
406
  ``,
369
407
  // Registered synchronously at module top level — see the doc above on why