@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
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  <MODULE_CONTRACT>
3
- <purpose>Canonical Compass inventory scanning logic. Moved from @warpgogol/site-kernel
4
- to @warpgogol/forge for full autonomous mode (RFC-0556). Provides file collection,
3
+ <purpose>Canonical Compass inventory scanning logic. Moved from the legacy site
4
+ kernel package to @warpgogol/forge for full autonomous mode (RFC-0556). Provides file collection,
5
5
  workspace detection, layer classification, risk assessment, and compliance checking
6
6
  for Compass source-file inventory.</purpose>
7
7
  <non-goals>
@@ -9,82 +9,37 @@ for Compass source-file inventory.</purpose>
9
9
  <item>Do not register commands — this is a pure utility module.</item>
10
10
  </non-goals>
11
11
  </MODULE_CONTRACT>
12
+ <KEY_DECISIONS>
13
+ <item>Authoring status is classified before compliance — generated, test, and excluded files never reach rule evaluation.</item>
14
+ <item>Risk class derives from layer rules plus high-risk globs — KEY_DECISIONS is required only on medium and high.</item>
15
+ <item>Policy literals come from resolveCompassPolicy — no stack- or consumer-specific values live in this module.</item>
16
+ </KEY_DECISIONS>
12
17
  <CHANGE_SUMMARY>
13
- <item>RFC-0348: collapsed to two-block contract (MODULE_CONTRACT + CHANGE_SUMMARY); removed MODULE_MAP, keywords, responsibilities, COMPASS_BLOCK anchors; coverage modes collapsed to standard/none.</item>
14
- <item>Post-refactor hardening: exclude src/templates generation inputs from authored Compass requirements.</item>
15
- <item>Post-refactor hardening: detect nested packages/os workspaces before deriving Compass layer and workspace name.</item>
16
- <item>RFC-0556: moved canonical implementation from @warpgogol/site-kernel to @warpgogol/forge for autonomous mode.</item>
17
- <item>Game extensions: added .cs, .tscn, .tres, .gd to SOURCE_EXTENSIONS; createCompassInventoryEntries now reads forge.yaml compass.fileExtensions at runtime and merges with hardcoded set.</item>
18
- <item>Added .md to SOURCE_EXTENSIONS for SKILL.md Compass coverage; detectAuthoringStatus excludes non-SKILL.md markdown files.</item>
19
18
  <item>RFC-1094: v2 contract — KEY_DECISIONS/history parsing, new inventory fields, evaluateV2Rules, deriveFileTokens, resolveCompassMode, shared GOVERNANCE_ID_RE.</item>
19
+ <item>RFC-1095: compass.summary.record, trim repair rewrite, commit integration</item>
20
+ <item>RFC-1096: all policy literals externalized to resolveCompassPolicy — scan roots, extensions, ignored dirs/paths, layer/risk rules, governance-ID and boilerplate patterns come from generic defaults + profile + bindings.compass.</item>
21
+ <item>RFC-1097: sweep — SKILL.md headers + classification fixes
22
+
23
+ Sweep batch 1: add Compass v2 headers to 45 SKILL.md files (purpose derived from frontmatter description). Fix non-skill-markdown exclusion to check filename not workspace-relative path (packages/AGENTS.md escaped it). Add .coverage to ignoredDirs.</item>
24
+ <item>RFC-1097: sweep — packages/forge + services clean
25
+
26
+ Sweep batch 2: real KEY_DECISIONS on 10 files, expanded purposes (CONTRACT-02/PURPOSE-02), headers on mission/index + gen-upstreams, sanitizeItemText in summary.record (literal Compass tags corrupted history), excludedPaths for wrangler types, test-fixtures testPattern. forge+services now 0 diagnostics under --mode error.</item>
27
+ <history>RFC-0348, RFC-0556</history>
20
28
  </CHANGE_SUMMARY>
21
29
  */
22
30
 
23
31
  import { readdir, readFile } from "node:fs/promises";
24
- import { readFileSync, existsSync } from "node:fs";
25
32
  import { join, relative, resolve } from "node:path";
26
- import { parse as parseYaml } from "yaml";
27
33
  import type { ForgeCommandInput } from "../../../src/types.ts";
28
34
  import { hasGeneratedMarker } from "../../../src/utils/generated-marker.ts";
35
+ import {
36
+ parseGovernanceIdParts,
37
+ resolveCompassPolicy,
38
+ type CompassPolicy,
39
+ type CompassRiskClass,
40
+ type CompassWorkspaceKind,
41
+ } from "../policy.ts";
29
42
 
