forge-workflow 0.0.9 → 0.1.0-beta.2

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 (479) hide show
  1. package/.claude/rules/{greptile-review-process.md → review-process.md} +56 -41
  2. package/.claude/scripts/{greptile-resolve.sh → review-resolve.sh} +13 -3
  3. package/.cursor/rules/permissions-guidance.mdc +2 -2
  4. package/.forge/hooks/check-tdd.js +3 -0
  5. package/.forge/hooks/forge-native-hook.js +245 -0
  6. package/.forge/protected-paths.yaml +157 -0
  7. package/AGENTS.md +151 -61
  8. package/CHANGELOG.md +681 -0
  9. package/CLAUDE.md +9 -106
  10. package/QUICKSTART.md +171 -0
  11. package/README.md +271 -363
  12. package/bin/forge-cmd.js +120 -9
  13. package/bin/forge-preflight.js +26 -5
  14. package/bin/forge.js +466 -489
  15. package/docs/INDEX.md +93 -0
  16. package/docs/PROJECT_DESIGN.md +685 -0
  17. package/docs/architecture/index.md +66 -0
  18. package/docs/architecture/notes/README.md +35 -0
  19. package/docs/architecture/subsystems/README.md +46 -0
  20. package/docs/{TOOLCHAIN.md → forge/TOOLCHAIN.md} +56 -47
  21. package/docs/forge/VALIDATION.md +82 -0
  22. package/docs/{AGENT_INSTALL_PROMPT.md → guides/AGENT_INSTALL_PROMPT.md} +3 -3
  23. package/docs/guides/BEADS_GITHUB_SYNC.md +32 -0
  24. package/docs/{ENHANCED_ONBOARDING.md → guides/ENHANCED_ONBOARDING.md} +16 -12
  25. package/docs/guides/GREPTILE_SETUP.md +46 -0
  26. package/docs/guides/MANUAL_REVIEW_GUIDE.md +58 -0
  27. package/docs/guides/MIGRATION.md +56 -0
  28. package/docs/guides/SETUP.md +118 -0
  29. package/docs/guides/SUPPORT.md +185 -0
  30. package/docs/guides/WORKFLOW_TEMPLATES.md +74 -0
  31. package/docs/guides/memory-backends.md +183 -0
  32. package/docs/reference/ADAPTERS.md +128 -0
  33. package/docs/reference/AGENT_SKILL_PARITY.md +175 -0
  34. package/docs/reference/COMMANDS.md +205 -0
  35. package/docs/reference/DECISION_DRIFT_GUARDS.md +97 -0
  36. package/docs/{EXAMPLES.md → reference/EXAMPLES.md} +7 -5
  37. package/docs/reference/FORGE_KERNEL_STORAGE_MODEL.md +135 -0
  38. package/docs/reference/HERMES_INTEGRATION.md +118 -0
  39. package/docs/reference/INSIGHTS_RECAP.md +63 -0
  40. package/docs/reference/INSTALL.md +164 -0
  41. package/docs/reference/KERNEL_TAXONOMY_VALIDATION.md +161 -0
  42. package/docs/reference/PROTECTED_PATH_MANIFEST.md +25 -0
  43. package/docs/reference/RELEASE.md +68 -0
  44. package/docs/reference/RESEARCH_TEMPLATE.md +292 -0
  45. package/docs/{ROADMAP.md → reference/ROADMAP.md} +12 -9
  46. package/docs/reference/SKILLS.md +35 -0
  47. package/docs/reference/STATUS_BOARD.md +80 -0
  48. package/docs/reference/TEMPLATES.md +106 -0
  49. package/docs/reference/TOOLCHAIN.md +658 -0
  50. package/docs/reference/VALIDATION.md +82 -0
  51. package/docs/reference/agent-permissions.md +169 -0
  52. package/docs/reference/beads-to-kernel-migration-ux.md +61 -0
  53. package/docs/reference/control-plane-guarantees.md +125 -0
  54. package/docs/reference/dependency-chain.md +331 -0
  55. package/docs/reference/forge-kernel-issue-command-contract.md +161 -0
  56. package/docs/reference/forge-kernel-schema.md +72 -0
  57. package/docs/reference/kernel-conflict-evaluators.md +27 -0
  58. package/docs/reference/patch-md-format.md +77 -0
  59. package/docs/reference/protected-state-surfaces.md +59 -0
  60. package/docs/reference/shepherd.md +115 -0
  61. package/docs/reference/superpowers-analysis.md +320 -0
  62. package/docs/reference/superpowers-integration-options.md +404 -0
  63. package/docs/reference/test-environment.md +519 -0
  64. package/docs/reference/upgrade-safety.md +59 -0
  65. package/lefthook.yml +18 -0
  66. package/lib/adapter-cli.js +307 -0
  67. package/lib/adapters/beads-issue-adapter.js +127 -0
  68. package/lib/adapters/beads-kernel-compat.js +1042 -0
  69. package/lib/adapters/greptile-review-adapter.js +141 -0
  70. package/lib/adapters/kernel-issue-adapter.js +101 -0
  71. package/lib/adapters/pr-state-adapter.js +484 -0
  72. package/lib/adoption-profiles.js +126 -0
  73. package/lib/agents/README.md +2 -6
  74. package/lib/agents/claude.plugin.json +3 -8
  75. package/lib/agents/codex.plugin.json +9 -1
  76. package/lib/agents/cursor.plugin.json +2 -6
  77. package/lib/agents/hermes.plugin.json +22 -0
  78. package/lib/agents-config.js +39 -1236
  79. package/lib/audit-evidence.js +282 -0
  80. package/lib/beads-setup.js +225 -28
  81. package/lib/beads-sync-scaffold.js +36 -107
  82. package/lib/codex-skills.js +51 -1
  83. package/lib/commands/_issue.js +744 -70
  84. package/lib/commands/_manifest.js +91 -0
  85. package/lib/commands/_registry.js +85 -34
  86. package/lib/commands/_resolve-command-opts.js +261 -0
  87. package/lib/commands/_serve-security.js +270 -0
  88. package/lib/commands/adapter.js +12 -0
  89. package/lib/commands/add.js +118 -0
  90. package/lib/commands/audit.js +70 -0
  91. package/lib/commands/blocked.js +5 -0
  92. package/lib/commands/board.js +64 -0
  93. package/lib/commands/claim.js +21 -2
  94. package/lib/commands/claims.js +7 -0
  95. package/lib/commands/clean.js +485 -75
  96. package/lib/commands/close.js +2 -2
  97. package/lib/commands/comment.js +5 -0
  98. package/lib/commands/control.js +148 -0
  99. package/lib/commands/create.js +2 -2
  100. package/lib/commands/dev.js +185 -7
  101. package/lib/commands/doc-gate.js +336 -0
  102. package/lib/commands/doctor.js +156 -0
  103. package/lib/commands/explain.js +15 -0
  104. package/lib/commands/export.js +237 -0
  105. package/lib/commands/gate.js +192 -0
  106. package/lib/commands/hooks.js +242 -0
  107. package/lib/commands/inbox.js +118 -0
  108. package/lib/commands/init.js +598 -0
  109. package/lib/commands/insights.js +79 -0
  110. package/lib/commands/issue.js +12 -1
  111. package/lib/commands/issues.js +66 -0
  112. package/lib/commands/lint.js +5 -0
  113. package/lib/commands/list.js +2 -2
  114. package/lib/commands/merge.js +312 -0
  115. package/lib/commands/migrate.js +523 -0
  116. package/lib/commands/new.js +12 -0
  117. package/lib/commands/options.js +241 -0
  118. package/lib/commands/orient.js +13 -0
  119. package/lib/commands/orphans.js +5 -0
  120. package/lib/commands/patch.js +67 -0
  121. package/lib/commands/plan.js +436 -24
  122. package/lib/commands/preflight.js +211 -0
  123. package/lib/commands/prime.js +13 -0
  124. package/lib/commands/push.js +69 -2
  125. package/lib/commands/ready.js +2 -2
  126. package/lib/commands/recall.js +116 -0
  127. package/lib/commands/recap.js +61 -0
  128. package/lib/commands/recommend.js +22 -2
  129. package/lib/commands/release.js +91 -0
  130. package/lib/commands/remember.js +74 -0
  131. package/lib/commands/role.js +99 -0
  132. package/lib/commands/serve.js +581 -0
  133. package/lib/commands/setup.js +851 -979
  134. package/lib/commands/shepherd.js +436 -0
  135. package/lib/commands/ship.js +23 -1
  136. package/lib/commands/show.js +2 -2
  137. package/lib/commands/stage.js +192 -0
  138. package/lib/commands/stale.js +5 -0
  139. package/lib/commands/status.js +329 -11
  140. package/lib/commands/sync.js +34 -46
  141. package/lib/commands/team.js +15 -2
  142. package/lib/commands/test.js +58 -7
  143. package/lib/commands/update.js +2 -2
  144. package/lib/commands/upgrade.js +47 -0
  145. package/lib/commands/validate.js +56 -25
  146. package/lib/commands/worktree.js +308 -128
  147. package/lib/config-writer.js +202 -0
  148. package/lib/control-plane.js +236 -0
  149. package/lib/core/runtime-graph.js +946 -0
  150. package/lib/dep-guard/keyword-ripple.js +184 -0
  151. package/lib/deprecated-sync-cleanup.js +362 -0
  152. package/lib/detect-agent.js +2 -28
  153. package/lib/detect-worktree.js +42 -17
  154. package/lib/doc-gate/declaration.js +177 -0
  155. package/lib/doc-gate/detect.js +289 -0
  156. package/lib/doc-gate/gate.js +375 -0
  157. package/lib/doc-gate/okf-config.js +128 -0
  158. package/lib/doc-gate/okf.js +429 -0
  159. package/lib/docs-command.js +1161 -6
  160. package/lib/forge-issues.js +697 -0
  161. package/lib/forge-lock.js +262 -0
  162. package/lib/gate-events.js +193 -0
  163. package/lib/global-flags.js +74 -0
  164. package/lib/greptile-match.js +7 -63
  165. package/lib/harness-capability-matrix.js +380 -0
  166. package/lib/hook-global-installer.js +347 -0
  167. package/lib/hook-renderer.js +451 -0
  168. package/lib/inbox.js +391 -0
  169. package/lib/insights.js +397 -0
  170. package/lib/issue-adapter.js +156 -0
  171. package/lib/issue-backend.js +145 -0
  172. package/lib/issue-render.js +220 -0
  173. package/lib/issue-sync/authority.js +100 -0
  174. package/lib/issue-sync/github-pull.js +184 -0
  175. package/lib/issue-sync/import-primitives.js +98 -0
  176. package/lib/issue-sync/legacy-link-bridge.js +436 -0
  177. package/lib/issue-sync/link-store.js +292 -0
  178. package/lib/issue-sync/project-github.js +123 -0
  179. package/lib/issue-sync/reconcile.js +195 -0
  180. package/lib/issue-sync/schema.js +126 -0
  181. package/lib/kernel/backing-issue.js +305 -0
  182. package/lib/kernel/broker.js +1218 -0
  183. package/lib/kernel/cli-broker-factory.js +130 -0
  184. package/lib/kernel/conflict-signal.js +82 -0
  185. package/lib/kernel/evaluators.js +195 -0
  186. package/lib/kernel/fs-class.js +495 -0
  187. package/lib/kernel/issue-command-contract.js +559 -0
  188. package/lib/kernel/issue-id-resolver.js +186 -0
  189. package/lib/kernel/lease-enforcer.js +158 -0
  190. package/lib/kernel/migrations.js +333 -0
  191. package/lib/kernel/planning-buckets-schema.js +109 -0
  192. package/lib/kernel/projection-jsonl-writer.js +450 -0
  193. package/lib/kernel/readiness-model.js +329 -0
  194. package/lib/kernel/schema.js +356 -0
  195. package/lib/kernel/sqlite-driver.js +2504 -0
  196. package/lib/kernel/taxonomy-validator.js +394 -0
  197. package/lib/lefthook-check.js +8 -4
  198. package/lib/lefthook-wiring.js +413 -0
  199. package/lib/mcp-config-renderer.js +288 -0
  200. package/lib/memory/graphiti-mcp.js +106 -0
  201. package/lib/memory/router.js +387 -0
  202. package/lib/memory/typed-api.js +102 -0
  203. package/lib/memory-digest.js +195 -0
  204. package/lib/merge-rules.js +395 -0
  205. package/lib/migrate-dry-run.js +466 -0
  206. package/lib/orientation.js +863 -0
  207. package/lib/package-manager-remediation.js +103 -0
  208. package/lib/package-root.js +381 -0
  209. package/lib/patch-intent.js +890 -0
  210. package/lib/plugin-catalog.js +3 -4
  211. package/lib/plugin-manager.js +0 -5
  212. package/lib/pr-bundle.js +186 -0
  213. package/lib/pr-monitor/differ.js +195 -0
  214. package/lib/pr-monitor/events.js +0 -0
  215. package/lib/pr-monitor/gather.js +124 -0
  216. package/lib/pr-monitor/journal.js +299 -0
  217. package/lib/pr-monitor/monitor.js +146 -0
  218. package/lib/pr-monitor/render-sticky.js +157 -0
  219. package/lib/pr-monitor/watch-lifecycle.js +95 -0
  220. package/lib/pr-monitor/watch.js +247 -0
  221. package/lib/pr-pull.js +1273 -0
  222. package/lib/pr-shepherd.js +494 -0
  223. package/lib/pr-state-validator.js +59 -0
  224. package/lib/preflight/gates.js +237 -0
  225. package/lib/preflight/runner.js +116 -0
  226. package/lib/project-discovery.js +0 -53
  227. package/lib/project-memory.js +166 -0
  228. package/lib/protected-path-manifest.js +281 -0
  229. package/lib/protected-state-surfaces.js +387 -0
  230. package/lib/release-readiness.js +2089 -0
  231. package/lib/reset.js +59 -45
  232. package/lib/review-adapter.js +68 -0
  233. package/lib/rules-sync.js +260 -0
  234. package/lib/runtime-health.js +332 -23
  235. package/lib/safety-config-renderer.js +268 -0
  236. package/lib/setup-action-log.js +1 -7
  237. package/lib/setup.js +27 -65
  238. package/lib/shell-utils.js +76 -6
  239. package/lib/skills-sync.js +330 -0
  240. package/lib/smart-status/conflicts.js +205 -0
  241. package/lib/smart-status/scoring.js +191 -0
  242. package/lib/status/beads-snapshot.js +145 -0
  243. package/lib/status/presenter.js +216 -0
  244. package/lib/status/snapshot.js +186 -0
  245. package/lib/sync-backend.js +202 -0
  246. package/lib/untrusted-content.js +52 -0
  247. package/lib/upgrade-safety.js +199 -0
  248. package/lib/workflow/enforce-stage.js +298 -47
  249. package/lib/workflow/stage-transition.js +115 -0
  250. package/lib/workflow/stages.js +30 -6
  251. package/lib/workflow/state-manager.js +159 -14
  252. package/lib/workflow/state.js +23 -1
  253. package/lib/workflow-profiles.js +17 -5
  254. package/package.json +46 -36
  255. package/rules/documentation.md +19 -0
  256. package/rules/kernel-tracking.md +26 -0
  257. package/rules/security.md +22 -0
  258. package/rules/tdd.md +20 -0
  259. package/rules/workflow.md +27 -0
  260. package/scripts/auto-backing-issue.js +47 -0
  261. package/scripts/beads-context.sh +165 -22
  262. package/scripts/beads-migrate-to-dolt.sh +7 -0
  263. package/scripts/beads-upgrade-smoke.sh +284 -0
  264. package/scripts/behavioral-judge.sh +115 -11
  265. package/scripts/benchmark.js +349 -63
  266. package/scripts/bootstrap-windows-tools.sh +78 -0
  267. package/scripts/branch-protection.js +2 -3
  268. package/scripts/check-agents.js +34 -137
  269. package/scripts/commitlint.js +3 -1
  270. package/scripts/conflict-detect.sh +3 -0
  271. package/scripts/dep-guard-analyze.js +52 -17
  272. package/scripts/dep-guard-keyword-ripple.js +29 -0
  273. package/scripts/dep-guard-render-review.js +86 -0
  274. package/scripts/dep-guard.sh +64 -232
  275. package/scripts/file-index.sh +3 -0
  276. package/scripts/forge-team/lib/claim.sh +34 -18
  277. package/scripts/forge-team/lib/dashboard.sh +61 -86
  278. package/scripts/forge-team/lib/epic.sh +99 -263
  279. package/scripts/forge-team/lib/hooks.sh +26 -28
  280. package/scripts/forge-team/lib/identity.sh +4 -4
  281. package/scripts/forge-team/lib/sync-github.sh +144 -47
  282. package/scripts/forge-team/lib/verify.sh +93 -83
  283. package/scripts/forge-team/lib/workload.sh +41 -65
  284. package/scripts/forge-team/tests/claim.test.sh +25 -19
  285. package/scripts/forge-team/tests/dashboard.test.sh +31 -46
  286. package/scripts/forge-team/tests/epic.test.sh +52 -71
  287. package/scripts/forge-team/tests/hooks.test.sh +38 -50
  288. package/scripts/forge-team/tests/identity.test.sh +3 -3
  289. package/scripts/forge-team/tests/integration.test.sh +44 -66
  290. package/scripts/forge-team/tests/sync-github.test.sh +183 -79
  291. package/scripts/forge-team/tests/verify.test.sh +37 -46
  292. package/scripts/forge-team/tests/workflow-integration.test.sh +4 -4
  293. package/scripts/forge-team/tests/workload.test.sh +32 -66
  294. package/scripts/gen-command-manifest.js +153 -0
  295. package/scripts/gen-embedded-assets.mjs +129 -0
  296. package/scripts/install.ps1 +139 -0
  297. package/scripts/install.sh +268 -0
  298. package/scripts/lib/beads-migrate-to-dolt.mjs +503 -0
  299. package/scripts/lib/release-asset.mjs +84 -0
  300. package/scripts/parity-check.mjs +145 -0
  301. package/scripts/parity-check.test.mjs +58 -0
  302. package/scripts/pin-agentic-workflow-images.js +112 -0
  303. package/scripts/pr-coordinator.sh +3 -0
  304. package/scripts/preflight-sonar.eslint.config.mjs +44 -0
  305. package/scripts/preflight.sh +108 -0
  306. package/scripts/protected-state-check.js +104 -0
  307. package/scripts/smart-status-score.js +31 -0
  308. package/scripts/smart-status-sessions.js +51 -0
  309. package/scripts/smart-status.sh +117 -369
  310. package/scripts/spikes/config-race-bench.js +111 -0
  311. package/scripts/spikes/harness-capability-matrix.js +13 -0
  312. package/scripts/spikes/patch-anchor-stability-bench.js +125 -0
  313. package/scripts/spikes/protected-path-manifest.js +20 -0
  314. package/scripts/spikes/skill-auto-invoke-parity.js +292 -0
  315. package/scripts/sync-agent-skills.js +62 -0
  316. package/scripts/sync-agentic-workflow.js +48 -0
  317. package/scripts/sync-utils.sh +3 -0
  318. package/scripts/test-ci-shard.js +251 -0
  319. package/scripts/test-dashboard.js +188 -52
  320. package/scripts/test-full-suite.js +186 -0
  321. package/scripts/test-profile.js +278 -0
  322. package/scripts/test.js +302 -28
  323. package/scripts/validate.js +143 -0
  324. package/scripts/validate.sh +18 -1
  325. package/skills/claim-safety/SKILL.md +102 -0
  326. package/skills/claim-safety/evals/evals.json +46 -0
  327. package/{.github/prompts/dev.prompt.md → skills/dev/SKILL.md} +46 -52
  328. package/skills/dev/evals/evals.json +50 -0
  329. package/skills/hermes-forge/SKILL.md +185 -0
  330. package/skills/hermes-forge/evals/evals.json +46 -0
  331. package/skills/issue-basics/SKILL.md +111 -0
  332. package/skills/issue-basics/evals/evals.json +46 -0
  333. package/skills/kernel/SKILL.md +166 -0
  334. package/skills/kernel/evals/evals.json +50 -0
  335. package/skills/memory/SKILL.md +102 -0
  336. package/skills/parallel-deep-research/SKILL.md +14 -11
  337. package/skills/parallel-deep-research/evals/evals.json +11 -27
  338. package/{.github/prompts/plan.prompt.md → skills/plan/SKILL.md} +134 -159
  339. package/skills/plan/evals/evals.json +42 -0
  340. package/skills/research/SKILL.md +195 -0
  341. package/skills/research/evals/evals.json +42 -0
  342. package/{.github/prompts/review.prompt.md → skills/review/SKILL.md} +98 -62
  343. package/skills/review/evals/evals.json +42 -0
  344. package/skills/rollback/SKILL.md +110 -0
  345. package/skills/rollback/evals/evals.json +46 -0
  346. package/skills/rollback/references/methods.md +204 -0
  347. package/{.cursor/commands/rollback.md → skills/rollback/references/workflow-integration.md} +10 -284
  348. package/skills/shepherd/SKILL.md +66 -0
  349. package/skills/shepherd/evals/evals.json +42 -0
  350. package/skills/ship/SKILL.md +251 -0
  351. package/skills/ship/evals/evals.json +42 -0
  352. package/skills/smith/SKILL.md +142 -0
  353. package/skills/smith/evals/evals.json +46 -0
  354. package/skills/smith/references/autonomy-and-gates.md +94 -0
  355. package/{.github/prompts/sonarcloud.prompt.md → skills/sonarcloud/SKILL.md} +14 -3
  356. package/skills/sonarcloud/evals/evals.json +46 -0
  357. package/skills/sonarcloud-analysis/SKILL.md +18 -13
  358. package/skills/sonarcloud-analysis/evals/evals.json +11 -15
  359. package/skills/status/SKILL.md +102 -0
  360. package/skills/status/evals/evals.json +50 -0
  361. package/skills/triage-ready/SKILL.md +121 -0
  362. package/skills/triage-ready/evals/evals.json +42 -0
  363. package/{.github/prompts/validate.prompt.md → skills/validate/SKILL.md} +52 -29
  364. package/skills/validate/evals/evals.json +42 -0
  365. package/skills/verify/SKILL.md +299 -0
  366. package/skills/verify/evals/evals.json +50 -0
  367. package/.claude/commands/dev.md +0 -345
  368. package/.claude/commands/plan.md +0 -566
  369. package/.claude/commands/premerge.md +0 -186
  370. package/.claude/commands/research.md +0 -42
  371. package/.claude/commands/review.md +0 -451
  372. package/.claude/commands/rollback.md +0 -721
  373. package/.claude/commands/ship.md +0 -213
  374. package/.claude/commands/sonarcloud.md +0 -152
  375. package/.claude/commands/status.md +0 -90
  376. package/.claude/commands/validate.md +0 -288
  377. package/.claude/commands/verify.md +0 -269
  378. package/.claude/rules/workflow.md +0 -121
  379. package/.cline/workflows/dev.md +0 -342
  380. package/.cline/workflows/plan.md +0 -563
  381. package/.cline/workflows/premerge.md +0 -183
  382. package/.cline/workflows/research.md +0 -39
  383. package/.cline/workflows/review.md +0 -448
  384. package/.cline/workflows/rollback.md +0 -718
  385. package/.cline/workflows/ship.md +0 -210
  386. package/.cline/workflows/sonarcloud.md +0 -146
  387. package/.cline/workflows/status.md +0 -87
  388. package/.cline/workflows/validate.md +0 -285
  389. package/.cline/workflows/verify.md +0 -266
  390. package/.codex/config.toml +0 -11
  391. package/.codex/skills/dev/SKILL.md +0 -345
  392. package/.codex/skills/plan/SKILL.md +0 -566
  393. package/.codex/skills/premerge/SKILL.md +0 -186
  394. package/.codex/skills/research/SKILL.md +0 -42
  395. package/.codex/skills/review/SKILL.md +0 -451
  396. package/.codex/skills/rollback/SKILL.md +0 -721
  397. package/.codex/skills/ship/SKILL.md +0 -213
  398. package/.codex/skills/sonarcloud/SKILL.md +0 -149
  399. package/.codex/skills/status/SKILL.md +0 -90
  400. package/.codex/skills/validate/SKILL.md +0 -288
  401. package/.codex/skills/verify/SKILL.md +0 -269
  402. package/.cursor/commands/dev.md +0 -342
  403. package/.cursor/commands/plan.md +0 -563
  404. package/.cursor/commands/premerge.md +0 -183
  405. package/.cursor/commands/research.md +0 -39
  406. package/.cursor/commands/review.md +0 -448
  407. package/.cursor/commands/ship.md +0 -210
  408. package/.cursor/commands/sonarcloud.md +0 -146
  409. package/.cursor/commands/status.md +0 -87
  410. package/.cursor/commands/validate.md +0 -285
  411. package/.cursor/commands/verify.md +0 -266
  412. package/.cursorrules +0 -149
  413. package/.github/prompts/premerge.prompt.md +0 -188
  414. package/.github/prompts/research.prompt.md +0 -44
  415. package/.github/prompts/rollback.prompt.md +0 -723
  416. package/.github/prompts/ship.prompt.md +0 -215
  417. package/.github/prompts/status.prompt.md +0 -92
  418. package/.github/prompts/verify.prompt.md +0 -271
  419. package/.github/workflows/beads-to-github.yml +0 -56
  420. package/.github/workflows/github-to-beads.yml +0 -97
  421. package/.kilocode/workflows/dev.md +0 -346
  422. package/.kilocode/workflows/plan.md +0 -567
  423. package/.kilocode/workflows/premerge.md +0 -187
  424. package/.kilocode/workflows/research.md +0 -43
  425. package/.kilocode/workflows/review.md +0 -452
  426. package/.kilocode/workflows/rollback.md +0 -722
  427. package/.kilocode/workflows/ship.md +0 -214
  428. package/.kilocode/workflows/sonarcloud.md +0 -150
  429. package/.kilocode/workflows/status.md +0 -91
  430. package/.kilocode/workflows/validate.md +0 -289
  431. package/.kilocode/workflows/verify.md +0 -270
  432. package/.opencode/commands/dev.md +0 -345
  433. package/.opencode/commands/plan.md +0 -566
  434. package/.opencode/commands/premerge.md +0 -186
  435. package/.opencode/commands/research.md +0 -42
  436. package/.opencode/commands/review.md +0 -451
  437. package/.opencode/commands/rollback.md +0 -721
  438. package/.opencode/commands/ship.md +0 -213
  439. package/.opencode/commands/sonarcloud.md +0 -149
  440. package/.opencode/commands/status.md +0 -90
  441. package/.opencode/commands/validate.md +0 -288
  442. package/.opencode/commands/verify.md +0 -269
  443. package/.roo/commands/dev.md +0 -346
  444. package/.roo/commands/plan.md +0 -567
  445. package/.roo/commands/premerge.md +0 -187
  446. package/.roo/commands/research.md +0 -43
  447. package/.roo/commands/review.md +0 -452
  448. package/.roo/commands/rollback.md +0 -722
  449. package/.roo/commands/ship.md +0 -214
  450. package/.roo/commands/sonarcloud.md +0 -150
  451. package/.roo/commands/status.md +0 -91
  452. package/.roo/commands/validate.md +0 -289
  453. package/.roo/commands/verify.md +0 -270
  454. package/docs/BEADS_GITHUB_SYNC.md +0 -255
  455. package/docs/GREPTILE_SETUP.md +0 -400
  456. package/docs/MANUAL_REVIEW_GUIDE.md +0 -106
  457. package/docs/SETUP.md +0 -663
  458. package/docs/VALIDATION.md +0 -363
  459. package/lib/agents/cline.plugin.json +0 -29
  460. package/lib/agents/copilot.plugin.json +0 -24
  461. package/lib/agents/kilocode.plugin.json +0 -22
  462. package/lib/agents/opencode.plugin.json +0 -23
  463. package/lib/agents/roo.plugin.json +0 -30
  464. package/lib/beads-health-check.js +0 -143
  465. package/lib/commands/commands-reset.js +0 -147
  466. package/opencode.json +0 -67
  467. package/scripts/beads-context.test.js +0 -567
  468. package/scripts/github-beads-sync/comment.mjs +0 -64
  469. package/scripts/github-beads-sync/config.mjs +0 -148
  470. package/scripts/github-beads-sync/github-api.mjs +0 -131
  471. package/scripts/github-beads-sync/index.mjs +0 -332
  472. package/scripts/github-beads-sync/label-mapper.mjs +0 -54
  473. package/scripts/github-beads-sync/mapping.mjs +0 -78
  474. package/scripts/github-beads-sync/reverse-sync-cli.mjs +0 -31
  475. package/scripts/github-beads-sync/reverse-sync.mjs +0 -138
  476. package/scripts/github-beads-sync/run-bd.mjs +0 -161
  477. package/scripts/github-beads-sync/sanitize.mjs +0 -121
  478. package/scripts/github-beads-sync.config.json +0 -26
  479. package/scripts/sync-commands.js +0 -600
