@warpgogol/forge 4.2.4 → 5.1.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 (363) hide show
  1. package/AGENTS.md +14 -6
  2. package/CHANGELOG.md +72 -0
  3. package/README.md +8 -8
  4. package/README.uk.md +5 -5
  5. package/dist/os/adr/handlers/list-create.d.ts.map +1 -1
  6. package/dist/os/adr/handlers/list-create.js +4 -1
  7. package/dist/os/adr/handlers/list-create.js.map +1 -1
  8. package/dist/os/adr/handlers/validate.d.ts.map +1 -1
  9. package/dist/os/adr/handlers/validate.js +6 -1
  10. package/dist/os/adr/handlers/validate.js.map +1 -1
  11. package/dist/os/adr/types.d.ts.map +1 -1
  12. package/dist/os/adr/types.js +7 -3
  13. package/dist/os/adr/types.js.map +1 -1
  14. package/dist/os/compass/compass.module.d.ts.map +1 -1
  15. package/dist/os/compass/compass.module.js +76 -10
  16. package/dist/os/compass/compass.module.js.map +1 -1
  17. package/dist/os/compass/handlers/compass-audit-handler.d.ts.map +1 -1
  18. package/dist/os/compass/handlers/compass-audit-handler.js +7 -3
  19. package/dist/os/compass/handlers/compass-audit-handler.js.map +1 -1
  20. package/dist/os/compass/handlers/compass-change-summary-handler.d.ts +1 -9
  21. package/dist/os/compass/handlers/compass-change-summary-handler.d.ts.map +1 -1
  22. package/dist/os/compass/handlers/compass-change-summary-handler.js +44 -186
  23. package/dist/os/compass/handlers/compass-change-summary-handler.js.map +1 -1
  24. package/dist/os/compass/handlers/compass-inventory-handler.d.ts +2 -0
  25. package/dist/os/compass/handlers/compass-inventory-handler.d.ts.map +1 -1
  26. package/dist/os/compass/handlers/compass-inventory-handler.js +24 -12
  27. package/dist/os/compass/handlers/compass-inventory-handler.js.map +1 -1
  28. package/dist/os/compass/handlers/compass-inventory.d.ts +7 -5
  29. package/dist/os/compass/handlers/compass-inventory.d.ts.map +1 -1
  30. package/dist/os/compass/handlers/compass-inventory.js +84 -241
  31. package/dist/os/compass/handlers/compass-inventory.js.map +1 -1
  32. package/dist/os/compass/handlers/compass-migrate-handler.d.ts +4 -0
  33. package/dist/os/compass/handlers/compass-migrate-handler.d.ts.map +1 -0
  34. package/dist/os/compass/handlers/compass-migrate-handler.js +100 -0
  35. package/dist/os/compass/handlers/compass-migrate-handler.js.map +1 -0
  36. package/dist/os/compass/handlers/compass-migrate.d.ts +34 -0
  37. package/dist/os/compass/handlers/compass-migrate.d.ts.map +1 -0
  38. package/dist/os/compass/handlers/compass-migrate.js +273 -0
  39. package/dist/os/compass/handlers/compass-migrate.js.map +1 -0
  40. package/dist/os/compass/handlers/resolve-scan-root.d.ts.map +1 -1
  41. package/dist/os/compass/handlers/resolve-scan-root.js +15 -8
  42. package/dist/os/compass/handlers/resolve-scan-root.js.map +1 -1
  43. package/dist/os/compass/handlers/summary-record.d.ts +46 -0
  44. package/dist/os/compass/handlers/summary-record.d.ts.map +1 -0
  45. package/dist/os/compass/handlers/summary-record.js +233 -0
  46. package/dist/os/compass/handlers/summary-record.js.map +1 -0
  47. package/dist/os/compass/index.d.ts +4 -1
  48. package/dist/os/compass/index.d.ts.map +1 -1
  49. package/dist/os/compass/index.js +9 -1
  50. package/dist/os/compass/index.js.map +1 -1
  51. package/dist/os/compass/policy.d.ts +65 -0
  52. package/dist/os/compass/policy.d.ts.map +1 -0
  53. package/dist/os/compass/policy.js +241 -0
  54. package/dist/os/compass/policy.js.map +1 -0
  55. package/dist/os/core/core.module.d.ts.map +1 -1
  56. package/dist/os/core/core.module.js +11 -23
  57. package/dist/os/core/core.module.js.map +1 -1
  58. package/dist/os/core/handlers/forge-autonomy-validate.d.ts.map +1 -1
  59. package/dist/os/core/handlers/forge-autonomy-validate.js +7 -1
  60. package/dist/os/core/handlers/forge-autonomy-validate.js.map +1 -1
  61. package/dist/os/core/handlers/package-health.d.ts.map +1 -1
  62. package/dist/os/core/handlers/package-health.js +6 -1
  63. package/dist/os/core/handlers/package-health.js.map +1 -1
  64. package/dist/os/core/handlers/pinned-check.d.ts.map +1 -1
  65. package/dist/os/core/handlers/pinned-check.js +6 -1
  66. package/dist/os/core/handlers/pinned-check.js.map +1 -1
  67. package/dist/os/core/handlers/pinned-validate.d.ts.map +1 -1
  68. package/dist/os/core/handlers/pinned-validate.js +6 -1
  69. package/dist/os/core/handlers/pinned-validate.js.map +1 -1
  70. package/dist/os/core/handlers/validate.d.ts.map +1 -1
  71. package/dist/os/core/handlers/validate.js +6 -1
  72. package/dist/os/core/handlers/validate.js.map +1 -1
  73. package/dist/os/exploration/exploration.module.d.ts.map +1 -1
  74. package/dist/os/exploration/exploration.module.js +6 -4
  75. package/dist/os/exploration/exploration.module.js.map +1 -1
  76. package/dist/os/exploration/handlers/list.d.ts.map +1 -1
  77. package/dist/os/exploration/handlers/list.js +4 -1
  78. package/dist/os/exploration/handlers/list.js.map +1 -1
  79. package/dist/os/mission/handlers/archive.d.ts.map +1 -1
  80. package/dist/os/mission/handlers/archive.js +7 -3
  81. package/dist/os/mission/handlers/archive.js.map +1 -1
  82. package/dist/os/mission/index.d.ts.map +1 -1
  83. package/dist/os/mission/index.js +13 -0
  84. package/dist/os/mission/index.js.map +1 -1
  85. package/dist/os/notes/index.d.ts.map +1 -1
  86. package/dist/os/notes/index.js +7 -1
  87. package/dist/os/notes/index.js.map +1 -1
  88. package/dist/os/rfc/frontmatter-io.d.ts.map +1 -1
  89. package/dist/os/rfc/frontmatter-io.js +6 -1
  90. package/dist/os/rfc/frontmatter-io.js.map +1 -1
  91. package/dist/os/rfc/handlers/archive.d.ts.map +1 -1
  92. package/dist/os/rfc/handlers/archive.js +6 -1
  93. package/dist/os/rfc/handlers/archive.js.map +1 -1
  94. package/dist/os/rfc/handlers/implement-stamp.d.ts.map +1 -1
  95. package/dist/os/rfc/handlers/implement-stamp.js +7 -3
  96. package/dist/os/rfc/handlers/implement-stamp.js.map +1 -1
  97. package/dist/os/rfc/handlers/index-graph.d.ts.map +1 -1
  98. package/dist/os/rfc/handlers/index-graph.js +6 -1
  99. package/dist/os/rfc/handlers/index-graph.js.map +1 -1
  100. package/dist/os/rfc/handlers/lifecycle.d.ts.map +1 -1
  101. package/dist/os/rfc/handlers/lifecycle.js +6 -1
  102. package/dist/os/rfc/handlers/lifecycle.js.map +1 -1
  103. package/dist/os/rfc/handlers/list-create.d.ts.map +1 -1
  104. package/dist/os/rfc/handlers/list-create.js +6 -1
  105. package/dist/os/rfc/handlers/list-create.js.map +1 -1
  106. package/dist/os/rfc/handlers/pipeline-status.d.ts.map +1 -1
  107. package/dist/os/rfc/handlers/pipeline-status.js +6 -1
  108. package/dist/os/rfc/handlers/pipeline-status.js.map +1 -1
  109. package/dist/os/rfc/handlers/supersede-propose.d.ts.map +1 -1
  110. package/dist/os/rfc/handlers/supersede-propose.js +6 -1
  111. package/dist/os/rfc/handlers/supersede-propose.js.map +1 -1
  112. package/dist/os/rfc/handlers/validate-rules.d.ts.map +1 -1
  113. package/dist/os/rfc/handlers/validate-rules.js +7 -3
  114. package/dist/os/rfc/handlers/validate-rules.js.map +1 -1
  115. package/dist/os/rfc/handlers/validate.d.ts.map +1 -1
  116. package/dist/os/rfc/handlers/validate.js +6 -1
  117. package/dist/os/rfc/handlers/validate.js.map +1 -1
  118. package/dist/os/rfc/index.d.ts.map +1 -1
  119. package/dist/os/rfc/index.js +6 -1
  120. package/dist/os/rfc/index.js.map +1 -1
  121. package/dist/os/rfc/rfc.module.d.ts.map +1 -1
  122. package/dist/os/rfc/rfc.module.js +6 -1
  123. package/dist/os/rfc/rfc.module.js.map +1 -1
  124. package/dist/os/rfc/types.d.ts +7 -4
  125. package/dist/os/rfc/types.d.ts.map +1 -1
  126. package/dist/os/rfc/types.js +7 -4
  127. package/dist/os/rfc/types.js.map +1 -1
  128. package/dist/os/session/handlers/save.d.ts.map +1 -1
  129. package/dist/os/session/handlers/save.js +6 -1
  130. package/dist/os/session/handlers/save.js.map +1 -1
  131. package/dist/os/werkstatt/handlers/werkstatt-lock-recover.d.ts.map +1 -1
  132. package/dist/os/werkstatt/handlers/werkstatt-lock-recover.js +6 -1
  133. package/dist/os/werkstatt/handlers/werkstatt-lock-recover.js.map +1 -1
  134. package/dist/os/werkstatt/handlers/workspace-deps.js +3 -3
  135. package/dist/os/werkstatt/handlers/workspace-deps.js.map +1 -1
  136. package/dist/os/workflow/index.d.ts.map +1 -1
  137. package/dist/os/workflow/index.js +6 -1
  138. package/dist/os/workflow/index.js.map +1 -1
  139. package/dist/src/config/forge-config.d.ts +62 -4
  140. package/dist/src/config/forge-config.d.ts.map +1 -1
  141. package/dist/src/config/forge-config.js +35 -19
  142. package/dist/src/config/forge-config.js.map +1 -1
  143. package/dist/src/forge-module.d.ts.map +1 -1
  144. package/dist/src/forge-module.js +6 -1
  145. package/dist/src/forge-module.js.map +1 -1
  146. package/dist/src/index.d.ts.map +1 -1
  147. package/dist/src/index.js +10 -2
  148. package/dist/src/index.js.map +1 -1
  149. package/dist/src/migration-adapters/git-utils.d.ts.map +1 -1
  150. package/dist/src/migration-adapters/git-utils.js +6 -2
  151. package/dist/src/migration-adapters/git-utils.js.map +1 -1
  152. package/dist/src/migration-adapters/ignored-files.d.ts.map +1 -1
  153. package/dist/src/migration-adapters/ignored-files.js +6 -1
  154. package/dist/src/migration-adapters/ignored-files.js.map +1 -1
  155. package/dist/src/migration-adapters/index.d.ts.map +1 -1
  156. package/dist/src/migration-adapters/index.js +4 -1
  157. package/dist/src/migration-adapters/index.js.map +1 -1
  158. package/dist/src/migration-adapters/types.d.ts.map +1 -1
  159. package/dist/src/migration-adapters/types.js +6 -2
  160. package/dist/src/migration-adapters/types.js.map +1 -1
  161. package/dist/src/onboarding/agents-generate.d.ts.map +1 -1
  162. package/dist/src/onboarding/agents-generate.js +18 -14
  163. package/dist/src/onboarding/agents-generate.js.map +1 -1
  164. package/dist/src/onboarding/create.d.ts.map +1 -1
  165. package/dist/src/onboarding/create.js +7 -6
  166. package/dist/src/onboarding/create.js.map +1 -1
  167. package/dist/src/onboarding/doctor.d.ts.map +1 -1
  168. package/dist/src/onboarding/doctor.js +7 -13
  169. package/dist/src/onboarding/doctor.js.map +1 -1
  170. package/dist/src/onboarding/init.d.ts.map +1 -1
  171. package/dist/src/onboarding/init.js +7 -12
  172. package/dist/src/onboarding/init.js.map +1 -1
  173. package/dist/src/onboarding/invariant-engine.d.ts.map +1 -1
  174. package/dist/src/onboarding/invariant-engine.js +7 -2
  175. package/dist/src/onboarding/invariant-engine.js.map +1 -1
  176. package/dist/src/onboarding/nested-agents-generate.d.ts.map +1 -1
  177. package/dist/src/onboarding/nested-agents-generate.js +7 -2
  178. package/dist/src/onboarding/nested-agents-generate.js.map +1 -1
  179. package/dist/src/onboarding/nested-agents-templates.d.ts.map +1 -1
  180. package/dist/src/onboarding/nested-agents-templates.js +11 -3
  181. package/dist/src/onboarding/nested-agents-templates.js.map +1 -1
  182. package/dist/src/onboarding/scaffold-project.d.ts.map +1 -1
  183. package/dist/src/onboarding/scaffold-project.js +6 -1
  184. package/dist/src/onboarding/scaffold-project.js.map +1 -1
  185. package/dist/src/onboarding/upgrade.d.ts.map +1 -1
  186. package/dist/src/onboarding/upgrade.js +7 -3
  187. package/dist/src/onboarding/upgrade.js.map +1 -1
  188. package/dist/src/profiles/profile-schema.d.ts +54 -4
  189. package/dist/src/profiles/profile-schema.d.ts.map +1 -1
  190. package/dist/src/profiles/profile-schema.js +34 -14
  191. package/dist/src/profiles/profile-schema.js.map +1 -1
  192. package/dist/src/profiles/stack-profile.d.ts +24 -0
  193. package/dist/src/profiles/stack-profile.d.ts.map +1 -1
  194. package/dist/src/profiles/stack-profile.js +7 -3
  195. package/dist/src/profiles/stack-profile.js.map +1 -1
  196. package/dist/src/registry.d.ts.map +1 -1
  197. package/dist/src/registry.js +7 -9
  198. package/dist/src/registry.js.map +1 -1
  199. package/dist/src/types.d.ts.map +1 -1
  200. package/dist/src/types.js +6 -1
  201. package/dist/src/types.js.map +1 -1
  202. package/dist/src/utils/fs-atomic.d.ts.map +1 -1
  203. package/dist/src/utils/fs-atomic.js +13 -1
  204. package/dist/src/utils/fs-atomic.js.map +1 -1
  205. package/dist/src/utils/fs-idempotent.d.ts.map +1 -1
  206. package/dist/src/utils/fs-idempotent.js +13 -0
  207. package/dist/src/utils/fs-idempotent.js.map +1 -1
  208. package/dist/src/utils/fs-trash-sync.d.ts.map +1 -1
  209. package/dist/src/utils/fs-trash-sync.js +13 -1
  210. package/dist/src/utils/fs-trash-sync.js.map +1 -1
  211. package/dist/src/utils/fs-trash.d.ts.map +1 -1
  212. package/dist/src/utils/fs-trash.js +13 -1
  213. package/dist/src/utils/fs-trash.js.map +1 -1
  214. package/dist/src/utils/fs.d.ts.map +1 -1
  215. package/dist/src/utils/fs.js +14 -3
  216. package/dist/src/utils/fs.js.map +1 -1
  217. package/dist/src/utils/hash.d.ts.map +1 -1
  218. package/dist/src/utils/hash.js +12 -1
  219. package/dist/src/utils/hash.js.map +1 -1
  220. package/dist/src/utils/index.d.ts.map +1 -1
  221. package/dist/src/utils/index.js +13 -2
  222. package/dist/src/utils/index.js.map +1 -1
  223. package/dist/src/utils/string-utils.d.ts.map +1 -1
  224. package/dist/src/utils/string-utils.js +12 -1
  225. package/dist/src/utils/string-utils.js.map +1 -1
  226. package/dist/src/validators/port-validate.js +1 -1
  227. package/dist/src/validators/port-validate.js.map +1 -1
  228. package/dist/src/validators/skill-validate.d.ts.map +1 -1
  229. package/dist/src/validators/skill-validate.js +7 -8
  230. package/dist/src/validators/skill-validate.js.map +1 -1
  231. package/docs/getting-started.md +1 -1
  232. package/docs/reference/forge-yaml.md +38 -9
  233. package/docs/reference/profiles.md +8 -8
  234. package/os/adr/handlers/list-create.ts +4 -1
  235. package/os/adr/handlers/validate.ts +6 -1
  236. package/os/adr/types.ts +7 -3
  237. package/os/compass/compass.module.ts +79 -13
  238. package/os/compass/handlers/compass-audit-handler.ts +22 -3
  239. package/os/compass/handlers/compass-change-summary-handler.ts +55 -227
  240. package/os/compass/handlers/compass-inventory-handler.ts +30 -12
  241. package/os/compass/handlers/compass-inventory.ts +103 -240
  242. package/os/compass/handlers/compass-migrate-handler.ts +122 -0
  243. package/os/compass/handlers/compass-migrate.ts +361 -0
  244. package/os/compass/handlers/resolve-scan-root-package.test.ts +70 -0
  245. package/os/compass/handlers/resolve-scan-root.ts +15 -8
  246. package/os/compass/handlers/summary-record.ts +313 -0
  247. package/os/compass/handlers/tests/compass-migrate.test.ts +291 -0
  248. package/os/compass/handlers/tests/compass-policy.test.ts +249 -0
  249. package/os/compass/handlers/tests/compass-v2-contract.test.ts +9 -9
  250. package/os/compass/handlers/tests/summary-record.test.ts +224 -0
  251. package/os/compass/index.ts +23 -5
  252. package/os/compass/policy.ts +350 -0
  253. package/os/core/core.module.ts +11 -23
  254. package/os/core/handlers/forge-autonomy-validate.ts +7 -1
  255. package/os/core/handlers/package-health.ts +6 -1
  256. package/os/core/handlers/pinned-check.ts +6 -1
  257. package/os/core/handlers/pinned-validate.ts +6 -1
  258. package/os/core/handlers/validate.ts +6 -1
  259. package/os/exploration/exploration.module.ts +72 -71
  260. package/os/exploration/handlers/list.ts +4 -1
  261. package/os/mission/handlers/archive.ts +7 -3
  262. package/os/mission/index.ts +14 -0
  263. package/os/notes/index.ts +7 -1
  264. package/os/rfc/frontmatter-io.ts +6 -1
  265. package/os/rfc/handlers/archive.ts +6 -1
  266. package/os/rfc/handlers/implement-stamp.ts +7 -3
  267. package/os/rfc/handlers/index-graph.ts +6 -1
  268. package/os/rfc/handlers/lifecycle.ts +6 -1
  269. package/os/rfc/handlers/list-create.ts +6 -1
  270. package/os/rfc/handlers/pipeline-status.ts +6 -1
  271. package/os/rfc/handlers/supersede-propose.ts +6 -1
  272. package/os/rfc/handlers/validate-rules.ts +7 -3
  273. package/os/rfc/handlers/validate.ts +6 -1
  274. package/os/rfc/index.ts +6 -1
  275. package/os/rfc/rfc.module.ts +6 -1
  276. package/os/rfc/types.ts +7 -4
  277. package/os/session/handlers/save.ts +6 -1
  278. package/os/werkstatt/handlers/werkstatt-lock-recover.ts +6 -1
  279. package/os/werkstatt/handlers/workspace-deps.ts +3 -3
  280. package/os/workflow/index.ts +6 -1
  281. package/package.json +1 -1
  282. package/profiles/{godot-csharp.yaml → godot-game.yaml} +2 -2
  283. package/profiles/{knowledge-typescript-turborepo.yaml → knowledge.yaml} +1 -1
  284. package/profiles/{phaser-turborepo.yaml → phaser-game.yaml} +2 -2
  285. package/profiles/{typescript-turborepo.yaml → typescript.yaml} +2 -2
  286. package/skills/fo/fo-add-tests/SKILL.md +14 -0
  287. package/skills/fo/fo-architecture/SKILL.md +14 -0
  288. package/skills/fo/fo-compass-annotate/SKILL.md +14 -0
  289. package/skills/fo/fo-design-summit/SKILL.md +14 -0
  290. package/skills/fo/fo-doc-audit/SKILL.md +14 -0
  291. package/skills/fo/fo-explore/SKILL.md +14 -0
  292. package/skills/fo/fo-extract-dna/SKILL.md +14 -0
  293. package/skills/fo/fo-fix/SKILL.md +14 -0
  294. package/skills/fo/fo-handoff/SKILL.md +14 -0
  295. package/skills/fo/fo-harvest/SKILL.md +14 -0
  296. package/skills/fo/fo-idea/SKILL.md +14 -0
  297. package/skills/fo/fo-idea-audit/SKILL.md +14 -0
  298. package/skills/fo/fo-idea-create-adr/SKILL.md +14 -0
  299. package/skills/fo/fo-idea-create-rfc/SKILL.md +14 -0
  300. package/skills/fo/fo-idea-enhance/SKILL.md +14 -0
  301. package/skills/fo/fo-idea-i-just-want-to-see-the-plan/SKILL.md +14 -0
  302. package/skills/fo/fo-idea-i-just-want-to-see-the-result/SKILL.md +14 -0
  303. package/skills/fo/fo-idea-implement/SKILL.md +14 -0
  304. package/skills/fo/fo-idea-plan/SKILL.md +14 -0
  305. package/skills/fo/fo-idea-status/SKILL.md +14 -0
  306. package/skills/fo/fo-knowledge-distill/SKILL.md +14 -0
  307. package/skills/fo/fo-memory-sync/SKILL.md +14 -0
  308. package/skills/fo/fo-qa/SKILL.md +14 -0
  309. package/skills/fo/fo-review/SKILL.md +14 -0
  310. package/skills/fo/fo-session-retro/SKILL.md +14 -0
  311. package/skills/fo/fo-session-save/SKILL.md +14 -0
  312. package/skills/fo/fo-spec-ingest/SKILL.md +14 -0
  313. package/skills/fo/fo-step-commit/SKILL.md +14 -0
  314. package/skills/fo/fo-triage/SKILL.md +14 -0
  315. package/skills/meta/forge-bootstrap/SKILL.md +14 -0
  316. package/skills/meta/port-to-forge/SKILL.md +14 -0
  317. package/skills/meta/skill-create/SKILL.md +14 -0
  318. package/skills/shared/grilling/SKILL.md +14 -0
  319. package/skills/shared/my-preferences/SKILL.md +14 -0
  320. package/skills/shared/windows-ai-tooling/SKILL.md +14 -0
  321. package/skills/shared/writing-great-skills/SKILL.md +14 -0
  322. package/src/config/forge-config.ts +40 -23
  323. package/src/forge-module.ts +6 -1
  324. package/src/index.ts +10 -2
  325. package/src/migration-adapters/git-utils.ts +6 -2
  326. package/src/migration-adapters/ignored-files.ts +6 -1
  327. package/src/migration-adapters/index.ts +4 -1
  328. package/src/migration-adapters/types.ts +6 -2
  329. package/src/onboarding/agents-generate.ts +23 -14
  330. package/src/onboarding/create.ts +7 -6
  331. package/src/onboarding/doctor.ts +7 -13
  332. package/src/onboarding/init.ts +7 -12
  333. package/src/onboarding/invariant-engine.ts +7 -2
  334. package/src/onboarding/nested-agents-generate.ts +7 -2
  335. package/src/onboarding/nested-agents-templates.ts +11 -3
  336. package/src/onboarding/scaffold-project.ts +6 -1
  337. package/src/onboarding/upgrade.ts +7 -3
  338. package/src/profiles/profile-schema.ts +42 -18
  339. package/src/profiles/stack-profile.ts +7 -3
  340. package/src/registry.ts +7 -9
  341. package/src/tests/agents-generate-domain.test.ts +3 -3
  342. package/src/tests/agents-generate.test.ts +2 -2
  343. package/src/tests/create.test.ts +4 -4
  344. package/src/tests/forge-autonomy-validate.test.ts +1 -1
  345. package/src/tests/package-files.test.ts +53 -1
  346. package/src/tests/profile-schema.test.ts +4 -4
  347. package/src/tests/scaffold-project.test.ts +3 -3
  348. package/src/tests/stack-profile.test.ts +12 -12
  349. package/src/types.ts +6 -1
  350. package/src/utils/fs-atomic.ts +13 -1
  351. package/src/utils/fs-idempotent.ts +13 -0
  352. package/src/utils/fs-trash-sync.ts +13 -1
  353. package/src/utils/fs-trash.ts +13 -1
  354. package/src/utils/fs.ts +14 -3
  355. package/src/utils/hash.ts +12 -1
  356. package/src/utils/index.ts +13 -2
  357. package/src/utils/string-utils.ts +12 -1
  358. package/src/validators/port-validate.ts +1 -1
  359. package/src/validators/skill-validate.ts +7 -8
  360. package/skills/shared/extension-e2e-mitm/SKILL.md +0 -53
  361. package/skills/shared/extension-e2e-mitm/fixture-proxy.ts +0 -113
  362. package/src/types/werkstatt-engine-shims.d.ts +0 -1416
  363. package/src/types/werkstatt-shared-shims.d.ts +0 -5686