30
- function loadCompassExtensionsFromForgeYaml(workspaceRoot: string): Set<string> {
31
- const forgeYamlPath = join(workspaceRoot, "forge.yaml");
32
- if (!existsSync(forgeYamlPath)) {
33
- return new Set();
34
- }
35
- try {
36
- const raw = readFileSync(forgeYamlPath, "utf8");
37
- const parsed = parseYaml(raw) as Record<string, unknown> | null;
38
- const bindings = parsed?.bindings as Record<string, unknown> | undefined;
39
- const compass = bindings?.compass as Record<string, unknown> | undefined;
40
- const extensions = compass?.fileExtensions;
41
- if (Array.isArray(extensions)) {
42
- return new Set(extensions.filter((e: unknown) => typeof e === "string"));
43
- }
44
- } catch {
45
- // forge.yaml not parseable or missing compass section — fall back to hardcoded set
46
- }
47
- return new Set();
48
- }
49
-
50
- const SOURCE_EXTENSIONS = new Set([
51
- ".ts",
52
- ".tsx",
53
- ".astro",
54
- ".js",
55
- ".jsx",
56
- ".mjs",
57
- ".mts",
58
- ".css",
59
- ".cs",
60
- ".tscn",
61
- ".tres",
62
- ".gd",
63
- ".md",
64
- ]);
65
- const IGNORED_DIRECTORY_NAMES = new Set([
66
- ".git",
67
- ".turbo",
68
- ".astro",
69
- ".wrangler",
70
- ".vscode",
71
- ".cache",
72
- "coverage",
73
- "dist",
74
- "node_modules",
75
- "spec",
76
- "todo",
77
- ]);
78
- const DEFAULT_SCAN_ROOTS = ["apps", "packages", "services"];
79
- const HIGH_RISK_EXACT_RELATIVE_PATHS = new Set([
80
- "apps/main/src/content/config.ts",
81
- "apps/main/src/middleware.ts",
82
- "packages/os/site-kernel/src/cli/index.ts",
83
- "packages/os/site-kernel/src/discovery.ts",
84
- "packages/os/site-kernel/src/registry.ts",
85
- "packages/os/site-kernel/src/runtime.ts",
86
- "packages/os/site-kernel/src/types.ts",
87
- ]);
88
43
  function forbiddenMarkerPattern(tagName: string): RegExp {
89
44
  return new RegExp(`<${tagName}\\b`);
90
45
  }
@@ -96,12 +51,7 @@ const FORBIDDEN_PATTERNS: Array<{ name: string; regex: RegExp }> = [
96
51
  { name: "COMPASS_BLOCK", regex: new RegExp(`</?${"COMPASS_BLOCK"}\\b`) },
97
52
  ];
98
53
 
99
- // RFC-1094: v2 contract constants. GOVERNANCE_ID_RE is the canonical
100
- // governance-ID pattern shared with the change-summary handler.
101
- export const GOVERNANCE_ID_RE = /\b([A-Z][A-Z0-9]*-)+\d+\b/;
102
- const GOVERNANCE_ID_PREFIX_RE = /^([A-Z][A-Z0-9]*-)+\d+\b/;
103
- const HISTORY_ID_RE = /^([A-Z][A-Z0-9]*-)+\d+$/;
104
- const HISTORY_ID_PARTS_RE = /^(([A-Z][A-Z0-9]*-)+)(\d+)$/;
54
+ // RFC-1096: governance-ID regexes are policy-derived (policy.idPattern*).
105
55
  const TODO_PLACEHOLDER_RE = /^TODO\b/i;
106
56
 
107
57
  const GENERIC_STEMS = new Set([
@@ -120,12 +70,6 @@ const SYMBOL_EXTENSIONS = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".mts"]
120
70
  const EXPORTED_SYMBOL_RE =
121
71
  /export\s+(?:async\s+)?(?:function|const|let|class|interface|type|enum)\s+(\w+)/g;
122
72
 
123
- const PURPOSE_BOILERPLATE_PATTERNS: RegExp[] = [
124
- /^(this file|the file|a file|this module|a module)\b/i,
125
- /^(utility|utilities|helper|helpers|misc)\b/i,
126
- /^(todo|placeholder|fixme)\b/i,
127
- ];
128
-
129
73
  const KEY_DECISIONS_MAX_ITEMS = 7;
130
74
  const KEY_DECISIONS_MAX_WORDS = 20;
131
75
  const CHANGE_SUMMARY_MAX_ITEMS = 5;
@@ -145,8 +89,6 @@ export function resolveCompassMode(input: ForgeCommandInput): CompassValidationM
145
89
  throw new Error(`--mode must be "warning" or "error", got "${String(raw)}"`);
146
90
  }
