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,192 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.selectDirectivesById = selectDirectivesById;
4
+ exports.resolveRoleDirectives = resolveRoleDirectives;
5
+ exports.assembleExecutionContext = assembleExecutionContext;
6
+ /**
7
+ * Agent execution context assembly (task-037-role-task-scoped-context, REQ-STATE-05) — the
8
+ * `directive-loader` + a minimal `context-builder` slice of
9
+ * `spec-012-context-loader-relevance-filtering` (§1 "placement & responsibilities", §5 "Directive
10
+ * resolution"). Satisfies REQ-STATE-05's Fit Criterion, verbatim: "The assembled context object
11
+ * exposes separate `dna`, `memory`, `directives` sections; it contains 100% of the role's assigned
12
+ * directives and 0 directives of other roles."
13
+ *
14
+ * **Archived elements are excluded** (REQ-STATE-06, the `{deprecated, superseded}` set ratified by
15
+ * `dl-028-archived-states-excluded-from-context`): {@link assembleExecutionContext} drops a resolved
16
+ * element whose `status` is archived, through the shared `isArchivedStatus` primitive rather than any
17
+ * local notion of "archived". This closed `bug-010-deprecated-reaches-agent-context` — which this
18
+ * header previously recorded as an open gap — together with the `wingfoil://memory/{type}` collection
19
+ * Resource, the other surface that bug covered (`task-069-fix-archived-excluded-from-agent-context`).
20
+ *
21
+ * Still deliberately NOT the full spec-012 pipeline: no `ContextRequest`/`stateRef` pinning, no tiered
22
+ * (T1–T4) Memory relevance ranking, no bounding caps, and no canonical byte-for-byte Markdown
23
+ * serialization (§7). Those are split across sibling Wave-1 v0.2 tasks
24
+ * (task-035-bounded-context-relevance REQ-PERF-05, task-038-deprecated-excluded-from-context
25
+ * REQ-STATE-06) and v0.3 (`REQ-SYS-07`, `REQ-PERF-01`, `REQ-STATE-09`, the real `agent execute`
26
+ * CLI/MCP surface) — see this task's Execution Notes for the full scope decision. This module wraps
27
+ * only existing, already-shipped primitives: `loadDirectives`/`loadRolesYaml` (`./loaders`),
28
+ * `findMemoryDocumentByTypeAndId` and `isArchivedStatus` (`../memory`, task-011/task-035) — no
29
+ * scan/parse logic and no second status predicate is reimplemented here.
30
+ */
31
+ const memory_1 = require("../memory");
32
+ const builtin_asset_1 = require("./builtin-asset");
33
+ /** `assignments` is a plain object parsed from YAML, so a role named `toString`, `constructor`,
34
+ * `valueOf`, `hasOwnProperty` or `__proto__` would otherwise resolve to an inherited
35
+ * `Object.prototype` member instead of `undefined`. Read own properties only. */
36
+ function ownAssignments(rolesYaml, role) {
37
+ return Object.prototype.hasOwnProperty.call(rolesYaml.assignments, role)
38
+ ? rolesYaml.assignments[role]
39
+ : undefined;
40
+ }
41
+ /**
42
+ * The single precedence rule between two directive files that share an id
43
+ * (`dl-037-builtin-vs-custom-directive-precedence` A.1, `spec-012` §5): negative when `a` wins.
44
+ *
45
+ * A `custom/` file beats any other — "custom" decided by REQ-SEC-07's structural discriminator
46
+ * {@link isRemovableCustomAssetPath}, which keys on the `custom` directory segment and accepts both
47
+ * path separators (`loadDirectives` builds `path` with the platform `join`). `frontmatter.kind` is not
48
+ * consulted: it can disagree with the directory, and the directory is what REQ-SEC-07 trusts. Within
49
+ * the same tier the lexicographically smallest `path` wins, so the order is total and independent of
50
+ * the order files arrive in (REQ-SYS-07).
51
+ */
52
+ function compareDirectivePrecedence(a, b) {
53
+ const aCustom = (0, builtin_asset_1.isRemovableCustomAssetPath)('directive', a.path);
54
+ const bCustom = (0, builtin_asset_1.isRemovableCustomAssetPath)('directive', b.path);
55
+ if (aCustom !== bCustom)
56
+ return aCustom ? -1 : 1;
57
+ return a.path < b.path ? -1 : a.path > b.path ? 1 : 0;
58
+ }
59
+ /**
60
+ * Deduplicate `directiveFiles` by `frontmatter.id` — the one place WingFoil decides which of several
61
+ * same-id directive files is in force. Both {@link resolveRoleDirectives} (context assembly) and the
62
+ * `directives list` payload (`./directives-list.ts`, `dl-042-directives-list-output-contract` A) call
63
+ * this, so the rule the listing *reports* is the rule context assembly *applies*.
64
+ *
65
+ * - Precedence: `custom/` wins over `built-in/`, then smallest path (see `compareDirectivePrecedence`).
66
+ * - A shadowed file is **reported, never silently dropped** (dl-037 B.1): one warning per shadowed id,
67
+ * naming every file that defines it (in precedence-independent path order) and the winner.
68
+ * - `ids`, when given, restricts selection (and warnings) to those directive ids.
69
+ */
70
+ function selectDirectivesById(directiveFiles, ids) {
71
+ const candidates = new Map();
72
+ for (const file of directiveFiles) {
73
+ const id = file.frontmatter.id;
74
+ if (ids !== undefined && !ids.has(id))
75
+ continue;
76
+ const list = candidates.get(id);
77
+ if (list === undefined)
78
+ candidates.set(id, [file]);
79
+ else
80
+ list.push(file);
81
+ }
82
+ const byId = new Map();
83
+ const warnings = [];
84
+ // Ascending by id (REQ-SYS-07): never `roles.yaml` listing order or file-system enumeration order.
85
+ // `candidates` is keyed by id, so no two keys are equal and a two-way compare is total here.
86
+ for (const [id, files] of [...candidates].sort(([a], [b]) => (a < b ? -1 : 1))) {
87
+ const [winner] = [...files].sort(compareDirectivePrecedence);
88
+ byId.set(id, winner);
89
+ if (files.length > 1) {
90
+ const paths = files.map((file) => file.path).sort();
91
+ warnings.push(`directive '${id}' defined in ${paths.join(', ')}; using ${winner.path}`);
92
+ }
93
+ }
94
+ return { byId, warnings };
95
+ }
96
+ /**
97
+ * Resolve the directives bound to `role` — its own `roles.yaml` `assignments[role]` entries plus every
98
+ * `global` directive — per `spec-012-context-loader-relevance-filtering` §5 and
99
+ * `dl-029-role-with-no-directive-assignments` (`ready`, option (c)).
100
+ *
101
+ * Behaviour, point by point:
102
+ *
103
+ * - **Globals are unconditional** (spec-012 §5). Every role gets them, including a role with no
104
+ * bindings of its own — globals exist precisely so that no role can be configured out of
105
+ * `security-secrets` (dl-029, Rationale).
106
+ * - **A role that contributes no assignments of its own** — absent from `assignments`, or bound to an
107
+ * explicitly empty list — resolves to exactly the globals **and** produces the warning
108
+ * `no directives assigned to role '<role>'`. This is dl-029's ratified hybrid, and it is the
109
+ * behaviour the edge scenario of `p3-directives/P3.6-auto-load-by-role.feature` specifies
110
+ * ("the agent context contains only the global directives" + that warning). Never an error.
111
+ * Role-name lookup reads **own** properties only, so a role named after an `Object.prototype`
112
+ * member (`toString`, `constructor`, …) is treated as unbound like any other unknown role.
113
+ * - **Deduplicated by directive id, `custom/` winning over `built-in/`** (spec-012 §5, dl-037 A.1),
114
+ * through the shared {@link selectDirectivesById}; each shadowed id the role is bound to adds a
115
+ * warning naming the files and the winner (dl-037 B.1).
116
+ * - **A dangling binding is reported** — an id bound to the role (own assignment or global) with no
117
+ * file in `directiveFiles` adds `directive '<id>' bound to role '<role>' has no directive file`
118
+ * (`dl-042-directives-list-output-contract` D). It is still not an error: the rest resolves.
119
+ * - **Sorted ascending by directive id** (spec-012 §5: "never rely on `roles.yaml` listing order or
120
+ * file-system enumeration order", REQ-SYS-07).
121
+ * - `warnings` has a fixed order: the no-assignments warning, then dangling ids ascending, then
122
+ * shadowed ids ascending.
123
+ *
124
+ * This is REQ-STATE-05's Fit Criterion made concrete: for any two distinct roles with disjoint
125
+ * `assignments`, the resolved sets are disjoint too (100% of one role's directives, 0% of the other's).
126
+ */
127
+ function resolveRoleDirectives(directiveFiles, rolesYaml, role) {
128
+ const assigned = ownAssignments(rolesYaml, role);
129
+ const warnings = assigned === undefined || assigned.length === 0
130
+ ? [`no directives assigned to role '${role}'`]
131
+ : [];
132
+ const allowedIds = new Set([...(assigned ?? []), ...rolesYaml.global]);
133
+ const selection = selectDirectivesById(directiveFiles, allowedIds);
134
+ const dangling = [...allowedIds].filter((id) => !selection.byId.has(id)).sort();
135
+ for (const id of dangling)
136
+ warnings.push(`directive '${id}' bound to role '${role}' has no directive file`);
137
+ warnings.push(...selection.warnings);
138
+ return { directives: [...selection.byId.values()], warnings };
139
+ }
140
+ /**
141
+ * Assemble a role- and task-scoped {@link ExecutionContext} (REQ-STATE-05) from already-loaded pillar
142
+ * config. Given the same inputs and unchanged on-disk Memory content, the output is deep-equal across
143
+ * calls: the function itself introduces no wall-clock, randomness, or unordered iteration
144
+ * (REQ-SYS-07's discipline, applied to this narrower slice per REQ-STATE-05's own rationale "crisp,
145
+ * low-noise context; determinism"), and `directives` is totally ordered by
146
+ * {@link resolveRoleDirectives}.
147
+ *
148
+ * `memory` resolves `element` via `findMemoryDocumentByTypeAndId`
149
+ * (`task-011-mcp-resources-read-only`). Two properties of that primitive are inherited here and are
150
+ * worth stating plainly:
151
+ *
152
+ * - It is **not** a single addressed read. It walks the Memory document paths derived from
153
+ * `memoryYaml` in sorted order and YAML-parses each document's frontmatter until one matches
154
+ * `element.type`/`element.id`, so cost grows with the size of the Memory tree (bounding that is
155
+ * `task-035-bounded-context-relevance`'s REQ-PERF-05 scope, not this task's).
156
+ * - It therefore **can throw**. An `element` that matches nothing yields an empty `memory` array
157
+ * rather than an error — an unresolvable element is the caller's concern to surface — but a
158
+ * `ValidationError` (`E_YAML_PARSE_ERROR`) propagates out of assembly if any document visited
159
+ * during that walk has unparseable frontmatter. This function adds no `try`/`catch` of its own.
160
+ *
161
+ * `memory` **is** filtered by document status, in one direction only: a resolved element whose
162
+ * `status` is archived — `{deprecated, superseded}`, per the shared {@link isArchivedStatus}
163
+ * (`../memory`, the set ratified by `dl-028-archived-states-excluded-from-context`) — is dropped,
164
+ * leaving `memory` empty. That is REQ-STATE-06's Fit Criterion as amended: "a `deprecated` or
165
+ * `superseded` document never appears in an assembled agent context". The filter is applied to the
166
+ * lookup's *result* rather than pushed into `findMemoryDocumentByTypeAndId`, because that same
167
+ * primitive also serves `wingfoil://memory/{type}/{id}`, where explicit retrieval of archived content
168
+ * must keep working.
169
+ *
170
+ * `draft` is deliberately **not** excluded here, unlike in `./relevance.ts`. That module filters
171
+ * *candidate* documents for relevance (spec-012 §6, which does bar `draft` from a context); this
172
+ * function resolves the **subject** element the context is being assembled for (spec-012 §3's
173
+ * `resolve-element` stage), and blanking the context for a task still in `draft` would defeat the
174
+ * point of assembling it. dl-028 ratified the archived set as `{deprecated, superseded}` and did not
175
+ * put `draft` in it.
176
+ *
177
+ * An archived element produces no warning: {@link ExecutionContext.warnings} carries
178
+ * directive-resolution diagnostics only (dl-029, dl-037, dl-042), and adding an unratified entry would change a payload
179
+ * REQ-SYS-07 governs. An archived element is therefore indistinguishable here from an unresolvable
180
+ * one — both yield `memory: []`.
181
+ */
182
+ function assembleExecutionContext(inputs) {
183
+ const { directives, warnings } = resolveRoleDirectives(inputs.directiveFiles, inputs.rolesYaml, inputs.role);
184
+ const doc = (0, memory_1.findMemoryDocumentByTypeAndId)(inputs.root, inputs.memoryYaml, inputs.element.type, inputs.element.id);
185
+ // `frontmatter` is untyped (`Record<string, unknown>`), so project `status` to the
186
+ // `string | undefined` shape `isArchivedStatus` takes — a non-string `status` is never archived,
187
+ // exactly as that predicate's contract states.
188
+ const status = typeof doc?.frontmatter.status === 'string' ? doc.frontmatter.status : undefined;
189
+ const memory = doc !== undefined && !(0, memory_1.isArchivedStatus)(status) ? [doc] : [];
190
+ return { dna: inputs.dna, directives, memory, warnings };
191
+ }
192
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/core/context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,131 @@
1
+ import type { CoreError, CoreResult } from './types';
2
+ /** The outcome of {@link updateRoleAssignments}: the role's list as it stands afterwards. */
3
+ export interface RoleAssignmentUpdate {
4
+ /** `assignments.<role>` after the update (unchanged when nothing needed writing). */
5
+ readonly assignments: readonly string[];
6
+ }
7
+ /**
8
+ * Validate an assignment request before anything is written. The role is checked first (it is the
9
+ * binding's target and the REQ-SYS-08 check), then each id in argument order; the first failure wins.
10
+ *
11
+ * **The role catalogue is read from the `dna.yaml` committed at `HEAD`**, never from the working tree
12
+ * (task-091, `bug-082-directive-assign-validates-role-against-worktree`,
13
+ * `dl-080-which-baseline-each-command-reads` option (B)). This function therefore takes `root`, not a
14
+ * `DnaYaml`: a caller cannot hand it a working-tree catalogue even by accident, which is what makes
15
+ * the committed baseline a property of the read rather than of a precondition someone must remember
16
+ * to run. An uncommitted role used to be enough to commit a `roles.yaml` binding to a role no commit
17
+ * of the repository defines — a dangling reference by construction, since **REQ-SYS-08** binds
18
+ * directives by role and every other clone sees the binding without the role.
19
+ *
20
+ * The cost was measured and accepted at `dl-080`'s ratification: extending the catalogue becomes
21
+ * *edit, commit, then assign*. `dl-081`/`task-093` are building the `dna` verbs that fold the first
22
+ * two steps into one command; nothing here waits on them.
23
+ *
24
+ * Two fail-closed refusals come with the baseline, mirroring `requireApprovalAuthority`'s: no
25
+ * committed `dna.yaml` at all, and a committed one that does not parse or validate.
26
+ *
27
+ * **The directive inventory is read the same way**, since task-096 /
28
+ * `bug-086-directive-inventory-read-from-the-worktree` — `loadDirectivesAtHead`, the tree at `HEAD`.
29
+ * task-091 left this half on the files on disk and offered a boundary for it (the role catalogue is
30
+ * *governance*, the directive file is the *asset*); its reviewer showed the boundary does not hold,
31
+ * and `bug-086` settles that what actually separated them was **cost**: reading the inventory at a
32
+ * revision needs a directory listing at a revision, which `src/storage` did not have. It has one now
33
+ * (`listPathsAtRev`), so the parameter is gone too and the two halves of this one function read one
34
+ * baseline. Before that, an untracked directive file produced a committed `roles.yaml` binding to a
35
+ * directive present in no commit.
36
+ *
37
+ * An absent or empty committed inventory needs no special case: no id is known, so every request is
38
+ * refused with `unknown directive: <id>`. That is the right answer rather than a tolerated one — "may
39
+ * this id be bound" needs the *positive* fact that a directive with it exists, and a missing record
40
+ * cannot supply one. (Contrast {@link checkUnreferenced}, which needs the *absence* of a fact and is
41
+ * therefore satisfied by a missing record.)
42
+ *
43
+ * @param root - Project root; both committed baselines are read from it.
44
+ * @param role - The role to bind to.
45
+ * @param ids - The directive ids to bind.
46
+ * @returns `undefined` when assignable; otherwise an error carrying P3.2's exact message verbatim as
47
+ * its first sentence — `unknown role '<role>' (not defined in dna.yaml)` or
48
+ * `unknown directive: <id>` — or, for an absent/unreadable committed catalogue or an unreadable
49
+ * committed directive file, a `VALIDATION` refusal naming the baseline.
50
+ */
51
+ export declare function checkAssignable(root: string, role: string, ids: readonly string[]): CoreError | undefined;
52
+ /**
53
+ * REQ-SEC-07 clause (b) for the Directives surface — *"removal of a still-referenced custom asset is
54
+ * rejected naming the referrer"* — assigned to `task-052-directive-remove` by
55
+ * `dl-030-req-sec-07-referenced-asset-ownership` (`ready`, option (b)). The counterpart to
56
+ * {@link checkAssignable}: that one guards a binding being *made*, this one guards an asset being
57
+ * removed while a binding still names it. It lives here for the same reason, over the same
58
+ * `roles.yaml` shape.
59
+ *
60
+ * `roles.yaml` binds by directive **`frontmatter.id`** (`src/directives/schema.ts`'s `RolesYaml`), so
61
+ * the comparison is against the id, never the filename.
62
+ *
63
+ * Two referrer kinds, checked in this order:
64
+ *
65
+ * 1. A role whose own `assignments` entry names the id → P3.3's verbatim message
66
+ * `cannot remove '<id>': still assigned to role '<role>'`
67
+ * (`p3-directives/P3.3-directive-remove.feature`, "Error - removing a directive still referenced"
68
+ * — do not reword). When several roles bind it the **alphabetically first** is named: the message
69
+ * must be a pure function of the file's content, never of YAML mapping order (REQ-SYS-07), and
70
+ * `directives-list.ts` already walks role names in the same ascending order.
71
+ * 2. `roles.yaml`'s `global` list, which binds the directive to *every* role
72
+ * (`spec-012-context-loader-relevance-filtering` §5) and is therefore the strongest reference of
73
+ * all. `[AUTHORING]`: P3.3 pins only the per-role wording, and a global binding cannot be phrased
74
+ * in it without naming a role the file does not name — so the message is
75
+ * `cannot remove '<id>': still assigned to every role via roles.yaml 'global'`. Recorded in
76
+ * `task-052-directive-remove`'s Execution Notes and raised for ratification, not assumed settled.
77
+ *
78
+ * A directive referenced from a *workflow* step is deliberately not checked: `src/workflow/schema.ts`
79
+ * has no field that can reference a directive, so P3.3's "or workflow step" precondition is vacuous
80
+ * today, and P4.9's workflow half of clause (b) has no owner in v0.2 per `dl-030`.
81
+ *
82
+ * **The bindings are read from the `roles.yaml` committed at `HEAD`**, never from the working tree
83
+ * (task-096, `bug-086-directive-inventory-read-from-the-worktree`, `dl-080` option (B)). This
84
+ * function therefore takes `root`, not a `RolesYaml`: a caller cannot hand it a working-tree copy
85
+ * even by accident, which is what makes the committed baseline a property of the read. Before that,
86
+ * deleting the two `- <id>` lines **without committing** was enough to walk past this check entirely
87
+ * and **destroy** a file the committed `roles.yaml` still bound — on the only verb in the system that
88
+ * deletes an artefact, and against the one clause REQ-SEC-07 (b) exists to enforce.
89
+ *
90
+ * A `roles.yaml` that is **not committed at all** is still "no bindings yet" (task-051/task-053's
91
+ * reading) and permits the removal. That is not a fail-open tolerance: this check needs the *negative*
92
+ * fact *nothing names this id*, and a record that does not exist answers it. (Contrast
93
+ * {@link checkAssignable}, which needs a positive fact and therefore refuses when its record is
94
+ * missing.) A committed `roles.yaml` that does not **validate** is the genuinely unanswerable case,
95
+ * and it refuses.
96
+ *
97
+ * @param root - Project root; the committed `roles.yaml` is read from it (`loadRolesYamlAtHead`).
98
+ * @param id - The directive id about to be removed.
99
+ * @returns `undefined` when nothing references it; otherwise a `CONFLICT` error (exit 1) carrying the
100
+ * message above. `CONFLICT` and not `VALIDATION`: the request is well-formed and the *state*
101
+ * refuses it — the same distinction `directiveCreate` draws for `directive already exists: <name>`,
102
+ * as against `requireCustomAsset`'s `VALIDATION` for a request inadmissible on its own terms. An
103
+ * unreadable committed `roles.yaml` is `VALIDATION`, naming the baseline.
104
+ */
105
+ export declare function checkUnreferenced(root: string, id: string): CoreError | undefined;
106
+ /**
107
+ * Apply `update` to `assignments.<role>` in `.wingfoil/roles.yaml` and commit the result as ONE commit
108
+ * staging only that file.
109
+ *
110
+ * - A missing `roles.yaml` means "no bindings yet" (task-053's reading) and is created with a
111
+ * deterministic `dump` — there is nothing to preserve.
112
+ * - An unchanged list is an idempotent success: nothing is written and no commit is made.
113
+ * - The edit goes through `setRoleAssignmentsInText`, keeping every comment and unrelated line. When it
114
+ * cannot apply, a whole-file `dump` is used only if the file contains no `#` at all; otherwise the
115
+ * result is `CONFLICT` and the file is left untouched.
116
+ * - No second schema pass on the output is needed: the in-place edit is only accepted when its
117
+ * re-parse equals the validated input with `assignments.<role>` replaced by a string list
118
+ * (`setRoleAssignmentsInText`'s self-check), and the whole-file `dump` serializes exactly that object.
119
+ *
120
+ * Commits through `commitPaths`, which since bug-027's fix (`git commit --only -- <paths>`) records
121
+ * exactly the path it is given and leaves anything else staged untouched.
122
+ *
123
+ * @param root - Project root.
124
+ * @param role - The role whose list is updated (already validated by the caller).
125
+ * @param update - Pure function from the current list to the desired list.
126
+ * @param message - The commit subject.
127
+ * @returns The role's resulting list, with the commit when one was made.
128
+ * @throws whatever `git` raises inside `commitPaths`.
129
+ */
130
+ export declare function updateRoleAssignments(root: string, role: string, update: (current: readonly string[]) => readonly string[], message: string): CoreResult<RoleAssignmentUpdate>;
131
+ //# sourceMappingURL=directive-assign.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"directive-assign.d.ts","sourceRoot":"","sources":["../../src/core/directive-assign.ts"],"names":[],"mappings":"AA2EA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAIrD,6FAA6F;AAC7F,MAAM,WAAW,oBAAoB;IACnC,qFAAqF;IACrF,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAiBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GAAG,SAAS,CAkDzG;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CA8BjF;AAqBD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,KAAK,SAAS,MAAM,EAAE,EACzD,OAAO,EAAE,MAAM,GACd,UAAU,CAAC,oBAAoB,CAAC,CA6ClC"}