@@ -0,0 +1,249 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>Unit tests for RFC-1096 Compass policy externalization — generic
4
+ defaults, profile overlay, bindings.compass overrides, merge semantics, and
5
+ the named-config-error contract.</purpose>
6
+ <non-goals>
7
+ <item>Do not test v2 rule diagnostics — covered by compass-v2-contract.test.ts.</item>
8
+ </non-goals>
9
+ </MODULE_CONTRACT>
10
+ <CHANGE_SUMMARY>
11
+ <item>RFC-1096: initial coverage for resolveCompassPolicy (AC-1..AC-5).</item>
12
+ </CHANGE_SUMMARY>
13
+ */
14
+
15
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
16
+ import { mkdtempSync, mkdirSync, rmSync, writeFileSync, readFileSync, readdirSync } from "node:fs";
17
+ import { join } from "node:path";
18
+ import { fileURLToPath } from "node:url";
19
+ import { tmpdir } from "node:os";
20
+ import {
21
+ resolveCompassPolicy,
22
+ CompassPolicyConfigError,
23
+ parseGovernanceIdParts,
24
+ } from "../../policy.ts";
25
+ import { runCompassInventory } from "../compass-inventory-handler.ts";
26
+ import type { ForgeRuntimeContext } from "../../../../src/types.ts";
27
+
28
+ // packages/forge — real forge root so `profile:` resolution finds shipped profiles.
29
+ const FORGE_ROOT = fileURLToPath(new URL("../../../../", import.meta.url));
30
+ // os/compass directory — scanned by the AC-4 literal check.
31
+ const COMPASS_DIR = fileURLToPath(new URL("../../", import.meta.url));
32
+
33
+ const FORGE_YAML = (bindings: string) => `
34
+ schema: forge/config@1
35
+ project:
36
+ name: test-ws
37
+ stack: [typescript]
38
+ packageManager: pnpm
39
+ paths:
40
+ rfcsDir: docs/rfcs
41
+ adrsDir: docs/adrs
42
+ plansDir: docs/plans
43
+ auditsDir: docs/audits
44
+ specsDir: docs/specs
45
+ skillsDir: .agents/skills
46
+ bindings:
47
+ schema: forge/bindings@1
48
+ commands: {}
49
+ paths: {}
50
+ ${bindings}`;
51
+
52
+ const FORGE_YAML_WITH_PROFILE = `
53
+ schema: forge/config@1
54
+ profile: godot-game
55
+ project:
56
+ name: test-ws
57
+ stack: [godot]
58
+ packageManager: pnpm
59
+ paths:
60
+ rfcsDir: docs/rfcs
61
+ adrsDir: docs/adrs
62
+ plansDir: docs/plans
63
+ auditsDir: docs/audits
64
+ specsDir: docs/specs
65
+ skillsDir: .agents/skills
66
+ `;
67
+
68
+ function makeContext(workspaceRoot: string): ForgeRuntimeContext {
69
+ return {
70
+ workspaceRoot,
71
+ forgeRoot: FORGE_ROOT,
72
+ dryRun: true,
73
+ logger: { info: () => {}, warn: () => {}, error: () => {} },
74
+ } as unknown as ForgeRuntimeContext;
75
+ }
76
+
77
+ function collectTsFiles(dir: string): string[] {
78
+ const out: string[] = [];
79
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
80
+ const abs = join(dir, entry.name);
81
+ if (entry.isDirectory()) out.push(...collectTsFiles(abs));
82
+ else if (entry.isFile() && entry.name.endsWith(".ts")) out.push(abs);
83
+ }
84
+ return out;
85
+ }
86
+
87
+ describe("resolveCompassPolicy (RFC-1096)", () => {
88
+ let root: string;
89
+
90
+ beforeEach(() => {
91
+ root = mkdtempSync(join(tmpdir(), "compass-policy-"));
92
+ });
93
+
94
+ afterEach(() => {
95
+ rmSync(root, { recursive: true, force: true });
96
+ });
97
+
98
+ it("AC-1: bindings.compass.scanRoots replaces the generic scan roots", () => {
99
+ writeFileSync(join(root, "forge.yaml"), FORGE_YAML(" compass:\n scanRoots: [src]\n"));
100
+ const policy = resolveCompassPolicy(root, FORGE_ROOT);
101
+ expect(policy.scanRoots).toEqual(["src"]);
102
+ expect(policy.source.overriddenKeys).toContain("scanRoots");
103
+ });
104
+
105
+ it("AC-1b: union keys merge and `!` subtracts", () => {
106
+ writeFileSync(
107
+ join(root, "forge.yaml"),
108
+ FORGE_YAML(
109
+ ' compass:\n fileExtensions: [.ts, .svelte, "!.astro"]\n ignoredDirs: [spec, "!coverage"]\n',
110
+ ),
111
+ );
112
+ const policy = resolveCompassPolicy(root, FORGE_ROOT);
113
+ expect(policy.fileExtensions.has(".svelte")).toBe(true);
114
+ expect(policy.fileExtensions.has(".ts")).toBe(true);
115
+ expect(policy.fileExtensions.has(".astro")).toBe(false);
116
+ expect(policy.ignoredDirs.has("spec")).toBe(true);
117
+ expect(policy.ignoredDirs.has("coverage")).toBe(false);
118
+ expect(policy.ignoredDirs.has("node_modules")).toBe(true);
119
+ });
120
+
121
+ it("AC-1c: consumer layerRules replace the generic set wholesale", () => {
122
+ writeFileSync(
123
+ join(root, "forge.yaml"),
124
+ FORGE_YAML(
125
+ " compass:\n layerRules:\n - pattern: src/pages/**\n layer: route\n risk: high\n",
126
+ ),
127
+ );
128
+ const policy = resolveCompassPolicy(root, FORGE_ROOT);
129
+ expect(policy.matchLayer("src/pages/index.astro")?.layer).toBe("route");
130
+ expect(policy.matchLayer("src/pages/index.astro")?.risk).toBe("high");
131
+ // Replace semantics: generic rules are gone when the consumer sets the key.
132
+ expect(policy.matchLayer("src/components/x.ts")).toBeUndefined();
133
+ expect(policy.layerRules).toHaveLength(1);
134
+ });
135
+
136
+ it("AC-2: profile compass section applies when forge.yaml declares `profile`", () => {
137
+ writeFileSync(join(root, "forge.yaml"), FORGE_YAML_WITH_PROFILE);
138
+ const policy = resolveCompassPolicy(root, FORGE_ROOT);
139
+ expect(policy.source.profile).toBe("godot-game");
140
+ for (const ext of [".cs", ".tscn", ".tres", ".gd"]) {
141
+ expect(policy.fileExtensions.has(ext), `missing ${ext}`).toBe(true);
142
+ }
143
+ expect(policy.source.overriddenKeys).toEqual([]);
144
+ });
145
+
146
+ it("AC-3: non-compilable idPattern throws CompassPolicyConfigError naming the key", () => {
147
+ writeFileSync(join(root, "forge.yaml"), FORGE_YAML(' compass:\n idPattern: "([invalid"\n'));
148
+ expect(() => resolveCompassPolicy(root, FORGE_ROOT)).toThrow(CompassPolicyConfigError);
149
+ try {
150
+ resolveCompassPolicy(root, FORGE_ROOT);
151
+ expect.unreachable();
152
+ } catch (err) {
153
+ expect(err).toBeInstanceOf(CompassPolicyConfigError);
154
+ expect((err as CompassPolicyConfigError).key).toBe("idPattern");
155
+ expect((err as Error).message).toContain("idPattern");
156
+ }
157
+ });
158
+
159
+ it("AC-3b: idPattern that fails the NAMESPACE-NUMBER probe is rejected", () => {
160
+ writeFileSync(join(root, "forge.yaml"), FORGE_YAML(' compass:\n idPattern: "^\\\\d+$"\n'));
161
+ expect(() => resolveCompassPolicy(root, FORGE_ROOT)).toThrow(CompassPolicyConfigError);
162
+ });
163
+
164
+ it("AC-4: no consumer- or stack-specific path literals under os/compass", () => {
165
+ // Constructed dynamically so this test file does not itself carry the literals.
166
+ // Path-literal forms only — historical `@warpgogol/<pkg>` specifiers in
167
+ // CHANGE_SUMMARY comments are package names, not path literals.
168
+ const bannedPathLiterals = [
169
+ "apps/" + "main",
170
+ "DEFAULT_" + "SCAN_" + "ROOTS",
171
+ "werk" + "statt",
172
+ "packages/" + "os/",
173
+ "site-" + "kernel/",
174
+ ];
175
+ // AC-4 names `site-kernel` (bare) as banned inside compass-inventory.ts.
176
+ const bannedInInventory = [...bannedPathLiterals, "site-" + "kernel"];
177
+ const inventoryFile = join(COMPASS_DIR, "handlers", "compass-inventory.ts");
178
+
179
+ const offenders: string[] = [];
180
+ for (const file of collectTsFiles(COMPASS_DIR)) {
181
+ const source = readFileSync(file, "utf8");
182
+ const banned = file === inventoryFile ? bannedInInventory : bannedPathLiterals;
183
+ for (const literal of banned) {
184
+ if (source.includes(literal)) {
185
+ offenders.push(`${file}: ${literal}`);
186
+ }
187
+ }
188
+ }
189
+ expect(offenders, `banned literals found:\n${offenders.join("\n")}`).toEqual([]);
190
+ });
191
+
192
+ it("AC-5: highRiskPaths from bindings mark matching entries riskClass=high", async () => {
193
+ writeFileSync(
194
+ join(root, "forge.yaml"),
195
+ FORGE_YAML(
196
+ ' compass:\n scanRoots: [packages]\n highRiskPaths: ["packages/fixture-pkg/src/kernel/**"]\n',
197
+ ),
198
+ );
199
+ const fixture = join(root, "packages", "fixture-pkg", "src", "kernel");
200
+ mkdirSync(fixture, { recursive: true });
201
+ const body = Array.from({ length: 25 }, (_, i) => `export const line${i} = ${i};`).join("\n");
202
+ writeFileSync(
203
+ join(fixture, "engine.ts"),
204
+ `/*\n<MODULE_CONTRACT>\n<purpose>Fixture kernel engine file for the compass policy high-risk test.</purpose>\n<non-goals>\n <item>No production logic.</item>\n</non-goals>\n</MODULE_CONTRACT>\n*/\n${body}\n`,
205
+ );
206
+
207
+ const result = await runCompassInventory({ flags: {} } as never, makeContext(root));
208
+ const entry = result.data?.entries.find((e) => e.path.endsWith("kernel/engine.ts"));
209
+ expect(entry, "fixture entry must be inventoried").toBeDefined();
210
+ expect(entry!.riskClass).toBe("high");
211
+ expect(result.data?.policySource.overriddenKeys).toContain("highRiskPaths");
212
+ });
213
+
214
+ it("excludedPaths mark matching files excluded with the configured reason", async () => {
215
+ writeFileSync(
216
+ join(root, "forge.yaml"),
217
+ FORGE_YAML(
218
+ " compass:\n scanRoots: [packages]\n excludedPaths:\n - pattern: src/templates/**\n reason: template-source\n",
219
+ ),
220
+ );
221
+ const dir = join(root, "packages", "fixture-pkg", "src", "templates");
222
+ mkdirSync(dir, { recursive: true });
223
+ writeFileSync(join(dir, "widget.ts"), "export const t = 1;\n");
224
+
225
+ const result = await runCompassInventory({ flags: {} } as never, makeContext(root));
226
+ const entry = result.data?.entries.find((e) => e.path.endsWith("templates/widget.ts"));
227
+ expect(entry).toBeDefined();
228
+ expect(entry!.authoringStatus).toBe("excluded");
229
+ expect(entry!.exclusionReason).toBe("template-source");
230
+ });
231
+
232
+ it("missing forge.yaml yields the generic policy", () => {
233
+ const policy = resolveCompassPolicy(root, FORGE_ROOT);
234
+ expect(policy.source.profile).toBeNull();
235
+ expect(policy.source.overriddenKeys).toEqual([]);
236
+ expect(policy.fileExtensions.has(".ts")).toBe(true);
237
+ expect(policy.scanRoots).toEqual(["src", "apps", "packages", "services"]);
238
+ expect(policy.idPattern.test("RFC-1096")).toBe(true);
239
+ });
240
+
241
+ it("parseGovernanceIdParts splits on the last dash", () => {
242
+ expect(parseGovernanceIdParts("RFC-1095")).toEqual({ namespace: "RFC", numeric: 1095 });
243
+ expect(parseGovernanceIdParts("COMPASS-CS-07")).toEqual({
244
+ namespace: "COMPASS-CS",
245
+ numeric: 7,
246
+ });
247
+ expect(parseGovernanceIdParts("no-number")).toBeNull();
248
+ });
249
+ });
@@ -3,7 +3,6 @@ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
3
3
  import { dirname, join } from "node:path";
