@khoavu882/doflow 2.0.0-beta.5 → 2.0.0

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 (471) hide show
  1. package/README.md +12 -11
  2. package/bin/doflow.js +21 -11
  3. package/core/.antigravity-plugin/plugin.json +1 -2
  4. package/core/.claude-plugin/plugin.json +1 -1
  5. package/core/.codex-plugin/plugin.json +1 -1
  6. package/core/.plugin/plugin.json +1 -1
  7. package/core/harnesses/claude/hooks/lib.sh +43 -24
  8. package/core/harnesses/claude/hooks/permission-audit.sh +3 -0
  9. package/core/harnesses/claude/hooks/post-compact.sh +35 -32
  10. package/core/harnesses/claude/hooks/post-edit-lint.sh +2 -0
  11. package/core/harnesses/claude/hooks/post-shell-observe.sh +48 -0
  12. package/core/harnesses/claude/hooks/post-tool-failure.sh +3 -0
  13. package/core/harnesses/claude/hooks/pre-compact.sh +23 -27
  14. package/core/harnesses/claude/hooks/session-end.sh +23 -10
  15. package/core/harnesses/claude/hooks/skill-config-audit.sh +3 -0
  16. package/core/harnesses/claude/settings/settings.json +18 -0
  17. package/core/harnesses/claude/statusline/doflow-statusline.sh +1 -0
  18. package/core/harnesses/codex/agents/core-implementer.toml +1 -0
  19. package/core/harnesses/codex/agents/delivery-lead.toml +10 -0
  20. package/core/harnesses/codex/agents/quality-guardian.toml +1 -1
  21. package/core/harnesses/codex/agents/research-writer.toml +1 -1
  22. package/core/harnesses/codex/agents/reviewer.toml +10 -0
  23. package/core/harnesses/codex/agents/spec-analyst.toml +1 -1
  24. package/core/harnesses/codex/agents/system-architect.toml +1 -1
  25. package/core/harnesses/codex/hooks/lib.sh +44 -25
  26. package/core/harnesses/codex/hooks/post-edit-lint.impl.sh +3 -1
  27. package/core/harnesses/codex/hooks/pre-compact.impl.sh +29 -26
  28. package/core/harnesses/codex/hooks/session-end.impl.sh +23 -10
  29. package/core/harnesses/codex/hooks/subagent-audit.sh +1 -0
  30. package/core/harnesses/copilot/hooks/doflow-lib.sh +93 -0
  31. package/core/harnesses/copilot/hooks/doflow-mcp-tool-guard.sh +9 -0
  32. package/core/harnesses/copilot/hooks/doflow-pre-bash-guard.sh +9 -0
  33. package/core/harnesses/copilot/hooks/doflow-pre-implement-gate.sh +9 -0
  34. package/core/harnesses/copilot/hooks/doflow-session-start.sh +9 -0
  35. package/core/harnesses/copilot/hooks/doflow-stop-check.sh +9 -0
  36. package/core/harnesses/copilot/hooks/doflow-subagent-start.sh +9 -0
  37. package/core/harnesses/copilot/hooks/doflow-subagent-stop.sh +9 -0
  38. package/core/harnesses/copilot/hooks/doflow.json +57 -0
  39. package/core/harnesses/kiro/agents/doflow.json +1 -1
  40. package/core/harnesses/kiro/hooks/lib.sh +26 -6
  41. package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +1 -0
  42. package/core/harnesses/kiro/hooks/pre-bash-guard.sh +1 -0
  43. package/core/harnesses/kiro/hooks/pre-implement-gate.sh +1 -0
  44. package/core/harnesses/kiro/hooks/session-start.sh +1 -0
  45. package/core/harnesses/kiro/hooks/stop-check.sh +1 -0
  46. package/core/harnesses/pi/extensions/doflow-guard.js +8 -0
  47. package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +0 -1
  48. package/core/harnesses/shared/hooks/bridge/bridge.sh +102 -9
  49. package/core/harnesses/shared/hooks/bridge/envelope.jq +87 -18
  50. package/core/harnesses/shared/hooks/bridge/observe-model.sh +75 -0
  51. package/core/harnesses/shared/hooks/bridge/respond.sh +10 -2
  52. package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +28 -14
  53. package/core/harnesses/shared/hooks/policies/capture-failure.sh +48 -15
  54. package/core/harnesses/shared/hooks/policies/compact-text.sh +46 -0
  55. package/core/harnesses/shared/hooks/policies/contract.sh +3 -0
  56. package/core/harnesses/shared/hooks/policies/deny-json.sh +2 -11
  57. package/core/harnesses/shared/hooks/policies/emission-rules.sh +37 -0
  58. package/core/harnesses/shared/hooks/policies/emission.jq +209 -0
  59. package/core/harnesses/shared/hooks/policies/emission.sh +383 -0
  60. package/core/harnesses/shared/hooks/policies/identity.sh +177 -0
  61. package/core/harnesses/shared/hooks/policies/lib.sh +63 -40
  62. package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +3 -2
  63. package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +213 -12
  64. package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +114 -38
  65. package/core/harnesses/shared/hooks/policies/session-context.sh +30 -1
  66. package/core/harnesses/shared/hooks/policies/stop-check.sh +20 -9
  67. package/core/harnesses/shared/hooks/policies/subagent-audit.sh +5 -2
  68. package/core/harnesses/shared/hooks/policies/summary-store.sh +347 -0
  69. package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +60 -155
  70. package/core/harnesses/shared/hooks/stream-hook-runner.js +47 -8
  71. package/core/harnesses/shared/locator/doflow-run +2 -3
  72. package/core/registry/advice.json +3 -3
  73. package/core/registry/assets.json +55 -82
  74. package/core/registry/capabilities.json +12 -0
  75. package/core/registry/contracts.json +58 -31
  76. package/core/registry/contribution-points.json +32 -0
  77. package/core/registry/gates.json +32 -0
  78. package/core/registry/guidance-caps.json +40 -0
  79. package/core/registry/harnesses.json +1442 -363
  80. package/core/registry/lifecycle.json +485 -304
  81. package/core/registry/mcp.json +5 -1
  82. package/core/registry/models.json +7 -1
  83. package/core/registry/package-templates/README.md +19 -0
  84. package/core/registry/package-templates/agent/files/agent.md +9 -0
  85. package/core/registry/package-templates/agent/template.json +4 -0
  86. package/core/registry/package-templates/capability-provider/files/provider.json +9 -0
  87. package/core/registry/package-templates/capability-provider/template.json +11 -0
  88. package/core/registry/package-templates/debate-preset/files/preset.json +26 -0
  89. package/core/registry/package-templates/debate-preset/template.json +4 -0
  90. package/core/registry/package-templates/review-preset/files/preset.json +30 -0
  91. package/core/registry/package-templates/review-preset/template.json +4 -0
  92. package/core/registry/package-templates/skill/files/SKILL.md +8 -0
  93. package/core/registry/package-templates/skill/template.json +4 -0
  94. package/core/registry/package-templates/worker/files/worker.json +92 -0
  95. package/core/registry/package-templates/worker/template.json +11 -0
  96. package/core/registry/packages/doflow/doflow-package.json +15 -0
  97. package/core/registry/packages/doflow/presets/debate-3.json +26 -0
  98. package/core/registry/protocols.json +61 -0
  99. package/core/registry/routes.json +17 -0
  100. package/core/registry/rules.json +431 -0
  101. package/core/registry/settings.json +276 -0
  102. package/core/registry/storage.json +679 -31
  103. package/core/registry/telemetry.json +236 -0
  104. package/core/registry/transcripts.json +142 -0
  105. package/core/registry/verification.json +76 -1
  106. package/core/registry/workflows.json +55 -0
  107. package/core/shared/agent-specs/core-implementer.md +3 -0
  108. package/core/shared/agent-specs/delivery-lead.md +68 -0
  109. package/core/shared/agent-specs/quality-guardian.md +5 -1
  110. package/core/shared/agent-specs/research-writer.md +10 -3
  111. package/core/shared/agent-specs/reviewer.md +53 -0
  112. package/core/shared/agent-specs/spec-analyst.md +5 -6
  113. package/core/shared/agent-specs/system-architect.md +5 -1
  114. package/core/shared/guidance/DOFLOW_CORE.md +1 -5
  115. package/core/shared/guidance/modes/MODE_DeepResearch.md +1 -1
  116. package/core/shared/guidance/modes/MODE_Orchestration.md +4 -3
  117. package/core/shared/guidance/modes/MODE_Task_Management.md +14 -14
  118. package/core/shared/guidance/pointers/codex.md +1 -4
  119. package/core/shared/guidance/references/ARTIFACT_FORMAT.md +30 -16
  120. package/core/shared/guidance/references/ARTIFACT_VOICE.md +3 -2
  121. package/core/shared/guidance/references/CONSTITUTION_BASE.md +17 -15
  122. package/core/shared/guidance/references/DIAGRAM_GUIDE.md +1 -1
  123. package/core/shared/guidance/references/DOFLOW_CHAIN.md +17 -19
  124. package/core/shared/guidance/references/EVIDENCE_LEDGER.md +7 -8
  125. package/core/shared/guidance/references/MODEL_SELECTION.md +5 -3
  126. package/core/shared/guidance/references/ORCHESTRATION.md +1 -2
  127. package/core/shared/guidance/rules/RULES.md +45 -0
  128. package/core/shared/scripts/doflow/bash/do-args.sh +37 -0
  129. package/core/shared/scripts/doflow/bash/do-exec-paths.sh +4 -0
  130. package/core/shared/scripts/doflow/bash/do-git-state.sh +15 -4
  131. package/core/shared/scripts/doflow/bash/do-parallel-check.sh +4 -0
  132. package/core/shared/scripts/doflow/bash/do-paths.sh +40 -2
  133. package/core/shared/scripts/doflow/bash/do-prereqs.sh +2 -1
  134. package/core/shared/scripts/doflow/bash/do-review-package.sh +4 -0
  135. package/core/shared/scripts/doflow/bash/do-task-brief.sh +58 -16
  136. package/core/shared/scripts/doflow/bash/render-audit.sh +26 -2
  137. package/core/shared/scripts/doflow/bash/render-diagrams.sh +1 -0
  138. package/core/shared/scripts/doflow/bash/render-puml.sh +2 -1
  139. package/core/shared/scripts/doflow/bash/validate-artifacts.sh +48 -4
  140. package/core/shared/scripts/doflow/bin/doflow-run +149 -34
  141. package/core/shared/skills/do/SKILL.md +12 -10
  142. package/core/shared/skills/do/references/advice_calls.md +26 -0
  143. package/core/shared/skills/do/references/pm_routing.md +1 -2
  144. package/core/shared/skills/do/references/program_mode.md +31 -0
  145. package/core/shared/skills/do-brainstorm/SKILL.md +20 -74
  146. package/core/shared/skills/do-brainstorm/references/handoff_branches.md +35 -0
  147. package/core/shared/skills/do-brainstorm/references/intent_seed.md +21 -0
  148. package/core/shared/skills/do-brainstorm/references/round_log.md +17 -0
  149. package/core/shared/skills/do-code-review/SKILL.md +41 -144
  150. package/core/shared/skills/do-code-review/content-types/markdown.md +24 -2
  151. package/core/shared/skills/do-code-review/references/tools.md +90 -0
  152. package/core/shared/skills/do-code-review/scripts/doc_quality_checker.py +549 -7
  153. package/core/shared/skills/do-design/SKILL.md +18 -17
  154. package/core/shared/skills/do-diagnose/SKILL.md +14 -4
  155. package/core/shared/skills/do-document/SKILL.md +11 -1
  156. package/core/shared/skills/do-document/references/deep_research.md +1 -2
  157. package/core/shared/skills/do-document/references/embedded-research.md +18 -6
  158. package/core/shared/skills/do-document/references/implementation-flow.md +5 -6
  159. package/core/shared/skills/do-execute-plan/SKILL.md +15 -58
  160. package/core/shared/skills/do-execute-plan/references/handoff_record.md +7 -2
  161. package/core/shared/skills/do-execute-plan/references/parallel_dispatch.md +13 -0
  162. package/core/shared/skills/do-execute-plan/references/readiness_gate.md +26 -1
  163. package/core/shared/skills/do-execute-plan/references/run_position.md +13 -1
  164. package/core/shared/skills/do-execute-plan/references/scaffold.md +17 -9
  165. package/core/shared/skills/do-execute-plan/references/state_checkpoint.md +11 -0
  166. package/core/shared/skills/do-execute-plan/references/task_attempts.md +78 -24
  167. package/core/shared/skills/do-flow/SKILL.md +8 -9
  168. package/core/shared/skills/do-flow/references/task_classes.md +2 -2
  169. package/core/shared/skills/do-git/SKILL.md +4 -0
  170. package/core/shared/skills/do-implement/SKILL.md +4 -7
  171. package/core/shared/skills/do-plan/SKILL.md +26 -15
  172. package/core/shared/skills/do-test/SKILL.md +7 -4
  173. package/core/shared/templates/doflow/constitution-template.md +9 -3
  174. package/core/shared/templates/doflow/data-model-template.md +1 -1
  175. package/core/shared/templates/doflow/design-template.md +113 -16
  176. package/core/shared/templates/doflow/plan-template.md +11 -10
  177. package/core/shared/templates/doflow/question-log-template.md +14 -2
  178. package/core/shared/templates/doflow/requirement-template.md +1 -1
  179. package/core/worker-evidence/antigravity/checks.json +14 -0
  180. package/core/worker-evidence/codex/2026-10-18-cli-check.json +107 -0
  181. package/core/worker-evidence/codex/2026-10-18-live-run-edit.json +73 -0
  182. package/core/worker-evidence/codex/2026-10-18-live-run-read-only.json +62 -0
  183. package/core/worker-evidence/codex/2026-10-24-cli-check.json +126 -0
  184. package/core/worker-evidence/codex/checks.json +38 -0
  185. package/package.json +10 -6
  186. package/src/adapters/antigravity/index.js +143 -66
  187. package/src/adapters/claude/index.js +24 -4
  188. package/src/adapters/codex/agents.js +25 -15
  189. package/src/adapters/codex/index.js +48 -13
  190. package/src/adapters/codex/native-projection.js +6 -2
  191. package/src/adapters/copilot/index.js +58 -23
  192. package/src/adapters/copy-tree.js +290 -44
  193. package/src/adapters/guidance-manifest.js +71 -0
  194. package/src/adapters/guidance-pointer.js +19 -0
  195. package/src/adapters/hook-commands.js +6 -10
  196. package/src/adapters/index.js +1 -0
  197. package/src/adapters/kiro/index.js +23 -12
  198. package/src/adapters/opencode/index.js +8 -8
  199. package/src/adapters/pi/hooks.js +2 -1
  200. package/src/adapters/pi/index.js +14 -7
  201. package/src/adapters/resolve-source.js +26 -2
  202. package/src/cli/commands/install.js +2 -1
  203. package/src/cli/commands/reconcile.js +53 -9
  204. package/src/cli/commands/remove.js +2 -2
  205. package/src/cli/commands/rollback.js +6 -3
  206. package/src/cli/commands/self-update.js +18 -9
  207. package/src/cli/commands/status.js +21 -2
  208. package/src/cli/commands/ui.js +99 -0
  209. package/src/cli/commands/update.js +2 -1
  210. package/src/cli/index.js +322 -29
  211. package/src/cli/runtime-commands.js +209 -57
  212. package/src/cli/shared.js +0 -2
  213. package/src/helper/git.js +1 -1
  214. package/src/helper/guardrails.js +1 -1
  215. package/src/helper/marker-merge.js +31 -13
  216. package/src/helper/package-ids.js +37 -0
  217. package/src/helper/semver.js +94 -0
  218. package/src/install/backup.js +105 -15
  219. package/src/install/context.js +1 -1
  220. package/src/install/targets.js +31 -16
  221. package/src/lifecycle/coverage.js +20 -3
  222. package/src/lifecycle/hook-tier.js +160 -0
  223. package/src/lifecycle/index.js +412 -78
  224. package/src/lifecycle/retired-harnesses.js +318 -0
  225. package/src/lifecycle/view.js +35 -15
  226. package/src/registry/index.js +551 -26
  227. package/src/registry/worker-ids.js +45 -0
  228. package/src/release/build-package.js +0 -3
  229. package/src/release/docs-blocks.js +492 -0
  230. package/src/release/preflight.js +425 -0
  231. package/src/release/rehearse.js +401 -0
  232. package/src/release/release-workflow.js +733 -0
  233. package/src/release/sync-plugin-versions.js +43 -7
  234. package/src/runtime/advice/bench-gate.js +25 -0
  235. package/src/runtime/advice/cli.js +59 -16
  236. package/src/runtime/advice/client.js +32 -9
  237. package/src/runtime/advice/config.js +98 -133
  238. package/src/runtime/advice/doctor.js +1 -1
  239. package/src/runtime/advice/elevation.js +97 -0
  240. package/src/runtime/advice/endpoint-guard.js +9 -3
  241. package/src/runtime/advice/mode.js +37 -0
  242. package/src/runtime/advice/record.js +18 -5
  243. package/src/runtime/advice/registry.js +4 -1
  244. package/src/runtime/advice/shadow-config.js +229 -0
  245. package/src/runtime/advice/shadow-dispatcher.js +276 -0
  246. package/src/runtime/brief-rules.js +81 -0
  247. package/src/runtime/brief-tools.js +111 -34
  248. package/src/runtime/candidate.js +20 -11
  249. package/src/runtime/capabilities/executor.js +450 -0
  250. package/src/runtime/capabilities/loopback.js +41 -0
  251. package/src/runtime/capabilities/sanitizer.js +88 -0
  252. package/src/runtime/capability-router.js +135 -3
  253. package/src/runtime/checkouts.js +73 -11
  254. package/src/runtime/claims.js +13 -7
  255. package/src/runtime/cli.js +196 -19
  256. package/src/runtime/config-home.js +42 -0
  257. package/src/runtime/context/arbitrate.js +767 -0
  258. package/src/runtime/context/delivery-store.js +4 -6
  259. package/src/runtime/context/emission-rules.js +118 -0
  260. package/src/runtime/context/identity.js +227 -0
  261. package/src/runtime/context/primary-command.js +117 -0
  262. package/src/runtime/context/primary.js +344 -0
  263. package/src/runtime/context/summary-store.js +655 -0
  264. package/src/runtime/context-pack.js +154 -78
  265. package/src/runtime/decision-register.js +13 -4
  266. package/src/runtime/dispatch/admission.js +392 -0
  267. package/src/runtime/dispatch/attempt-end.js +274 -0
  268. package/src/runtime/dispatch/command.js +628 -0
  269. package/src/runtime/dispatch/controls.js +904 -0
  270. package/src/runtime/dispatch/envelope.js +276 -0
  271. package/src/runtime/dispatch/grants.js +310 -0
  272. package/src/runtime/dispatch/integrate.js +617 -0
  273. package/src/runtime/dispatch/loop.js +1835 -0
  274. package/src/runtime/dispatch/store.js +352 -0
  275. package/src/runtime/dispatch/verify-run.js +201 -0
  276. package/src/runtime/evidence-ledger.js +34 -7
  277. package/src/runtime/failure/capture.js +3 -1
  278. package/src/runtime/failure/cli.js +25 -9
  279. package/src/runtime/failure/home.js +57 -30
  280. package/src/runtime/failure/store.js +7 -2
  281. package/src/runtime/feature-pointer.js +174 -0
  282. package/src/runtime/feature-resolve.js +13 -2
  283. package/src/runtime/fleet/command.js +77 -0
  284. package/src/runtime/fleet/projects.js +178 -0
  285. package/src/runtime/fleet/tree.js +383 -0
  286. package/src/runtime/freshness.js +33 -3
  287. package/src/runtime/git-memo.js +46 -0
  288. package/src/runtime/guidance/ablation.js +427 -0
  289. package/src/runtime/health.js +565 -17
  290. package/src/runtime/history-compactor.js +13 -3
  291. package/src/runtime/implementation-gate.js +277 -22
  292. package/src/runtime/inventory/coverage.js +1 -1
  293. package/src/runtime/inventory/index.js +1 -1
  294. package/src/runtime/inventory/precedence.js +2 -2
  295. package/src/runtime/inventory/siblings.js +6 -4
  296. package/src/runtime/knowledge/code-corpus.js +277 -0
  297. package/src/runtime/knowledge/code-lexical.js +130 -0
  298. package/src/runtime/knowledge/code-retrieval.js +307 -0
  299. package/src/runtime/knowledge/code-validation.js +106 -0
  300. package/src/runtime/knowledge/gp3-gate.js +74 -0
  301. package/src/runtime/knowledge/graph-adapter.js +288 -0
  302. package/src/runtime/knowledge/graph-fallback.js +394 -0
  303. package/src/runtime/knowledge/hybrid.js +136 -8
  304. package/src/runtime/knowledge/retrieval.js +34 -1
  305. package/src/runtime/knowledge/rtk-compress.js +77 -0
  306. package/src/runtime/knowledge/semble-bridge.py +106 -0
  307. package/src/runtime/knowledge/semble-lifecycle.js +114 -0
  308. package/src/runtime/knowledge/semble-provider.js +365 -0
  309. package/src/runtime/lifecycle/cli.js +16 -6
  310. package/src/runtime/lifecycle/event-store.js +8 -6
  311. package/src/runtime/lifecycle/followup.js +6 -1
  312. package/src/runtime/lifecycle/overview.js +24 -1
  313. package/src/runtime/lifecycle/report-store.js +8 -10
  314. package/src/runtime/lifecycle/retention.js +95 -31
  315. package/src/runtime/lifecycle/root.js +9 -1
  316. package/src/runtime/lifecycle/status.js +34 -17
  317. package/src/runtime/lifecycle/store-upkeep.js +56 -7
  318. package/src/runtime/mask.js +134 -7
  319. package/src/runtime/mcp-consultations.js +40 -6
  320. package/src/runtime/model-router.js +79 -7
  321. package/src/runtime/models/catalogue.js +191 -0
  322. package/src/runtime/models/command.js +153 -0
  323. package/src/runtime/off-words.js +12 -0
  324. package/src/runtime/outcome.js +2 -2
  325. package/src/runtime/packages/command.js +1009 -0
  326. package/src/runtime/packages/contributions.js +116 -0
  327. package/src/runtime/packages/digest.js +132 -0
  328. package/src/runtime/packages/fetch.js +182 -0
  329. package/src/runtime/packages/grants.js +192 -0
  330. package/src/runtime/packages/index-file.js +134 -0
  331. package/src/runtime/packages/instruction.js +90 -0
  332. package/src/runtime/packages/manifest.js +331 -0
  333. package/src/runtime/packages/points.js +477 -0
  334. package/src/runtime/packages/programs.js +216 -0
  335. package/src/runtime/packages/scaffold.js +277 -0
  336. package/src/runtime/packages/source.js +90 -0
  337. package/src/runtime/packages/state.js +164 -0
  338. package/src/runtime/packages/store.js +449 -0
  339. package/src/runtime/packages/tar.js +106 -0
  340. package/src/runtime/packages/view.js +406 -0
  341. package/src/runtime/plan-check.js +59 -0
  342. package/src/runtime/plan-content.js +324 -0
  343. package/src/runtime/policy-init.js +118 -0
  344. package/src/runtime/profiles/doctor.js +96 -0
  345. package/src/runtime/profiles/resolve.js +244 -0
  346. package/src/runtime/profiles/rollback.js +254 -0
  347. package/src/runtime/profiles/sampler.js +41 -0
  348. package/src/runtime/readiness-record.js +16 -11
  349. package/src/runtime/readiness.js +46 -5
  350. package/src/runtime/research-request.js +50 -25
  351. package/src/runtime/retrieval-plan.js +10 -30
  352. package/src/runtime/retrieval-record.js +75 -5
  353. package/src/runtime/review/command.js +246 -0
  354. package/src/runtime/review/dominance.js +68 -0
  355. package/src/runtime/review/findings.js +434 -0
  356. package/src/runtime/review/identity.js +255 -0
  357. package/src/runtime/review/protocol-command.js +109 -0
  358. package/src/runtime/review/protocols.js +405 -0
  359. package/src/runtime/review/resolve-protocol.js +302 -0
  360. package/src/runtime/review/start.js +530 -0
  361. package/src/runtime/roadmap/facts.js +479 -0
  362. package/src/runtime/roadmap/gates.js +270 -0
  363. package/src/runtime/roadmap/loop.js +1636 -0
  364. package/src/runtime/roadmap/merge.js +381 -0
  365. package/src/runtime/roadmap/rows.js +1113 -0
  366. package/src/runtime/roadmap/seed.js +951 -0
  367. package/src/runtime/rules-report.js +336 -0
  368. package/src/runtime/scaffold/artifacts.js +60 -9
  369. package/src/runtime/scaffold/generate.js +21 -9
  370. package/src/runtime/settings/autopilot.js +106 -0
  371. package/src/runtime/settings/cli.js +280 -0
  372. package/src/runtime/settings/config-write.js +314 -0
  373. package/src/runtime/settings/files.js +271 -0
  374. package/src/runtime/settings/legacy.js +154 -0
  375. package/src/runtime/settings/lexical.js +56 -0
  376. package/src/runtime/settings/prepared-run.js +269 -0
  377. package/src/runtime/settings/project-layers.js +118 -0
  378. package/src/runtime/settings/refs.js +288 -0
  379. package/src/runtime/settings/registry.js +553 -0
  380. package/src/runtime/settings/resolve.js +713 -0
  381. package/src/runtime/settings/shared.js +37 -0
  382. package/src/runtime/settings/worker-billing.js +107 -0
  383. package/src/runtime/settings/write.js +269 -0
  384. package/src/runtime/slot-command.js +78 -0
  385. package/src/runtime/slots.js +671 -0
  386. package/src/runtime/source-version.js +1 -0
  387. package/src/runtime/state-root.js +217 -0
  388. package/src/runtime/storage/cli.js +12 -5
  389. package/src/runtime/storage/engine.js +9 -2
  390. package/src/runtime/storage/registry.js +14 -1
  391. package/src/runtime/storage/roots.js +66 -16
  392. package/src/runtime/storage/rules.js +82 -10
  393. package/src/runtime/storage/upkeep.js +10 -2
  394. package/src/runtime/storage/work-status.js +245 -26
  395. package/src/runtime/task-attempts.js +589 -69
  396. package/src/runtime/task-classifier.js +47 -11
  397. package/src/runtime/task-graph.js +373 -0
  398. package/src/runtime/task-run.js +778 -0
  399. package/src/runtime/task-scope.js +176 -55
  400. package/src/runtime/task-sources.js +147 -0
  401. package/src/runtime/task-state.js +374 -64
  402. package/src/runtime/team/admission.js +301 -0
  403. package/src/runtime/team/envelope.js +324 -0
  404. package/src/runtime/team/review-guard.js +156 -0
  405. package/src/runtime/trace/analyze.js +427 -0
  406. package/src/runtime/trace/baseline.js +770 -0
  407. package/src/runtime/trace/ledger.js +63 -140
  408. package/src/runtime/trace/otlp-export.js +568 -0
  409. package/src/runtime/trace/otlp-map.js +481 -0
  410. package/src/runtime/trace/spans.js +745 -0
  411. package/src/runtime/trace/telemetry.js +225 -0
  412. package/src/runtime/trace/views.js +1 -0
  413. package/src/runtime/transcripts/command.js +98 -0
  414. package/src/runtime/transcripts/cursor.js +326 -0
  415. package/src/runtime/transcripts/read.js +126 -0
  416. package/src/runtime/transcripts/readers/claude-jsonl.js +74 -0
  417. package/src/runtime/transcripts/readers/codex-rollout.js +70 -0
  418. package/src/runtime/transcripts/readers/copilot-events.js +90 -0
  419. package/src/runtime/transcripts/readers/kiro-jsonl.js +77 -0
  420. package/src/runtime/transcripts/readers/pi-jsonl.js +59 -0
  421. package/src/runtime/transcripts/registry.js +161 -0
  422. package/src/runtime/transcripts/stats.js +161 -0
  423. package/src/runtime/verification/check-key.js +628 -0
  424. package/src/runtime/verification/contract-runner.js +546 -11
  425. package/src/runtime/verification/engine.js +499 -9
  426. package/src/runtime/verification/impact.js +617 -0
  427. package/src/runtime/verification/inflight.js +205 -0
  428. package/src/runtime/verification/registry.js +176 -0
  429. package/src/runtime/verification/reuse.js +184 -0
  430. package/src/runtime/verification/telemetry-emit.js +254 -0
  431. package/src/runtime/verification/test-selection.js +67 -7
  432. package/src/runtime/worker/active.js +90 -0
  433. package/src/runtime/worker/cancel.js +222 -0
  434. package/src/runtime/worker/catalogue.js +122 -0
  435. package/src/runtime/worker/command.js +677 -0
  436. package/src/runtime/worker/evidence.js +288 -0
  437. package/src/runtime/worker/launch.js +288 -0
  438. package/src/runtime/worker/models.js +137 -0
  439. package/src/runtime/worker/parsers.js +218 -0
  440. package/src/runtime/worker/probes/index.js +706 -0
  441. package/src/runtime/worker/process-tree.js +415 -0
  442. package/src/runtime/worker/quota.js +43 -0
  443. package/src/runtime/worker/readiness.js +713 -0
  444. package/src/runtime/worker/run-store.js +166 -0
  445. package/src/runtime/worker/supervisor.js +541 -0
  446. package/src/runtime/worker/worktree-check.js +252 -0
  447. package/src/runtime/workflow-engine.js +5 -1
  448. package/src/runtime/workflow-orchestrator.js +578 -63
  449. package/src/runtime/worktree.js +149 -22
  450. package/src/state/index.js +1 -1
  451. package/src/state/lockfile.js +64 -10
  452. package/src/state/run-lock.js +1 -1
  453. package/types/node-commonjs.d.ts +106 -7
  454. package/core/harnesses/gemini/hooks/hooks.json +0 -77
  455. package/core/harnesses/gemini/hooks/lib.sh +0 -302
  456. package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +0 -10
  457. package/core/harnesses/gemini/hooks/post-edit-lint.sh +0 -39
  458. package/core/harnesses/gemini/hooks/pre-bash-guard.sh +0 -10
  459. package/core/harnesses/gemini/hooks/pre-compact.sh +0 -38
  460. package/core/harnesses/gemini/hooks/pre-implement-gate.sh +0 -10
  461. package/core/harnesses/gemini/hooks/session-end.impl.sh +0 -89
  462. package/core/harnesses/gemini/hooks/session-end.sh +0 -5
  463. package/core/harnesses/gemini/hooks/session-start.sh +0 -7
  464. package/core/harnesses/gemini/settings/adapter-defaults.json +0 -4
  465. package/core/shared/guidance/FLAGS.md +0 -25
  466. package/core/shared/guidance/rules/RULE_01_SAFETY.md +0 -25
  467. package/core/shared/guidance/rules/RULE_02_WORKFLOW.md +0 -23
  468. package/core/shared/guidance/rules/RULE_03_QUALITY.md +0 -14
  469. package/core/shared/guidance/rules/RULE_04_QUESTIONS.md +0 -46
  470. package/src/adapters/gemini/hooks.js +0 -251
  471. package/src/adapters/gemini/index.js +0 -429
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env bash
2
+ case "${OSTYPE:-}" in msys*|cygwin*|win32*) if command -v jq >/dev/null 2>&1; then jq() { command jq -b "$@"; }; fi ;; esac # native jq.exe ends lines with CRLF unless -b
2
3
  # lib.sh — shared constants and helpers for the Canonical Policy Library