@@ -0,0 +1,946 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const YAML = require('yaml');
6
+
7
+ const RUNTIME_GRAPH_SCHEMA_VERSION = '0.0.17';
8
+ const CONFIG_SOURCE = '.forge/config.yaml';
9
+
10
+ const RUNTIME_GRAPH_SCHEMA = {
11
+ $schema: 'https://json-schema.org/draft/2020-12/schema',
12
+ $id: 'https://forge.local/schemas/runtime-graph-0.0.17.json',
13
+ title: 'Forge Runtime Graph Artifact',
14
+ type: 'object',
15
+ required: ['schemaVersion', 'kind', 'schema', 'graph'],
16
+ properties: {
17
+ schemaVersion: { const: RUNTIME_GRAPH_SCHEMA_VERSION },
18
+ kind: { const: 'forge.runtimeGraph' },
19
+ schema: {
20
+ type: 'object',
21
+ required: ['$schema', '$id'],
22
+ },
23
+ graph: {
24
+ type: 'object',
25
+ required: [
26
+ 'primitives',
27
+ 'phases',
28
+ 'actions',
29
+ 'artifacts',
30
+ 'evaluatorRegions',
31
+ 'gates',
32
+ 'evidence',
33
+ 'rails',
34
+ 'adapters',
35
+ 'edges',
36
+ ],
37
+ },
38
+ },
39
+ };
40
+
41
+ function withRuntimeState(primitive, { enabled = true, locked = false, configSource = 'package-defaults' } = {}) {
42
+ return {
43
+ ...primitive,
44
+ enabled,
45
+ disabled: enabled === false,
46
+ locked,
47
+ configSource,
48
+ };
49
+ }
50
+
51
+ function Phase({ id, label, command, actions, artifacts, gates, evidence, enabled = true, locked = false }) {
52
+ return withRuntimeState({ id, label, command, actions, artifacts, gates, evidence }, { enabled, locked });
53
+ }
54
+
55
+ function Action({ id, kind, command, label, reads = [], writes = [], evidence = [], enabled = true, locked = false }) {
56
+ return withRuntimeState({ id, kind, command, label, reads, writes, evidence }, { enabled, locked });
57
+ }
58
+
59
+ function Artifact({ id, kind, path, description }) {
60
+ return { id, kind, path, description };
61
+ }
62
+
63
+ function EvaluatorRegion({ id, label, phase, description }) {
64
+ return { id, label, phase, description };
65
+ }
66
+
67
+ function Gate({ id, phase, label, requires, enabled = true, locked = false }) {
68
+ return withRuntimeState({ id, phase, label, requires }, { enabled, locked });
69
+ }
70
+
71
+ function Evidence({ id, kind, source, description }) {
72
+ return { id, kind, source, description };
73
+ }
74
+
75
+ function Rail({ id, key, label, description, locked = true, enabled = true }) {
76
+ return withRuntimeState({ id, key, label, description, layer: 'L1' }, { enabled, locked });
77
+ }
78
+
79
+ function Adapter({ id, kind, label, config = {}, enabled = true }) {
80
+ return withRuntimeState({ id, kind, label, config }, { enabled });
81
+ }
82
+
83
+ function PlanningSubSkill({ id, label, inputs, outputs, evidence, gate, enabled = true }) {
84
+ return withRuntimeState({ id, label, inputs, outputs, evidence, gate }, { enabled });
85
+ }
86
+
87
+ function Role({ id, role, skill, ideology, configSource = 'package-defaults' }) {
88
+ const primitive = { id, role, skill, configSource };
89
+ if (ideology !== undefined) primitive.ideology = ideology;
90
+ return primitive;
91
+ }
92
+
93
+ // Role -> default skill binding. Role names are the canonical stage ids
94
+ // (lib/workflow/stages.js STAGE_IDS); each ships bound to its like-named skill.
95
+ // This is the closed set of KNOWN roles; the bound skill name is open-world
96
+ // (resolved via .skills/ > skills/), never checked against PLAN_SUBSKILL_IDS.
97
+ // Additive to the graph — see docs/work/2026-07-04-kernel-native-skills/
98
+ // extensibility-architecture.md.
99
+ const ROLE_DEFINITIONS = [
100
+ { role: 'plan', skill: 'plan' },
101
+ { role: 'dev', skill: 'dev' },
102
+ { role: 'validate', skill: 'validate' },
103
+ { role: 'ship', skill: 'ship' },
104
+ { role: 'review', skill: 'review' },
105
+ { role: 'verify', skill: 'verify' },
106
+ ];
107
+
108
+ const ROLE_IDS = new Set(ROLE_DEFINITIONS.map(definition => definition.role));
109
+
110
+ function roleFromDefinition(definition) {
111
+ return Role({ id: `role.${definition.role}`, role: definition.role, skill: definition.skill });
112
+ }
113
+
114
+ const PLAN_SUBSKILL_DEFINITIONS = [
115
+ {
116
+ id: 'plan.intent_capture',
117
+ label: 'Intent capture',
118
+ inputs: ['feature request', 'constraints'],
119
+ outputs: ['purpose', 'success criteria', 'out of scope'],
120
+ actionLabel: 'Capture planning intent',
121
+ reads: [],
122
+ writes: ['artifact.design-doc'],
123
+ },
124
+ {
125
+ id: 'plan.parallel_research',
126
+ label: 'Parallel research',
127
+ inputs: ['intent capture'],
128
+ outputs: ['technical approach options', 'risk notes'],
129
+ actionLabel: 'Research planning options',
130
+ reads: ['artifact.design-doc'],
131
+ writes: ['artifact.design-doc'],
132
+ },
133
+ {
134
+ id: 'plan.parallel_critics',
135
+ label: 'Parallel critics',
136
+ inputs: ['approach options'],
137
+ outputs: ['spec critique', 'risk critique', 'test critique'],
138
+ actionLabel: 'Critique planning options',
139
+ reads: ['artifact.design-doc'],
140
+ writes: ['artifact.design-doc'],
141
+ },
142
+ {
143
+ id: 'plan.synthesis',
144
+ label: 'Synthesis',
145
+ inputs: ['research', 'critic output'],
146
+ outputs: ['selected approach', 'tradeoffs'],
147
+ actionLabel: 'Synthesize selected plan',
148
+ reads: ['artifact.design-doc'],
149
+ writes: ['artifact.design-doc'],
150
+ },
151
+ {
152
+ id: 'plan.final_lock',
153
+ label: 'Final lock',
154
+ inputs: ['selected approach'],
155
+ outputs: ['design doc', 'task list'],
156
+ actionLabel: 'Lock planning artifacts',
157
+ reads: ['artifact.design-doc'],
158
+ writes: ['artifact.design-doc', 'artifact.task-list'],
159
+ },
160
+ ];
161
+
162
+ const PLAN_SUBSKILL_IDS = new Set(PLAN_SUBSKILL_DEFINITIONS.map(definition => definition.id));
163
+
164
+ function planningSubSkillFromDefinition(definition) {
165
+ return PlanningSubSkill({
166
+ id: definition.id,
167
+ label: definition.label,
168
+ inputs: definition.inputs,
169
+ outputs: definition.outputs,
170
+ evidence: ['evidence.command-docs'],
171
+ gate: 'gate.plan-exit',
172
+ });
173
+ }
174
+
175
+ function planningActionFromDefinition(definition) {
176
+ return Action({
177
+ id: `action.${definition.id}`,
178
+ kind: 'plan-subskill',
179
+ command: definition.id,
180
+ label: definition.actionLabel,
181
+ reads: definition.reads,
182
+ writes: definition.writes,
183
+ evidence: ['evidence.command-docs'],
184
+ });
185
+ }
186
+
187
+ const RESOLVED_RUNTIME_GRAPH = {
188
+ id: 'forge.runtimeGraph.currentCommandFlow',
189
+ version: RUNTIME_GRAPH_SCHEMA_VERSION,
190
+ description: 'Resolved graph for the current /plan -> /dev -> /validate -> /ship command flow.',
191
+ primitives: {
192
+ Phase: 'phases',
193
+ Action: 'actions',
194
+ PlanningSubSkill: 'planning.subSkills',
195
+ Artifact: 'artifacts',
196
+ EvaluatorRegion: 'evaluatorRegions',
197
+ Gate: 'gates',
198
+ Evidence: 'evidence',
199
+ Rail: 'rails',
200
+ Adapter: 'adapters',
201
+ },
202
+ phases: [
203
+ Phase({
204
+ id: 'plan',
205
+ label: '/plan',
206
+ command: 'plan',
207
+ actions: PLAN_SUBSKILL_DEFINITIONS.map(definition => `action.${definition.id}`),
208
+ artifacts: ['artifact.design-doc', 'artifact.task-list'],
209
+ gates: ['gate.plan-exit'],
210
+ evidence: ['evidence.command-docs'],
211
+ }),
212
+ Phase({
213
+ id: 'dev',
214
+ label: '/dev',
215
+ command: 'dev',
216
+ actions: ['action.dev.command'],
217
+ artifacts: ['artifact.changed-files'],
218
+ gates: ['gate.dev-exit'],
219
+ evidence: ['evidence.tdd-tests'],
220
+ }),
221
+ Phase({
222
+ id: 'validate',
223
+ label: '/validate',
224
+ command: 'validate',
225
+ actions: ['action.validate.command'],
226
+ artifacts: ['artifact.validation-output'],
227
+ gates: ['gate.validate-exit'],
228
+ evidence: ['evidence.validation-output'],
229
+ }),
230
+ Phase({
231
+ id: 'ship',
232
+ label: '/ship',
233
+ command: 'ship',
234
+ actions: ['action.ship.command'],
235
+ artifacts: ['artifact.pull-request'],
236
+ gates: ['gate.ship-entry'],
237
+ evidence: ['evidence.dry-run-report'],
238
+ }),
239
+ ],
240
+ planning: {
241
+ template: {
242
+ mode: 'full',
243
+ convergenceThreshold: 0.75,
244
+ criticSet: ['spec', 'risk', 'test'],
245
+ partialInvocation: {
246
+ only: [],
247
+ skip: [],
248
+ },
249
+ configSource: 'package-defaults',
250
+ },
251
+ subSkills: PLAN_SUBSKILL_DEFINITIONS.map(planningSubSkillFromDefinition),
252
+ },
253
+ roles: ROLE_DEFINITIONS.map(roleFromDefinition),
254
+ actions: [
255
+ Action({
256
+ id: 'action.plan.command',
257
+ kind: 'command',
258
+ command: 'plan',
259
+ label: 'Capture design intent and task list',
260
+ writes: ['artifact.design-doc', 'artifact.task-list'],
261
+ evidence: ['evidence.command-docs'],
262
+ }),
263
+ ...PLAN_SUBSKILL_DEFINITIONS.map(planningActionFromDefinition),
264
+ Action({
265
+ id: 'action.dev.command',
266
+ kind: 'command',
267
+ command: 'dev',
268
+ label: 'Implement tasks with TDD evidence',
269
+ reads: ['artifact.design-doc', 'artifact.task-list'],
270
+ writes: ['artifact.changed-files'],
271
+ evidence: ['evidence.tdd-tests'],
272
+ }),
273
+ Action({
274
+ id: 'action.validate.command',
275
+ kind: 'command',
276
+ command: 'validate',
277
+ label: 'Run type, lint, test, and security checks',
278
+ reads: ['artifact.changed-files'],
279
+ writes: ['artifact.validation-output'],
280
+ evidence: ['evidence.validation-output'],
281
+ }),
282
+ Action({
283
+ id: 'action.ship.command',
284
+ kind: 'command',
285
+ command: 'ship',
286
+ label: 'Push branch and open a pull request',
287
+ reads: ['artifact.validation-output'],
288
+ writes: ['artifact.pull-request'],
289
+ evidence: ['evidence.dry-run-report'],
290
+ }),
291
+ ],
292
+ artifacts: [
293
+ Artifact({
294
+ id: 'artifact.design-doc',
295
+ kind: 'markdown',
296
+ path: 'docs/work/YYYY-MM-DD-<slug>/plan.md',
297
+ description: 'Design intent, constraints, and research.',
298
+ }),
299
+ Artifact({
300
+ id: 'artifact.task-list',
301
+ kind: 'markdown',
302
+ path: 'docs/work/YYYY-MM-DD-<slug>/tasks.md',
303
+ description: 'TDD task list consumed by /dev.',
304
+ }),
305
+ Artifact({
306
+ id: 'artifact.changed-files',
307
+ kind: 'git-diff',
308
+ path: '<worktree>',
309
+ description: 'Source and test changes produced during /dev.',
310
+ }),
311
+ Artifact({
312
+ id: 'artifact.validation-output',
313
+ kind: 'terminal-output',
314
+ path: '<session>',
315
+ description: 'Fresh validation command output.',
316
+ }),
317
+ Artifact({
318
+ id: 'artifact.pull-request',
319
+ kind: 'github-pr',
320
+ path: '<pr-url>',
321
+ description: 'Pull request created by /ship.',
322
+ }),
323
+ ],
324
+ evaluatorRegions: [
325
+ EvaluatorRegion({
326
+ id: 'evaluator.plan-entry',
327
+ label: 'Plan entry and exit gates',
328
+ phase: 'plan',
329
+ description: 'Checks worktree isolation, design artifacts, and task handoff.',
330
+ }),
331
+ EvaluatorRegion({
332
+ id: 'evaluator.tdd-loop',
333
+ label: 'TDD implementation loop',
334
+ phase: 'dev',
335
+ description: 'Checks RED/GREEN/REFACTOR evidence and task completion.',
336
+ }),
337
+ EvaluatorRegion({
338
+ id: 'evaluator.validation-suite',
339
+ label: 'Validation suite',
340
+ phase: 'validate',
341
+ description: 'Checks type, lint, tests, and security scan evidence.',
342
+ }),
343
+ EvaluatorRegion({
344
+ id: 'evaluator.ship-readiness',
345
+ label: 'Ship readiness',
346
+ phase: 'ship',
347
+ description: 'Checks branch freshness, PR body, and issue linkage.',
348
+ }),
349
+ ],
350
+ gates: [
351
+ Gate({
352
+ id: 'gate.plan-exit',
353
+ phase: 'plan',
354
+ label: '/plan exit',
355
+ requires: ['artifact.design-doc', 'artifact.task-list', 'evidence.command-docs'],
356
+ }),
357
+ Gate({
358
+ id: 'gate.dev-exit',
359
+ phase: 'dev',
360
+ label: '/dev exit',
361
+ requires: ['artifact.changed-files', 'evidence.tdd-tests'],
362
+ }),
363
+ Gate({
364
+ id: 'gate.validate-exit',
365
+ phase: 'validate',
366
+ label: '/validate exit',
367
+ requires: ['artifact.validation-output', 'evidence.validation-output'],
368
+ }),
369
+ Gate({
370
+ id: 'gate.ship-entry',
371
+ phase: 'ship',
372
+ label: '/ship entry',
373
+ requires: ['artifact.validation-output'],
374
+ }),
375
+ // Human gates — satisfied by a `gate.approved` kernel EVENT on the issue, not by
376
+ // an artifact (see lib/gate-events.js + docs/work/2026-07-04-kernel-native-skills/
377
+ // decisions.md). Additive + backward-compatible: they widen the known-gate set so
378
+ // `forge gate approve|reject|status|check` recognize them and each is toggleable
379
+ // via `workflow.gates.<id>.enabled`. `requires: []` because approval, not evidence,
380
+ // is the exit condition. Default three: intent · plan-approval · merge.
381
+ Gate({
382
+ id: 'gate.intent',
383
+ phase: 'plan',
384
+ label: 'Intent approval',
385
+ requires: [],
386
+ }),
387
+ Gate({
388
+ id: 'gate.plan-approval',
389
+ phase: 'plan',
390
+ label: 'Plan approval',
391
+ requires: [],
392
+ }),
393
+ Gate({
394
+ id: 'gate.merge',
395
+ phase: 'ship',
396
+ label: 'Merge approval',
397
+ requires: [],
398
+ }),
399
+ // gate.issue_verify — check-after-write verification on kernel issue mutations
400
+ // (create|update|close|claim|comment) at the lib/commands/_issue.js boundary:
401
+ // after a successful write, re-read through the same broker and confirm the
402
+ // intended delta landed (verified/mismatches in the envelope; warn-only — the
403
+ // write's ok is never overturned). Default ON and UNLOCKED, toggled via
404
+ // `forge gate disable gate.issue_verify` through the same workflow.gates
405
+ // surface as rail.kernel_tracking. Justified by two proven "ok:true lied"
406
+ // bugs: 145d9ad1 (close --reason/closed_at dropped in the projection) and
407
+ // d71a824b (idempotent claim replay telling a losing agent it won). No phase:
408
+ // it guards the issue-command boundary, not a workflow stage exit.
409
+ // `requires: []` — the read-back itself, not evidence, is the check.
410
+ Gate({
411
+ id: 'gate.issue_verify',
412
+ label: 'Issue write verification (check-after-write)',
413
+ requires: [],
414
+ }),
415
+ ],
416
+ evidence: [
417
+ Evidence({
418
+ id: 'evidence.command-docs',
419
+ kind: 'checked-in-doc',
420
+ source: 'skills/*/SKILL.md',
421
+ description: 'Skill docs define the compatibility surface.',
422
+ }),
423
+ Evidence({
424
+ id: 'evidence.tdd-tests',
425
+ kind: 'test-output',
426
+ source: 'bun test',
427
+ description: 'Task-level failing then passing test output.',
428
+ }),
429
+ Evidence({
430
+ id: 'evidence.validation-output',
431
+ kind: 'validation-output',
432
+ source: 'bun run typecheck; bun run lint; bun test; bun audit',
433
+ description: 'Fresh validation evidence for the branch.',
434
+ }),
435
+ Evidence({
436
+ id: 'evidence.dry-run-report',
437
+ kind: 'dry-run-output',
438
+ source: 'forge migrate --dry-run',
439
+ description: 'Resolved graph can be printed without side effects.',
440
+ }),
441
+ ],
442
+ // Rails are declared as a data table and mapped through Rail() so each entry
443
+ // stays a single line (Rail defaults locked: true; kernel_tracking is the one
444
+ // UNLOCKED, default-ON rail — it ingrains "file every issue/idea/bug/decision
445
+ // to the kernel immediately; nothing discussed goes missing", and a maintainer
446
+ // may opt out via `forge gate disable rail.kernel_tracking`, consumed by the
447
+ // resolver's rail-aware gate loop over workflow.gates.<id>.enabled).
448
+ rails: [
449
+ { key: 'tdd_intent', label: 'TDD intent evidence', description: 'Source changes require test intent and TDD evidence.' },
450
+ { key: 'secret_scan', label: 'Secret scan', description: 'Validation must not knowingly ship secrets.' },
451
+ { key: 'branch_protection', label: 'Branch protection', description: 'Ship through reviewed branches instead of direct protected-branch edits.' },
452
+ { key: 'signed_commits', label: 'Signed commits', description: 'Preserve commit provenance requirements where configured.' },
453
+ { key: 'schema_integrity', label: 'Schema integrity', description: 'Keep runtime graph and config schemas internally consistent.' },
454
+ { key: 'kernel_tracking', label: 'Kernel issue tracking', description: 'Every issue, idea, bug, and decision discussed is filed to the Forge Kernel.', locked: false },
455
+ ].map(def => Rail({ id: `rail.${def.key}`, ...def })),
456
+ adapters: [
457
+ Adapter({
458
+ id: 'adapter.issue',
459
+ kind: 'issue',
460
+ label: 'Issue tracking adapter',
461
+ config: { primary: 'beads', mirrors: ['github'] },
462
+ }),
463
+ Adapter({
464
+ id: 'adapter.harness',
465
+ kind: 'harness',
466
+ label: 'Agent harness adapter',
467
+ config: { targets: ['codex', 'claude', 'cursor'] },
468
+ }),
469
+ ],
470
+ protectedPaths: [],
471
+ edges: [
472
+ { from: 'phase.plan', to: 'phase.dev', reason: 'design and task list feed implementation' },
473
+ { from: 'phase.dev', to: 'phase.validate', reason: 'implementation feeds validation' },
474
+ { from: 'phase.validate', to: 'phase.ship', reason: 'validated branch can be shipped' },
475
+ ],
476
+ };
477
+
478
+ function cloneJson(value) {
479
+ return JSON.parse(JSON.stringify(value));
480
+ }
481
+
482
+ function getConfigPath(projectRoot = process.cwd()) {
483
+ return path.join(projectRoot, '.forge', 'config.yaml');
484
+ }
485
+
486
+ function loadRuntimeGraphConfig({ projectRoot = process.cwd() } = {}) {
487
+ const configPath = getConfigPath(projectRoot);
488
+ if (!fs.existsSync(configPath)) {
489
+ return { config: {}, configPath, exists: false, errors: [] };
490
+ }
491
+
492
+ try {
493
+ const rawConfig = fs.readFileSync(configPath, 'utf8');
494
+ const parsed = rawConfig.trim() === '' ? {} : YAML.parse(rawConfig);
495
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
496
+ return {
497
+ config: {},
498
+ configPath,
499
+ exists: true,
500
+ errors: [{
501
+ code: 'CONFIG_ROOT_INVALID',
502
+ message: `${CONFIG_SOURCE} root must be an object.`,
503
+ }],
504
+ };
505
+ }
506
+ return {
507
+ config: parsed,
508
+ configPath,
509
+ exists: true,
510
+ errors: [],
511
+ };
512
+ } catch (err) {
513
+ return {
514
+ config: {},
515
+ configPath,
516
+ exists: true,
517
+ errors: [{
518
+ code: 'CONFIG_PARSE_ERROR',
519
+ message: `Invalid ${CONFIG_SOURCE}: ${err.message}`,
520
+ }],
521
+ };
522
+ }
523
+ }
524
+
525
+ function markConfigured(primitive, options) {
526
+ Object.assign(primitive, withRuntimeState(primitive, {
527
+ enabled: options.enabled,
528
+ locked: primitive.locked === true,
529
+ configSource: CONFIG_SOURCE,
530
+ }));
531
+ }
532
+
533
+ function readEnabledOption(options, errors, typeName, id) {
534
+ if (!options || typeof options !== 'object' || Array.isArray(options)) {
535
+ errors.push({
536
+ code: 'INVALID_PRIMITIVE_CONFIG',
537
+ message: `${typeName} '${id}' config must be an object.`,
538
+ });
539
+ return { present: false, valid: false, value: undefined };
540
+ }
541
+ if (!Object.hasOwn(options, 'enabled')) {
542
+ return { present: false, valid: true, value: undefined };
543
+ }
544
+ if (typeof options.enabled !== 'boolean') {
545
+ errors.push({
546
+ code: 'INVALID_ENABLED_VALUE',
547
+ message: `${typeName} '${id}' enabled must be a boolean.`,
548
+ });
549
+ return { present: true, valid: false, value: undefined };
550
+ }
551
+ return { present: true, valid: true, value: options.enabled };
552
+ }
553
+
554
+ function readConfigSection(parent, key, errors, path) {
555
+ if (!Object.hasOwn(parent, key)) {
556
+ return {};
557
+ }
558
+ const value = parent[key];
559
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
560
+ errors.push({
561
+ code: 'INVALID_CONFIG_SECTION',
562
+ message: `${path} must be an object.`,
563
+ });
564
+ return {};
565
+ }
566
+ return value;
567
+ }
568
+
569
+ function applyEnabledConfig(collection, id, options, errors, typeName) {
570
+ const primitive = collection.find(candidate => candidate.id === id);
571
+ if (!primitive) {
572
+ errors.push({
573
+ code: `UNKNOWN_${typeName.toUpperCase()}`,
574
+ message: `Unknown ${typeName} '${id}' in ${CONFIG_SOURCE}.`,
575
+ });
576
+ return;
577
+ }
578
+
579
+ const enabled = readEnabledOption(options, errors, typeName, id);
580
+ if (!enabled.valid) return;
581
+ if (enabled.present) {
582
+ if (enabled.value === false && primitive.locked === true) {
583
+ errors.push({
584
+ code: 'LOCKED_PRIMITIVE_DISABLED',
585
+ message: `Cannot disable locked ${typeName} '${id}'.`,
586
+ });
587
+ return;
588
+ }
589
+ markConfigured(primitive, { enabled: enabled.value });
590
+ }
591
+ }
592
+
593
+ function applyRailConfig(graph, config, errors) {
594
+ const rails = readConfigSection(config, 'rails', errors, 'rails');
595
+ for (const [key, options] of Object.entries(rails)) {
596
+ const rail = graph.rails.find(candidate => candidate.key === key || candidate.id === key);
597
+ if (!rail) {
598
+ errors.push({
599
+ code: 'UNKNOWN_RAIL',
600
+ message: `Unknown rail '${key}' in ${CONFIG_SOURCE}.`,
601
+ });
602
+ continue;
603
+ }
604
+ const enabled = readEnabledOption(options, errors, 'rail', key);
605
+ if (!enabled.valid) {
606
+ continue;
607
+ }
608
+ if (enabled.value === false) {
609
+ errors.push({
610
+ code: 'LOCKED_L1_RAIL_DISABLED',
611
+ message: `Cannot disable locked L1 rail '${key}'.`,
612
+ });
613
+ continue;
614
+ }
615
+ if (options && Object.keys(options).length > 0) {
616
+ rail.configSource = CONFIG_SOURCE;
617
+ }
618
+ }
619
+ }
620
+
621
+ function applyWorkflowConfig(graph, config, errors) {
622
+ const workflow = readConfigSection(config, 'workflow', errors, 'workflow');
623
+ const phases = readConfigSection(workflow, 'phases', errors, 'workflow.phases');
624
+ for (const [id, options] of Object.entries(phases)) {
625
+ applyEnabledConfig(graph.phases, id, options, errors, 'stage');
626
+ }
627
+
628
+ // The `workflow.gates.<id>.enabled` surface (driven by `forge gate enable|disable`)
629
+ // governs gates AND unlocked toggleable rails (e.g. rail.kernel_tracking): the id
630
+ // namespaces are disjoint (gate.* vs rail.*), so a single combined lookup routes each
631
+ // id to its primitive. applyEnabledConfig still honors each primitive's `locked` flag,
632
+ // so a locked L1 rail cannot be disabled through this surface either.
633
+ const gates = readConfigSection(workflow, 'gates', errors, 'workflow.gates');
634
+ const toggleable = [...graph.gates, ...graph.rails];
635
+ for (const [id, options] of Object.entries(gates)) {
636
+ applyEnabledConfig(toggleable, id, options, errors, 'gate');
637
+ }
638
+ }
639
+
640
+ function applyAdapterConfig(graph, config, errors) {
641
+ const adapters = readConfigSection(config, 'adapters', errors, 'adapters');
642
+ for (const [kind, options] of Object.entries(adapters)) {
643
+ const adapter = graph.adapters.find(candidate => candidate.kind === kind || candidate.id === kind);
644
+ if (!adapter) {
645
+ errors.push({
646
+ code: 'UNKNOWN_ADAPTER',
647
+ message: `Unknown adapter '${kind}' in ${CONFIG_SOURCE}.`,
648
+ });
649
+ continue;
650
+ }
651
+ if (!options || typeof options !== 'object' || Array.isArray(options)) {
652
+ errors.push({
653
+ code: 'INVALID_ADAPTER_CONFIG',
654
+ message: `adapter '${kind}' config must be an object.`,
655
+ });
656
+ continue;
657
+ }
658
+ const enabled = readEnabledOption(options, errors, 'adapter', kind);
659
+ if (!enabled.valid) {
660
+ continue;
661
+ }
662
+ const nextConfig = { ...options };
663
+ delete nextConfig.enabled;
664
+ if (enabled.present) {
665
+ markConfigured(adapter, { enabled: enabled.value });
666
+ }
667
+ adapter.config = { ...adapter.config };
668
+ Object.assign(adapter.config, nextConfig);
669
+ if (enabled.present || Object.keys(nextConfig).length > 0) {
670
+ adapter.configSource = CONFIG_SOURCE;
671
+ }
672
+ }
673
+ }
674
+
675
+ function readRoleStringField(options, field, role, errors) {
676
+ if (!Object.hasOwn(options, field)) {
677
+ return { present: false, value: undefined };
678
+ }
679
+ const value = options[field];
680
+ if (typeof value !== 'string' || value.trim() === '') {
681
+ errors.push({
682
+ code: `INVALID_ROLE_${field.toUpperCase()}`,
683
+ message: `role '${role}' ${field} must be a non-empty string.`,
684
+ });
685
+ return { present: true, value: undefined };
686
+ }
687
+ return { present: true, value: value.trim() };
688
+ }
689
+
690
+ // Reader for the `roles` section (sibling to `workflow.gates`). Role names are
691
+ // the KNOWN closed set (ROLE_IDS); the bound `skill` / `ideology` names are
692
+ // OPEN-WORLD — validated only as non-empty strings here, never against
693
+ // PLAN_SUBSKILL_IDS (that closed enum stays scoped to
694
+ // planning.template.partialInvocation). Skill EXISTENCE is enforced at write
695
+ // time by the `forge role` verb, not mid-run.
696
+ function applyRoleConfig(graph, config, errors) {
697
+ const roles = readConfigSection(config, 'roles', errors, 'roles');
698
+ for (const [role, options] of Object.entries(roles)) {
699
+ const entry = graph.roles.find(candidate => candidate.role === role || candidate.id === role);
700
+ if (!entry) {
701
+ errors.push({
702
+ code: 'UNKNOWN_ROLE',
703
+ message: `Unknown role '${role}' in ${CONFIG_SOURCE}.`,
704
+ });
705
+ continue;
706
+ }
707
+ if (!options || typeof options !== 'object' || Array.isArray(options)) {
708
+ errors.push({
709
+ code: 'INVALID_ROLE_CONFIG',
710
+ message: `role '${role}' config must be an object.`,
711
+ });
712
+ continue;
713
+ }
714
+
715
+ let changed = false;
716
+ const skill = readRoleStringField(options, 'skill', role, errors);
717
+ if (skill.present && skill.value !== undefined) {
718
+ entry.skill = skill.value;
719
+ changed = true;
720
+ }
721
+ const ideology = readRoleStringField(options, 'ideology', role, errors);
722
+ if (ideology.present && ideology.value !== undefined) {
723
+ entry.ideology = ideology.value;
724
+ changed = true;
725
+ }
726
+ if (changed) {
727
+ entry.configSource = CONFIG_SOURCE;
728
+ }
729
+ }
730
+ }
731
+
732
+ function assertStringList(value, errors, code, message) {
733
+ if (!Array.isArray(value)) {
734
+ errors.push({ code, message });
735
+ return false;
736
+ }
737
+
738
+ let valid = true;
739
+ for (const item of value) {
740
+ if (typeof item !== 'string' || item.trim() === '') {
741
+ errors.push({ code, message });
742
+ valid = false;
743
+ break;
744
+ }
745
+ }
746
+ return valid;
747
+ }
748
+
749
+ function validatePlanSubSkillList(value, errors, path) {
750
+ if (!assertStringList(
751
+ value,
752
+ errors,
753
+ 'INVALID_PLAN_SUBSKILL_LIST',
754
+ `${path} must be a list of planning sub-skill IDs.`
755
+ )) {
756
+ return undefined;
757
+ }
758
+
759
+ const normalized = value.map(item => item.trim());
760
+ let hasUnknown = false;
761
+ for (const id of normalized) {
762
+ if (!PLAN_SUBSKILL_IDS.has(id)) {
763
+ hasUnknown = true;
764
+ errors.push({
765
+ code: 'UNKNOWN_PLAN_SUBSKILL',
766
+ message: `Unknown planning sub-skill '${id}' in ${CONFIG_SOURCE}.`,
767
+ });
768
+ }
769
+ }
770
+ if (hasUnknown) {
771
+ return undefined;
772
+ }
773
+ return normalized;
774
+ }
775
+
776
+ function applyPlanningMode(template, nextTemplate, errors) {
777
+ if (!Object.hasOwn(template, 'mode')) return;
778
+ if (!['full', 'partial'].includes(template.mode)) {
779
+ errors.push({
780
+ code: 'INVALID_PLANNING_MODE',
781
+ message: 'planning.template.mode must be full or partial.',
782
+ });
783
+ return;
784
+ }
785
+ nextTemplate.mode = template.mode;
786
+ }
787
+
788
+ function applyConvergenceThreshold(template, nextTemplate, errors) {
789
+ if (!Object.hasOwn(template, 'convergenceThreshold')) return;
790
+ if (!Number.isFinite(template.convergenceThreshold)
791
+ || template.convergenceThreshold < 0
792
+ || template.convergenceThreshold > 1) {
793
+ errors.push({
794
+ code: 'INVALID_CONVERGENCE_THRESHOLD',
795
+ message: 'planning.template.convergenceThreshold must be a number from 0 to 1.',
796
+ });
797
+ return;
798
+ }
799
+ nextTemplate.convergenceThreshold = template.convergenceThreshold;
800
+ }
801
+
802
+ function applyCriticSet(template, nextTemplate, errors) {
803
+ if (!Object.hasOwn(template, 'criticSet')) return;
804
+ if (assertStringList(template.criticSet, errors, 'INVALID_CRITIC', 'planning.template.criticSet must contain non-empty strings.')) {
805
+ nextTemplate.criticSet = template.criticSet.map(item => item.trim());
806
+ }
807
+ }
808
+
809
+ function applyPartialInvocation(template, nextTemplate, errors) {
810
+ if (!Object.hasOwn(template, 'partialInvocation')) return;
811
+ const partial = readConfigSection(template, 'partialInvocation', errors, 'planning.template.partialInvocation');
812
+ if (Object.hasOwn(partial, 'only')) {
813
+ const only = validatePlanSubSkillList(partial.only, errors, 'planning.template.partialInvocation.only');
814
+ if (only) nextTemplate.partialInvocation.only = only;
815
+ }
816
+ if (Object.hasOwn(partial, 'skip')) {
817
+ const skip = validatePlanSubSkillList(partial.skip, errors, 'planning.template.partialInvocation.skip');
818
+ if (skip) nextTemplate.partialInvocation.skip = skip;
819
+ }
820
+ }
821
+
822
+ function applyPlanningConfig(graph, config, errors) {
823
+ const planning = readConfigSection(config, 'planning', errors, 'planning');
824
+ const template = readConfigSection(planning, 'template', errors, 'planning.template');
825
+ if (Object.keys(template).length === 0) {
826
+ return;
827
+ }
828
+
829
+ const nextTemplate = { ...graph.planning.template, partialInvocation: { ...graph.planning.template.partialInvocation } };
830
+ applyPlanningMode(template, nextTemplate, errors);
831
+ applyConvergenceThreshold(template, nextTemplate, errors);
832
+ applyCriticSet(template, nextTemplate, errors);
833
+ applyPartialInvocation(template, nextTemplate, errors);
834
+ nextTemplate.configSource = CONFIG_SOURCE;
835
+ graph.planning.template = nextTemplate;
836
+ }
837
+
838
+ function validateProtectedPaths(config, errors) {
839
+ if (!Object.hasOwn(config, 'protectedPaths')) {
840
+ return [];
841
+ }
842
+ if (!Array.isArray(config.protectedPaths)) {
843
+ errors.push({
844
+ code: 'PROTECTED_PATHS_INVALID',
845
+ message: 'protectedPaths must be a list of path patterns.',
846
+ });
847
+ return [];
848
+ }
849
+
850
+ const protectedPaths = [];
851
+ for (const [index, entry] of config.protectedPaths.entries()) {
852
+ if (typeof entry !== 'string' || entry.trim() === '') {
853
+ errors.push({
854
+ code: 'PROTECTED_PATH_INVALID',
855
+ message: `protectedPaths[${index}] must be a non-empty string.`,
856
+ });
857
+ continue;
858
+ }
859
+ const pattern = entry.trim();
860
+ if (['*', '**', '**/*', '.', './', '/'].includes(pattern)) {
861
+ errors.push({
862
+ code: 'PROTECTED_PATH_TOO_BROAD',
863
+ message: `protectedPaths[${index}] is too broad: ${pattern}`,
864
+ });
865
+ continue;
866
+ }
867
+ protectedPaths.push(pattern);
868
+ }
869
+ return protectedPaths;
870
+ }
871
+
872
+ function resolveRuntimeGraph({ projectRoot = process.cwd(), throwOnError = true } = {}) {
873
+ const graph = cloneJson(RESOLVED_RUNTIME_GRAPH);
874
+ const loaded = loadRuntimeGraphConfig({ projectRoot });
875
+ const errors = [...loaded.errors];
876
+
877
+ if (loaded.errors.length === 0) {
878
+ applyRailConfig(graph, loaded.config, errors);
879
+ applyWorkflowConfig(graph, loaded.config, errors);
880
+ applyAdapterConfig(graph, loaded.config, errors);
881
+ applyRoleConfig(graph, loaded.config, errors);
882
+ applyPlanningConfig(graph, loaded.config, errors);
883
+ graph.protectedPaths = validateProtectedPaths(loaded.config, errors);
884
+ }
885
+
886
+ graph.config = {
887
+ path: CONFIG_SOURCE,
888
+ loaded: loaded.exists,
889
+ errors,
890
+ };
891
+
892
+ if (throwOnError && errors.length > 0) {
893
+ throw new Error(errors.map(error => error.message).join('\n'));
894
+ }
895
+
896
+ return { graph, errors };
897
+ }
898
+
899
+ function getResolvedRuntimeGraph(options = {}) {
900
+ return resolveRuntimeGraph(options).graph;
901
+ }
902
+
903
+ function getDefaultRuntimeGraph() {
904
+ return cloneJson(RESOLVED_RUNTIME_GRAPH);
905
+ }
906
+
907
+ function lintRuntimeGraphConfig(options = {}) {
908
+ const { graph, errors } = resolveRuntimeGraph({ ...options, throwOnError: false });
909
+ return {
910
+ ok: errors.length === 0,
911
+ errors,
912
+ warnings: [],
913
+ graph,
914
+ };
915
+ }
916
+
917
+ function buildRuntimeGraphEnvelope(graph = getResolvedRuntimeGraph()) {
918
+ return {
919
+ schemaVersion: RUNTIME_GRAPH_SCHEMA_VERSION,
920
+ kind: 'forge.runtimeGraph',
921
+ schema: cloneJson(RUNTIME_GRAPH_SCHEMA),
922
+ graph: cloneJson(graph),
923
+ };
924
+ }
925
+
926
+ module.exports = {
927
+ RUNTIME_GRAPH_SCHEMA_VERSION,
928
+ RUNTIME_GRAPH_SCHEMA,
929
+ Phase,
930
+ Action,
931
+ Artifact,
932
+ EvaluatorRegion,
933
+ Gate,
934
+ Evidence,
935
+ Rail,
936
+ Adapter,
937
+ PlanningSubSkill,
938
+ Role,
939
+ ROLE_IDS,
940
+ loadRuntimeGraphConfig,
941
+ lintRuntimeGraphConfig,
942
+ resolveRuntimeGraph,
943
+ getDefaultRuntimeGraph,
944
+ buildRuntimeGraphEnvelope,
945
+ getResolvedRuntimeGraph,
946
+ };