4
4
  import { tmpdir } from "node:os";
5
5
  import { runCompassValidation, runCompassInventory } from "../compass-inventory-handler.ts";
6
- import { runCompassChangeSummaryValidate } from "../compass-change-summary-handler.ts";
7
6
  import type {
8
7
  ForgeCommandInput,
9
8
  ForgeCommandResult,
@@ -34,8 +33,12 @@ const MC = (purpose: string) =>
34
33
  const KD = (items: string[]) =>
35
34
  `<KEY_DECISIONS>\n${items.map((i) => ` <item>${i}</item>`).join("\n")}\n</KEY_DECISIONS>`;
36
35
  const KD_EMPTY = `<KEY_DECISIONS>\n</KEY_DECISIONS>`;
36
+ // Dynamic tags: a literal CHANGE_SUMMARY block in this file would be matched
37
+ // by compass.summary.record itself and rewritten at commit time.
38
+ const CS_OPEN = "<" + "CHANGE_SUMMARY>";
39
+ const CS_CLOSE = "</" + "CHANGE_SUMMARY>";
37
40
  const CS = (items: string[], history?: string) =>
38
- `<CHANGE_SUMMARY>\n${items.map((i) => ` <item>${i}</item>`).join("\n")}${history ? `\n <history>${history}</history>` : ""}\n</CHANGE_SUMMARY>`;
41
+ `${CS_OPEN}\n${items.map((i) => ` <item>${i}</item>`).join("\n")}\n${history ? ` <history>${history}</history>\n` : ""}${CS_CLOSE}`;
39
42
  const wrap = (...blocks: string[]) => `/*\n${blocks.join("\n")}\n*/`;
40
43
 
41
44
  const PURPOSE_WITH_TOKEN =
@@ -100,7 +103,8 @@ describe("compass v2 contract (RFC-1094)", () => {
100
103
  const items = [1, 2, 3, 4, 5, 6].map((n) => `RFC-10${n}0: change number ${n}.`);
101
104
  writeFixture(MEDIUM_FILE, wrap(MC(PURPOSE_WITH_TOKEN), KD([KD_ITEM]), CS(items)));
102
105
 
103
- const result = await runCompassChangeSummaryValidate(makeInput(), makeContext(tempDir));
106
+ // RFC-1095: CS rules are covered by compass.validate via shared evaluateV2Rules.
107
+ const result = await runCompassValidation(makeInput(), makeContext(tempDir));
104
108
  const diag = dataOf(result).diagnostics.find((d) => d.ruleId === "COMPASS-CS-05");
105
109
  expect(diag, "CS-05 must fire when CHANGE_SUMMARY exceeds 5 items").toBeDefined();
106
110
  expect(diag!.severity).toBe("warning");
@@ -113,7 +117,7 @@ describe("compass v2 contract (RFC-1094)", () => {
113
117
  wrap(MC(PURPOSE_WITH_TOKEN), KD([KD_ITEM]), CS(["fixed a bug in the widget"])),
114
118
  );
115
119
 
116
- const result = await runCompassChangeSummaryValidate(makeInput(), makeContext(tempDir));
120
+ const result = await runCompassValidation(makeInput(), makeContext(tempDir));
117
121
  const diag = dataOf(result).diagnostics.find((d) => d.ruleId === "COMPASS-CS-06");
118
122
  expect(diag, "CS-06 must fire for items lacking a governance ID").toBeDefined();
119
123
  });
@@ -203,7 +207,7 @@ describe("compass v2 contract (RFC-1094)", () => {
203
207
  wrap(MC(PURPOSE_WITH_TOKEN), KD([KD_ITEM]), CS([CS_ITEM], "RFC-0350, RFC-0348")),
204
208
  );
205
209
 
206
- const result = await runCompassChangeSummaryValidate(makeInput(), makeContext(tempDir));
210
+ const result = await runCompassValidation(makeInput(), makeContext(tempDir));
207
211
  expect(
208
212
  dataOf(result).diagnostics.find((d) => d.ruleId === "COMPASS-CS-07"),
209
213
  "CS-07 must fire on descending per-namespace order",
@@ -214,13 +218,9 @@ describe("compass v2 contract (RFC-1094)", () => {
214
218
  writeFixture(MEDIUM_FILE, wrap(MC(PURPOSE_WITH_TOKEN), KD([KD_ITEM]), CS([])));
215
219
 
216
220
  const warnResult = await runCompassValidation(makeInput(), makeContext(tempDir));
217
- const csResult = await runCompassChangeSummaryValidate(makeInput(), makeContext(tempDir));
218
221
  expect(
219
222
  dataOf(warnResult).diagnostics.filter((d) => d.ruleId.startsWith("COMPASS-CS-")),
220
223
  ).toHaveLength(0);
221
- expect(
222
- dataOf(csResult).diagnostics.filter((d) => d.ruleId.startsWith("COMPASS-CS-")),
223
- ).toHaveLength(0);
224
224
  });
225
225
 
226
226
  it("fully compliant v2 file produces zero v2 diagnostics", async () => {
@@ -0,0 +1,224 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { mkdtemp, mkdir, writeFile, readFile, rm } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import {
6
+ runCompassSummaryRecord,
7
+ parseChangeSummary,
8
+ mergeHistoryIds,
9
+ buildChangeSummaryBlock,
10
+ stripConventionalPrefix,
11
+ isValidGovernanceId,
12
+ CHANGE_SUMMARY_WINDOW,
13
+ } from "../summary-record.ts";
14
+ import { resolveCompassPolicy } from "../../policy.ts";
15
+ import type { ForgeRuntimeContext } from "../../../../src/types.ts";
16
+
17
+ // Generic policy — this path has no forge.yaml, so resolution falls back to
18
+ // GENERIC defaults.
19
+ const policy = resolveCompassPolicy(join(tmpdir(), "summary-record-no-forge-yaml"));
20
+
21
+ const logger = {
22
+ section() {},
23
+ info() {},
24
+ warn() {},
25
+ error() {},
26
+ success() {},
27
+ getEvents() {
28
+ return [];
29
+ },
30
+ };
31
+
32
+ function makeContext(workspaceRoot: string, dryRun = false): ForgeRuntimeContext {
33
+ return {
34
+ workspaceRoot,
35
+ logger: logger as never,
36
+ dryRun,
37
+ outputFormat: "pretty",
38
+ };
39
+ }
40
+
41
+ function makeInput(flags: Record<string, unknown>) {
42
+ return { argv: [], flags: flags as never };
43
+ }
44
+
45
+ // Dynamic tags: a literal CHANGE_SUMMARY block in this file would be matched
46
+ // by compass.summary.record itself and rewritten at commit time.
47
+ const CS_OPEN = "<" + "CHANGE_SUMMARY>";
48
+ const CS_CLOSE = "</" + "CHANGE_SUMMARY>";
49
+ const CS_BLOCK_RE = new RegExp(`${CS_OPEN}[\\s\\S]*?${CS_CLOSE}`);
50
+
51
+ const TS_HEADER = (items: string[], history?: string[]) => `/*
52
+ <MODULE_CONTRACT>
53
+ <purpose>Test fixture file for summary record coverage.</purpose>
54
+ <non-goals><item>No production logic.</item></non-goals>
55
+ </MODULE_CONTRACT>
56
+ ${CS_OPEN}
57
+ ${items.map((i) => ` <item>${i}</item>`).join("\n")}
58
+ ${history && history.length > 0 ? ` <history>${history.join(", ")}</history>\n` : ""}${CS_CLOSE}
59
+ */
60
+ export const x = 1;
61
+ `;
62
+
63
+ describe("compass.summary.record", () => {
64
+ let root: string;
65
+
66
+ beforeEach(async () => {
67
+ root = await mkdtemp(join(tmpdir(), "summary-record-"));
68
+ await mkdir(join(root, "src"), { recursive: true });
69
+ });
70
+
71
+ afterEach(async () => {
72
+ await rm(root, { recursive: true, force: true });
73
+ });
74
+
75
+ it("appends an ID-prefixed item to CHANGE_SUMMARY (AC-1)", async () => {
76
+ await writeFile(join(root, "src", "a.ts"), TS_HEADER(["RFC-0001: existing item."]));
77
+ const result = await runCompassSummaryRecord(
78
+ makeInput({ id: "RFC-1095", files: ["src/a.ts"], text: "added record command" }),
79
+ makeContext(root),
80
+ );
81
+ expect(result.exitCode).toBe(0);
82
+ const source = await readFile(join(root, "src", "a.ts"), "utf8");
83
+ expect(source).toContain("<item>RFC-1095: added record command</item>");
84
+ expect(result.data?.recorded).toEqual(["src/a.ts"]);
85
+ });
86
+
87
+ it("skips duplicate items with identical ID+text (AC-2)", async () => {
88
+ await writeFile(join(root, "src", "a.ts"), TS_HEADER(["RFC-1095: added record command"]));
89
+ const result = await runCompassSummaryRecord(
90
+ makeInput({ id: "RFC-1095", files: ["src/a.ts"], text: "added record command" }),
91
+ makeContext(root),
92
+ );
93
+ expect(result.data?.skipped).toEqual([{ file: "src/a.ts", reason: "duplicate" }]);
94
+ expect(result.data?.recorded).toEqual([]);
95
+ });
96
+
97
+ it("collapses the oldest item into <history> past the 5-item window (AC-3)", async () => {
98
+ const items = Array.from(
99
+ { length: CHANGE_SUMMARY_WINDOW },
100
+ (_, i) => `RFC-000${i}: item ${i}.`,
101
+ );
102
+ await writeFile(join(root, "src", "a.ts"), TS_HEADER(items));
103
+ const result = await runCompassSummaryRecord(
104
+ makeInput({ id: "RFC-1095", files: ["src/a.ts"], text: "overflow item" }),
105
+ makeContext(root),
106
+ );
107
+ const source = await readFile(join(root, "src", "a.ts"), "utf8");
108
+ const parsed = parseChangeSummary(source.match(CS_BLOCK_RE)![0]);
109
+ expect(parsed.items).toHaveLength(CHANGE_SUMMARY_WINDOW);
110
+ expect(parsed.items.at(-1)).toBe("RFC-1095: overflow item");
111
+ expect(parsed.historyIds).toContain("RFC-0000");
112
+ expect(result.data?.collapsed).toEqual(["src/a.ts"]);
113
+ });
114
+
115
+ it("merges collapsed IDs into existing history deduped and per-namespace ascending (AC-4)", async () => {
116
+ const items = Array.from(
117
+ { length: CHANGE_SUMMARY_WINDOW },
118
+ (_, i) => `RFC-000${i}: item ${i}.`,
119
+ );
120
+ await writeFile(join(root, "src", "a.ts"), TS_HEADER(items, ["RFC-0000", "ADR-0002"]));
121
+ await runCompassSummaryRecord(
122
+ makeInput({ id: "RFC-1095", files: ["src/a.ts"], text: "overflow" }),
123
+ makeContext(root),
124
+ );
125
+ const source = await readFile(join(root, "src", "a.ts"), "utf8");
126
+ const parsed = parseChangeSummary(source.match(CS_BLOCK_RE)![0]);
127
+ // RFC-0000 deduped; ADR-0002 sorts before RFC namespace.
128
+ expect(parsed.historyIds).toEqual(["ADR-0002", "RFC-0000"]);
129
+ });
130
+
131
+ it("skips files without a CHANGE_SUMMARY block (AC-5)", async () => {
132
+ await writeFile(join(root, "src", "plain.ts"), "export const y = 2;\n");
133
+ const result = await runCompassSummaryRecord(
134
+ makeInput({ id: "RFC-1095", files: ["src/plain.ts"], text: "x" }),
135
+ makeContext(root),
136
+ );
137
+ expect(result.data?.skipped).toEqual([{ file: "src/plain.ts", reason: "no-block" }]);
138
+ });
139
+
140
+ it("skips missing files as unparseable without failing (AC-5)", async () => {
141
+ const result = await runCompassSummaryRecord(
142
+ makeInput({ id: "RFC-1095", files: ["src/ghost.ts"], text: "x" }),
143
+ makeContext(root),
144
+ );
145
+ expect(result.exitCode).toBe(0);
146
+ expect(result.data?.skipped).toEqual([{ file: "src/ghost.ts", reason: "unparseable" }]);
147
+ });
148
+
149
+ it("rejects a missing or malformed --id", async () => {
150
+ const bad = await runCompassSummaryRecord(
151
+ makeInput({ id: "not-an-id", files: ["src/a.ts"] }),
152
+ makeContext(root),
153
+ );
154
+ expect(bad.exitCode).toBe(1);
155
+ const missing = await runCompassSummaryRecord(
156
+ makeInput({ files: ["src/a.ts"] }),
157
+ makeContext(root),
158
+ );
159
+ expect(missing.exitCode).toBe(1);
160
+ });
161
+
162
+ it("defaults item text to the bare ID when --text is absent", async () => {
163
+ await writeFile(join(root, "src", "a.ts"), TS_HEADER([]));
164
+ await runCompassSummaryRecord(
165
+ makeInput({ id: "ADR-0042", files: ["src/a.ts"] }),
166
+ makeContext(root),
167
+ );
168
+ const source = await readFile(join(root, "src", "a.ts"), "utf8");
169
+ expect(source).toContain("<item>ADR-0042</item>");
170
+ });
171
+
172
+ it("does not write files in dry-run mode", async () => {
173
+ const original = TS_HEADER(["RFC-0001: existing."]);
174
+ await writeFile(join(root, "src", "a.ts"), original);
175
+ const result = await runCompassSummaryRecord(
176
+ makeInput({ id: "RFC-1095", files: ["src/a.ts"], text: "dry run item" }),
177
+ makeContext(root, true),
178
+ );
179
+ expect(result.data?.recorded).toEqual(["src/a.ts"]);
180
+ expect(await readFile(join(root, "src", "a.ts"), "utf8")).toBe(original);
181
+ });
182
+
183
+ it("strips a duplicated leading ID from --text", async () => {
184
+ await writeFile(join(root, "src", "a.ts"), TS_HEADER([]));
185
+ await runCompassSummaryRecord(
186
+ makeInput({ id: "RFC-1095", files: ["src/a.ts"], text: "RFC-1095 — record command" }),
187
+ makeContext(root),
188
+ );
189
+ const source = await readFile(join(root, "src", "a.ts"), "utf8");
190
+ expect(source).toContain("<item>RFC-1095: record command</item>");
191
+ expect(source).not.toContain("RFC-1095: RFC-1095");
192
+ });
193
+ });
194
+
195
+ describe("summary-record helpers", () => {
196
+ it("stripConventionalPrefix removes conventional commit prefixes", () => {
197
+ expect(stripConventionalPrefix("implement: RFC-1095 — record")).toBe("RFC-1095 — record");
198
+ expect(stripConventionalPrefix("fix(scope)!: bug")).toBe("bug");
199
+ expect(stripConventionalPrefix("plain subject")).toBe("plain subject");
200
+ });
201
+
202
+ it("isValidGovernanceId accepts RFC/ADR/ticket shapes only", () => {
203
+ expect(isValidGovernanceId("RFC-1095", policy)).toBe(true);
204
+ expect(isValidGovernanceId("ADR-0042", policy)).toBe(true);
205
+ expect(isValidGovernanceId("PROJ-123", policy)).toBe(true);
206
+ expect(isValidGovernanceId("rfc-1095", policy)).toBe(false);
207
+ expect(isValidGovernanceId("RFC1095", policy)).toBe(false);
208
+ expect(isValidGovernanceId("RFC-1095 extra", policy)).toBe(false);
209
+ });
210
+
211
+ it("mergeHistoryIds dedupes and sorts per-namespace ascending", () => {
212
+ expect(mergeHistoryIds(["RFC-0005"], ["RFC-0002", "ADR-0001", "RFC-0005"], policy)).toEqual([
213
+ "ADR-0001",
214
+ "RFC-0002",
215
+ "RFC-0005",
216
+ ]);
217
+ });
218
+
219
+ it("buildChangeSummaryBlock omits <history> when empty", () => {
220
+ const block = buildChangeSummaryBlock(["RFC-1: a"], [], "x.ts");
221
+ expect(block).not.toContain("<history>");
222
+ expect(block).toContain("<item>RFC-1: a</item>");
223
+ });
224
+ });
@@ -7,6 +7,11 @@
7
7
  </MODULE_CONTRACT>
8
8
  <CHANGE_SUMMARY>
9
9
  <item>RFC-0374: initial compass module barrel.</item>
10
+ <item>RFC-1095: compass.summary.record, trim repair rewrite, commit integration</item>
11
+ <item>RFC-1095: review findings: execFileSync git add, drop speculative barrel exports</item>
12
+ <item>RFC-1097: steps 1-4 — compass.migrate codemod
13
+
14
+ Add the v1 to v2 Compass header codemod: migrateFile pure transform (collapse, strip, seed, reorder, purpose-flag actions), migrateWorkspace walker, runCompassMigrate handler with dirty-tree refusal and --force/--files/--dry-run flags, module registration, and 15 unit tests.</item>
10
15
  </CHANGE_SUMMARY>
11
16
  */
12
17
 
@@ -25,9 +30,22 @@ export {
25
30
  runCompassAuditValidate,
26
31
  isAuditDue,
27
32
  } from "./handlers/compass-audit-handler.ts";
33
+ export { runCompassSummaryTrim } from "./handlers/compass-change-summary-handler.ts";
34
+ export { runCompassMigrate } from "./handlers/compass-migrate-handler.ts";
35
+ export {
36
+ migrateFile,
37
+ migrateWorkspace,
38
+ type MigrateAction,
39
+ type MigrateResult,
40
+ } from "./handlers/compass-migrate.ts";
28
41
  export {
29
- runCompassChangeSummaryValidate,
30
- runCompassSummaryTrim,
31
- classifyChangeSummaryItem,
32
- type ChangeSummaryItemClass,
33
- } from "./handlers/compass-change-summary-handler.ts";
42
+ runCompassSummaryRecord,
43
+ parseChangeSummary,
44
+ mergeHistoryIds,
45
+ buildChangeSummaryBlock,
46
+ stripConventionalPrefix,
47
+ isValidGovernanceId,
48
+ CHANGE_SUMMARY_WINDOW,
49
+ type SummaryRecordResult,
50
+ type SummaryRecordSkipReason,
51
+ } from "./handlers/summary-record.ts";