wingfoil 0.2.1

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 (339) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +334 -0
  3. package/dist/cli/error.d.ts +11 -0
  4. package/dist/cli/error.d.ts.map +1 -0
  5. package/dist/cli/error.js +27 -0
  6. package/dist/cli/error.js.map +1 -0
  7. package/dist/cli/exit.d.ts +19 -0
  8. package/dist/cli/exit.d.ts.map +1 -0
  9. package/dist/cli/exit.js +14 -0
  10. package/dist/cli/exit.js.map +1 -0
  11. package/dist/cli/index.d.ts +28 -0
  12. package/dist/cli/index.d.ts.map +1 -0
  13. package/dist/cli/index.js +36 -0
  14. package/dist/cli/index.js.map +1 -0
  15. package/dist/cli/init-command.d.ts +34 -0
  16. package/dist/cli/init-command.d.ts.map +1 -0
  17. package/dist/cli/init-command.js +100 -0
  18. package/dist/cli/init-command.js.map +1 -0
  19. package/dist/cli/mcp-command.d.ts +24 -0
  20. package/dist/cli/mcp-command.d.ts.map +1 -0
  21. package/dist/cli/mcp-command.js +42 -0
  22. package/dist/cli/mcp-command.js.map +1 -0
  23. package/dist/cli/output.d.ts +13 -0
  24. package/dist/cli/output.d.ts.map +1 -0
  25. package/dist/cli/output.js +28 -0
  26. package/dist/cli/output.js.map +1 -0
  27. package/dist/cli/program.d.ts +12 -0
  28. package/dist/cli/program.d.ts.map +1 -0
  29. package/dist/cli/program.js +279 -0
  30. package/dist/cli/program.js.map +1 -0
  31. package/dist/cli/registrar.d.ts +69 -0
  32. package/dist/cli/registrar.d.ts.map +1 -0
  33. package/dist/cli/registrar.js +82 -0
  34. package/dist/cli/registrar.js.map +1 -0
  35. package/dist/cli.d.ts +3 -0
  36. package/dist/cli.d.ts.map +1 -0
  37. package/dist/cli.js +45 -0
  38. package/dist/cli.js.map +1 -0
  39. package/dist/core/approval-authority.d.ts +83 -0
  40. package/dist/core/approval-authority.d.ts.map +1 -0
  41. package/dist/core/approval-authority.js +112 -0
  42. package/dist/core/approval-authority.js.map +1 -0
  43. package/dist/core/builtin-asset.d.ts +71 -0
  44. package/dist/core/builtin-asset.d.ts.map +1 -0
  45. package/dist/core/builtin-asset.js +137 -0
  46. package/dist/core/builtin-asset.js.map +1 -0
  47. package/dist/core/builtin-integrity.d.ts +32 -0
  48. package/dist/core/builtin-integrity.d.ts.map +1 -0
  49. package/dist/core/builtin-integrity.js +148 -0
  50. package/dist/core/builtin-integrity.js.map +1 -0
  51. package/dist/core/confinement.d.ts +57 -0
  52. package/dist/core/confinement.d.ts.map +1 -0
  53. package/dist/core/confinement.js +108 -0
  54. package/dist/core/confinement.js.map +1 -0
  55. package/dist/core/context.d.ts +187 -0
  56. package/dist/core/context.d.ts.map +1 -0
  57. package/dist/core/context.js +192 -0
  58. package/dist/core/context.js.map +1 -0
  59. package/dist/core/directive-assign.d.ts +131 -0
  60. package/dist/core/directive-assign.d.ts.map +1 -0
  61. package/dist/core/directive-assign.js +352 -0
  62. package/dist/core/directive-assign.js.map +1 -0
  63. package/dist/core/directives-list.d.ts +80 -0
  64. package/dist/core/directives-list.d.ts.map +1 -0
  65. package/dist/core/directives-list.js +148 -0
  66. package/dist/core/directives-list.js.map +1 -0
  67. package/dist/core/exit-code.d.ts +85 -0
  68. package/dist/core/exit-code.d.ts.map +1 -0
  69. package/dist/core/exit-code.js +163 -0
  70. package/dist/core/exit-code.js.map +1 -0
  71. package/dist/core/git-identity.d.ts +32 -0
  72. package/dist/core/git-identity.d.ts.map +1 -0
  73. package/dist/core/git-identity.js +72 -0
  74. package/dist/core/git-identity.js.map +1 -0
  75. package/dist/core/index.d.ts +388 -0
  76. package/dist/core/index.d.ts.map +1 -0
  77. package/dist/core/index.js +1331 -0
  78. package/dist/core/index.js.map +1 -0
  79. package/dist/core/init.d.ts +78 -0
  80. package/dist/core/init.d.ts.map +1 -0
  81. package/dist/core/init.js +167 -0
  82. package/dist/core/init.js.map +1 -0
  83. package/dist/core/loaders.d.ts +174 -0
  84. package/dist/core/loaders.d.ts.map +1 -0
  85. package/dist/core/loaders.js +364 -0
  86. package/dist/core/loaders.js.map +1 -0
  87. package/dist/core/memory-add-type.d.ts +43 -0
  88. package/dist/core/memory-add-type.d.ts.map +1 -0
  89. package/dist/core/memory-add-type.js +151 -0
  90. package/dist/core/memory-add-type.js.map +1 -0
  91. package/dist/core/memory-transition.d.ts +111 -0
  92. package/dist/core/memory-transition.d.ts.map +1 -0
  93. package/dist/core/memory-transition.js +269 -0
  94. package/dist/core/memory-transition.js.map +1 -0
  95. package/dist/core/registry.d.ts +189 -0
  96. package/dist/core/registry.d.ts.map +1 -0
  97. package/dist/core/registry.js +66 -0
  98. package/dist/core/registry.js.map +1 -0
  99. package/dist/core/relevance.d.ts +86 -0
  100. package/dist/core/relevance.d.ts.map +1 -0
  101. package/dist/core/relevance.js +258 -0
  102. package/dist/core/relevance.js.map +1 -0
  103. package/dist/core/require-reason.d.ts +27 -0
  104. package/dist/core/require-reason.d.ts.map +1 -0
  105. package/dist/core/require-reason.js +95 -0
  106. package/dist/core/require-reason.js.map +1 -0
  107. package/dist/core/types.d.ts +45 -0
  108. package/dist/core/types.d.ts.map +1 -0
  109. package/dist/core/types.js +22 -0
  110. package/dist/core/types.js.map +1 -0
  111. package/dist/core/usage-error.d.ts +20 -0
  112. package/dist/core/usage-error.d.ts.map +1 -0
  113. package/dist/core/usage-error.js +29 -0
  114. package/dist/core/usage-error.js.map +1 -0
  115. package/dist/core/write-guard.d.ts +99 -0
  116. package/dist/core/write-guard.d.ts.map +1 -0
  117. package/dist/core/write-guard.js +247 -0
  118. package/dist/core/write-guard.js.map +1 -0
  119. package/dist/directives/create.d.ts +47 -0
  120. package/dist/directives/create.d.ts.map +1 -0
  121. package/dist/directives/create.js +82 -0
  122. package/dist/directives/create.js.map +1 -0
  123. package/dist/directives/index.d.ts +20 -0
  124. package/dist/directives/index.d.ts.map +1 -0
  125. package/dist/directives/index.js +32 -0
  126. package/dist/directives/index.js.map +1 -0
  127. package/dist/directives/roles-edit.d.ts +53 -0
  128. package/dist/directives/roles-edit.d.ts.map +1 -0
  129. package/dist/directives/roles-edit.js +286 -0
  130. package/dist/directives/roles-edit.js.map +1 -0
  131. package/dist/directives/schema.d.ts +65 -0
  132. package/dist/directives/schema.d.ts.map +1 -0
  133. package/dist/directives/schema.js +68 -0
  134. package/dist/directives/schema.js.map +1 -0
  135. package/dist/dna/edit.d.ts +61 -0
  136. package/dist/dna/edit.d.ts.map +1 -0
  137. package/dist/dna/edit.js +429 -0
  138. package/dist/dna/edit.js.map +1 -0
  139. package/dist/dna/index.d.ts +24 -0
  140. package/dist/dna/index.d.ts.map +1 -0
  141. package/dist/dna/index.js +59 -0
  142. package/dist/dna/index.js.map +1 -0
  143. package/dist/dna/mutate.d.ts +74 -0
  144. package/dist/dna/mutate.d.ts.map +1 -0
  145. package/dist/dna/mutate.js +318 -0
  146. package/dist/dna/mutate.js.map +1 -0
  147. package/dist/dna/path.d.ts +175 -0
  148. package/dist/dna/path.d.ts.map +1 -0
  149. package/dist/dna/path.js +289 -0
  150. package/dist/dna/path.js.map +1 -0
  151. package/dist/dna/roles.d.ts +57 -0
  152. package/dist/dna/roles.d.ts.map +1 -0
  153. package/dist/dna/roles.js +50 -0
  154. package/dist/dna/roles.js.map +1 -0
  155. package/dist/dna/schema.d.ts +172 -0
  156. package/dist/dna/schema.d.ts.map +1 -0
  157. package/dist/dna/schema.js +182 -0
  158. package/dist/dna/schema.js.map +1 -0
  159. package/dist/dna/set.d.ts +145 -0
  160. package/dist/dna/set.d.ts.map +1 -0
  161. package/dist/dna/set.js +428 -0
  162. package/dist/dna/set.js.map +1 -0
  163. package/dist/mcp/dna-resource.d.ts +16 -0
  164. package/dist/mcp/dna-resource.d.ts.map +1 -0
  165. package/dist/mcp/dna-resource.js +56 -0
  166. package/dist/mcp/dna-resource.js.map +1 -0
  167. package/dist/mcp/index.d.ts +49 -0
  168. package/dist/mcp/index.d.ts.map +1 -0
  169. package/dist/mcp/index.js +60 -0
  170. package/dist/mcp/index.js.map +1 -0
  171. package/dist/mcp/memory-resource.d.ts +17 -0
  172. package/dist/mcp/memory-resource.d.ts.map +1 -0
  173. package/dist/mcp/memory-resource.js +133 -0
  174. package/dist/mcp/memory-resource.js.map +1 -0
  175. package/dist/mcp/prompt.d.ts +48 -0
  176. package/dist/mcp/prompt.d.ts.map +1 -0
  177. package/dist/mcp/prompt.js +176 -0
  178. package/dist/mcp/prompt.js.map +1 -0
  179. package/dist/mcp/read-only.d.ts +42 -0
  180. package/dist/mcp/read-only.d.ts.map +1 -0
  181. package/dist/mcp/read-only.js +93 -0
  182. package/dist/mcp/read-only.js.map +1 -0
  183. package/dist/mcp/registrar.d.ts +54 -0
  184. package/dist/mcp/registrar.d.ts.map +1 -0
  185. package/dist/mcp/registrar.js +72 -0
  186. package/dist/mcp/registrar.js.map +1 -0
  187. package/dist/mcp/server.d.ts +58 -0
  188. package/dist/mcp/server.d.ts.map +1 -0
  189. package/dist/mcp/server.js +64 -0
  190. package/dist/mcp/server.js.map +1 -0
  191. package/dist/mcp/workflow-resource.d.ts +17 -0
  192. package/dist/mcp/workflow-resource.d.ts.map +1 -0
  193. package/dist/mcp/workflow-resource.js +63 -0
  194. package/dist/mcp/workflow-resource.js.map +1 -0
  195. package/dist/memory/add.d.ts +50 -0
  196. package/dist/memory/add.d.ts.map +1 -0
  197. package/dist/memory/add.js +131 -0
  198. package/dist/memory/add.js.map +1 -0
  199. package/dist/memory/audit.d.ts +177 -0
  200. package/dist/memory/audit.d.ts.map +1 -0
  201. package/dist/memory/audit.js +287 -0
  202. package/dist/memory/audit.js.map +1 -0
  203. package/dist/memory/commit-message.d.ts +146 -0
  204. package/dist/memory/commit-message.d.ts.map +1 -0
  205. package/dist/memory/commit-message.js +203 -0
  206. package/dist/memory/commit-message.js.map +1 -0
  207. package/dist/memory/entry.d.ts +56 -0
  208. package/dist/memory/entry.d.ts.map +1 -0
  209. package/dist/memory/entry.js +28 -0
  210. package/dist/memory/entry.js.map +1 -0
  211. package/dist/memory/frontmatter-edit.d.ts +68 -0
  212. package/dist/memory/frontmatter-edit.d.ts.map +1 -0
  213. package/dist/memory/frontmatter-edit.js +333 -0
  214. package/dist/memory/frontmatter-edit.js.map +1 -0
  215. package/dist/memory/git-log.d.ts +32 -0
  216. package/dist/memory/git-log.d.ts.map +1 -0
  217. package/dist/memory/git-log.js +100 -0
  218. package/dist/memory/git-log.js.map +1 -0
  219. package/dist/memory/history.d.ts +142 -0
  220. package/dist/memory/history.d.ts.map +1 -0
  221. package/dist/memory/history.js +250 -0
  222. package/dist/memory/history.js.map +1 -0
  223. package/dist/memory/index.d.ts +58 -0
  224. package/dist/memory/index.d.ts.map +1 -0
  225. package/dist/memory/index.js +107 -0
  226. package/dist/memory/index.js.map +1 -0
  227. package/dist/memory/query.d.ts +150 -0
  228. package/dist/memory/query.d.ts.map +1 -0
  229. package/dist/memory/query.js +307 -0
  230. package/dist/memory/query.js.map +1 -0
  231. package/dist/memory/reject.d.ts +11 -0
  232. package/dist/memory/reject.d.ts.map +1 -0
  233. package/dist/memory/reject.js +32 -0
  234. package/dist/memory/reject.js.map +1 -0
  235. package/dist/memory/schema.d.ts +104 -0
  236. package/dist/memory/schema.d.ts.map +1 -0
  237. package/dist/memory/schema.js +161 -0
  238. package/dist/memory/schema.js.map +1 -0
  239. package/dist/memory/state-machine.d.ts +166 -0
  240. package/dist/memory/state-machine.d.ts.map +1 -0
  241. package/dist/memory/state-machine.js +431 -0
  242. package/dist/memory/state-machine.js.map +1 -0
  243. package/dist/memory/submit.d.ts +11 -0
  244. package/dist/memory/submit.d.ts.map +1 -0
  245. package/dist/memory/submit.js +44 -0
  246. package/dist/memory/submit.js.map +1 -0
  247. package/dist/storage/builtin-directives.d.ts +54 -0
  248. package/dist/storage/builtin-directives.d.ts.map +1 -0
  249. package/dist/storage/builtin-directives.js +152 -0
  250. package/dist/storage/builtin-directives.js.map +1 -0
  251. package/dist/storage/commit.d.ts +92 -0
  252. package/dist/storage/commit.d.ts.map +1 -0
  253. package/dist/storage/commit.js +201 -0
  254. package/dist/storage/commit.js.map +1 -0
  255. package/dist/storage/confinement.d.ts +93 -0
  256. package/dist/storage/confinement.d.ts.map +1 -0
  257. package/dist/storage/confinement.js +164 -0
  258. package/dist/storage/confinement.js.map +1 -0
  259. package/dist/storage/document.d.ts +27 -0
  260. package/dist/storage/document.d.ts.map +1 -0
  261. package/dist/storage/document.js +52 -0
  262. package/dist/storage/document.js.map +1 -0
  263. package/dist/storage/errors.d.ts +36 -0
  264. package/dist/storage/errors.d.ts.map +1 -0
  265. package/dist/storage/errors.js +46 -0
  266. package/dist/storage/errors.js.map +1 -0
  267. package/dist/storage/frontmatter.d.ts +27 -0
  268. package/dist/storage/frontmatter.d.ts.map +1 -0
  269. package/dist/storage/frontmatter.js +37 -0
  270. package/dist/storage/frontmatter.js.map +1 -0
  271. package/dist/storage/git-root.d.ts +17 -0
  272. package/dist/storage/git-root.d.ts.map +1 -0
  273. package/dist/storage/git-root.js +49 -0
  274. package/dist/storage/git-root.js.map +1 -0
  275. package/dist/storage/index.d.ts +29 -0
  276. package/dist/storage/index.d.ts.map +1 -0
  277. package/dist/storage/index.js +66 -0
  278. package/dist/storage/index.js.map +1 -0
  279. package/dist/storage/init-state.d.ts +9 -0
  280. package/dist/storage/init-state.d.ts.map +1 -0
  281. package/dist/storage/init-state.js +24 -0
  282. package/dist/storage/init-state.js.map +1 -0
  283. package/dist/storage/layout.d.ts +51 -0
  284. package/dist/storage/layout.d.ts.map +1 -0
  285. package/dist/storage/layout.js +88 -0
  286. package/dist/storage/layout.js.map +1 -0
  287. package/dist/storage/memory-path.d.ts +72 -0
  288. package/dist/storage/memory-path.d.ts.map +1 -0
  289. package/dist/storage/memory-path.js +127 -0
  290. package/dist/storage/memory-path.js.map +1 -0
  291. package/dist/storage/snapshot.d.ts +16 -0
  292. package/dist/storage/snapshot.d.ts.map +1 -0
  293. package/dist/storage/snapshot.js +72 -0
  294. package/dist/storage/snapshot.js.map +1 -0
  295. package/dist/storage/templates.d.ts +88 -0
  296. package/dist/storage/templates.d.ts.map +1 -0
  297. package/dist/storage/templates.js +422 -0
  298. package/dist/storage/templates.js.map +1 -0
  299. package/dist/validation/error-mapper.d.ts +16 -0
  300. package/dist/validation/error-mapper.d.ts.map +1 -0
  301. package/dist/validation/error-mapper.js +18 -0
  302. package/dist/validation/error-mapper.js.map +1 -0
  303. package/dist/validation/errors.d.ts +69 -0
  304. package/dist/validation/errors.d.ts.map +1 -0
  305. package/dist/validation/errors.js +69 -0
  306. package/dist/validation/errors.js.map +1 -0
  307. package/dist/validation/id.d.ts +16 -0
  308. package/dist/validation/id.d.ts.map +1 -0
  309. package/dist/validation/id.js +177 -0
  310. package/dist/validation/id.js.map +1 -0
  311. package/dist/validation/index.d.ts +18 -0
  312. package/dist/validation/index.d.ts.map +1 -0
  313. package/dist/validation/index.js +37 -0
  314. package/dist/validation/index.js.map +1 -0
  315. package/dist/validation/secret-scan.d.ts +129 -0
  316. package/dist/validation/secret-scan.d.ts.map +1 -0
  317. package/dist/validation/secret-scan.js +367 -0
  318. package/dist/validation/secret-scan.js.map +1 -0
  319. package/dist/validation/two-pass.d.ts +24 -0
  320. package/dist/validation/two-pass.d.ts.map +1 -0
  321. package/dist/validation/two-pass.js +24 -0
  322. package/dist/validation/two-pass.js.map +1 -0
  323. package/dist/validation/warning.d.ts +31 -0
  324. package/dist/validation/warning.d.ts.map +1 -0
  325. package/dist/validation/warning.js +81 -0
  326. package/dist/validation/warning.js.map +1 -0
  327. package/dist/validation/yaml.d.ts +9 -0
  328. package/dist/validation/yaml.d.ts.map +1 -0
  329. package/dist/validation/yaml.js +30 -0
  330. package/dist/validation/yaml.js.map +1 -0
  331. package/dist/workflow/index.d.ts +10 -0
  332. package/dist/workflow/index.d.ts.map +1 -0
  333. package/dist/workflow/index.js +16 -0
  334. package/dist/workflow/index.js.map +1 -0
  335. package/dist/workflow/schema.d.ts +89 -0
  336. package/dist/workflow/schema.d.ts.map +1 -0
  337. package/dist/workflow/schema.js +73 -0
  338. package/dist/workflow/schema.js.map +1 -0
  339. package/package.json +78 -0
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.verifyBuiltinTemplates = verifyBuiltinTemplates;
4
+ /**
5
+ * Built-in template schema check (task-044-builtin-template-integrity, REQ-SEC-10 "Schema checks on
6
+ * built-in templates"): "Built-in directive and workflow templates are schema-checked before
7
+ * installation during `init`... A corrupted or schema-invalid built-in template aborts `init` before
8
+ * writing partial assets, with a message naming the failing template"
9
+ * (docs/02_requirements/03_sard/05_security-compliance.md).
10
+ *
11
+ * SCOPE, per `dl-031-req-sec-10-integrity-depth` (`ready`), which retitled the requirement: schema
12
+ * validation IS the REQ-SEC-10 contract. There is deliberately NO digest and NO manifest here — the
13
+ * threat the requirement's rationale names is accidental corruption (truncated file, bad merge), not
14
+ * post-install tampering; a manifest shipped inside the same package is no control against an
15
+ * adversary who can rewrite a template; and a content hash would add a line-ending / encoding /
16
+ * ordering determinism surface against REQ-SYS-07. Distribution-channel assurance lives in `adr-009`
17
+ * / `spec-015` (npm provenance) instead. Do not "strengthen" this module with hashing.
18
+ *
19
+ * A cross-pillar concern by construction — checking a directive source needs the Directives pillar's
20
+ * `DirectiveFrontmatter` schema, checking a workflow source needs the Workflow pillar's `Workflow`
21
+ * schema — so, per `src/core/loaders.ts`'s own precedent ("Cross-file concerns ... need the caller to
22
+ * have actually loaded the referenced files ... live in the loader, not [a pillar] schema module"),
23
+ * this lives in `core`, not in either pillar module or in `storage` (which only carries the raw
24
+ * {@link BuiltinTemplateSource} shape — see `src/storage/templates.ts`). Reuses the exact same
25
+ * two-pass validation primitives (`parseYaml`, `runValidation`) every pillar loader already uses, so a
26
+ * built-in source is checked against precisely the same schema its custom counterpart would be loaded
27
+ * with — no bespoke re-implementation of YAML/frontmatter parsing.
28
+ *
29
+ * `verifyBuiltinTemplates` is a pure function of its input list (REQ-SYS-07: no wall-clock, no
30
+ * randomness) — the same corrupted input always yields the same failure, and callers may pass a
31
+ * fixture list in tests via {@link BuiltinTemplateSource} without touching disk.
32
+ */
33
+ const schema_1 = require("../directives/schema");
34
+ const storage_1 = require("../storage");
35
+ const validation_1 = require("../validation");
36
+ const schema_2 = require("../workflow/schema");
37
+ /**
38
+ * `true` iff `source.content` parses as a `.md` document with a frontmatter block that validates
39
+ * against `DirectiveFrontmatter` (the same schema `core/loaders.ts`'s `loadDirectives` runs custom
40
+ * directive files through). Any parse or schema failure — missing frontmatter, unparsable YAML, a
41
+ * missing required field — is "corrupted" for REQ-SEC-10's purposes; the exact cause is not surfaced
42
+ * (the fit criterion only requires naming *which* template failed, not why).
43
+ */
44
+ function isValidDirectiveSource(source) {
45
+ const frontmatterText = (0, storage_1.extractFrontmatter)(source.content);
46
+ if (frontmatterText === null)
47
+ return false;
48
+ try {
49
+ const data = (0, validation_1.parseYaml)(frontmatterText, source.name);
50
+ (0, validation_1.runValidation)(schema_1.DirectiveFrontmatter, data, source.name);
51
+ return true;
52
+ }
53
+ catch {
54
+ return false;
55
+ }
56
+ }
57
+ /**
58
+ * `true` iff `source.content` parses as YAML and validates against the `Workflow` schema (the same
59
+ * schema `core/loaders.ts`'s `loadWorkflowsYaml` runs every included workflow file through).
60
+ */
61
+ function isValidWorkflowSource(source) {
62
+ try {
63
+ const data = (0, validation_1.parseYaml)(source.content, source.name);
64
+ (0, validation_1.runValidation)(schema_2.Workflow, data, source.name);
65
+ return true;
66
+ }
67
+ catch {
68
+ return false;
69
+ }
70
+ }
71
+ /**
72
+ * Per-kind schema-check policy: the `isValid` predicate to apply and the exact REQ-SEC-10 abort-message
73
+ * builder to use when it fails. Keyed by {@link BuiltinTemplateKind} so each kind's validator and its
74
+ * BDD wording live together and `verifyBuiltinTemplates` branches on `kind` exactly once. The message
75
+ * strings are verbatim BDD contracts — P3.8 "Error - a built-in template fails its integrity check"
76
+ * and P4.17 "Error - a built-in workflow template is structurally invalid" — do not reword.
77
+ *
78
+ * Declared as an EXHAUSTIVE `Record` so widening {@link BuiltinTemplateKind} is a compile error until
79
+ * the new kind gets a policy; look it up only through {@link policyFor}, never by bare indexing.
80
+ */
81
+ const INTEGRITY_POLICY = {
82
+ directive: {
83
+ isValid: isValidDirectiveSource,
84
+ message: (name) => `built-in directive template integrity check failed: ${name}`,
85
+ },
86
+ workflow: {
87
+ isValid: isValidWorkflowSource,
88
+ message: (name) => `built-in workflow template invalid: ${name}`,
89
+ },
90
+ };
91
+ /**
92
+ * The REQ-SEC-10 message for a source whose `kind` has no policy — the fail-closed branch. Not a BDD
93
+ * contract string (the P3.8/P4.17 scenarios only cover the two known kinds), but it obeys the fit
94
+ * criterion's one requirement of every abort message: it NAMES the failing template. The offending
95
+ * `kind` is echoed so the operator can see what arrived.
96
+ */
97
+ function unknownKindMessage(name, kind) {
98
+ return `built-in template integrity check failed: ${name} (unrecognized kind "${kind}")`;
99
+ }
100
+ /**
101
+ * The policy for `kind`, or `undefined` when there is none.
102
+ *
103
+ * `INTEGRITY_POLICY[kind]` on its own is FAIL-OPEN at runtime: TypeScript types the result as always
104
+ * present, but a `kind` outside the union — reachable through a cast, a JS caller, a JSON boundary, or
105
+ * a future disk-derived source list — yields `undefined` (or, for `constructor`/`toString`/`__proto__`,
106
+ * an inherited `Object.prototype` member), and the call on it throws a `TypeError`. That throw escapes
107
+ * `initWingfoilProject`, whose guard-5 call sits outside its `try`/`catch`, as an uncaught exception
108
+ * instead of the `VALIDATION` CoreResult / exit 1 REQ-SEC-10 demands. `hasOwnProperty` keeps inherited
109
+ * members out, so an unrecognized kind resolves to `undefined` and the caller fails it closed.
110
+ */
111
+ function policyFor(kind) {
112
+ return Object.prototype.hasOwnProperty.call(INTEGRITY_POLICY, kind) ? INTEGRITY_POLICY[kind] : undefined;
113
+ }
114
+ /**
115
+ * Schema-check every `sources` entry, in list order (REQ-SYS-07: deterministic, no unordered
116
+ * iteration), and return the FIRST one that fails — or `null` when every source is valid (including
117
+ * the trivial, always-passing case of an empty list, which is what `src/storage/templates.ts`'s
118
+ * `builtinTemplateSources` derives from the P1.1 minimal skeleton, whose built-in directory holds only
119
+ * a `.gitkeep`; `templateScaffold` yields the six P3.8 built-in directives since
120
+ * `task-057-builtin-directive-templates`).
121
+ *
122
+ * FAILS CLOSED on an unrecognized `kind`: a source whose kind has no {@link INTEGRITY_POLICY} entry is
123
+ * reported as a failure ({@link unknownKindMessage}) rather than skipped or thrown on — it cannot be
124
+ * checked, so it must not be installed.
125
+ *
126
+ * Callers — `initWingfoilProject` AND `initWingfoilStorage`, both in `src/core/init.ts`; every
127
+ * `initStorage` write path there is one — MUST run this before writing any file: REQ-SEC-10's fit
128
+ * criterion requires the abort to happen "before writing partial assets". This function itself never
129
+ * writes or reads from disk, so calling it before the scaffold write is sufficient to satisfy that
130
+ * ordering.
131
+ */
132
+ function verifyBuiltinTemplates(sources) {
133
+ for (const source of sources) {
134
+ const policy = policyFor(source.kind);
135
+ if (policy === undefined) {
136
+ return {
137
+ name: source.name,
138
+ kind: source.kind,
139
+ message: unknownKindMessage(source.name, String(source.kind)),
140
+ };
141
+ }
142
+ if (!policy.isValid(source)) {
143
+ return { name: source.name, kind: source.kind, message: policy.message(source.name) };
144
+ }
145
+ }
146
+ return null;
147
+ }
148
+ //# sourceMappingURL=builtin-integrity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builtin-integrity.js","sourceRoot":"","sources":["../../src/core/builtin-integrity.ts"],"names":[],"mappings":";;AAsJA,wDAiBC;AAvKD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,iDAA4D;AAC5D,wCAAsG;AACtG,8CAAyD;AACzD,+CAA8C;AAe9C;;;;;;GAMG;AACH,SAAS,sBAAsB,CAAC,MAA6B;IAC3D,MAAM,eAAe,GAAG,IAAA,4BAAkB,EAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC3D,IAAI,eAAe,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC3C,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAA,sBAAS,EAAC,eAAe,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QACrD,IAAA,0BAAa,EAAC,6BAAoB,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QACvD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,qBAAqB,CAAC,MAA6B;IAC1D,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAA,sBAAS,EAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QACpD,IAAA,0BAAa,EAAC,iBAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QAC3C,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAQD;;;;;;;;;GASG;AACH,MAAM,gBAAgB,GAA2D;IAC/E,SAAS,EAAE;QACT,OAAO,EAAE,sBAAsB;QAC/B,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,uDAAuD,IAAI,EAAE;KACjF;IACD,QAAQ,EAAE;QACR,OAAO,EAAE,qBAAqB;QAC9B,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,uCAAuC,IAAI,EAAE;KACjE;CACF,CAAC;AAEF;;;;;GAKG;AACH,SAAS,kBAAkB,CAAC,IAAY,EAAE,IAAY;IACpD,OAAO,6CAA6C,IAAI,wBAAwB,IAAI,IAAI,CAAC;AAC3F,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,SAAS,CAAC,IAAyB;IAC1C,OAAO,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3G,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,SAAgB,sBAAsB,CACpC,OAAyC;IAEzC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO;gBACL,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,OAAO,EAAE,kBAAkB,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;aAC9D,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC5B,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QACxF,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,57 @@
1
+ import { type CoreResult } from './types';
2
+ /**
3
+ * Refuse, before anything on disk changes, a target whose **real** path lies outside the project
4
+ * root.
5
+ *
6
+ * Resolution is the point. A prefix check over `relativePath` cannot see this: the path this bug is
7
+ * about — `.wingfoil/directives/custom/legacy-rule.md` under a `custom` that is a symlink to another
8
+ * directory — is inside the project in every segment and outside it on the filesystem. So the check
9
+ * resolves first and compares afterwards.
10
+ *
11
+ * Returns a `VALIDATION` `CoreResult.error`, which `exitCodeForError` maps to exit **1**: a
12
+ * well-formed invocation refused on the state of the repository, never the `2` that
13
+ * `spec-005-cli-command-contract` §1 reserves for a malformed one. The message names both spellings
14
+ * of the path — the one the operator typed against and the one it really resolves to — because the
15
+ * two differing is the entire finding, and it deliberately carries no child-process text
16
+ * (`bug-071`/`bug-093`).
17
+ *
18
+ * **What it answers, and what it does not.** This decides where the path *leads*: the target's
19
+ * parent is real-resolved and its own name is kept, which is right for a deletion and insufficient
20
+ * for a write ({@link resolveRealPathInRoot}'s own doc, `bug-120`). A caller that is about to
21
+ * **write** asks {@link requireConfinedWriteTarget} instead, which adds the second question this one
22
+ * does not ask.
23
+ *
24
+ * @param root - Project root.
25
+ * @param relativePath - Root-relative POSIX path of the file the operation is about to touch.
26
+ * @param action - The verb for the refusal's first clause, e.g. `'remove'` → `cannot remove '<path>'`
27
+ * (P3.3's own message shape, shared with `requireCustomAsset`).
28
+ */
29
+ export declare function requireConfinedTarget(root: string, relativePath: string, action: string): CoreResult<void>;
30
+ /**
31
+ * {@link requireConfinedTarget} plus the question a **write** has to ask and a delete must not: the
32
+ * target's own name may not be a symbolic link (task-106,
33
+ * `bug-120-a-symlinked-document-leaf-is-followed-by-the-write`).
34
+ *
35
+ * `unlinkSync` acts on a link; `writeFileSync` follows it. So the parent-resolved, leaf-unresolved
36
+ * shape that makes a symlinked directive file removable (`bug-044`'s benign case) lets a write land
37
+ * wherever the link points — measured on the four Memory transition verbs, which rewrote a linked
38
+ * document *outside the project root* and then failed with git's own text (`bug-120` D2). The
39
+ * asymmetry is therefore read per verb, which is `dl-086`'s ground and is argued there rather than
40
+ * here: resolve the parent everywhere, refuse a symlinked leaf where the syscall would follow it.
41
+ *
42
+ * Order matters and is the confinement one first: a path that leaves the project is reported as
43
+ * leaving the project, whatever its leaf is, so the operator reads the boundary finding and not a
44
+ * narrower one that happens to fire first. Both refusals are `VALIDATION` → exit `1`, and the
45
+ * symlink sentence is {@link symlinkTargetRefusal}, shared verbatim with the `StorageError`
46
+ * `memory add` raises through `resolveConfinedMemoryPath`, so the two surfaces cannot drift.
47
+ *
48
+ * The TOCTOU window between this check and the write is real and is not closable on a path-based
49
+ * API — Node's `fs` exposes no `O_NOFOLLOW` (see {@link targetIsSymlink}). This turns a routine,
50
+ * self-inflicted loss into a refusal; it is not an adversarial defence.
51
+ *
52
+ * @param root - Project root.
53
+ * @param relativePath - Root-relative POSIX path of the file the operation is about to write.
54
+ * @param action - The verb for the refusal's first clause, e.g. `'write'` → `cannot write '<path>'`.
55
+ */
56
+ export declare function requireConfinedWriteTarget(root: string, relativePath: string, action: string): CoreResult<void>;
57
+ //# sourceMappingURL=confinement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"confinement.d.ts","sourceRoot":"","sources":["../../src/core/confinement.ts"],"names":[],"mappings":"AAgCA,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,GACb,UAAU,CAAC,IAAI,CAAC,CAUlB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,GACb,UAAU,CAAC,IAAI,CAAC,CAKlB"}
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.requireConfinedTarget = requireConfinedTarget;
4
+ exports.requireConfinedWriteTarget = requireConfinedWriteTarget;
5
+ /**
6
+ * Project-root confinement as a mutating-op **pre-flight** (REQ-SEC-06 —
7
+ * `task-102`, `bug-044-symlinked-directives-custom-escapes-confinement`; extended to the write
8
+ * paths' own question by `task-106`, `bug-120`).
9
+ *
10
+ * The boundary is decided once, in `src/storage/confinement.ts`; this is the thin layer that turns
11
+ * that answer into the `CoreResult` a core operation returns, exactly as `requireGitIdentity`
12
+ * (REQ-SEC-01) and `requireCustomAsset` (REQ-SEC-07) do for their own rules. It lives in `src/core`
13
+ * for their reason too: the rule crosscuts pillars, and the CLI and MCP surfaces must enforce it
14
+ * identically because they run the same function (REQ-SYS-05).
15
+ *
16
+ * Two entry points, because a delete and a write do not ask the same thing:
17
+ * {@link requireConfinedTarget} for an operation whose syscall acts on a link (`unlinkSync`), and
18
+ * {@link requireConfinedWriteTarget} for one whose syscall follows it (`writeFileSync`).
19
+ *
20
+ * **Why a pre-flight and not a check at the write.** `bug-044`'s whole defect is order. The shipped
21
+ * `directive remove` unlinked the outside file and only then failed, on `git add`, with a raw
22
+ * `execFileSync` message — so the operator lost a file, got git's text instead of a mapped refusal,
23
+ * and no commit recorded any of it. A check that runs after the deletion reports a loss it could
24
+ * have prevented; the refusal has to land before the filesystem is touched at all.
25
+ *
26
+ * **Baseline.** This guard resolves against the **working tree**, not `HEAD`, and that is deliberate
27
+ * — a departure from the `command-baseline` directive's read half, argued (as that directive
28
+ * requires) in `task-102`'s Execution Notes and in the decision-log filed from them, never settled
29
+ * here. The one-line reason: what this predicts is where `unlinkSync` — or, on the write path,
30
+ * `writeFileSync` — will land, and both follow the symlinks that are on disk, not the ones a commit
31
+ * records.
32
+ */
33
+ const node_path_1 = require("node:path");
34
+ const confinement_1 = require("../storage/confinement");
35
+ const types_1 = require("./types");
36
+ /**
37
+ * Refuse, before anything on disk changes, a target whose **real** path lies outside the project
38
+ * root.
39
+ *
40
+ * Resolution is the point. A prefix check over `relativePath` cannot see this: the path this bug is
41
+ * about — `.wingfoil/directives/custom/legacy-rule.md` under a `custom` that is a symlink to another
42
+ * directory — is inside the project in every segment and outside it on the filesystem. So the check
43
+ * resolves first and compares afterwards.
44
+ *
45
+ * Returns a `VALIDATION` `CoreResult.error`, which `exitCodeForError` maps to exit **1**: a
46
+ * well-formed invocation refused on the state of the repository, never the `2` that
47
+ * `spec-005-cli-command-contract` §1 reserves for a malformed one. The message names both spellings
48
+ * of the path — the one the operator typed against and the one it really resolves to — because the
49
+ * two differing is the entire finding, and it deliberately carries no child-process text
50
+ * (`bug-071`/`bug-093`).
51
+ *
52
+ * **What it answers, and what it does not.** This decides where the path *leads*: the target's
53
+ * parent is real-resolved and its own name is kept, which is right for a deletion and insufficient
54
+ * for a write ({@link resolveRealPathInRoot}'s own doc, `bug-120`). A caller that is about to
55
+ * **write** asks {@link requireConfinedWriteTarget} instead, which adds the second question this one
56
+ * does not ask.
57
+ *
58
+ * @param root - Project root.
59
+ * @param relativePath - Root-relative POSIX path of the file the operation is about to touch.
60
+ * @param action - The verb for the refusal's first clause, e.g. `'remove'` → `cannot remove '<path>'`
61
+ * (P3.3's own message shape, shared with `requireCustomAsset`).
62
+ */
63
+ function requireConfinedTarget(root, relativePath, action) {
64
+ const resolved = (0, confinement_1.resolveRealPathInRoot)(root, relativePath);
65
+ if (resolved.within)
66
+ return (0, types_1.coreOk)(undefined);
67
+ return (0, types_1.coreErr)({
68
+ code: 'VALIDATION',
69
+ message: `cannot ${action} '${relativePath}': it resolves to '${resolved.real}', outside the project root. ` +
70
+ 'Nothing has been written or deleted — this check runs before the operation touches the filesystem; ' +
71
+ 'check whether a directory on the way to it is a symlink.',
72
+ });
73
+ }
74
+ /**
75
+ * {@link requireConfinedTarget} plus the question a **write** has to ask and a delete must not: the
76
+ * target's own name may not be a symbolic link (task-106,
77
+ * `bug-120-a-symlinked-document-leaf-is-followed-by-the-write`).
78
+ *
79
+ * `unlinkSync` acts on a link; `writeFileSync` follows it. So the parent-resolved, leaf-unresolved
80
+ * shape that makes a symlinked directive file removable (`bug-044`'s benign case) lets a write land
81
+ * wherever the link points — measured on the four Memory transition verbs, which rewrote a linked
82
+ * document *outside the project root* and then failed with git's own text (`bug-120` D2). The
83
+ * asymmetry is therefore read per verb, which is `dl-086`'s ground and is argued there rather than
84
+ * here: resolve the parent everywhere, refuse a symlinked leaf where the syscall would follow it.
85
+ *
86
+ * Order matters and is the confinement one first: a path that leaves the project is reported as
87
+ * leaving the project, whatever its leaf is, so the operator reads the boundary finding and not a
88
+ * narrower one that happens to fire first. Both refusals are `VALIDATION` → exit `1`, and the
89
+ * symlink sentence is {@link symlinkTargetRefusal}, shared verbatim with the `StorageError`
90
+ * `memory add` raises through `resolveConfinedMemoryPath`, so the two surfaces cannot drift.
91
+ *
92
+ * The TOCTOU window between this check and the write is real and is not closable on a path-based
93
+ * API — Node's `fs` exposes no `O_NOFOLLOW` (see {@link targetIsSymlink}). This turns a routine,
94
+ * self-inflicted loss into a refusal; it is not an adversarial defence.
95
+ *
96
+ * @param root - Project root.
97
+ * @param relativePath - Root-relative POSIX path of the file the operation is about to write.
98
+ * @param action - The verb for the refusal's first clause, e.g. `'write'` → `cannot write '<path>'`.
99
+ */
100
+ function requireConfinedWriteTarget(root, relativePath, action) {
101
+ const confined = requireConfinedTarget(root, relativePath, action);
102
+ if (!confined.ok)
103
+ return confined;
104
+ if (!(0, confinement_1.targetIsSymlink)((0, node_path_1.join)(root, relativePath)))
105
+ return (0, types_1.coreOk)(undefined);
106
+ return (0, types_1.coreErr)({ code: 'VALIDATION', message: (0, confinement_1.symlinkTargetRefusal)(action, relativePath) });
107
+ }
108
+ //# sourceMappingURL=confinement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"confinement.js","sourceRoot":"","sources":["../../src/core/confinement.ts"],"names":[],"mappings":";;AA6DA,sDAcC;AA4BD,gEASC;AAhHD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,yCAAiC;AAEjC,wDAAsG;AAEtG,mCAA2D;AAE3D;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,SAAgB,qBAAqB,CACnC,IAAY,EACZ,YAAoB,EACpB,MAAc;IAEd,MAAM,QAAQ,GAAG,IAAA,mCAAqB,EAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IAC3D,IAAI,QAAQ,CAAC,MAAM;QAAE,OAAO,IAAA,cAAM,EAAO,SAAS,CAAC,CAAC;IACpD,OAAO,IAAA,eAAO,EAAC;QACb,IAAI,EAAE,YAAY;QAClB,OAAO,EACL,UAAU,MAAM,KAAK,YAAY,sBAAsB,QAAQ,CAAC,IAAI,+BAA+B;YACnG,qGAAqG;YACrG,0DAA0D;KAC7D,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,SAAgB,0BAA0B,CACxC,IAAY,EACZ,YAAoB,EACpB,MAAc;IAEd,MAAM,QAAQ,GAAG,qBAAqB,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;IACnE,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,OAAO,QAAQ,CAAC;IAClC,IAAI,CAAC,IAAA,6BAAe,EAAC,IAAA,gBAAI,EAAC,IAAI,EAAE,YAAY,CAAC,CAAC;QAAE,OAAO,IAAA,cAAM,EAAO,SAAS,CAAC,CAAC;IAC/E,OAAO,IAAA,eAAO,EAAC,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,IAAA,kCAAoB,EAAC,MAAM,EAAE,YAAY,CAAC,EAAE,CAAC,CAAC;AAC9F,CAAC"}
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Agent execution context assembly (task-037-role-task-scoped-context, REQ-STATE-05) — the
3
+ * `directive-loader` + a minimal `context-builder` slice of
4
+ * `spec-012-context-loader-relevance-filtering` (§1 "placement & responsibilities", §5 "Directive
5
+ * resolution"). Satisfies REQ-STATE-05's Fit Criterion, verbatim: "The assembled context object
6
+ * exposes separate `dna`, `memory`, `directives` sections; it contains 100% of the role's assigned
7
+ * directives and 0 directives of other roles."
8
+ *
9
+ * **Archived elements are excluded** (REQ-STATE-06, the `{deprecated, superseded}` set ratified by
10
+ * `dl-028-archived-states-excluded-from-context`): {@link assembleExecutionContext} drops a resolved
11
+ * element whose `status` is archived, through the shared `isArchivedStatus` primitive rather than any
12
+ * local notion of "archived". This closed `bug-010-deprecated-reaches-agent-context` — which this
13
+ * header previously recorded as an open gap — together with the `wingfoil://memory/{type}` collection
14
+ * Resource, the other surface that bug covered (`task-069-fix-archived-excluded-from-agent-context`).
15
+ *
16
+ * Still deliberately NOT the full spec-012 pipeline: no `ContextRequest`/`stateRef` pinning, no tiered
17
+ * (T1–T4) Memory relevance ranking, no bounding caps, and no canonical byte-for-byte Markdown
18
+ * serialization (§7). Those are split across sibling Wave-1 v0.2 tasks
19
+ * (task-035-bounded-context-relevance REQ-PERF-05, task-038-deprecated-excluded-from-context
20
+ * REQ-STATE-06) and v0.3 (`REQ-SYS-07`, `REQ-PERF-01`, `REQ-STATE-09`, the real `agent execute`
21
+ * CLI/MCP surface) — see this task's Execution Notes for the full scope decision. This module wraps
22
+ * only existing, already-shipped primitives: `loadDirectives`/`loadRolesYaml` (`./loaders`),
23
+ * `findMemoryDocumentByTypeAndId` and `isArchivedStatus` (`../memory`, task-011/task-035) — no
24
+ * scan/parse logic and no second status predicate is reimplemented here.
25
+ */
26
+ import { type MemoryDocumentSummary } from '../memory';
27
+ import type { MemoryYaml } from '../memory/schema';
28
+ import type { RolesYaml } from '../directives/schema';
29
+ import type { DnaYaml } from '../dna/schema';
30
+ import type { DirectiveFile } from './loaders';
31
+ /**
32
+ * The outcome of {@link resolveRoleDirectives}: the resolved directive files **and** any operator
33
+ * diagnostics produced while resolving them.
34
+ *
35
+ * `warnings` is returned rather than written to `stderr` (or to a logger) on purpose: this is a
36
+ * context-building path, where REQ-SYS-07 requires the output to be a pure function of the inputs.
37
+ * A returned array keeps the diagnostic deterministic, unit-testable without capturing process
38
+ * streams, and free for the eventual CLI/MCP surface (`task-055-auto-load-directives-by-role`) to
39
+ * render however that surface renders warnings.
40
+ */
41
+ export interface RoleDirectiveResolution {
42
+ /** Deduplicated by directive id and sorted ascending by id — see {@link resolveRoleDirectives}. */
43
+ readonly directives: readonly DirectiveFile[];
44
+ /** Operator diagnostics, in a fixed order. Empty when the role's bindings are unremarkable. */
45
+ readonly warnings: readonly string[];
46
+ }
47
+ /**
48
+ * The outcome of {@link selectDirectivesById}: one winning file per directive id, and one shadow
49
+ * warning per id that more than one file defines.
50
+ */
51
+ export interface DirectiveSelection {
52
+ /** Winning file per directive id, keyed and iterated in ascending id order. */
53
+ readonly byId: ReadonlyMap<string, DirectiveFile>;
54
+ /** `directive '<id>' defined in <path>, <path>; using <winner>` — one per shadowed id, ascending by id. */
55
+ readonly warnings: readonly string[];
56
+ }
57
+ /**
58
+ * Deduplicate `directiveFiles` by `frontmatter.id` — the one place WingFoil decides which of several
59
+ * same-id directive files is in force. Both {@link resolveRoleDirectives} (context assembly) and the
60
+ * `directives list` payload (`./directives-list.ts`, `dl-042-directives-list-output-contract` A) call
61
+ * this, so the rule the listing *reports* is the rule context assembly *applies*.
62
+ *
63
+ * - Precedence: `custom/` wins over `built-in/`, then smallest path (see `compareDirectivePrecedence`).
64
+ * - A shadowed file is **reported, never silently dropped** (dl-037 B.1): one warning per shadowed id,
65
+ * naming every file that defines it (in precedence-independent path order) and the winner.
66
+ * - `ids`, when given, restricts selection (and warnings) to those directive ids.
67
+ */
68
+ export declare function selectDirectivesById(directiveFiles: readonly DirectiveFile[], ids?: ReadonlySet<string>): DirectiveSelection;
69
+ /**
70
+ * Resolve the directives bound to `role` — its own `roles.yaml` `assignments[role]` entries plus every
71
+ * `global` directive — per `spec-012-context-loader-relevance-filtering` §5 and
72
+ * `dl-029-role-with-no-directive-assignments` (`ready`, option (c)).
73
+ *
74
+ * Behaviour, point by point:
75
+ *
76
+ * - **Globals are unconditional** (spec-012 §5). Every role gets them, including a role with no
77
+ * bindings of its own — globals exist precisely so that no role can be configured out of
78
+ * `security-secrets` (dl-029, Rationale).
79
+ * - **A role that contributes no assignments of its own** — absent from `assignments`, or bound to an
80
+ * explicitly empty list — resolves to exactly the globals **and** produces the warning
81
+ * `no directives assigned to role '<role>'`. This is dl-029's ratified hybrid, and it is the
82
+ * behaviour the edge scenario of `p3-directives/P3.6-auto-load-by-role.feature` specifies
83
+ * ("the agent context contains only the global directives" + that warning). Never an error.
84
+ * Role-name lookup reads **own** properties only, so a role named after an `Object.prototype`
85
+ * member (`toString`, `constructor`, …) is treated as unbound like any other unknown role.
86
+ * - **Deduplicated by directive id, `custom/` winning over `built-in/`** (spec-012 §5, dl-037 A.1),
87
+ * through the shared {@link selectDirectivesById}; each shadowed id the role is bound to adds a
88
+ * warning naming the files and the winner (dl-037 B.1).
89
+ * - **A dangling binding is reported** — an id bound to the role (own assignment or global) with no
90
+ * file in `directiveFiles` adds `directive '<id>' bound to role '<role>' has no directive file`
91
+ * (`dl-042-directives-list-output-contract` D). It is still not an error: the rest resolves.
92
+ * - **Sorted ascending by directive id** (spec-012 §5: "never rely on `roles.yaml` listing order or
93
+ * file-system enumeration order", REQ-SYS-07).
94
+ * - `warnings` has a fixed order: the no-assignments warning, then dangling ids ascending, then
95
+ * shadowed ids ascending.
96
+ *
97
+ * This is REQ-STATE-05's Fit Criterion made concrete: for any two distinct roles with disjoint
98
+ * `assignments`, the resolved sets are disjoint too (100% of one role's directives, 0% of the other's).
99
+ */
100
+ export declare function resolveRoleDirectives(directiveFiles: readonly DirectiveFile[], rolesYaml: RolesYaml, role: string): RoleDirectiveResolution;
101
+ /** The active Memory element an execution context is scoped to (spec-012 §2 `ContextRequest.element`,
102
+ * narrowed here to just `type`/`id` — this task does not implement the full `ContextRequest` shape). */
103
+ export interface ExecutionContextElement {
104
+ readonly type: string;
105
+ readonly id: string;
106
+ }
107
+ /**
108
+ * The assembled execution context (REQ-STATE-05 Fit Criterion, P5.4.4 "a context object containing
109
+ * distinct DNA, Memory, and Directives sections", "each section is individually addressable"): three
110
+ * plain, independently-readable properties — `context.dna`, `context.memory`,
111
+ * `context.directives` — never merged into one flat structure.
112
+ */
113
+ export interface ExecutionContext {
114
+ /** The full parsed `dna.yaml` (P5.4.4: DNA section — this task does not sub-select DNA sections by
115
+ * scope, unlike spec-012 §4's full `dna-loader`; that selection logic is out of this task's scope). */
116
+ readonly dna: DnaYaml;
117
+ /** This role's assigned directives + global, resolved by {@link resolveRoleDirectives}. */
118
+ readonly directives: readonly DirectiveFile[];
119
+ /** Memory relevant to `element` — today, exactly the element's own document (0 or 1 entries), and
120
+ * empty when that document is archived (REQ-STATE-06; see {@link assembleExecutionContext}); the
121
+ * full T1–T4 tiered relevance expansion is task-035/038/v0.3's scope (see the module doc comment). */
122
+ readonly memory: readonly MemoryDocumentSummary[];
123
+ /** Operator diagnostics gathered during assembly — currently only
124
+ * {@link RoleDirectiveResolution.warnings}: no-assignments (dl-029), dangling binding (dl-042 D),
125
+ * shadowed directive (dl-037 B.1). Diagnostics *about* the context, not content
126
+ * *of* it: spec-012 §7's canonical envelope has no warnings section, so this never enters the
127
+ * serialized payload. */
128
+ readonly warnings: readonly string[];
129
+ }
130
+ /** Inputs to {@link assembleExecutionContext} — every pillar's config, already loaded by its own
131
+ * loader (`./loaders.ts`), plus the `role`/`element` the context is being assembled for. Nothing here
132
+ * reads the filesystem itself beyond resolving `element` via `findMemoryDocumentByTypeAndId`. */
133
+ export interface ExecutionContextInputs {
134
+ /** Project root the Memory element lookup resolves against (same root the pillar loaders were
135
+ * called with). */
136
+ readonly root: string;
137
+ readonly dna: DnaYaml;
138
+ readonly memoryYaml: MemoryYaml;
139
+ readonly directiveFiles: readonly DirectiveFile[];
140
+ readonly rolesYaml: RolesYaml;
141
+ readonly role: string;
142
+ readonly element: ExecutionContextElement;
143
+ }
144
+ /**
145
+ * Assemble a role- and task-scoped {@link ExecutionContext} (REQ-STATE-05) from already-loaded pillar
146
+ * config. Given the same inputs and unchanged on-disk Memory content, the output is deep-equal across
147
+ * calls: the function itself introduces no wall-clock, randomness, or unordered iteration
148
+ * (REQ-SYS-07's discipline, applied to this narrower slice per REQ-STATE-05's own rationale "crisp,
149
+ * low-noise context; determinism"), and `directives` is totally ordered by
150
+ * {@link resolveRoleDirectives}.
151
+ *
152
+ * `memory` resolves `element` via `findMemoryDocumentByTypeAndId`
153
+ * (`task-011-mcp-resources-read-only`). Two properties of that primitive are inherited here and are
154
+ * worth stating plainly:
155
+ *
156
+ * - It is **not** a single addressed read. It walks the Memory document paths derived from
157
+ * `memoryYaml` in sorted order and YAML-parses each document's frontmatter until one matches
158
+ * `element.type`/`element.id`, so cost grows with the size of the Memory tree (bounding that is
159
+ * `task-035-bounded-context-relevance`'s REQ-PERF-05 scope, not this task's).
160
+ * - It therefore **can throw**. An `element` that matches nothing yields an empty `memory` array
161
+ * rather than an error — an unresolvable element is the caller's concern to surface — but a
162
+ * `ValidationError` (`E_YAML_PARSE_ERROR`) propagates out of assembly if any document visited
163
+ * during that walk has unparseable frontmatter. This function adds no `try`/`catch` of its own.
164
+ *
165
+ * `memory` **is** filtered by document status, in one direction only: a resolved element whose
166
+ * `status` is archived — `{deprecated, superseded}`, per the shared {@link isArchivedStatus}
167
+ * (`../memory`, the set ratified by `dl-028-archived-states-excluded-from-context`) — is dropped,
168
+ * leaving `memory` empty. That is REQ-STATE-06's Fit Criterion as amended: "a `deprecated` or
169
+ * `superseded` document never appears in an assembled agent context". The filter is applied to the
170
+ * lookup's *result* rather than pushed into `findMemoryDocumentByTypeAndId`, because that same
171
+ * primitive also serves `wingfoil://memory/{type}/{id}`, where explicit retrieval of archived content
172
+ * must keep working.
173
+ *
174
+ * `draft` is deliberately **not** excluded here, unlike in `./relevance.ts`. That module filters
175
+ * *candidate* documents for relevance (spec-012 §6, which does bar `draft` from a context); this
176
+ * function resolves the **subject** element the context is being assembled for (spec-012 §3's
177
+ * `resolve-element` stage), and blanking the context for a task still in `draft` would defeat the
178
+ * point of assembling it. dl-028 ratified the archived set as `{deprecated, superseded}` and did not
179
+ * put `draft` in it.
180
+ *
181
+ * An archived element produces no warning: {@link ExecutionContext.warnings} carries
182
+ * directive-resolution diagnostics only (dl-029, dl-037, dl-042), and adding an unratified entry would change a payload
183
+ * REQ-SYS-07 governs. An archived element is therefore indistinguishable here from an unresolvable
184
+ * one — both yield `memory: []`.
185
+ */
186
+ export declare function assembleExecutionContext(inputs: ExecutionContextInputs): ExecutionContext;
187
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/core/context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EAAmD,KAAK,qBAAqB,EAAE,MAAM,WAAW,CAAC;AACxG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAG7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C;;;;;;;;;GASG;AACH,MAAM,WAAW,uBAAuB;IACtC,mGAAmG;IACnG,QAAQ,CAAC,UAAU,EAAE,SAAS,aAAa,EAAE,CAAC;IAC9C,+FAA+F;IAC/F,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAWD;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAClD,2GAA2G;IAC3G,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAoBD;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAClC,cAAc,EAAE,SAAS,aAAa,EAAE,EACxC,GAAG,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GACxB,kBAAkB,CAuBpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,qBAAqB,CACnC,cAAc,EAAE,SAAS,aAAa,EAAE,EACxC,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,MAAM,GACX,uBAAuB,CAczB;AAED;wGACwG;AACxG,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B;2GACuG;IACvG,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,2FAA2F;IAC3F,QAAQ,CAAC,UAAU,EAAE,SAAS,aAAa,EAAE,CAAC;IAC9C;;0GAEsG;IACtG,QAAQ,CAAC,MAAM,EAAE,SAAS,qBAAqB,EAAE,CAAC;IAClD;;;;6BAIyB;IACzB,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAED;;iGAEiG;AACjG,MAAM,WAAW,sBAAsB;IACrC;uBACmB;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,cAAc,EAAE,SAAS,aAAa,EAAE,CAAC;IAClD,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,uBAAuB,CAAC;CAC3C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,sBAAsB,GAAG,gBAAgB,CASzF"}