3
4
  #
4
5
  # Usage: source "<policy-dir>/lib.sh"
@@ -9,7 +10,7 @@
9
10
  # 3. capture stdin once: INPUT=$(cat)
10
11
  # 4. use: json_field "$INPUT" ".field_name"
11
12
 
12
- case ${BASH_SOURCE[0]} in */*) _DOFLOW_LIB_DIR=${BASH_SOURCE[0]%/*} ;; *) _DOFLOW_LIB_DIR=. ;; esac
13
+ _DOFLOW_SRC=${BASH_SOURCE[0]//\\//}; case $_DOFLOW_SRC in */*) _DOFLOW_LIB_DIR=${_DOFLOW_SRC%/*} ;; *) _DOFLOW_LIB_DIR=. ;; esac
13
14
  #
14
15
  # Deployment:
15
16
  # This file is installed by the doflow CLI and overwritten on update — do
@@ -17,7 +18,7 @@ case ${BASH_SOURCE[0]} in */*) _DOFLOW_LIB_DIR=${BASH_SOURCE[0]%/*} ;; *) _DOFLO
17
18
  #
18
19
  # Consolidation note (022-normalize-hooks, Phase B): this is the single
19
20
  # canonical copy every front door's dispatched policy script sources — the
20
- # claude/gemini/kiro/codex copies it replaces were already byte-identical
21
+ # claude/kiro/codex copies it replaces were already byte-identical
21
22
  # except for one Claude-only default (see DOFLOW_AGENT below), confirmed via
22
23
  # diff before consolidating.
23
24
 
@@ -25,16 +26,33 @@ set -euo pipefail
25
26
 
26
27
  # ── State directories ────────────────────────────────────────────────────────
27
28
 
28
- # XDG-compliant, agent-agnostic store shared by every harness DoFlow supports.
29
- DOFLOW_HOME="${XDG_CONFIG_HOME:-$HOME/.config}/doflow"
30
- STATE_DIR="$DOFLOW_HOME/session-env"
31
- SESSION_DIR="$STATE_DIR/sessions"
32
- PROJECTS_DIR="$STATE_DIR/projects"
29
+ # XDG-compliant, agent-agnostic store shared by every harness DoFlow supports, under the config home
30
+ # (doflow_config_home: an absolute XDG_CONFIG_HOME, else an absolute HOME + /.config, else none). With
31
+ # no config home every state path below is empty, sourcing this file still never exits, and each state
32
+ # function returns 0 at once: session work is skipped and a guard reaches its own decision (086 IC-010).
33
+ # BEGIN doflow-config-home v1 (rule: src/runtime/config-home.js; checked by test/guards/settings.test.js)
34
+ doflow_config_home() {
35
+ DOFLOW_HOME=""
36
+ case "${XDG_CONFIG_HOME:-}" in
37
+ /*) DOFLOW_HOME="${XDG_CONFIG_HOME}/doflow"; return 0 ;;
38
+ [A-Za-z]:[\\/]*) case "${OSTYPE:-}" in msys*|cygwin*|win32*) DOFLOW_HOME="${XDG_CONFIG_HOME}/doflow"; return 0 ;; esac ;;
39
+ esac
40
+ case "${HOME:-}" in
41
+ /*) DOFLOW_HOME="${HOME}/.config/doflow" ;;
42
+ [A-Za-z]:[\\/]*) case "${OSTYPE:-}" in msys*|cygwin*|win32*) DOFLOW_HOME="${HOME}/.config/doflow" ;; esac ;;
43
+ esac
44
+ return 0
45
+ }
46
+ # END doflow-config-home v1
47
+ doflow_config_home
48
+ STATE_DIR="${DOFLOW_HOME:+$DOFLOW_HOME/session-env}"
49
+ SESSION_DIR="${STATE_DIR:+$STATE_DIR/sessions}"
50
+ PROJECTS_DIR="${STATE_DIR:+$STATE_DIR/projects}"
33
51
  # shellcheck disable=SC2034 # used by session-context.sh and stop-check.sh which source this file
34
- SESSIONS_LOG="$DOFLOW_HOME/sessions.log"
52
+ SESSIONS_LOG="${DOFLOW_HOME:+$DOFLOW_HOME/sessions.log}"
35
53
 
36
54
  # Identifies which harness is running. A front door sets this before sourcing
37
- # this file (e.g. `export DOFLOW_AGENT=gemini; exec bash .../policies/...sh`);
55
+ # this file (e.g. `export DOFLOW_AGENT=codex; exec bash .../policies/...sh`);
38
56
  # "unknown" here — not any one harness's name — is the honest fallback when a
39
57
  # front door omits it, since every prior per-harness copy of this file
40
58
  # defaulted to "claude-code" regardless of which harness actually shipped it.
@@ -253,18 +271,22 @@ has_uncommitted_changes() {
253
271
  # ── Directory helpers ─────────────────────────────────────────────────────────
254
272
 
255
273
  # Create and return the session-scoped state directory for a given session_id.
256
- # Safe to call multiple times (mkdir -p is idempotent).
274
+ # Safe to call multiple times (mkdir -p is idempotent). Prints nothing and creates nothing when
275
+ # there is no config home (SESSION_DIR empty).
257
276
  ensure_session_dir() {
258
277
  local session_id="$1"
278
+ [ -n "$SESSION_DIR" ] || return 0
259
279
  mkdir -p "$SESSION_DIR/$session_id"
260
280
  echo "$SESSION_DIR/$session_id"
261
281
  }
262
282
 
263
283
  # Create and return the project-scoped state directory for a given cwd.
264
- # Shared across all sessions in the same directory.
284
+ # Shared across all sessions in the same directory. Prints nothing and creates nothing when there
285
+ # is no config home (PROJECTS_DIR empty).
265
286
  ensure_project_dir() {
266
287
  local cwd="$1"
267
288
  local hash
289
+ [ -n "$PROJECTS_DIR" ] || return 0
268
290
  hash=$(cwd_hash "$cwd")
269
291
  mkdir -p "$PROJECTS_DIR/$hash"
270
292
  echo "$PROJECTS_DIR/$hash"
@@ -293,30 +315,16 @@ require_jq() {
293
315
  fi
294
316
  }
295
317
 
296
- # ── Session role ──────────────────────────────────────────────────────────────
297
-
298
- # Print `main` or `worker` for this session, read from DOFLOW_SESSION_ROLE (matched
299
- # case-insensitively). Unset or empty is `main`; a value that is neither `main` nor
300
- # `worker` is `worker` and writes one line to stderr. A worker never writes or claims the
301
- # project's compact summary or warning. Always returns 0. bash 3.2 safe (no ${var,,}).
302
- doflow_session_role() {
303
- local role
304
- role=$(printf '%s' "${DOFLOW_SESSION_ROLE:-}" | tr '[:upper:]' '[:lower:]') || role=""
305
- case "$role" in
306
- ""|main) printf 'main\n' ;;
307
- worker) printf 'worker\n' ;;
308
- *)
309
- echo "[doflow] unknown DOFLOW_SESSION_ROLE; treating session as worker" >&2
310
- printf 'worker\n'
311
- ;;
312
- esac
313
- return 0
314
- }
318
+ # ── Session role and identity ─────────────────────────────────────────────────
319
+
320
+ # doflow_session_role and doflow_identity live in identity.sh, their only definition (IC-009).
321
+ . "$_DOFLOW_LIB_DIR/identity.sh"
315
322
 
316
323
  # Start storage cleanup without changing the hook's output, status, or latency.
317
324
  storage_upkeep_async() {
318
- local cwd="${1:-}" root run home config_dir
325
+ local cwd="${1:-}" root run home config_dir detach=""
319
326
  [ -n "$cwd" ] || return 0
327
+ command -v setsid >/dev/null 2>&1 && detach="setsid"
320
328
  home="${HOME:-}"
321
329
  config_dir="${CLAUDE_CONFIG_DIR:-$home/.claude}"
322
330
  root=$(nudge_repo_root "$cwd" 2>/dev/null) || root="$cwd"
@@ -329,7 +337,9 @@ storage_upkeep_async() {
329
337
  # Background the fully redirected subshell; an inner `&` leaves this shell
330
338
  # holding the hook's captured stdout open until the dispatcher exits. The
331
339
  # storage verb takes its project from the working directory: run it at the root.
332
- ( cd "$root" 2>/dev/null && nohup "$run" storage --action upkeep </dev/null >/dev/null 2>&1 ) >/dev/null 2>&1 3>&- 5>&- &
340
+ # setsid (absent on macOS) moves the upkeep out of the hook's session, so a harness that
341
+ # signals the hook's process group when the hook exits does not take the upkeep with it.
342
+ ( cd "$root" 2>/dev/null && nohup $detach "$run" storage --action upkeep </dev/null >/dev/null 2>&1 ) >/dev/null 2>&1 3>&- 5>&- &
333
343
  return 0
334
344
  done
335
345
  return 0
@@ -379,7 +389,7 @@ nudge_repo_root() {
379
389
  # file that cannot be read, is empty or holds anything else is `off`.
380
390
  nudge_setting() {
381
391
  local root="${1:-}" file line=""
382
- for file in "${root:+$root/.doflow/prompt-nudge}" "$DOFLOW_HOME/prompt-nudge"; do
392
+ for file in "${root:+$root/.doflow/prompt-nudge}" "${DOFLOW_HOME:+$DOFLOW_HOME/prompt-nudge}"; do
383
393
  [ -n "$file" ] && [ -e "$file" ] || continue
384
394
  line=""
385
395
  { IFS= read -r line || true; } 2>/dev/null < "$file" || line=""
@@ -406,17 +416,20 @@ nudge_safe_id() {
406
416
  return 0
407
417
  }
408
418
 
409
- # Print the marker path for a session id. The caller checks nudge_safe_id first.
419
+ # Print the marker path for a session id. The caller checks nudge_safe_id first. With no config
420
+ # home there is no marker path and nothing is printed.
410
421
  nudge_marker_file() {
422
+ [ -n "$STATE_DIR" ] || return 0
411
423
  printf '%s\n' "$STATE_DIR/nudge/$1"
412
424
  }
413
425
 
414
426
  # Create the marker for <id> holding <nudged|suppressed>. Succeeds only when this call
415
427
  # created it: the create is exclusive, so a second call for the same id fails and
416
428
  # leaves the first content. After a create, markers untouched for over 30 days are
417
- # removed. Silent on every failure.
429
+ # removed. Silent on every failure. With no config home it creates nothing and returns 0 at once.
418
430
  nudge_mark() {
419
- local id="${1:-}" word="${2:-}" dir="$STATE_DIR/nudge" file
431
+ local id="${1:-}" word="${2:-}" dir="${STATE_DIR:+$STATE_DIR/nudge}" file
432
+ [ -n "$dir" ] || return 0
420
433
  nudge_safe_id "$id" || return 1
421
434
  case "$word" in nudged|suppressed) ;; *) return 1 ;; esac
422
435
  file="$dir/$id"
@@ -452,7 +465,8 @@ nudge_feature_active() {
452
465
  # S3: succeed when a `/do-*` skill already ran this session, read from the run ledger:
453
466
  # the lexically last <config>/state/runs/*.jsonl, whose last line's timestamp is not
454
467
  # earlier than the session's captured_at (both YYYY-MM-DDTHH:MM:SSZ). Config dir:
455
- # $DOFLOW_CONFIG_DIR, else the nearest `.doflow` at or above <cwd>, else $HOME/.doflow.
468
+ # $DOFLOW_CONFIG_DIR, else the nearest non-nested `.doflow` at or above <cwd>, else $HOME/.doflow
469
+ # (none when HOME is unset or empty).
456
470
  # Without a captured_at the ledger counts when it is named for today's UTC date; a last
457
471
  # line without a usable timestamp counts too. No ledger, or an empty one, does not.
458
472
  nudge_ledger_active() {
@@ -462,10 +476,19 @@ nudge_ledger_active() {
462
476
  if [ -n "${DOFLOW_CONFIG_DIR:-}" ]; then
463
477
  config="$DOFLOW_CONFIG_DIR"
464
478
  else
465
- config="$HOME/.doflow"
479
+ config="${HOME:+$HOME/.doflow}"
466
480
  # The ancestor in the walk's own spelling (`\` read as `/`), the form its test succeeded on.
467
- if dir=$(nudge_find_up "$cwd" .doflow -d); then config="${dir//\\//}/.doflow"; fi
481
+ if dir=$(nudge_find_up "$cwd" .doflow -d); then
482
+ while :; do
483
+ dir="${dir//\\//}"
484
+ case "$dir/" in
485
+ */.doflow/*) dir=$(nudge_find_up "${dir%/*}" .doflow -d) || break ;;
486
+ *) config="$dir/.doflow"; break ;;
487
+ esac
488
+ done
489
+ fi
468
490
  fi
491
+ [ -n "$config" ] || return 1
469
492
  for f in "$config"/state/runs/*.jsonl; do
470
493
  [ -f "$f" ] && ledger="$f"
471
494
  done
@@ -493,7 +516,7 @@ nudge_registry() {
493
516
  "${policy_dir:+$policy_dir/../../../runtime/core/registry/workflows.json}" \
494
517
  "${policy_dir:+$policy_dir/../../../../registry/workflows.json}" \
495
518
  "${root:+$root/.doflow/runtime/core/registry/workflows.json}" \
496
- "$HOME/.doflow/runtime/core/registry/workflows.json"; do
519
+ "${HOME:+$HOME/.doflow/runtime/core/registry/workflows.json}"; do
497
520
  if [ -n "$candidate" ] && [ -f "$candidate" ]; then
498
521
  printf '%s\n' "$candidate"
499
522
  return 0
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env bash
2
- case $0 in */*) _DOFLOW_POLDIR=${0%/*} ;; *) _DOFLOW_POLDIR=. ;; esac
2
+ case "${OSTYPE:-}" in msys*|cygwin*|win32*) if command -v jq >/dev/null 2>&1; then jq() { command jq -b "$@"; }; fi ;; esac # native jq.exe ends lines with CRLF unless -b
3
+ _DOFLOW_SRC=${0//\\//}; case $_DOFLOW_SRC in */*) _DOFLOW_POLDIR=${_DOFLOW_SRC%/*} ;; *) _DOFLOW_POLDIR=. ;; esac
3
4
  # mcp-tool-guard.sh — Canonical Policy Library: PreToolUse(mcp_*) hook
4
5
  #
5
6
  # Intercepts every MCP tool call and blocks matches against mcp-policy.conf, the
@@ -20,7 +21,7 @@ case $0 in */*) _DOFLOW_POLDIR=${0%/*} ;; *) _DOFLOW_POLDIR=. ;; esac
20
21
  #
21
22
  # mcp-policy.conf's shape (TAB-separated regex<TAB>reason, '#'-comments,
22
23
  # blank lines skipped) is identical across every harness that ships one today
23
- # (claude/codex/gemini/kiro) — reconciliation found no divergence to resolve
24
+ # (claude/codex/kiro) — reconciliation found no divergence to resolve
24
25
  # there.
25
26
 
26
27
  set -uo pipefail
@@ -1,25 +1,28 @@
1
1
  #!/usr/bin/env bash
2
- case $0 in */*) _DOFLOW_POLDIR=${0%/*} ;; *) _DOFLOW_POLDIR=. ;; esac
2
+ case "${OSTYPE:-}" in msys*|cygwin*|win32*) if command -v jq >/dev/null 2>&1; then jq() { command jq -b "$@"; }; fi ;; esac # native jq.exe ends lines with CRLF unless -b
3
+ _DOFLOW_SRC=${0//\\//}; case $_DOFLOW_SRC in */*) _DOFLOW_POLDIR=${_DOFLOW_SRC%/*} ;; *) _DOFLOW_POLDIR=. ;; esac
3
4
  # pre-bash-guard.sh — Canonical Policy Library: PreToolUse(shell-command) hook
4
5
  #
5
6
  # Intercepts every shell-command tool call and blocks dangerous commands by
6
7
  # matching against POSIX ERE patterns in blocked-patterns.conf (grep -E).
7
8
  #
8
- # Multi-session safe: stateless — reads only the conf file, no shared state.
9
+ # Multi-session safe: reads the conf and canonical worker/cwd directories only.
10
+ # Marked reset/clean rules and an exact DROP DATABASE launch snapshot may exempt a dispatch worker;
11
+ # every failed scope/command check retains the ordinary pattern verdict.
9
12
  # Must complete in <50ms.
10
13
  #
11
14
  # Canonical Policy Script Contract (design.md §4):
12
15
  # env DOFLOW_PROJECT_DIR, DOFLOW_AGENT (both optional; not required to
13
16
  # evaluate this policy)
14
17
  # stdin a native payload with envelope v1 under doflow_event; only the
15
- # envelope's operation, tool and command fields are used.
18
+ # envelope's operation, tool and command and native root cwd are used.
16
19
  # exit exit 2 + doflow_decide deny = deny; other faults per IC-011.
17
20
  #
18
21
  # Reconciliation note (022-normalize-hooks, Phase A / D1): this also absorbs
19
22
  # stream-hook-runner.js's inline DESTRUCTIVE_COMMAND_PATTERN
20
23
  # (`\brm\s+-rf\s+[/~]`, since narrowed to the recursive-rm-of-catastrophic-target
21
- # rule below), which was the only pre-bash-guard protection Gemini
22
- # and Antigravity sessions routed through that runner actually had — it
24
+ # rule below), which was the only pre-bash-guard protection
25
+ # Antigravity sessions routed through that runner actually had — it
23
26
  # duplicated, rather than delegated to, this policy's fuller
24
27
  # blocked-patterns.conf coverage (git force-push, git reset --hard, git clean
25
28
  # -fd, SQL DROP/DELETE/TRUNCATE, curl|wget-pipe-to-shell, chmod -R 777, dd
@@ -28,8 +31,8 @@ case $0 in */*) _DOFLOW_POLDIR=${0%/*} ;; *) _DOFLOW_POLDIR=. ;; esac
28
31
  # only the hardcoded regex before this change does not lose even that
29
32
  # minimum floor of protection; once blocked-patterns.conf is present (the
30
33
  # normal case), the full config-driven pattern list applies as it already
31
- # does for claude/gemini/codex today, which is a real, intended widening of
32
- # coverage for Gemini/Antigravity once B.5 wires this policy for them (see
34
+ # does for claude/codex today, which is a real, intended widening of
35
+ # coverage for Antigravity once B.5 wires this policy for them (see
33
36
  # design.md R2/R3), not a silent side effect.
34
37
 
35
38
  set -uo pipefail
@@ -87,6 +90,112 @@ PATTERNS_FILE="$_DOFLOW_POLDIR/blocked-patterns.conf"
87
90
  # byte-wise (much faster than multibyte-aware in a UTF-8 locale).
88
91
  export LC_ALL=C
89
92
 
93
+ # Shape checks accept one terminal line ending, never an additional command.
94
+ # Keep COMMAND raw for all ordinary pattern verdicts.
95
+ SHAPE_COMMAND=$COMMAND
96
+ case $SHAPE_COMMAND in
97
+ *$'\r\n') SHAPE_COMMAND=${SHAPE_COMMAND%$'\r\n'} ;;
98
+ *$'\n') SHAPE_COMMAND=${SHAPE_COMMAND%$'\n'} ;;
99
+ esac
100
+
101
+ # Lazy and cached: shape checks precede the shared scope's only two subshells
102
+ # (cd/pwd builtins), then one git probe for reset/clean only.
103
+ # Never infer worker identity from command text.
104
+ _WORKER_EXEMPT=
105
+ _worker_exempt() {
106
+ case $_WORKER_EXEMPT in allow) return 0 ;; deny) return 1 ;; esac
107
+ _WORKER_EXEMPT=deny
108
+ local reset_re='^[[:blank:]]*git[[:blank:]]+reset([[:blank:]]+(--hard|-q|--quiet))+([[:blank:]]+[A-Za-z0-9_@][A-Za-z0-9_./^~@{}-]*)?[[:blank:]]*$'
109
+ local clean_re='^[[:blank:]]*git[[:blank:]]+clean([[:blank:]]+(-[dfqxX]+|--force|--quiet))+[[:blank:]]*$'
110
+ local forces command=$SHAPE_COMMAND
111
+ if [[ $command =~ $reset_re ]]; then
112
+ case " $command " in *[[:blank:]]--hard[[:blank:]]*) ;; *) return 1 ;; esac
113
+ elif [[ $command =~ $clean_re ]]; then
114
+ forces=${command//[!f]/}
115
+ [ "${#forces}" -eq 1 ] || return 1
116
+ else
117
+ return 1
118
+ fi
119
+ _worker_scope || return 1
120
+ local project=${_WORKER_DIR%/.doflow/state/dispatch-worktrees/*}
121
+ local name=${_WORKER_DIR##*/} info git_dir top suffix
122
+ [ -d "$project/.git" ] || return 1
123
+ # No inherited GIT_* / config environment can redirect this read-only probe.
124
+ info=$(env -i PATH="$PATH" HOME="${HOME:-}" git -C "$_WORKER_CWD" rev-parse --path-format=absolute --git-dir --show-toplevel 2>/dev/null) || return 1
125
+ git_dir=${info%%$'\n'*}; top=${info#*$'\n'}
126
+ [ "$top" = "$_WORKER_DIR" ] || return 1
127
+ case $git_dir in "$project/.git/worktrees/$name"*) ;; *) return 1 ;; esac
128
+ suffix=${git_dir#"$project/.git/worktrees/$name"}
129
+ case $suffix in *[!0-9]*) return 1 ;; esac
130
+ _WORKER_EXEMPT=allow
131
+ return 0
132
+ }
133
+
134
+ # Both exemptions use exactly the same worker, dispatch-path, cwd and GIT_* checks.
135
+ _WORKER_SCOPE=
136
+ _worker_scope() {
137
+ case $_WORKER_SCOPE in allow) return 0 ;; deny) return 1 ;; esac
138
+ _WORKER_SCOPE=deny
139
+ local worktree cwd config_key
140
+ [ "${DOFLOW_SESSION_ROLE:-}" = worker ] || return 1
141
+ [ -z "${GIT_DIR+x}${GIT_WORK_TREE+x}${GIT_INDEX_FILE+x}${GIT_COMMON_DIR+x}${GIT_CONFIG_PARAMETERS+x}${GIT_CONFIG_COUNT+x}" ] || return 1
142
+ for config_key in ${!GIT_CONFIG_KEY_@}; do return 1; done
143
+ case ${DOFLOW_WORKTREE:-} in /*) ;; *) return 1 ;; esac
144
+ [ -d "$DOFLOW_WORKTREE" ] || return 1
145
+ # A sentinel preserves path-ending newlines across command substitution.
146
+ worktree=$(CDPATH= cd -P -- "$DOFLOW_WORKTREE" 2>/dev/null && pwd -P && printf '.') || return 1
147
+ worktree=${worktree%.}; worktree=${worktree%$'\n'}
148
+ local dispatch_re='/\.doflow/state/dispatch-worktrees/d-[0-9a-f]{12}$'
149
+ [[ $worktree =~ $dispatch_re ]] || return 1
150
+ [ -n "${DF_ENV_CWD:-}" ] || return 1
151
+ cwd=$(CDPATH= cd -P -- "$DF_ENV_CWD" 2>/dev/null && pwd -P && printf '.') || return 1
152
+ cwd=${cwd%.}; cwd=${cwd%$'\n'}
153
+ case $cwd in "$worktree"|"$worktree"/*) ;; *) return 1 ;; esac
154
+ _WORKER_DIR=$worktree _WORKER_CWD=$cwd
155
+ _WORKER_SCOPE=allow
156
+ return 0
157
+ }
158
+
159
+ # 112: no settings reads here. Re-validate only the launcher's snapshot, then
160
+ # recognise one full bare client invocation and one statement. No shell eval,
161
+ # substitutions, variables, wrappers, redirects, additional args or statements.
162
+ _worker_dropdb_exempt() {
163
+ local list=${DOFLOW_GUARD_DROP_DATABASES:-} list_re='^[a-z][a-z0-9_]{0,62}( [a-z][a-z0-9_]{0,62})*$'
164
+ [ "${#list}" -le 1024 ] && [[ $list =~ $list_re ]] || return 1
165
+ local names=() name rest client quote statement id_re sql_re command=$SHAPE_COMMAND
166
+ IFS=' ' read -r -a names <<< "$list"
167
+ [ "${#names[@]}" -le 32 ] || return 1
168
+ local psql_re='^[[:blank:]]*psql([[:blank:]]+(-[hpUd][[:blank:]]+[A-Za-z0-9_.:-]+|--(host|port|username|dbname)=[A-Za-z0-9_.:-]+|-w))*[[:blank:]]+-c[[:blank:]]+'
169
+ local mysql_re='^[[:blank:]]*mysql([[:blank:]]+(-[hPu][[:blank:]]+[A-Za-z0-9_.:-]+|--(host|port|user)=[A-Za-z0-9_.:-]+))*[[:blank:]]+-e[[:blank:]]+'
170
+ if [[ $command =~ $psql_re ]]; then client=psql
171
+ elif [[ $command =~ $mysql_re ]]; then client=mysql
172
+ else return 1
173
+ fi
174
+ rest=${command:${#BASH_REMATCH[0]}}
175
+ local single_re="^'([^']*)'[[:blank:]]*$" double_re='^"([^"]*)"[[:blank:]]*$'
176
+ if [[ $rest =~ $single_re ]]; then quote=single
177
+ elif [[ $rest =~ $double_re ]]; then quote=double
178
+ else return 1
179
+ fi
180
+ statement=${BASH_REMATCH[1]}
181
+ id_re='[a-z][a-z0-9_]*'
182
+ if [ "$quote" = single ]; then
183
+ if [ "$client" = psql ]; then id_re='([a-z][a-z0-9_]*|"[a-z][a-z0-9_]*")'
184
+ else id_re='([a-z][a-z0-9_]*|`[a-z][a-z0-9_]*`)'
185
+ fi
186
+ fi
187
+ sql_re="^[[:blank:]]*[Dd][Rr][Oo][Pp][[:blank:]]+[Dd][Aa][Tt][Aa][Bb][Aa][Ss][Ee][[:blank:]]+([Ii][Ff][[:blank:]]+[Ee][Xx][Ii][Ss][Tt][Ss][[:blank:]]+)?($id_re)[[:blank:]]*;?[[:blank:]]*$"
188
+ [[ $statement =~ $sql_re ]] || return 1
189
+ name=${BASH_REMATCH[2]}
190
+ case $name in \"*\"|\`*\`) name=${name#?}; name=${name%?} ;; esac
191
+ local listed found=false
192
+ for listed in "${names[@]}"; do
193
+ if [ "$name" = "$listed" ]; then found=true; break; fi
194
+ done
195
+ [ "$found" = true ] || return 1
196
+ _worker_scope
197
+ }
198
+
90
199
  # ── Quoted text, comments and heredoc bodies are not commands ────────────────
91
200
  # scrub_quotes <text> -> sets SCRUBBED. Anchored shell-command patterns must
92
201
  # not fire on text that is merely data (a commit message, an echo, a comment,
@@ -164,7 +273,7 @@ scrub_quotes() {
164
273
  *) SCRUBBED=$text; return ;;
165
274
  esac
166
275
  case $text in
167
- *rm[[:space:]]*|*git[[:space:]]*|*curl[[:space:]]*|*wget[[:space:]]*|*chmod[[:space:]]*|*dd[[:space:]]*|*find[[:space:]]*|*eval*|*sh[[:space:]]*) ;;
276
+ *rm[[:space:]]*|*git[[:space:]]*|*curl[[:space:]]*|*wget[[:space:]]*|*chmod[[:space:]]*|*dd[[:space:]]*|*find[[:space:]]*|*eval*|*sh[[:space:]]*|*echo[[:space:]]*|*printf[[:space:]]*|*cat[[:space:]]*|*tee[[:space:]]*|*mkdir[[:space:]]*|*cd[[:space:]]*|*true[[:space:]]*|*:[[:space:]]*|*gh[[:space:]]*) ;;
168
277
  *) SCRUBBED=$text; return ;;
169
278
  esac
170
279
  if [ "$SCRUB_DEPTH" -ge 8 ]; then SCRUBBED=$text; return; fi
@@ -269,12 +378,77 @@ scrub_quotes() {
269
378
  scrub_quotes "$COMMAND"
270
379
  SHELL_TEXT=$SCRUBBED
271
380
 
381
+ # Positive SQL text-only allow-list, ALWAYS on raw COMMAND, never SHELL_TEXT.
382
+ # Whole-command matches permit only commit messages, echo/printf literals,
383
+ # cat with a single-quoted heredoc, and gh pr/issue create/comment/edit bodies.
384
+ # Double quotes forbid $, backticks and backslashes; file paths have no dot-led
385
+ # segment. Heredoc delimiters are single-quoted identifiers, close exactly once
386
+ # on the final line, and allow one trailing newline. The sole substitution is
387
+ # the exact Claude commit $(cat <<'WORD' ... WORD) idiom. No eval or shell parser:
388
+ # every non-match keeps the original raw SQL verdict, including malformed input.
389
+ _text_only_command() {
390
+ local text=${COMMAND%$'\n'} b='[[:blank:]]' single="'[^']*'" literal path git gh args re header body word line closed=false
391
+ literal="($single|\"[^\"\$\`\\\\]*\")"
392
+ path='/?[A-Za-z0-9_-][A-Za-z0-9_.-]*(/[A-Za-z0-9_-][A-Za-z0-9_.-]*)*'
393
+ git="git${b}+commit(${b}+(-a|-q|--no-verify))*"
394
+ args="(${b}+([A-Za-z0-9_./:=,@%+-]+|$literal))*"
395
+ gh="gh${b}+(pr|issue)${b}+(create|comment|edit)$args"
396
+ re="^${b}*$git(${b}+-m${b}+$literal(${b}+(-a|-q|--no-verify))*)+${b}*$"
397
+ [[ $text =~ $re ]] && return 0
398
+ re="^${b}*(echo|printf)${b}+$literal(${b}*>>?${b}*$path)?${b}*$"
399
+ [[ $text =~ $re ]] && return 0
400
+ re="^${b}*$gh${b}+--body${b}+$single$args${b}*$"
401
+ [[ $text =~ $re ]] && return 0
402
+
403
+ # Split only the first line. The body is inert only if its first delimiter
404
+ # occurrence is also its last line; otherwise a hidden second command runs.
405
+ case $text in *$'\n'*) ;; *) return 1 ;; esac
406
+ header=${text%%$'\n'*}; body=${text#*$'\n'}
407
+ local hd="<<'([A-Za-z_][A-Za-z0-9_]*)'" cat_start='"\$\(cat' cat_end=$'\n)"'
408
+ local redir=">>?${b}*$path" tee_flag="(-a|--append)"
409
+ local re_git="^${b}*($git${b}+-F${b}+-${b}+|$gh${b}+--body-file${b}+-${b}+)${hd}${b}*$"
410
+ local re_cat_before="^${b}*cat(${b}+${redir})?${b}+${hd}${b}*$"
411
+ local re_cat_after="^${b}*cat${b}+${hd}(${b}*${redir})?${b}*$"
412
+ local re_tee_before="^${b}*tee(${b}+${tee_flag})?${b}+${path}${b}+${hd}${b}*$"
413
+ local re_tee_after="^${b}*tee(${b}+${tee_flag})?${b}+${hd}${b}+(${tee_flag}${b}+)?${path}${b}*$"
414
+ if [[ $header =~ $re_git ]] || [[ $header =~ $re_cat_before ]] || [[ $header =~ $re_cat_after ]] || [[ $header =~ $re_tee_before ]] || [[ $header =~ $re_tee_after ]]; then
415
+ # Match the delimiter separately so prefix capture groups cannot change its index.
416
+ [[ $header =~ $hd ]] || return 1
417
+ word=${BASH_REMATCH[1]}
418
+ else
419
+ re="^${b}*$git${b}+-m${b}+$cat_start${b}+${hd}${b}*$"
420
+ [[ $header =~ $re ]] || return 1
421
+ [[ $header =~ $hd ]] || return 1
422
+ word=${BASH_REMATCH[1]}
423
+ [[ $body == *"$cat_end" ]] || return 1
424
+ body=${body%"$cat_end"}
425
+ fi
426
+ while IFS= read -r line; do
427
+ [ "$closed" = false ] || return 1
428
+ [ "$line" != "$word" ] || closed=true
429
+ done <<< "$body"
430
+ [ "$closed" = true ]
431
+ }
432
+ DATA_ONLY=false
433
+ _text_only_command && DATA_ONLY=true
434
+
435
+ # Project-specific unanchored patterns keep their verdicts too: only these
436
+ # three shipped SQL families participate in the text-only exception.
437
+ _sql_pattern() {
438
+ case $1 in
439
+ 'DROP[[:space:]]+(TABLE|DATABASE|SCHEMA)[[:space:]]'|\
440
+ 'DELETE[[:space:]]+FROM[[:space:]]+[[:alnum:]_]+[[:space:]]*;'|\
441
+ 'TRUNCATE[[:space:]]+TABLE') return 0 ;;
442
+ esac
443
+ return 1
444
+ }
445
+
272
446
  # Hardcoded floor, used only when blocked-patterns.conf is missing: recursive
273
447
  # rm (any flag spelling, flag before or after the target) of a catastrophic
274
448
  # target — the same rule as the rm lines in blocked-patterns.conf. Subpaths
275
449
  # such as /tmp/x are not blocked.
276
450
  _CMDPOS='(^|[;&|(`])[[:space:]]*'
277
- _WRAP='((sudo([[:space:]]+(-[ugChprtUDR][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|--(user|group|host|prompt|role|type|chdir)[[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|xargs([[:space:]]+(-[nILPsdEa][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|env([[:space:]]+(-[uCS][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|(command|time|nohup|exec|eval|then|do|else)([[:space:]]+(-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*)[[:space:]]+)*'
451
+ _WRAP='((sudo([[:space:]]+(-[ugChprtUDR][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|--(user|group|host|prompt|role|type|chdir)[[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|xargs([[:space:]]+(-[nILPsdEa][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|env([[:space:]]+(-[uCS][[:space:]]+[^-[:space:];&|][^[:space:];&|]*|-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*|(command|time|nohup|exec|eval|then|do|else)([[:space:]]+(-[^[:space:];&|]*|[A-Za-z_][A-Za-z0-9_]*=[^[:space:];&|]*))*)[[:space:]]+)*'
278
452
  _PATHRM='(\\|/(usr/)?bin/)?rm'
279
453
  _RM_FLAG='(-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)'
280
454
  _RM_ARGS='([[:space:]]+[^[:space:];&|]+)*'
@@ -315,9 +489,14 @@ fi
315
489
  # grep match or regex error falls through to the original per-line loop, which
316
490
  # still owns the reason and optional exclude semantics.
317
491
  _anchored_patterns="" _full_patterns="" _has_backreference=false
492
+ _has_worker_marker=false
318
493
  while IFS=$'\t' read -r pattern _reason _exclude || [ -n "$pattern" ]; do
319
494
  [ -z "$pattern" ] && continue
320
- case "$pattern" in \#*) continue ;; esac
495
+ case "$pattern" in
496
+ '#@exempt-next worker-worktree') _has_worker_marker=true; continue ;;
497
+ \#*) continue ;;
498
+ esac
499
+ if [ "$DATA_ONLY" = true ] && _sql_pattern "$pattern"; then continue; fi
321
500
  if [[ "$pattern" =~ \\[1-9] ]]; then _has_backreference=true; fi
322
501
  case "$pattern" in
323
502
  '(^|'*) _anchored_patterns="${_anchored_patterns:+$_anchored_patterns|}($pattern)" ;;
@@ -333,14 +512,27 @@ if [ -n "$_full_patterns" ]; then
333
512
  grep -qiE -- "$_full_patterns" <<<"$COMMAND" 2>/dev/null
334
513
  _full_status=$?
335
514
  fi
336
- if [ "$_anchored_status" -eq 1 ] && [ "$_full_status" -eq 1 ] && [ "$_has_backreference" != true ]; then exit 0; fi
515
+ if [ "$_anchored_status" -eq 1 ] && [ "$_full_status" -eq 1 ] && [ "$_has_backreference" != true ]; then
516
+ # Some accepted worker shapes (reset -q --hard, clean -f) already match no
517
+ # deny rule. Give those allows the same scope code without widening a rule.
518
+ if [ "$_has_worker_marker" = true ] && _worker_exempt; then doflow_decide allow worker-worktree; fi
519
+ exit 0
520
+ fi
337
521
 
338
522
  # ── Pattern matching ──────────────────────────────────────────────────────────
339
523
 
524
+ _exempt_next= _worker_skipped=false
340
525
  while IFS=$'\t' read -r pattern reason exclude || [ -n "$pattern" ]; do
341
526
  # Skip comments and empty lines
342
527
  [ -z "$pattern" ] && continue
343
- case "$pattern" in \#*) continue ;; esac
528
+ case "$pattern" in
529
+ '#@exempt-next worker-worktree') _exempt_next=worker-worktree; continue ;;
530
+ '#@exempt-next worker-dropdb') _exempt_next=worker-dropdb; continue ;;
531
+ \#*) continue ;;
532
+ esac
533
+ exemption=$_exempt_next
534
+ _exempt_next=
535
+ if [ "$DATA_ONLY" = true ] && _sql_pattern "$pattern"; then continue; fi
344
536
 
345
537
  # Match pattern against command (case-insensitive, POSIX extended regex: the default macOS
346
538
  # filesystem is case-insensitive, so RM and GIT run there).
@@ -368,6 +560,14 @@ while IFS=$'\t' read -r pattern reason exclude || [ -n "$pattern" ]; do
368
560
  fi
369
561
 
370
562
  if [ "$matched" = "true" ]; then
563
+ if [ "$exemption" = worker-worktree ] && _worker_exempt; then
564
+ _worker_skipped=worker-worktree
565
+ continue
566
+ fi
567
+ if [ "$exemption" = worker-dropdb ] && _worker_dropdb_exempt; then
568
+ _worker_skipped=worker-dropdb
569
+ continue
570
+ fi
371
571
  echo "[pre-bash-guard] ${reason:-Command blocked by pre-bash-guard}" >&2
372
572
  doflow_decide deny blocked-pattern
373
573
  exit 2
@@ -375,4 +575,5 @@ while IFS=$'\t' read -r pattern reason exclude || [ -n "$pattern" ]; do
375
575
  done < "$PATTERNS_FILE"
376
576
 
377
577
  # No match — allow
578
+ [ "$_worker_skipped" != false ] && doflow_decide allow "$_worker_skipped"
378
579
  exit 0