147
91
 
148
- type CompassWorkspaceKind = "app" | "package" | "service";
149
- type CompassRiskClass = "high" | "medium" | "low";
150
92
  type CompassComplexity = "non-trivial" | "trivial";
151
93
  type CompassScaffoldingMode = "standard" | "none";
152
94
  type CompassAuthoringStatus = "authored" | "excluded";
@@ -186,45 +128,33 @@ function getFlagValues(input: ForgeCommandInput, key: string): string[] {
186
128
  return typeof value === "string" ? [value] : [];
187
129
  }
188
130
 
189
- function resolveScanRoots(workspaceRoot: string, input: ForgeCommandInput): string[] {
131
+ function resolveScanRoots(
132
+ workspaceRoot: string,
133
+ input: ForgeCommandInput,
134
+ policy: CompassPolicy,
135
+ ): string[] {
190
136
  const values = getFlagValues(input, "root");
191
- const roots = values.length > 0 ? values : DEFAULT_SCAN_ROOTS;
137
+ const roots = values.length > 0 ? values : policy.scanRoots;
192
138
  return roots.map((value) => resolve(workspaceRoot, value));
193
139
  }
194
140
 
195
- function shouldIgnoreDirectory(name: string): boolean {
196
- if (IGNORED_DIRECTORY_NAMES.has(name)) {
141
+ function shouldIgnoreDirectory(name: string, policy: CompassPolicy): boolean {
142
+ if (policy.ignoredDirs.has(name)) {
197
143
  return true;
198
144
  }
199
- if (name.startsWith("old-")) {
200
- return true;
201
- }
202
- if (name.startsWith("-")) {
203
- return true;
204
- }
205
- return false;
145
+ return policy.ignoredDirPrefixes.some((prefix) => name.startsWith(prefix));
206
146
  }
207
147
 
208
- function hasRelevantExtension(filePath: string, extraExtensions?: Set<string>): boolean {
209
- for (const extension of SOURCE_EXTENSIONS) {
148
+ function hasRelevantExtension(filePath: string, policy: CompassPolicy): boolean {
149
+ for (const extension of policy.fileExtensions) {
210
150
  if (filePath.endsWith(extension)) {
211
151
  return true;
212
152
  }
213
153
  }
214
- if (extraExtensions) {
215
- for (const extension of extraExtensions) {
216
- if (filePath.endsWith(extension)) {
217
- return true;
218
- }
219
- }
220
- }
221
154
  return false;
222
155
  }
223
156
 
224
- async function collectSourceFiles(
225
- targetPath: string,
226
- extraExtensions?: Set<string>,
227
- ): Promise<string[]> {
157
+ async function collectSourceFiles(targetPath: string, policy: CompassPolicy): Promise<string[]> {
228
158
  let stat;
229
159
  try {
230
160
  const { stat: fsStat } = await import("node:fs/promises");
@@ -234,7 +164,7 @@ async function collectSourceFiles(
234
164
  }
235
165
 
236
166
  if (stat.isFile()) {
237
- return hasRelevantExtension(targetPath, extraExtensions) ? [targetPath] : [];
167
+ return hasRelevantExtension(targetPath, policy) ? [targetPath] : [];
238
168
  }
239
169
 
240
170
  let entries;
@@ -249,25 +179,15 @@ async function collectSourceFiles(
249
179
  const absolutePath = join(targetPath, entry.name);
250
180
 
251
181
  if (entry.isDirectory()) {
252
- if (shouldIgnoreDirectory(entry.name)) {
182
+ if (shouldIgnoreDirectory(entry.name, policy)) {
253
183
  continue;
254
184
  }
255
185
 
256
- const normalizedPath = absolutePath.replace(/\\/g, "/");
257
- if (
258
- normalizedPath.endsWith("/src/assets") ||
259
- normalizedPath.endsWith("/src/icons/gen") ||
260
- normalizedPath.includes("/public/_video") ||
261
- normalizedPath.includes("/public/_img")
262
- ) {
263
- continue;
264
- }
265
-
266
- files.push(...(await collectSourceFiles(absolutePath, extraExtensions)));
186
+ files.push(...(await collectSourceFiles(absolutePath, policy)));
267
187
  continue;
268
188
  }
269
189
 
270
- if (entry.isFile() && hasRelevantExtension(absolutePath, extraExtensions)) {
190
+ if (entry.isFile() && hasRelevantExtension(absolutePath, policy)) {
271
191
  files.push(absolutePath);
272
192
  }
273
193
  }
@@ -275,77 +195,48 @@ async function collectSourceFiles(
275
195
  return files;
276
196
  }
277
197
 
278
- function getRelativeSegments(filePath: string, workspaceRoot: string): string[] {
198
+ export function getRelativeSegments(filePath: string, workspaceRoot: string): string[] {
279
199
  return relative(workspaceRoot, filePath).replace(/\\/g, "/").split("/").filter(Boolean);
280
200
  }
281
201
 
282
- function detectWorkspaceKind(segments: string[]): CompassWorkspaceKind {
283
- if (segments[0] === "services") return "service";
284
- return segments[0] === "packages" ? "package" : "app";
202
+ function isWorkspaceDir(segment: string | undefined, policy: CompassPolicy): boolean {
203
+ return segment !== undefined && segment in policy.workspaceKinds;
285
204
  }
286
205
 
287
- function detectWorkspaceName(segments: string[]): string {
288
- if (segments[0] === "packages" && segments[1] === "os") {
289
- return segments[2] ?? "unknown";
290
- }
291
- return segments[1] ?? "unknown";
206
+ function detectWorkspaceKind(segments: string[], policy: CompassPolicy): CompassWorkspaceKind {
207
+ const first = segments[0];
208
+ return (first !== undefined && policy.workspaceKinds[first]) || "app";
292
209
  }
293
210
 
294
- function getWorkspaceRelativeSegments(segments: string[]): string[] {
295
- if (segments[0] === "packages" && segments[1] === "os") {
296
- return segments.slice(3);
211
+ function detectWorkspaceName(segments: string[], policy: CompassPolicy): string {
212
+ if (isWorkspaceDir(segments[0], policy)) {
213
+ return segments[1] ?? "unknown";
297
214
  }
298
- return segments.slice(2);
215
+ // Files outside any workspace dir (e.g. tools/*) report "root" rather than
216
+ // leaking a filename into the workspace-name slot.
217
+ return "root";
299
218
  }
300
219
 
301
- function detectLayer(relativePath: string): string {
302
- if (relativePath.startsWith("bin/")) return "bin";
303
- if (relativePath === "tools/kernel.config.ts") return "tool-config";
304
- if (relativePath.startsWith("tools/modules/")) return "tool-module";
305
- if (relativePath.startsWith("tools/runtime/")) return "tool-runtime";
306
- if (relativePath.startsWith("src/middleware/")) return "middleware";
307
- if (relativePath === "src/middleware.ts") return "middleware";
308
- if (relativePath.startsWith("src/pages/")) return "page";
309
- if (relativePath.startsWith("src/components/")) return "component";
310
- if (relativePath.startsWith("src/content/schemas/")) return "schema";
311
- if (relativePath.startsWith("src/content/")) return "content";
312
- if (relativePath.startsWith("src/styles/")) return "style";
313
- if (relativePath.startsWith("src/scripts/")) return "script";
314
- if (relativePath.startsWith("src/layouts/")) return "layout";
315
- if (relativePath.startsWith("src/utils/")) return "utility";
316
- if (relativePath.startsWith("src/configure/")) return "config";
317
- if (
318
- relativePath.startsWith("test/") ||
319
- relativePath.startsWith("src/tests/") ||
320
- relativePath.endsWith(".test.ts") ||
321
- relativePath.endsWith(".spec.ts")
322
- )
323
- return "test";
324
- if (relativePath.startsWith("src/")) return "source";
325
- if (relativePath.endsWith("SKILL.md")) return "skill";
326
- return "other";
327
- }
328
-
329
- function detectRiskClass(pathFromRoot: string, layer: string): CompassRiskClass {
330
- if (HIGH_RISK_EXACT_RELATIVE_PATHS.has(pathFromRoot)) {
331
- return "high";
332
- }
333
- if (pathFromRoot.includes("/src/scripts/layout-scroll/")) {
334
- return "high";
335
- }
336
- if (pathFromRoot.includes("/src/layouts/")) {
337
- return "high";
220
+ export function getWorkspaceRelativeSegments(segments: string[], policy: CompassPolicy): string[] {
221
+ if (isWorkspaceDir(segments[0], policy)) {
222
+ return segments.slice(2);
338
223
  }
339
- if (pathFromRoot.includes("/src/middleware/")) {
224
+ return segments;
225
+ }
226
+
227
+ export function detectLayer(relativePath: string, policy: CompassPolicy): string {
228
+ return policy.matchLayer(relativePath)?.layer ?? "other";
229
+ }
230
+
231
+ export function detectRiskClass(
232
+ pathFromRoot: string,
233
+ workspaceRelativePath: string,
234
+ policy: CompassPolicy,
235
+ ): CompassRiskClass {
236
+ if (policy.isHighRisk(pathFromRoot)) {
340
237
  return "high";
341
238
  }
342
- if (layer === "tool-runtime" || layer === "tool-config" || layer === "schema") {
343
- return "medium";
344
- }
345
- if (layer === "page" || layer === "component" || layer === "utility" || layer === "script") {
346
- return "medium";
347
- }
348
- return "low";
239
+ return policy.matchLayer(workspaceRelativePath)?.risk ?? "low";
349
240
  }
350
241
 
351
242
  function detectComplexity(source: string): CompassComplexity {
@@ -356,20 +247,11 @@ function detectComplexity(source: string): CompassComplexity {
356
247
  return "non-trivial";
357
248
  }
358
249
 
359
- function isSimpleSvgLogoComponent(segments: string[]): boolean {
360
- return (
361
- segments.length >= 4 &&
362
- segments[0] === "apps" &&
363
- segments[2] === "src" &&
364
- segments[3] === "components" &&
365
- segments[4] === "logo"
366
- );
367
- }
368
-
369
250
  function detectAuthoringStatus(
370
251
  segments: string[],
371
252
  relativePath: string,
372
253
  source: string,
254
+ policy: CompassPolicy,
373
255
  ): {
374
256
  authoringStatus: CompassAuthoringStatus;
375
257
  exclusionReason?: string;
@@ -381,24 +263,11 @@ function detectAuthoringStatus(
381
263
  };
382
264
  }
383
265
 
384
- if (relativePath.startsWith("src/icons/gen/")) {
266
+ const policyExclusion = policy.excludedReason(relativePath);
267
+ if (policyExclusion !== undefined) {
385
268
  return {
386
269
  authoringStatus: "excluded",
387
- exclusionReason: "generated-icon-tree",
388
- };
389
- }
390
-
391
- if (relativePath.startsWith("src/assets/")) {
392
- return {
393
- authoringStatus: "excluded",
394
- exclusionReason: "assets-directory",
395
- };
396
- }
397
-
398
- if (isSimpleSvgLogoComponent(segments)) {
399
- return {
400
- authoringStatus: "excluded",
401
- exclusionReason: "svg-component",
270
+ exclusionReason: policyExclusion,
402
271
  };
403
272
  }
404
273
 
@@ -416,20 +285,7 @@ function detectAuthoringStatus(
416
285
  };
417
286
  }
418
287
 
419
- if (relativePath.startsWith("src/templates/")) {
420
- return {
421
- authoringStatus: "excluded",
422
- exclusionReason: "template-source",
423
- };
424
- }
425
-
426
- if (
427
- relativePath.startsWith("test/") ||
428
- relativePath.endsWith(".test.ts") ||
429
- relativePath.endsWith(".test.js") ||
430
- relativePath.endsWith(".spec.ts") ||
431
- relativePath.endsWith(".spec.js")
432
- ) {
288
+ if (policy.isTestPath(relativePath)) {
433
289
  return {
434
290
  authoringStatus: "excluded",
435
291
  exclusionReason: "test-file",
@@ -443,7 +299,11 @@ function detectAuthoringStatus(
443
299
  };
444
300
  }
445
301
 
446
- if (relativePath.endsWith(".md") && !relativePath.endsWith("SKILL.md")) {
302
+ // Check the filename, not the workspace-relative path: files directly under
303
+ // a workspace dir (packages/AGENTS.md) get an empty workspace-relative path
304
+ // and would otherwise escape the markdown exclusion.
305
+ const fileName = segments[segments.length - 1] ?? relativePath;
306
+ if (fileName.endsWith(".md") && !fileName.endsWith("SKILL.md")) {
447
307
  return {
448
308
  authoringStatus: "excluded",
449
309
  exclusionReason: "non-skill-markdown",
@@ -642,9 +502,9 @@ export function deriveFileTokens(pathFromRoot: string, source: string): Set<stri
642
502
  return tokens;
643
503
  }
644
504
 
645
- function validateHistoryIds(historyIds: string[]): string | null {
505
+ function validateHistoryIds(historyIds: string[], policy: CompassPolicy): string | null {
646
506
  for (const token of historyIds) {
647
- if (!HISTORY_ID_RE.test(token)) {
507
+ if (!policy.idPatternFull.test(token)) {
648
508
  return `<history> contains a non-ID token: "${token}"`;
649
509
  }
650
510
  }
@@ -653,14 +513,12 @@ function validateHistoryIds(historyIds: string[]): string | null {
653
513
  }
654
514
  const lastSeenByNamespace = new Map<string, number>();
655
515
  for (const token of historyIds) {
656
- const parts = token.match(HISTORY_ID_PARTS_RE)!;
657
- const namespace = parts[1]!.replace(/-$/, "");
658
- const numeric = Number(parts[3]);
659
- const last = lastSeenByNamespace.get(namespace);
660
- if (last !== undefined && numeric <= last) {
661
- return `<history> IDs are not in per-namespace ascending numeric order (${token} after ${namespace}-${last})`;
516
+ const parts = parseGovernanceIdParts(token)!;
517
+ const last = lastSeenByNamespace.get(parts.namespace);
518
+ if (last !== undefined && parts.numeric <= last) {
519
+ return `<history> IDs are not in per-namespace ascending numeric order (${token} after ${parts.namespace}-${last})`;
662
520
  }
663
- lastSeenByNamespace.set(namespace, numeric);
521
+ lastSeenByNamespace.set(parts.namespace, parts.numeric);
664
522
  }
665
523
  return null;
666
524
  }
@@ -668,6 +526,7 @@ function validateHistoryIds(historyIds: string[]): string | null {
668
526
  export function evaluateV2Rules(
669
527
  entry: Pick<CompassInventoryEntry, "path" | "keyDecisionsRequired">,
670
528
  source: string,
529
+ policy: CompassPolicy,
671
530
  ): CompassV2Diagnostic[] {
672
531
  const parse = parseCompassBlocks(source);
673
532
  const diagnostics: CompassV2Diagnostic[] = [];
@@ -715,7 +574,7 @@ export function evaluateV2Rules(
715
574
  fix: "fix: compress the item to a single decision statement",
716
575
  });
717
576
  }
718
- if (GOVERNANCE_ID_PREFIX_RE.test(item)) {
577
+ if (policy.idPatternPrefix.test(item)) {
719
578
  diagnostics.push({
720
579
  ruleId: "COMPASS-KD-05",
721
580
  message:
@@ -734,7 +593,7 @@ export function evaluateV2Rules(
734
593
  }
735
594
 
736
595
  if (parse.purposeText.length > 0) {
737
- if (PURPOSE_BOILERPLATE_PATTERNS.some((pattern) => pattern.test(parse.purposeText))) {
596
+ if (policy.purposeBoilerplatePatterns.some((pattern) => pattern.test(parse.purposeText))) {
738
597
  diagnostics.push({
739
598
  ruleId: "COMPASS-PURPOSE-01",
740
599
  message: "<purpose> matches a boilerplate pattern",
@@ -767,7 +626,7 @@ export function evaluateV2Rules(
767
626
  });
768
627
  }
769
628
  for (const item of parse.changeSummaryItems) {
770
- if (!GOVERNANCE_ID_RE.test(item)) {
629
+ if (!policy.idPattern.test(item)) {
771
630
  diagnostics.push({
772
631
  ruleId: "COMPASS-CS-06",
773
632
  message: `CHANGE_SUMMARY item lacks a governance-ID reference: "${item.slice(0, 60)}"`,
@@ -776,7 +635,7 @@ export function evaluateV2Rules(
776
635
  }
777
636
  }
778
637
  if (parse.historyRaw !== null) {
779
- const historyError = validateHistoryIds(parse.historyIds);
638
+ const historyError = validateHistoryIds(parse.historyIds, policy);
780
639
  if (historyError) {
781
640
  diagnostics.push({
782
641
  ruleId: "COMPASS-CS-07",
@@ -839,11 +698,12 @@ export async function createCompassInventoryEntries(
839
698
  workspaceRoot: string,
840
699
  input: ForgeCommandInput,
841
700
  scanRoot?: string,
701
+ policy?: CompassPolicy,
842
702
  ): Promise<CompassInventoryEntry[]> {
843
- const roots = scanRoot ? [scanRoot] : resolveScanRoots(workspaceRoot, input);
844
- const extraExtensions = loadCompassExtensionsFromForgeYaml(workspaceRoot);
703
+ const resolvedPolicy = policy ?? resolveCompassPolicy(workspaceRoot);
704
+ const roots = scanRoot ? [scanRoot] : resolveScanRoots(workspaceRoot, input, resolvedPolicy);
845
705
  const files = (
846
- await Promise.all(roots.map((root) => collectSourceFiles(root, extraExtensions)))
706
+ await Promise.all(roots.map((root) => collectSourceFiles(root, resolvedPolicy)))
847
707
  ).flat();
848
708
  const entries: CompassInventoryEntry[] = [];
849
709
 
@@ -851,14 +711,17 @@ export async function createCompassInventoryEntries(
851
711
  const source = await readFile(filePath, "utf8");
852
712
  const pathFromRoot = relative(workspaceRoot, filePath).replace(/\\/g, "/");
853
713
  const segments = getRelativeSegments(filePath, workspaceRoot);
854
- const relativePathWithinWorkspace = getWorkspaceRelativeSegments(segments).join("/");
855
- const layer = detectLayer(relativePathWithinWorkspace);
856
- const riskClass = detectRiskClass(pathFromRoot, layer);
714
+ const relativePathWithinWorkspace = getWorkspaceRelativeSegments(segments, resolvedPolicy).join(
715
+ "/",
716
+ );
717
+ const layer = detectLayer(relativePathWithinWorkspace, resolvedPolicy);
718
+ const riskClass = detectRiskClass(pathFromRoot, relativePathWithinWorkspace, resolvedPolicy);
857
719
  const complexity = detectComplexity(source);
858
720
  const { authoringStatus, exclusionReason } = detectAuthoringStatus(
859
721
  segments,
860
722
  relativePathWithinWorkspace,
861
723
  source,
724
+ resolvedPolicy,
862
725
  );
863
726
  const requiredScaffolding = detectRequiredScaffolding(authoringStatus);
864
727
  const markup = detectMarkup(source);
@@ -869,8 +732,8 @@ export async function createCompassInventoryEntries(
869
732
  const nonEmptyLineCount = source.split(/\r?\n/).filter((line) => line.trim().length > 0).length;
870
733
  const candidate: CompassInventoryEntry = {
871
734
  path: pathFromRoot,
872
- workspaceKind: detectWorkspaceKind(segments),
873
- workspaceName: detectWorkspaceName(segments),
735
+ workspaceKind: detectWorkspaceKind(segments, resolvedPolicy),
736
+ workspaceName: detectWorkspaceName(segments, resolvedPolicy),
874
737
  layer,
875
738
  extension:
876
739
  segments.length > 0
@@ -0,0 +1,122 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>compass.migrate kernel handler (RFC-1097). Resolves scan root and
4
+ policy, enforces the dirty-tree refusal before any write, then delegates to
5
+ the migrateWorkspace codemod and returns the per-file action manifest.</purpose>
6
+ <non-goals>
7
+ <item>Do not implement transform logic — that lives in compass-migrate.ts.</item>
8
+ <item>Do not scaffold headers — no-header files are reported, not written.</item>
9
+ </non-goals>
10
+ </MODULE_CONTRACT>
11
+ <CHANGE_SUMMARY>
12
+ <item>RFC-1097: initial handler — dirty-tree refusal with --force, --files bypass, dry-run manifest.</item>
13
+ <item>RFC-1097: steps 1-4 — compass.migrate codemod
14
+
15
+ 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>
16
+ <item>RFC-1097: step 6 — compass.migrate codemod run
17
+
18
+ Mechanical v1 to v2 header migration across the workspace: 942 files rewritten — CHANGE_SUMMARY windows collapsed into history, forbidden v1 blocks stripped, KEY_DECISIONS seeded from @ai-invariant comments (5 files) or TODO placeholders (103 files), blocks reordered to canonical order.</item>
19
+ <item>RFC-1097: sweep — engine package clean
20
+
21
+ Sweep batch 4: 73 Compass headers on headerless engine files (certification, component-runtime, isolation, evolution, testing), real KEY_DECISIONS on 75 files (kernel, cache, dht, swim, gitmesh, runtime), ~80 purpose expansions (CONTRACT-02/PURPOSE-02), non-goals on 13 CONTRACT-03 files, CS-07 history literal fix repo-wide (253 files). Policy: .template.ts/.template.astro excludedPaths. engine package now 0 diagnostics.</item>
22
+ <item>RFC-1097: AC-4 banned literal in os/compass handlers
23
+
24
+ compass-migrate-handler hint used a consumer-specific run command — switched to generic 'pnpm exec forge run' convention. Reworded recorded CHANGE_SUMMARY items in 3 handlers to drop the consumer-specific literal. compass-policy AC-4 test green (65/65).</item>
25
+ </CHANGE_SUMMARY>
26
+ */
27
+
28
+ import { execFileSync } from "node:child_process";
29
+ import { migrateWorkspace, type MigrateResult } from "./compass-migrate.ts";
30
+ import { resolveCompassScanRoot } from "./resolve-scan-root.ts";
31
+ import { resolveCompassPolicy } from "../policy.ts";
32
+ import type {
33
+ ForgeCommandInput,
34
+ ForgeCommandResult,
35
+ ForgeRuntimeContext,
36
+ } from "../../../src/types.ts";
37
+
38
+ const DIRTY_LIST_CAP = 20;
39
+
40
+ function listDirtyPaths(repoRoot: string): string[] | null {
41
+ try {
42
+ const out = execFileSync("git", ["-C", repoRoot, "status", "--porcelain"], {
43
+ encoding: "utf8",
44
+ stdio: ["ignore", "pipe", "ignore"],
45
+ timeout: 15000,
46
+ });
47
+ return out
48
+ .split("\n")
49
+ .map((line) => line.trim())
50
+ .filter(Boolean)
51
+ .map((line) => line.slice(3));
52
+ } catch {
53
+ // Not a git repository — nothing to protect, skip the check.
54
+ return null;
55
+ }
56
+ }
57
+
58
+ export async function runCompassMigrate(
59
+ input: ForgeCommandInput,
60
+ context: ForgeRuntimeContext,
61
+ ): Promise<ForgeCommandResult<MigrateResult>> {
62
+ const scanRoot = resolveCompassScanRoot(input, context);
63
+ const baseRoot = scanRoot ?? context.workspaceRoot;
64
+ const policy = resolveCompassPolicy(baseRoot, context.forgeRoot);
65
+
66
+ const dryRun = context.dryRun || input.flags["dry-run"] === true;
67
+ const force = input.flags["force"] === true;
68
+
69
+ const rawFiles = input.flags["files"];
70
+ const files = (
71
+ Array.isArray(rawFiles)
72
+ ? rawFiles.filter((entry): entry is string => typeof entry === "string")
73
+ : typeof rawFiles === "string"
74
+ ? [rawFiles]
75
+ : []
76
+ )
77
+ .flatMap((entry) => entry.split(","))
78
+ .map((entry) => entry.trim())
79
+ .filter(Boolean);
80
+ const explicitFiles = files.length > 0 ? files : undefined;
81
+
82
+ // Dirty-tree refusal guards the write only — a dry-run writes nothing and
83
+ // stays useful as a pre-commit preview on a dirty tree.
84
+ if (!dryRun && !force) {
85
+ const dirty = listDirtyPaths(baseRoot);
86
+ if (dirty && dirty.length > 0) {
87
+ const shown = dirty.slice(0, DIRTY_LIST_CAP);
88
+ const suffix =
89
+ dirty.length > shown.length ? ` … and ${dirty.length - shown.length} more` : "";
90
+ context.logger.error(
91
+ `[compass.migrate] refusing to run on a dirty git tree (${dirty.length} uncommitted path(s)):\n${shown.map((p) => ` ${p}`).join("\n")}${suffix}\nCommit or stash first, or pass --force to override.`,
92
+ );
93
+ return { exitCode: 1, summary: "dirty git tree — refused" };
94
+ }
95
+ }
96
+
97
+ const result = await migrateWorkspace(context.workspaceRoot, input, scanRoot, policy, {
98
+ files: explicitFiles,
99
+ dryRun,
100
+ });
101
+
102
+ const counts = new Map<string, number>();
103
+ for (const file of result.files) {
104
+ for (const action of file.actions) {
105
+ counts.set(action, (counts.get(action) ?? 0) + 1);
106
+ }
107
+ }
108
+ const tally = [...counts.entries()].map(([k, v]) => `${k}=${v}`).join(", ");
109
+
110
+ return {
111
+ data: result,
112
+ exitCode: 0,
113
+ summary: `[compass.migrate] scanned=${result.scanned} ${tally}${dryRun ? " (dry-run)" : ""}`,
114
+ nextSteps: [
115
+ {
116
+ action:
117
+ "Run `pnpm exec forge run compass.inventory --json` to list todo/purpose-flagged files for the agent sweep.",
118
+ kind: "optional",
119
+ },
120
+ ],
121
+ };
122
+ }