@khoavu882/doflow 2.0.0-beta.4 → 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 (497) hide show
  1. package/README.md +12 -11
  2. package/bin/doflow.js +21 -11
  3. package/core/.antigravity-plugin/plugin.json +1 -3
  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/mcp-tool-guard.sh +7 -17
  9. package/core/harnesses/claude/hooks/permission-audit.sh +3 -0
  10. package/core/harnesses/claude/hooks/post-compact.sh +35 -32
  11. package/core/harnesses/claude/hooks/post-edit-lint.sh +2 -0
  12. package/core/harnesses/claude/hooks/post-shell-observe.sh +48 -0
  13. package/core/harnesses/claude/hooks/post-tool-failure.sh +3 -0
  14. package/core/harnesses/claude/hooks/pre-bash-guard.sh +7 -17
  15. package/core/harnesses/claude/hooks/pre-compact.sh +23 -27
  16. package/core/harnesses/claude/hooks/pre-implement-gate.sh +7 -18
  17. package/core/harnesses/claude/hooks/session-end.sh +23 -10
  18. package/core/harnesses/claude/hooks/session-start.sh +5 -3
  19. package/core/harnesses/claude/hooks/skill-config-audit.sh +3 -0
  20. package/core/harnesses/claude/hooks/stop-check.sh +5 -3
  21. package/core/harnesses/claude/hooks/subagent-audit.sh +5 -5
  22. package/core/harnesses/claude/hooks/user-prompt-submit.sh +5 -3
  23. package/core/harnesses/claude/settings/settings.json +18 -0
  24. package/core/harnesses/claude/statusline/doflow-statusline.sh +1 -0
  25. package/core/harnesses/codex/agents/core-implementer.toml +1 -0
  26. package/core/harnesses/codex/agents/delivery-lead.toml +10 -0
  27. package/core/harnesses/codex/agents/quality-guardian.toml +1 -1
  28. package/core/harnesses/codex/agents/research-writer.toml +1 -1
  29. package/core/harnesses/codex/agents/reviewer.toml +10 -0
  30. package/core/harnesses/codex/agents/spec-analyst.toml +1 -1
  31. package/core/harnesses/codex/agents/system-architect.toml +1 -1
  32. package/core/harnesses/codex/hooks/lib.sh +44 -25
  33. package/core/harnesses/codex/hooks/mcp-tool-guard.sh +7 -15
  34. package/core/harnesses/codex/hooks/post-edit-lint.impl.sh +3 -1
  35. package/core/harnesses/codex/hooks/pre-bash-guard.sh +7 -15
  36. package/core/harnesses/codex/hooks/pre-compact.impl.sh +29 -26
  37. package/core/harnesses/codex/hooks/pre-implement-gate.sh +7 -16
  38. package/core/harnesses/codex/hooks/session-end.impl.sh +23 -10
  39. package/core/harnesses/codex/hooks/session-start.sh +5 -3
  40. package/core/harnesses/codex/hooks/stop-check.sh +6 -5
  41. package/core/harnesses/codex/hooks/subagent-audit.sh +18 -6
  42. package/core/harnesses/codex/hooks/user-prompt-submit.sh +5 -4
  43. package/core/harnesses/copilot/hooks/doflow-lib.sh +93 -0
  44. package/core/harnesses/copilot/hooks/doflow-mcp-tool-guard.sh +9 -0
  45. package/core/harnesses/copilot/hooks/doflow-pre-bash-guard.sh +9 -0
  46. package/core/harnesses/copilot/hooks/doflow-pre-implement-gate.sh +9 -0
  47. package/core/harnesses/copilot/hooks/doflow-session-start.sh +9 -0
  48. package/core/harnesses/copilot/hooks/doflow-stop-check.sh +9 -0
  49. package/core/harnesses/copilot/hooks/doflow-subagent-start.sh +9 -0
  50. package/core/harnesses/copilot/hooks/doflow-subagent-stop.sh +9 -0
  51. package/core/harnesses/copilot/hooks/doflow.json +57 -0
  52. package/core/harnesses/kiro/agents/doflow.json +1 -1
  53. package/core/harnesses/kiro/hooks/lib.sh +26 -6
  54. package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +21 -5
  55. package/core/harnesses/kiro/hooks/pre-bash-guard.sh +21 -5
  56. package/core/harnesses/kiro/hooks/pre-implement-gate.sh +21 -5
  57. package/core/harnesses/kiro/hooks/session-start.sh +12 -5
  58. package/core/harnesses/kiro/hooks/stop-check.sh +22 -30
  59. package/core/harnesses/pi/extensions/doflow-guard.js +167 -0
  60. package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +27 -12
  61. package/core/harnesses/shared/hooks/adapters/antigravity.js +14 -57
  62. package/core/harnesses/shared/hooks/bridge/bridge.sh +277 -0
  63. package/core/harnesses/shared/hooks/bridge/envelope.jq +129 -0
  64. package/core/harnesses/shared/hooks/bridge/observe-model.sh +75 -0
  65. package/core/harnesses/shared/hooks/bridge/respond.sh +70 -0
  66. package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +28 -14
  67. package/core/harnesses/shared/hooks/policies/capture-failure.sh +51 -16
  68. package/core/harnesses/shared/hooks/policies/compact-text.sh +46 -0
  69. package/core/harnesses/shared/hooks/policies/contract.sh +78 -0
  70. package/core/harnesses/shared/hooks/policies/deny-json.sh +35 -9
  71. package/core/harnesses/shared/hooks/policies/emission-rules.sh +37 -0
  72. package/core/harnesses/shared/hooks/policies/emission.jq +209 -0
  73. package/core/harnesses/shared/hooks/policies/emission.sh +383 -0
  74. package/core/harnesses/shared/hooks/policies/identity.sh +177 -0
  75. package/core/harnesses/shared/hooks/policies/lib.sh +65 -40
  76. package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +48 -35
  77. package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +284 -33
  78. package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +180 -110
  79. package/core/harnesses/shared/hooks/policies/session-context.sh +66 -7
  80. package/core/harnesses/shared/hooks/policies/stop-check.sh +61 -23
  81. package/core/harnesses/shared/hooks/policies/subagent-audit.sh +39 -6
  82. package/core/harnesses/shared/hooks/policies/summary-store.sh +347 -0
  83. package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +97 -159
  84. package/core/harnesses/shared/hooks/stream-hook-runner.js +114 -187
  85. package/core/harnesses/shared/locator/doflow-run +2 -3
  86. package/core/registry/advice.json +3 -3
  87. package/core/registry/assets.json +72 -80
  88. package/core/registry/capabilities.json +12 -0
  89. package/core/registry/contracts.json +60 -33
  90. package/core/registry/contribution-points.json +32 -0
  91. package/core/registry/gates.json +32 -0
  92. package/core/registry/guidance-caps.json +40 -0
  93. package/core/registry/harnesses.json +1542 -277
  94. package/core/registry/lifecycle.json +2837 -37
  95. package/core/registry/mcp.json +5 -1
  96. package/core/registry/models.json +7 -1
  97. package/core/registry/package-templates/README.md +19 -0
  98. package/core/registry/package-templates/agent/files/agent.md +9 -0
  99. package/core/registry/package-templates/agent/template.json +4 -0
  100. package/core/registry/package-templates/capability-provider/files/provider.json +9 -0
  101. package/core/registry/package-templates/capability-provider/template.json +11 -0
  102. package/core/registry/package-templates/debate-preset/files/preset.json +26 -0
  103. package/core/registry/package-templates/debate-preset/template.json +4 -0
  104. package/core/registry/package-templates/review-preset/files/preset.json +30 -0
  105. package/core/registry/package-templates/review-preset/template.json +4 -0
  106. package/core/registry/package-templates/skill/files/SKILL.md +8 -0
  107. package/core/registry/package-templates/skill/template.json +4 -0
  108. package/core/registry/package-templates/worker/files/worker.json +92 -0
  109. package/core/registry/package-templates/worker/template.json +11 -0
  110. package/core/registry/packages/doflow/doflow-package.json +15 -0
  111. package/core/registry/packages/doflow/presets/debate-3.json +26 -0
  112. package/core/registry/protocols.json +61 -0
  113. package/core/registry/routes.json +17 -0
  114. package/core/registry/rules.json +431 -0
  115. package/core/registry/settings.json +276 -0
  116. package/core/registry/storage.json +701 -30
  117. package/core/registry/telemetry.json +236 -0
  118. package/core/registry/transcripts.json +142 -0
  119. package/core/registry/verification.json +76 -1
  120. package/core/registry/workflows.json +55 -0
  121. package/core/shared/agent-specs/core-implementer.md +3 -0
  122. package/core/shared/agent-specs/delivery-lead.md +68 -0
  123. package/core/shared/agent-specs/quality-guardian.md +5 -1
  124. package/core/shared/agent-specs/research-writer.md +10 -3
  125. package/core/shared/agent-specs/reviewer.md +53 -0
  126. package/core/shared/agent-specs/spec-analyst.md +5 -6
  127. package/core/shared/agent-specs/system-architect.md +5 -1
  128. package/core/shared/guidance/DOFLOW_CORE.md +1 -5
  129. package/core/shared/guidance/modes/MODE_DeepResearch.md +1 -1
  130. package/core/shared/guidance/modes/MODE_Orchestration.md +4 -3
  131. package/core/shared/guidance/modes/MODE_Task_Management.md +14 -14
  132. package/core/shared/guidance/pointers/codex.md +1 -4
  133. package/core/shared/guidance/references/ARTIFACT_FORMAT.md +30 -16
  134. package/core/shared/guidance/references/ARTIFACT_VOICE.md +3 -2
  135. package/core/shared/guidance/references/CONSTITUTION_BASE.md +17 -15
  136. package/core/shared/guidance/references/DIAGRAM_GUIDE.md +1 -1
  137. package/core/shared/guidance/references/DOFLOW_CHAIN.md +17 -19
  138. package/core/shared/guidance/references/EVIDENCE_LEDGER.md +7 -8
  139. package/core/shared/guidance/references/MODEL_SELECTION.md +5 -3
  140. package/core/shared/guidance/references/ORCHESTRATION.md +1 -2
  141. package/core/shared/guidance/rules/RULES.md +45 -0
  142. package/core/shared/scripts/doflow/bash/do-args.sh +37 -0
  143. package/core/shared/scripts/doflow/bash/do-exec-paths.sh +4 -0
  144. package/core/shared/scripts/doflow/bash/do-git-state.sh +15 -4
  145. package/core/shared/scripts/doflow/bash/do-parallel-check.sh +4 -0
  146. package/core/shared/scripts/doflow/bash/do-paths.sh +40 -2
  147. package/core/shared/scripts/doflow/bash/do-prereqs.sh +2 -1
  148. package/core/shared/scripts/doflow/bash/do-review-package.sh +4 -0
  149. package/core/shared/scripts/doflow/bash/do-task-brief.sh +58 -16
  150. package/core/shared/scripts/doflow/bash/render-audit.sh +26 -2
  151. package/core/shared/scripts/doflow/bash/render-diagrams.sh +1 -0
  152. package/core/shared/scripts/doflow/bash/render-puml.sh +2 -1
  153. package/core/shared/scripts/doflow/bash/validate-artifacts.sh +48 -4
  154. package/core/shared/scripts/doflow/bin/doflow-run +154 -35
  155. package/core/shared/skills/do/SKILL.md +12 -10
  156. package/core/shared/skills/do/references/advice_calls.md +26 -0
  157. package/core/shared/skills/do/references/pm_routing.md +1 -2
  158. package/core/shared/skills/do/references/program_mode.md +31 -0
  159. package/core/shared/skills/do-brainstorm/SKILL.md +20 -74
  160. package/core/shared/skills/do-brainstorm/references/handoff_branches.md +35 -0
  161. package/core/shared/skills/do-brainstorm/references/intent_seed.md +21 -0
  162. package/core/shared/skills/do-brainstorm/references/round_log.md +17 -0
  163. package/core/shared/skills/do-code-review/SKILL.md +41 -144
  164. package/core/shared/skills/do-code-review/content-types/markdown.md +24 -2
  165. package/core/shared/skills/do-code-review/references/tools.md +90 -0
  166. package/core/shared/skills/do-code-review/scripts/doc_quality_checker.py +549 -7
  167. package/core/shared/skills/do-design/SKILL.md +24 -23
  168. package/core/shared/skills/do-diagnose/SKILL.md +14 -4
  169. package/core/shared/skills/do-document/SKILL.md +11 -1
  170. package/core/shared/skills/do-document/references/deep_research.md +1 -2
  171. package/core/shared/skills/do-document/references/embedded-research.md +18 -6
  172. package/core/shared/skills/do-document/references/implementation-flow.md +5 -6
  173. package/core/shared/skills/do-execute-plan/SKILL.md +17 -59
  174. package/core/shared/skills/do-execute-plan/references/handoff_record.md +7 -2
  175. package/core/shared/skills/do-execute-plan/references/parallel_dispatch.md +13 -0
  176. package/core/shared/skills/do-execute-plan/references/readiness_gate.md +28 -1
  177. package/core/shared/skills/do-execute-plan/references/run_position.md +13 -1
  178. package/core/shared/skills/do-execute-plan/references/scaffold.md +17 -9
  179. package/core/shared/skills/do-execute-plan/references/state_checkpoint.md +11 -0
  180. package/core/shared/skills/do-execute-plan/references/task_attempts.md +78 -24
  181. package/core/shared/skills/do-flow/SKILL.md +8 -9
  182. package/core/shared/skills/do-flow/references/task_classes.md +2 -2
  183. package/core/shared/skills/do-git/SKILL.md +4 -0
  184. package/core/shared/skills/do-implement/SKILL.md +7 -9
  185. package/core/shared/skills/do-plan/SKILL.md +29 -19
  186. package/core/shared/skills/do-test/SKILL.md +7 -4
  187. package/core/shared/templates/doflow/constitution-template.md +9 -3
  188. package/core/shared/templates/doflow/data-model-template.md +1 -1
  189. package/core/shared/templates/doflow/design-template.md +113 -16
  190. package/core/shared/templates/doflow/plan-template.md +11 -10
  191. package/core/shared/templates/doflow/question-log-template.md +14 -2
  192. package/core/shared/templates/doflow/requirement-template.md +1 -1
  193. package/core/worker-evidence/antigravity/checks.json +14 -0
  194. package/core/worker-evidence/codex/2026-10-18-cli-check.json +107 -0
  195. package/core/worker-evidence/codex/2026-10-18-live-run-edit.json +73 -0
  196. package/core/worker-evidence/codex/2026-10-18-live-run-read-only.json +62 -0
  197. package/core/worker-evidence/codex/2026-10-24-cli-check.json +126 -0
  198. package/core/worker-evidence/codex/checks.json +38 -0
  199. package/package.json +10 -6
  200. package/src/adapters/antigravity/index.js +226 -113
  201. package/src/adapters/claude/index.js +39 -5
  202. package/src/adapters/codex/agents.js +25 -15
  203. package/src/adapters/codex/hooks.js +40 -5
  204. package/src/adapters/codex/index.js +100 -22
  205. package/src/adapters/codex/native-projection.js +6 -2
  206. package/src/adapters/copilot/index.js +58 -23
  207. package/src/adapters/copy-tree.js +294 -48
  208. package/src/adapters/guidance-manifest.js +71 -0
  209. package/src/adapters/guidance-pointer.js +19 -0
  210. package/src/adapters/hook-commands.js +6 -10
  211. package/src/adapters/index.js +1 -0
  212. package/src/adapters/kiro/index.js +38 -13
  213. package/src/adapters/opencode/index.js +8 -8
  214. package/src/adapters/pi/hooks.js +120 -0
  215. package/src/adapters/pi/index.js +48 -16
  216. package/src/adapters/resolve-source.js +26 -2
  217. package/src/cli/commands/install.js +3 -2
  218. package/src/cli/commands/reconcile.js +53 -9
  219. package/src/cli/commands/remove.js +2 -2
  220. package/src/cli/commands/rollback.js +6 -3
  221. package/src/cli/commands/self-update.js +18 -9
  222. package/src/cli/commands/status.js +21 -2
  223. package/src/cli/commands/ui.js +99 -0
  224. package/src/cli/commands/update.js +3 -2
  225. package/src/cli/index.js +338 -33
  226. package/src/cli/runtime-commands.js +243 -52
  227. package/src/cli/shared.js +0 -2
  228. package/src/helper/git.js +1 -1
  229. package/src/helper/guardrails.js +1 -1
  230. package/src/helper/marker-merge.js +31 -13
  231. package/src/helper/package-ids.js +37 -0
  232. package/src/helper/semver.js +94 -0
  233. package/src/install/backup.js +105 -15
  234. package/src/install/context.js +1 -1
  235. package/src/install/targets.js +31 -16
  236. package/src/lifecycle/coverage.js +74 -0
  237. package/src/lifecycle/hook-step.js +61 -0
  238. package/src/lifecycle/hook-tier.js +160 -0
  239. package/src/lifecycle/index.js +434 -86
  240. package/src/lifecycle/retired-harnesses.js +318 -0
  241. package/src/lifecycle/view.js +35 -15
  242. package/src/registry/index.js +686 -32
  243. package/src/registry/worker-ids.js +45 -0
  244. package/src/release/build-package.js +0 -3
  245. package/src/release/docs-blocks.js +492 -0
  246. package/src/release/preflight.js +425 -0
  247. package/src/release/rehearse.js +401 -0
  248. package/src/release/release-workflow.js +733 -0
  249. package/src/release/sync-plugin-versions.js +43 -7
  250. package/src/runtime/advice/bench-gate.js +25 -0
  251. package/src/runtime/advice/cli.js +59 -16
  252. package/src/runtime/advice/client.js +32 -9
  253. package/src/runtime/advice/config.js +98 -133
  254. package/src/runtime/advice/doctor.js +1 -1
  255. package/src/runtime/advice/elevation.js +97 -0
  256. package/src/runtime/advice/endpoint-guard.js +9 -3
  257. package/src/runtime/advice/mode.js +37 -0
  258. package/src/runtime/advice/record.js +18 -5
  259. package/src/runtime/advice/registry.js +4 -1
  260. package/src/runtime/advice/shadow-config.js +229 -0
  261. package/src/runtime/advice/shadow-dispatcher.js +276 -0
  262. package/src/runtime/brief-rules.js +81 -0
  263. package/src/runtime/brief-tools.js +111 -34
  264. package/src/runtime/candidate.js +514 -0
  265. package/src/runtime/capabilities/executor.js +450 -0
  266. package/src/runtime/capabilities/loopback.js +41 -0
  267. package/src/runtime/capabilities/sanitizer.js +88 -0
  268. package/src/runtime/capability-router.js +135 -3
  269. package/src/runtime/checkouts.js +73 -11
  270. package/src/runtime/claims.js +27 -12
  271. package/src/runtime/cli-result.js +11 -4
  272. package/src/runtime/cli.js +221 -277
  273. package/src/runtime/config-home.js +42 -0
  274. package/src/runtime/context/arbitrate.js +767 -0
  275. package/src/runtime/context/budget.js +203 -0
  276. package/src/runtime/context/compose.js +1367 -0
  277. package/src/runtime/context/delivery-store.js +100 -0
  278. package/src/runtime/context/emission-rules.js +118 -0
  279. package/src/runtime/context/identity.js +227 -0
  280. package/src/runtime/context/mandatory.js +594 -0
  281. package/src/runtime/context/pointer.js +142 -0
  282. package/src/runtime/context/primary-command.js +117 -0
  283. package/src/runtime/context/primary.js +344 -0
  284. package/src/runtime/context/summary-store.js +655 -0
  285. package/src/runtime/context-pack.js +1106 -245
  286. package/src/runtime/decision-register.js +13 -4
  287. package/src/runtime/dispatch/admission.js +392 -0
  288. package/src/runtime/dispatch/attempt-end.js +274 -0
  289. package/src/runtime/dispatch/command.js +628 -0
  290. package/src/runtime/dispatch/controls.js +904 -0
  291. package/src/runtime/dispatch/envelope.js +276 -0
  292. package/src/runtime/dispatch/grants.js +310 -0
  293. package/src/runtime/dispatch/integrate.js +617 -0
  294. package/src/runtime/dispatch/loop.js +1835 -0
  295. package/src/runtime/dispatch/store.js +352 -0
  296. package/src/runtime/dispatch/verify-run.js +201 -0
  297. package/src/runtime/evidence-fields.js +39 -0
  298. package/src/runtime/evidence-ledger.js +41 -7
  299. package/src/runtime/failure/capture.js +3 -1
  300. package/src/runtime/failure/cli.js +25 -9
  301. package/src/runtime/failure/home.js +57 -30
  302. package/src/runtime/failure/store.js +7 -2
  303. package/src/runtime/feature-pointer.js +174 -0
  304. package/src/runtime/feature-resolve.js +13 -2
  305. package/src/runtime/fleet/command.js +77 -0
  306. package/src/runtime/fleet/projects.js +178 -0
  307. package/src/runtime/fleet/tree.js +383 -0
  308. package/src/runtime/freshness.js +77 -10
  309. package/src/runtime/git-memo.js +46 -0
  310. package/src/runtime/guidance/ablation.js +427 -0
  311. package/src/runtime/health.js +565 -17
  312. package/src/runtime/history-compactor.js +13 -3
  313. package/src/runtime/implementation-gate.js +277 -22
  314. package/src/runtime/inventory/coverage.js +1 -1
  315. package/src/runtime/inventory/index.js +1 -1
  316. package/src/runtime/inventory/precedence.js +2 -2
  317. package/src/runtime/inventory/siblings.js +6 -4
  318. package/src/runtime/knowledge/code-corpus.js +277 -0
  319. package/src/runtime/knowledge/code-lexical.js +130 -0
  320. package/src/runtime/knowledge/code-retrieval.js +307 -0
  321. package/src/runtime/knowledge/code-validation.js +106 -0
  322. package/src/runtime/knowledge/gp3-gate.js +74 -0
  323. package/src/runtime/knowledge/graph-adapter.js +288 -0
  324. package/src/runtime/knowledge/graph-fallback.js +394 -0
  325. package/src/runtime/knowledge/hybrid.js +136 -8
  326. package/src/runtime/knowledge/retrieval.js +34 -1
  327. package/src/runtime/knowledge/rtk-compress.js +77 -0
  328. package/src/runtime/knowledge/semble-bridge.py +106 -0
  329. package/src/runtime/knowledge/semble-lifecycle.js +114 -0
  330. package/src/runtime/knowledge/semble-provider.js +365 -0
  331. package/src/runtime/lifecycle/cli.js +16 -6
  332. package/src/runtime/lifecycle/event-store.js +8 -6
  333. package/src/runtime/lifecycle/followup.js +6 -1
  334. package/src/runtime/lifecycle/overview.js +24 -1
  335. package/src/runtime/lifecycle/report-store.js +8 -10
  336. package/src/runtime/lifecycle/retention.js +95 -31
  337. package/src/runtime/lifecycle/root.js +9 -1
  338. package/src/runtime/lifecycle/status.js +34 -17
  339. package/src/runtime/lifecycle/store-upkeep.js +56 -7
  340. package/src/runtime/mask.js +134 -7
  341. package/src/runtime/mcp-consultations.js +40 -6
  342. package/src/runtime/model-router.js +79 -7
  343. package/src/runtime/models/catalogue.js +191 -0
  344. package/src/runtime/models/command.js +153 -0
  345. package/src/runtime/off-words.js +12 -0
  346. package/src/runtime/outcome.js +3 -3
  347. package/src/runtime/packages/command.js +1009 -0
  348. package/src/runtime/packages/contributions.js +116 -0
  349. package/src/runtime/packages/digest.js +132 -0
  350. package/src/runtime/packages/fetch.js +182 -0
  351. package/src/runtime/packages/grants.js +192 -0
  352. package/src/runtime/packages/index-file.js +134 -0
  353. package/src/runtime/packages/instruction.js +90 -0
  354. package/src/runtime/packages/manifest.js +331 -0
  355. package/src/runtime/packages/points.js +477 -0
  356. package/src/runtime/packages/programs.js +216 -0
  357. package/src/runtime/packages/scaffold.js +277 -0
  358. package/src/runtime/packages/source.js +90 -0
  359. package/src/runtime/packages/state.js +164 -0
  360. package/src/runtime/packages/store.js +449 -0
  361. package/src/runtime/packages/tar.js +106 -0
  362. package/src/runtime/packages/view.js +406 -0
  363. package/src/runtime/plan-check.js +59 -0
  364. package/src/runtime/plan-content.js +324 -0
  365. package/src/runtime/policy-init.js +118 -0
  366. package/src/runtime/profiles/doctor.js +96 -0
  367. package/src/runtime/profiles/resolve.js +244 -0
  368. package/src/runtime/profiles/rollback.js +254 -0
  369. package/src/runtime/profiles/sampler.js +41 -0
  370. package/src/runtime/readiness-record.js +16 -11
  371. package/src/runtime/readiness.js +127 -16
  372. package/src/runtime/research-request.js +50 -25
  373. package/src/runtime/retrieval-plan.js +402 -73
  374. package/src/runtime/retrieval-record.js +293 -0
  375. package/src/runtime/review/command.js +246 -0
  376. package/src/runtime/review/dominance.js +68 -0
  377. package/src/runtime/review/findings.js +434 -0
  378. package/src/runtime/review/identity.js +255 -0
  379. package/src/runtime/review/protocol-command.js +109 -0
  380. package/src/runtime/review/protocols.js +405 -0
  381. package/src/runtime/review/resolve-protocol.js +302 -0
  382. package/src/runtime/review/start.js +530 -0
  383. package/src/runtime/roadmap/facts.js +479 -0
  384. package/src/runtime/roadmap/gates.js +270 -0
  385. package/src/runtime/roadmap/loop.js +1636 -0
  386. package/src/runtime/roadmap/merge.js +381 -0
  387. package/src/runtime/roadmap/rows.js +1113 -0
  388. package/src/runtime/roadmap/seed.js +951 -0
  389. package/src/runtime/rules-report.js +336 -0
  390. package/src/runtime/scaffold/artifacts.js +63 -9
  391. package/src/runtime/scaffold/generate.js +21 -9
  392. package/src/runtime/settings/autopilot.js +106 -0
  393. package/src/runtime/settings/cli.js +280 -0
  394. package/src/runtime/settings/config-write.js +314 -0
  395. package/src/runtime/settings/files.js +271 -0
  396. package/src/runtime/settings/legacy.js +154 -0
  397. package/src/runtime/settings/lexical.js +56 -0
  398. package/src/runtime/settings/prepared-run.js +269 -0
  399. package/src/runtime/settings/project-layers.js +118 -0
  400. package/src/runtime/settings/refs.js +288 -0
  401. package/src/runtime/settings/registry.js +553 -0
  402. package/src/runtime/settings/resolve.js +713 -0
  403. package/src/runtime/settings/shared.js +37 -0
  404. package/src/runtime/settings/worker-billing.js +107 -0
  405. package/src/runtime/settings/write.js +269 -0
  406. package/src/runtime/slot-command.js +78 -0
  407. package/src/runtime/slots.js +671 -0
  408. package/src/runtime/source-version.js +290 -0
  409. package/src/runtime/state-root.js +217 -0
  410. package/src/runtime/storage/cli.js +12 -5
  411. package/src/runtime/storage/engine.js +9 -2
  412. package/src/runtime/storage/registry.js +14 -1
  413. package/src/runtime/storage/roots.js +66 -16
  414. package/src/runtime/storage/rules.js +82 -10
  415. package/src/runtime/storage/upkeep.js +10 -2
  416. package/src/runtime/storage/work-status.js +245 -26
  417. package/src/runtime/task-attempts.js +589 -69
  418. package/src/runtime/task-classifier.js +47 -11
  419. package/src/runtime/task-graph.js +373 -0
  420. package/src/runtime/task-run.js +778 -0
  421. package/src/runtime/task-scope.js +202 -56
  422. package/src/runtime/task-sources.js +147 -0
  423. package/src/runtime/task-state.js +440 -65
  424. package/src/runtime/team/admission.js +301 -0
  425. package/src/runtime/team/envelope.js +324 -0
  426. package/src/runtime/team/review-guard.js +156 -0
  427. package/src/runtime/trace/analyze.js +427 -0
  428. package/src/runtime/trace/baseline.js +770 -0
  429. package/src/runtime/trace/ledger.js +63 -140
  430. package/src/runtime/trace/otlp-export.js +568 -0
  431. package/src/runtime/trace/otlp-map.js +481 -0
  432. package/src/runtime/trace/spans.js +745 -0
  433. package/src/runtime/trace/telemetry.js +225 -0
  434. package/src/runtime/trace/views.js +1 -0
  435. package/src/runtime/transcripts/command.js +98 -0
  436. package/src/runtime/transcripts/cursor.js +326 -0
  437. package/src/runtime/transcripts/read.js +126 -0
  438. package/src/runtime/transcripts/readers/claude-jsonl.js +74 -0
  439. package/src/runtime/transcripts/readers/codex-rollout.js +70 -0
  440. package/src/runtime/transcripts/readers/copilot-events.js +90 -0
  441. package/src/runtime/transcripts/readers/kiro-jsonl.js +77 -0
  442. package/src/runtime/transcripts/readers/pi-jsonl.js +59 -0
  443. package/src/runtime/transcripts/registry.js +161 -0
  444. package/src/runtime/transcripts/stats.js +161 -0
  445. package/src/runtime/verification/check-key.js +628 -0
  446. package/src/runtime/verification/contract-runner.js +546 -11
  447. package/src/runtime/verification/engine.js +499 -9
  448. package/src/runtime/verification/impact.js +617 -0
  449. package/src/runtime/verification/inflight.js +205 -0
  450. package/src/runtime/verification/registry.js +176 -0
  451. package/src/runtime/verification/reuse.js +184 -0
  452. package/src/runtime/verification/telemetry-emit.js +254 -0
  453. package/src/runtime/verification/test-selection.js +67 -7
  454. package/src/runtime/worker/active.js +90 -0
  455. package/src/runtime/worker/cancel.js +222 -0
  456. package/src/runtime/worker/catalogue.js +122 -0
  457. package/src/runtime/worker/command.js +677 -0
  458. package/src/runtime/worker/evidence.js +288 -0
  459. package/src/runtime/worker/launch.js +288 -0
  460. package/src/runtime/worker/models.js +137 -0
  461. package/src/runtime/worker/parsers.js +218 -0
  462. package/src/runtime/worker/probes/index.js +706 -0
  463. package/src/runtime/worker/process-tree.js +415 -0
  464. package/src/runtime/worker/quota.js +43 -0
  465. package/src/runtime/worker/readiness.js +713 -0
  466. package/src/runtime/worker/run-store.js +166 -0
  467. package/src/runtime/worker/supervisor.js +541 -0
  468. package/src/runtime/worker/worktree-check.js +252 -0
  469. package/src/runtime/workflow-engine.js +5 -1
  470. package/src/runtime/workflow-orchestrator.js +578 -63
  471. package/src/runtime/worktree.js +149 -22
  472. package/src/state/index.js +1 -1
  473. package/src/state/lockfile.js +64 -10
  474. package/src/state/run-lock.js +1 -1
  475. package/types/node-commonjs.d.ts +106 -7
  476. package/core/.antigravity-plugin/hooks.json +0 -39
  477. package/core/harnesses/gemini/hooks/hooks.json +0 -77
  478. package/core/harnesses/gemini/hooks/lib.sh +0 -302
  479. package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +0 -21
  480. package/core/harnesses/gemini/hooks/post-edit-lint.sh +0 -39
  481. package/core/harnesses/gemini/hooks/pre-bash-guard.sh +0 -21
  482. package/core/harnesses/gemini/hooks/pre-compact.sh +0 -38
  483. package/core/harnesses/gemini/hooks/pre-implement-gate.sh +0 -22
  484. package/core/harnesses/gemini/hooks/session-end.impl.sh +0 -89
  485. package/core/harnesses/gemini/hooks/session-end.sh +0 -5
  486. package/core/harnesses/gemini/hooks/session-start.sh +0 -5
  487. package/core/harnesses/gemini/settings/adapter-defaults.json +0 -4
  488. package/core/harnesses/shared/hooks/adapters/arg-fields.js +0 -19
  489. package/core/harnesses/shared/hooks/adapters/gemini.js +0 -114
  490. package/core/harnesses/shared/hooks/hooks.json +0 -47
  491. package/core/shared/guidance/FLAGS.md +0 -25
  492. package/core/shared/guidance/rules/RULE_01_SAFETY.md +0 -25
  493. package/core/shared/guidance/rules/RULE_02_WORKFLOW.md +0 -23
  494. package/core/shared/guidance/rules/RULE_03_QUALITY.md +0 -14
  495. package/core/shared/guidance/rules/RULE_04_QUESTIONS.md +0 -46
  496. package/src/adapters/gemini/hooks.js +0 -210
  497. package/src/adapters/gemini/index.js +0 -390
@@ -0,0 +1,129 @@
1
+ def nonempty($v): if ($v | type) == "string" and $v != "" then $v else null end;
2
+ def valid_model_id($v):
3
+ ($v | type) == "string" and ($v | length) >= 1 and ($v | length) <= 120 and
4
+ ($v | test("^[A-Za-z0-9][A-Za-z0-9 ._:/@()\\[\\]#+-]*$")) and
5
+ (($v | endswith(" ")) | not);
6
+ def model_identity($v):
7
+ if ($v | type) != "string" then {}
8
+ elif valid_model_id($v) then {model:$v}
9
+ else {modelUnparsed:true} end;
10
+ # Same reading as identity.ts and policies/identity.sh: ASCII-lowercased, main or worker kept, any other non-empty value is worker. Unset or empty stays null.
11
+ def norm_role($v): if ($v | type) == "string" and $v != "" then ($v | ascii_downcase) as $l | if $l == "main" or $l == "worker" then $l else "worker" end else null end;
12
+ def put($o; $k; $v): if $v == null then $o else $o + {($k): $v} end;
13
+ def event_name($fallback): if (.hook_event_name | type) == "string" then .hook_event_name else $fallback end;
14
+ def path_values($obj; $keys): first([$keys[] as $k | $obj[$k] | select(type == "string" and length > 0)][]) // null | if . == null then null else [.] end;
15
+ def string_value($obj; $keys): first([$keys[] as $k | $obj[$k] | select(type == "string") ][]) // null;
16
+ def session_identity($s): {session: nonempty($s)};
17
+ def correlation($id;$parent): {toolCallId:nonempty($id),parentToolCallId:nonempty($parent)};
18
+ def event_operation($e):
19
+ if $e == "SessionStart" or $e == "agentSpawn" then "session-start"
20
+ elif $e == "SessionEnd" then "session-end"
21
+ elif $e == "UserPromptSubmit" then "prompt"
22
+ elif $e == "Stop" or $e == "stop" then "stop"
23
+ elif $e == "SubagentStart" then "subagent-start"
24
+ elif $e == "SubagentStop" then "subagent-stop"
25
+ elif $e == "PreCompact" then "compact"
26
+ elif $e == "PostToolUse" then "post-tool"
27
+ else "tool" end;
28
+ def canonical_tool($name; $harness):
29
+ if ($name | type) != "string" then {operation:"tool"}
30
+ elif (($harness == "claude" or $harness == "codex" or $harness == "kiro" or $harness == "copilot") and ($name | test("^mcp__"))) or ($harness == "antigravity" and ($name | test("^mcp_"))) or ($harness == "pi" and ($name | test("^mcp__"))) then {operation:"mcp",tool:$name}
31
+ elif (($harness == "claude" or $harness == "codex" or $harness == "kiro" or $harness == "copilot") and (["Edit","Write","MultiEdit","apply_patch"] | index($name)) != null) or ($harness == "antigravity" and (["write_to_file","replace_file_content","multi_replace_file_content","edit_file","write_file","create_file","replace_file","replace","Edit","Write","MultiEdit","apply_patch"] | index($name)) != null) or ($harness == "pi" and (["edit","write"] | index($name)) != null) then {operation:"edit",tool:$name}
32
+ elif (($harness == "claude" or $harness == "codex" or $harness == "kiro" or $harness == "copilot") and $name == "Bash") or ($harness == "antigravity" and (["run_command","run_shell_command","Bash","bash"] | index($name)) != null) or ($harness == "pi" and $name == "bash") then {operation:"command",tool:$name}
33
+ else {operation:"tool",tool:$name} end;
34
+ def decode_claude:
35
+ (canonical_tool(.tool_name // ""; "claude")) as $t |
36
+ (if event_name($event) == "SessionStart" then model_identity(.model) else {} end) as $model |
37
+ if .tool_name == null then {operation: event_operation($event), identity: (session_identity(.session_id) + $model)}
38
+ else {operation:$t.operation, tool:$t.tool, paths:(if $t.operation == "edit" then path_values(.tool_input; ["file_path"]) else null end), command:(if $t.operation == "command" then string_value(.tool_input; ["command"]) else null end), identity:(session_identity(.session_id) + $model)} end;
39
+ def decode_codex:
40
+ decode_claude as $d |
41
+ (model_identity(.model)) as $model |
42
+ (if .tool_name == "apply_patch" then $d + {patch:(string_value(.tool_input; ["command"]))} else $d end) |
43
+ .identity += $model;
44
+ # Copilot CLI payloads (normalised to the Claude shape by the front door) carry no model, so the
45
+ # envelope names none, whatever a payload happens to hold under .model.
46
+ def decode_copilot:
47
+ decode_claude as $d |
48
+ (if .tool_name == "apply_patch" then $d + {patch:(string_value(.tool_input; ["command"]))} else $d end) |
49
+ .identity |= del(.model, .modelUnparsed);
50
+ def decode_kiro:
51
+ (if ((.tool_name // "") | type) == "string" and ((.tool_name // "") | startswith("mcp_")) then {operation:"mcp",tool:.tool_name}
52
+ elif ((.tool_name // "") | type) == "string" and ((.tool_name // "") | test("^@[^/]+/.+")) then
53
+ ((.tool_name | capture("^@(?<server>[^/]+)/(?<tool>.+)$")) as $m | {operation:"mcp",tool:("mcp__" + $m.server + "__" + $m.tool)})
54
+ else canonical_tool(.tool_name // ""; "kiro") end) as $t |
55
+ if .tool_name == null then {operation:event_operation($event),identity:session_identity(.session_id)}
56
+ else {operation:$t.operation,tool:$t.tool,paths:(if $t.operation == "edit" then path_values(.tool_input; ["file_path"]) else null end),command:(if $t.operation == "command" then string_value(.tool_input; ["command"]) else null end),identity:session_identity(.session_id)} end;
57
+ def decode_antigravity:
58
+ (model_identity(.modelName)) as $model |
59
+ (session_identity(if (.session_id | type) == "string" and .session_id != "" then .session_id else .conversationId end) + $model) as $identity |
60
+ if .toolCall == null then {operation:event_operation($event),identity:$identity}
61
+ else (canonical_tool(.toolCall.name // ""; "antigravity")) as $t |
62
+ {operation:$t.operation,tool:$t.tool,paths:(if $t.operation == "edit" then path_values(.toolCall.args; ["AbsolutePath","TargetFile","Path","file_path","path"]) else null end),command:(if $t.operation == "command" then string_value(.toolCall.args; ["CommandLine","command"]) else null end),identity:$identity} end;
63
+ def decode_pi:
64
+ (canonical_tool(.toolName // ""; "pi")) as $t |
65
+ {operation:$t.operation,tool:$t.tool,paths:(if $t.operation == "edit" then path_values(.input; ["path"]) else null end),command:(if $t.operation == "command" then string_value(.input; ["command"]) else null end),identity:(session_identity(.sessionId) + model_identity(.model)),correlation:(correlation(.toolCallId;.parentToolCallId))};
66
+ # IC-005: only this closed vocabulary enters telemetry. The policy envelope keeps its command.
67
+ def command_segment_class:
68
+ gsub("^\\s+|\\s+$"; "") | sub("^([A-Za-z_][A-Za-z0-9_]*=[^\\s]*\\s+)+"; "") |
69
+ if test("^cd\\s+") then "none"
70
+ elif test("^(doflow-run|doflow) verify( |$)") then "verify.verb"
71
+ elif test("^(npm|pnpm|yarn|bun)( run)? test$|^node --test( --[^ ]+)*( test/?)?$|^pytest( -[^ ]+)*$|^go test \\./\\.\\.\\.$|^cargo test$") then "test.full"
72
+ elif test("^(npm|pnpm|yarn|bun)( run)? test |^node --test |^pytest |^go test |^cargo test ") then "test.scoped"
73
+ elif test("(^| )(jest|vitest|mocha|playwright test)( |$)|^(npm|pnpm|yarn|bun) run test:[^ ]+") then "test.other"
74
+ else "none" end;
75
+ def classify($cmd):
76
+ try (if ($cmd | type) != "string" then "none" else
77
+ ["verify.verb", "test.full", "test.scoped", "test.other", "none"] as $rank |
78
+ [$cmd | splits("&&|\\|\\||;|\\|") | command_segment_class | . as $c | $rank | index($c)] |
79
+ (min // 4) as $best | $rank[$best]
80
+ end) catch "none";
81
+ # Telemetry tuple (IC-003): the first line in explicit `--arg mode encode`, read and stripped by
82
+ # bridge.sh. Every telemetry expression sits inside the `try`, so a fault here yields {dft:1} and
83
+ # never reaches the envelope; exactly one value comes out whatever the inside yields.
84
+ def dft_token($v): if ($v | type) == "string" and ($v | test("^[A-Za-z0-9][A-Za-z0-9._:-]{0,63}$")) then $v else null end;
85
+ def dft_id($v): if ($v | type) == "string" and ($v | test("^[A-Za-z0-9][A-Za-z0-9_-]{0,55}$")) then $v else null end;
86
+ def dft_tuple_base($native; $d):
87
+ if $ENV.DOFLOW_TELEMETRY == "off" then {dft:1} else
88
+ [try (now as $n | ($n | floor) as $s |
89
+ {dft:1, date:($s | strftime("%Y-%m-%d")),
90
+ timestamp:(($s | strftime("%Y-%m-%dT%H:%M:%S")) + "." + (((($n - $s) * 1000 | floor) + 1000 | tostring)[1:]) + "Z"),
91
+ session:dft_token($d.identity.session),
92
+ tool:(if $d.operation == "mcp" then null else dft_token($d.tool) end),
93
+ tool_call_id:dft_token(if $harness == "pi" then $native.toolCallId elif $harness == "claude" or $harness == "codex" then $native.tool_use_id else null end),
94
+ run:(dft_id($ENV.DOFLOW_RUN_ID) | if . == "local" then null else . end),
95
+ lineage:dft_id($ENV.DOFLOW_LINEAGE_ID),
96
+ attempt:($ENV.DOFLOW_ATTEMPT | if type == "string" and test("^[1-9][0-9]{0,3}$") then tonumber else null end),
97
+ role:norm_role($ENV.DOFLOW_SESSION_ROLE),
98
+ worker_run:($ENV.DOFLOW_WORKER_RUN | if type == "string" and test("\\Aw-[0-9a-f]{12}\\z") then . else null end)}) catch {dft:1}]
99
+ | if length == 1 then .[0] else {dft:1} end end;
100
+ def dft_tuple($native; $d):
101
+ dft_tuple_base($native; $d) |
102
+ [try (
103
+ if . == {dft:1} then . else
104
+ (if $ARGS.named.mode == "observe" then . + {duration_ms:null, tool_duration_ms:($native.duration_ms |
105
+ if type == "number" and . >= 0 and . <= 9007199254740991 and . == floor then . else null end)} else . + {tool_duration_ms:null} end) |
106
+ if ($policy == "pre-bash-guard" or $ARGS.named.mode == "observe") and $d.operation == "command"
107
+ then . + {test_class:classify($d.command)} else . + {test_class:null} end
108
+ end) catch {dft:1}] | if length == 1 then .[0] else {dft:1} end;
109
+ if ($ARGS.named.mode // "encode") == "minimal" then {doflow_event:{version:(1),harness:$harness,event:(event_name($event)),operation:event_operation($event)}} elif type != "object" then . else
110
+ . as $native |
111
+ ($ENV.DOFLOW_TASK_ID | nonempty(.)) as $task |
112
+ ($ENV.DOFLOW_RUN_ID | nonempty(.)) as $run |
113
+ ($ENV.DOFLOW_ATTEMPT | try tonumber catch null) as $attempt |
114
+ ($ENV.DOFLOW_LINEAGE_ID | nonempty(.)) as $lineage |
115
+ ($ENV.DOFLOW_WORKTREE | nonempty(.)) as $worktree |
116
+ norm_role($ENV.DOFLOW_SESSION_ROLE) as $role |
117
+ (if $harness == "claude" then decode_claude elif $harness == "codex" then decode_codex elif $harness == "kiro" then decode_kiro elif $harness == "copilot" then decode_copilot elif $harness == "antigravity" then decode_antigravity elif $harness == "pi" then decode_pi else {operation:"tool"} end) as $d |
118
+ if $ARGS.named.mode == "observe" then
119
+ if $harness == "claude" and $d.operation == "command" and $native.hook_event_name == $event and
120
+ ($event == "PostToolUse" or $event == "PostToolUseFailure")
121
+ then dft_tuple($native; $d) else {dft:1} end
122
+ else
123
+ ({version:1,harness:$harness,event:(event_name($event)),operation:$d.operation,tool:$d.tool,paths:$d.paths,command:$d.command,patch:$d.patch,taskId:$task,
124
+ identity:($d.identity + {run:$run,attempt:$attempt,lineage:$lineage,role:$role,worktree:$worktree} | with_entries(select(.value != null)) | if length > 0 then . else null end),
125
+ correlation:(($d.correlation // {}) | with_entries(select(.value != null)) | if length > 0 then . else null end) } | with_entries(select(.value != null))) as $envelope |
126
+ (if $ARGS.named.mode == "encode" then dft_tuple($native; $d) else empty end),
127
+ $native + {doflow_event:$envelope}
128
+ end
129
+ end
@@ -0,0 +1,75 @@
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
3
+ # observe-model.sh — the shared bridge step `observe-model` (row 120 IC-005).
4
+ #
5
+ # Defines doflow_observe_model <harness> <envelope>. Sourcing defines it only. bridge.sh calls it once
6
+ # per event, after the envelope is built, in a subshell with every stream discarded:
7
+ #
8
+ # ( . "<bridge-dir>/observe-model.sh" && doflow_observe_model "$harness" "$payload" ) >/dev/null 2>&1 </dev/null || true
9
+ #
10
+ # so it never changes the decision, the exit code or the output, and every I/O error is ignored.
11
+ #
12
+ # When the envelope carries identity.model and a session id it records the model in one file under
13
+ # lib.sh's STATE_DIR (<config home>/doflow/session-env):
14
+ # - main session (DOFLOW_SESSION_ROLE unset or main): primary/<cwd_hash cwd>/<harness>/<sessionKey>.json,
15
+ # where cwd is the payload's absolute `cwd`, else the hook's working directory;
16
+ # - worker (any other role, identity.sh's reading) with a launched identity (DOFLOW_RUN_ID other
17
+ # than `local`, DOFLOW_LINEAGE_ID and DOFLOW_ATTEMPT, identity.sh's patterns):
18
+ # worker-model/L.<run>.<lineage>.<attempt>.json. A worker without one writes nothing.
19
+ # sessionKey is the first 16 hex of the sha256 of the session id's bytes. The file holds exactly
20
+ # {"schema":1,"harness":"<id>","model":"<model-id>","event":"<event>","observedAt":"<UTC>","session":"<sessionKey>"}
21
+ # and is written only when absent or when its model differs: a tmp file in the same folder, then a
22
+ # rename, mode 0600. With no config home, no sha256 tool or a link in place of the file, nothing.
23
+ # The storage stores model-observations and worker-model-observations bound both folders.
24
+
25
+ _DOFLOW_SRC=${BASH_SOURCE[0]//\\//}; case $_DOFLOW_SRC in */*) _DOFLOW_OBSERVE_DIR=${_DOFLOW_SRC%/*} ;; *) _DOFLOW_OBSERVE_DIR=. ;; esac
26
+
27
+ doflow_observe_model() {
28
+ local harness="${1:-}" envelope="${2:-}" fields="" model="" model_json="" session="" cwd="" event="" now=""
29
+ local key="" dir="" file="" current="" tmp="" run="${DOFLOW_RUN_ID:-}" lineage="${DOFLOW_LINEAGE_ID:-}" attempt="${DOFLOW_ATTEMPT:-}"
30
+ case "$harness" in ''|*[!a-z0-9-]*) return 0 ;; esac
31
+ # lib.sh gives STATE_DIR (the config-home rule), cwd_hash and identity.sh's role reading.
32
+ . "$_DOFLOW_OBSERVE_DIR/../policies/lib.sh" || return 0
33
+ [ -n "${STATE_DIR:-}" ] || return 0
34
+ fields=$(printf '%s\n' "$envelope" | jq -r '
35
+ def text: type == "string" and length > 0 and (test("[[:cntrl:]]") | not);
36
+ .doflow_event as $e
37
+ | select(($e.identity.model | text) and ($e.identity.session | text))
38
+ | $e.identity.model, ($e.identity.model | tojson), $e.identity.session,
39
+ (if (.cwd | text) and (.cwd | startswith("/")) then .cwd else "-" end),
40
+ ($e.event | tojson), (now | strftime("%Y-%m-%dT%H:%M:%SZ"))' 2>/dev/null) || return 0
41
+ { IFS= read -r model && IFS= read -r model_json && IFS= read -r session && IFS= read -r cwd \
42
+ && IFS= read -r event && IFS= read -r now; } <<<"$fields" || return 0
43
+
44
+ if command -v sha256sum >/dev/null 2>&1; then key=$(printf '%s' "$session" | sha256sum | cut -c1-16)
45
+ elif command -v shasum >/dev/null 2>&1; then key=$(printf '%s' "$session" | shasum -a 256 | cut -c1-16)
46
+ else return 0; fi
47
+ [[ $key =~ ^[0-9a-f]{16}$ ]] || return 0
48
+
49
+ _doflow_role_resolve
50
+ if [ "$_DF_ROLE" = main ]; then
51
+ case "$cwd" in /*) ;; *) cwd=$PWD ;; esac
52
+ dir="$STATE_DIR/primary/$(cwd_hash "$cwd")/$harness"
53
+ file="$dir/$key.json"
54
+ else
55
+ [ "$run" != local ] && [[ $run =~ ^[A-Za-z0-9][A-Za-z0-9_-]{0,55}$ ]] || return 0
56
+ [[ $lineage =~ ^[A-Za-z0-9][A-Za-z0-9_-]{0,55}$ ]] && [[ $attempt =~ ^[1-9][0-9]{0,3}$ ]] || return 0
57
+ dir="$STATE_DIR/worker-model"
58
+ file="$dir/L.$run.$lineage.$attempt.json"
59
+ fi
60
+
61
+ [ ! -L "$file" ] || return 0
62
+ if [ -f "$file" ]; then
63
+ current=$(jq -r 'if (.model | type) == "string" then .model else empty end' "$file" 2>/dev/null) || current=""
64
+ [ "$current" != "$model" ] || return 0
65
+ fi
66
+ umask 077
67
+ mkdir -p "$dir" || return 0
68
+ tmp="$file.tmp.$$"
69
+ if printf '{"schema":1,"harness":"%s","model":%s,"event":%s,"observedAt":"%s","session":"%s"}\n' \
70
+ "$harness" "$model_json" "$event" "$now" "$key" >"$tmp" && mv -f "$tmp" "$file"; then
71
+ return 0
72
+ fi
73
+ rm -f "$tmp"
74
+ return 0
75
+ }
@@ -0,0 +1,70 @@
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
3
+ # Native hook response renderers for the shared lifecycle bridge.
4
+ _DOFLOW_SRC=${BASH_SOURCE[0]//\\//}; case $_DOFLOW_SRC in */*) _DOFLOW_RESPOND_DIR=${_DOFLOW_SRC%/*} ;; *) _DOFLOW_RESPOND_DIR=. ;; esac
5
+ . "$_DOFLOW_RESPOND_DIR/../policies/deny-json.sh"
6
+
7
+ doflow_bridge_native_event_var() {
8
+ DF_NE=$2
9
+ case "$1:$2" in
10
+ kiro:preToolUse) DF_NE=PreToolUse ;;
11
+ kiro:agentSpawn) DF_NE=SessionStart ;;
12
+ kiro:stop) DF_NE=Stop ;;
13
+ esac
14
+ }
15
+
16
+ doflow_bridge_native_event() {
17
+ local harness="$1" event="$2"
18
+ case "$harness:$event" in
19
+ kiro:preToolUse) event=PreToolUse ;;
20
+ kiro:agentSpawn) event=SessionStart ;;
21
+ kiro:stop) event=Stop ;;
22
+ esac
23
+ printf '%s\n' "$event"
24
+ }
25
+
26
+ doflow_render_response() {
27
+ local harness="$1" event="$2" outcome="$3" reason="$4" status="$5" engine="${DOFLOW_KIRO_ENGINE:-2x}"
28
+ doflow_bridge_native_event_var "$harness" "$event"; event=$DF_NE
29
+ if [ "$outcome" = deny ]; then
30
+ case "$harness:$event" in
31
+ claude:PreToolUse|codex:PreToolUse) emit_pretooluse_deny_nested "$reason" || return $?; return 0 ;;
32
+ kiro:PreToolUse) printf '%s' "$reason" >&2; return 2 ;;
33
+ antigravity:PreToolUse)
34
+ if command -v jq >/dev/null 2>&1; then jq -cn --arg reason "$reason" '{decision:"deny",reason:$reason}'
35
+ else printf '{"decision":"deny","reason":%s}\n' "$( _doflow_json_reason "$reason" )"; fi
36
+ return $? ;;
37
+ antigravity:Stop)
38
+ if command -v jq >/dev/null 2>&1; then jq -cn --arg reason "$reason" '{decision:"continue",reason:$reason}'
39
+ else printf '{"decision":"continue","reason":%s}\n' "$( _doflow_json_reason "$reason" )"; fi
40
+ return $? ;;
41
+ copilot:PreToolUse)
42
+ if command -v jq >/dev/null 2>&1; then jq -cn --arg reason "$reason" '{permissionDecision:"deny",permissionDecisionReason:$reason}'
43
+ else printf '{"permissionDecision":"deny","permissionDecisionReason":%s}\n' "$( _doflow_json_reason "$reason" )"; fi
44
+ return $? ;;
45
+ copilot:Stop)
46
+ if command -v jq >/dev/null 2>&1; then jq -cn --arg reason "$reason" '{decision:"block",reason:$reason}'
47
+ else printf '{"decision":"block","reason":%s}\n' "$( _doflow_json_reason "$reason" )"; fi
48
+ return $? ;;
49
+ pi:tool_call)
50
+ if command -v jq >/dev/null 2>&1; then jq -cn --arg reason "$reason" '{block:true,reason:$reason}'
51
+ else printf '{"block":true,"reason":%s}\n' "$( _doflow_json_reason "$reason" )"; fi
52
+ return $? ;;
53
+ kiro:Stop|kiro:stop)
54
+ if [ "$engine" = v3 ]; then
55
+ if command -v jq >/dev/null 2>&1; then jq -cn --arg reason "$reason" '{decision:"block",reason:$reason}'
56
+ else printf '{"decision":"block","reason":%s}\n' "$( _doflow_json_reason "$reason" )"; fi
57
+ return $?
58
+ fi
59
+ printf '%s' "$reason" >&2; return 2 ;;
60
+ codex:Stop|codex:stop) printf '%s\n' '{}' >&1; printf '%s\n' "$reason" >&2; return 2 ;;
61
+ *) printf '%s\n' "$reason" >&2; return 2 ;;
62
+ esac
63
+ fi
64
+ case "$harness:$event" in
65
+ codex:Stop|codex:stop) printf '%s\n' '{}' ;;
66
+ antigravity:PreToolUse) printf '%s\n' '{"decision":"allow"}' ;;
67
+ kiro:PreToolUse|claude:Stop) if [ "$status" -ne 0 ]; then printf '%s' "$reason" >&2; fi ;;
68
+ esac
69
+ return "$status"
70
+ }
@@ -4,27 +4,36 @@
4
4
  # regex<TAB>reason shown to the harness<TAB>exclude_regex (optional)
5
5
  #
6
6
  # Rules:
7
- # - Lines beginning with # are comments (skipped)
7
+ # - Lines beginning with # are comments (skipped), except the marker
8
+ # #@exempt-next worker-worktree arms only the next pattern line for the
9
+ # hook's exact dispatch-worker reset/clean exemption. The worker-dropdb
10
+ # marker arms only the DROP row for one exact listed database invocation
11
+ # from the launch snapshot. Other rules still run.
8
12
  # - Empty lines are skipped
9
13
  # - Regex is POSIX Extended Regular Expression (ERE, as used by `grep -E`),
10
14
  # matched case-insensitively against the full command string (RM and GIT run
11
15
  # on a case-insensitive filesystem such as the macOS default), which
12
16
  # includes any expanded heredocs or -m "..." arguments. Git and rm
13
17
  # patterns use a command-context anchor:
14
- # (^|[[:space:]]*(&&|\|\||;)[[:space:]]*)
18
+ # (^|[;&|(`])[[:space:]]*
15
19
  # so they only fire when the dangerous text is an actual invocation,
16
20
  # not when it appears as text inside a commit message or other argument.
17
21
  # - Anchored patterns (those beginning with "(^|") are matched against the
18
22
  # command with quoted text blanked out (pre-bash-guard.sh scrub_quotes),
19
23
  # so echo "x && rm -rf /" or git commit -m "git reset --hard" is not a
20
24
  # command. The argument of sh -c, bash -c, zsh -c and eval is NOT blanked
21
- # (it executes). Every other pattern is matched against the full text.
22
- # - The rm lines use a wider command position than the others: start of a
25
+ # (it executes). SQL rules use the raw full text. Only the three shipped
26
+ # SQL rows may be skipped by the hook's positive raw-command text-only
27
+ # allow-list: literal commit/echo/printf/gh bodies or exact quoted heredocs
28
+ # to commit/cat/gh (including the Claude commit cat substitution).
29
+ # Other shapes retain the raw SQL verdict; dot-led file targets are excluded.
30
+ # - The git, rm and find lines use command positions at the start of a
23
31
  # line or after ; & | ( or a backtick, then optional sudo/command/time/
24
32
  # nohup/exec/env (with options, VAR=x and, per wrapper, the options that take
25
33
  # a separate argument: sudo -u/-g/-C/-h/-p/-r/-t/-U/-D/-R and --user/--group
26
- # root, xargs -n/-I/-L/-P/-s/-d/-E/-a, env -u/-C/-S)/xargs/eval/then/do/else, then rm,
27
- # \rm, /bin/rm or /usr/bin/rm. "git rm" and "echo rm -rf /" are therefore
34
+ # root, xargs -n/-I/-L/-P/-s/-d/-E/-a, env -u/-C/-S)/xargs/eval/then/do/else
35
+ # or bare NAME=value assignments, then git/rm/find, optionally prefixed
36
+ # with \, /bin/ or /usr/bin/. "git rm" and "echo rm -rf /" are therefore
28
37
  # not matched. pre-bash-guard.sh's hardcoded floor uses the same rule.
29
38
  # Matching is case-insensitive, so an uppercase flag-only option that shares a
30
39
  # letter with a value-taking one (sudo -H beside -h, xargs -p beside -P) is
@@ -34,6 +43,8 @@
34
43
  # inside a flag cluster such as -uf), --force and a +refspec; the
35
44
  # --force-with-lease form stays allowed, as the reason text advises, but a
36
45
  # +refspec still blocks next to a lease (git push --force-with-lease origin +main).
46
+ # The clean rule also blocks -ff: double force can delete nested repos and
47
+ # is never eligible for the worker-worktree exemption.
37
48
  # - The find rule blocks -delete, and -exec rm, off the root, a home directory
38
49
  # or a system directory (among other start paths, find or \find or
39
50
  # /usr/bin/find) when only -type, -maxdepth/-mindepth N, -mount, -xdev,
@@ -54,18 +65,21 @@
54
65
  # Add project-specific patterns at the bottom of this file.
55
66
 
56
67
  # ── Dangerous git operations ──────────────────────────────────────────────────
57
- (^|[[:space:]]*(&&|\|\||;)[[:space:]]*)git([[:space:]]+(-[Cc][[:space:]]+[^[:space:];&|]+|--(git-dir|work-tree|namespace|exec-path)[[:space:]]+[^[:space:];&|]+|-[^[:space:];&|]+))*[[:space:]]+push([[:space:]]+[^[:space:];&|]+)*[[:space:]]+(--force|-[a-zA-Z]*f[a-zA-Z]*)([^-[:alnum:]]|$) Force push blocked — use --force-with-lease or confirm with user first
58
- (^|[[:space:]]*(&&|\|\||;)[[:space:]]*)git([[:space:]]+(-[Cc][[:space:]]+[^[:space:];&|]+|--(git-dir|work-tree|namespace|exec-path)[[:space:]]+[^[:space:];&|]+|-[^[:space:];&|]+))*[[:space:]]+push([[:space:]]+[^[:space:];&|]+)*[[:space:]]+\+[^[:space:];&|]+ Force push blocked — use --force-with-lease or confirm with user first
59
- (^|[[:space:]]*(&&|\|\||;)[[:space:]]*)git([[:space:]]+(-[Cc][[:space:]]+[^[:space:];&|]+|--(git-dir|work-tree|namespace|exec-path)[[:space:]]+[^[:space:];&|]+|-[^[:space:];&|]+))*[[:space:]]+reset --hard Destructive reset blocked — commit or stash changes before resetting
60
- (^|[[:space:]]*(&&|\|\||;)[[:space:]]*)git([[:space:]]+(-[Cc][[:space:]]+[^[:space:];&|]+|--(git-dir|work-tree|namespace|exec-path)[[:space:]]+[^[:space:];&|]+|-[^[:space:];&|]+))*[[:space:]]+clean -fd Irreversible clean blocked — use --dry-run to preview what would be deleted
68
+ (^|[;&|(`])[[:space:]]*((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:]]+)*(\\|/(usr/)?bin/)?git([[:space:]]+(-[Cc][[:space:]]+[^[:space:];&|]+|--(git-dir|work-tree|namespace|exec-path)[[:space:]]+[^[:space:];&|]+|-[^[:space:];&|]+))*[[:space:]]+push([[:space:]]+[^[:space:];&|]+)*[[:space:]]+(--force|-[a-zA-Z]*f[a-zA-Z]*)([^-[:alnum:]]|$) Force push blocked — use --force-with-lease or confirm with user first
69
+ (^|[;&|(`])[[:space:]]*((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:]]+)*(\\|/(usr/)?bin/)?git([[:space:]]+(-[Cc][[:space:]]+[^[:space:];&|]+|--(git-dir|work-tree|namespace|exec-path)[[:space:]]+[^[:space:];&|]+|-[^[:space:];&|]+))*[[:space:]]+push([[:space:]]+[^[:space:];&|]+)*[[:space:]]+\+[^[:space:];&|]+ Force push blocked — use --force-with-lease or confirm with user first
70
+ #@exempt-next worker-worktree
71
+ (^|[;&|(`])[[:space:]]*((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:]]+)*(\\|/(usr/)?bin/)?git([[:space:]]+(-[Cc][[:space:]]+[^[:space:];&|]+|--(git-dir|work-tree|namespace|exec-path)[[:space:]]+[^[:space:];&|]+|-[^[:space:];&|]+))*[[:space:]]+reset --hard Destructive reset blocked — commit or stash changes before resetting
72
+ #@exempt-next worker-worktree
73
+ (^|[;&|(`])[[:space:]]*((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:]]+)*(\\|/(usr/)?bin/)?git([[:space:]]+(-[Cc][[:space:]]+[^[:space:];&|]+|--(git-dir|work-tree|namespace|exec-path)[[:space:]]+[^[:space:];&|]+|-[^[:space:];&|]+))*[[:space:]]+(clean -fd|clean -ff) Irreversible clean blocked — use --dry-run to preview what would be deleted
61
74
 
62
75
  # ── Filesystem catastrophes ───────────────────────────────────────────────────
63
- (^|[;&|(`])[[:space:]]*((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:]]+)*(\\|/(usr/)?bin/)?rm([[:space:]]+[^[:space:];&|]+)*[[:space:]]+((-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)([[:space:]]+[^[:space:];&|]+)*[[:space:]]+(/+\*{0,2})([[:space:];&|)`]|$)|(/+\*{0,2})([[:space:]]+[^[:space:];&|]+)*[[:space:]]+(-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)([[:space:];&|)`]|$)) Catastrophic delete blocked — recursive rm of the root directory (/)
64
- (^|[;&|(`])[[:space:]]*((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:]]+)*(\\|/(usr/)?bin/)?rm([[:space:]]+[^[:space:];&|]+)*[[:space:]]+((-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)([[:space:]]+[^[:space:];&|]+)*[[:space:]]+((~[A-Za-z_][A-Za-z0-9_.-]*|~|\$HOME|\$\{HOME\})(/\*{0,2})?)([[:space:];&|)`]|$)|((~[A-Za-z_][A-Za-z0-9_.-]*|~|\$HOME|\$\{HOME\})(/\*{0,2})?)([[:space:]]+[^[:space:];&|]+)*[[:space:]]+(-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)([[:space:];&|)`]|$)) Catastrophic delete blocked — recursive rm of the home directory
65
- (^|[;&|(`])[[:space:]]*((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:]]+)*(\\|/(usr/)?bin/)?rm([[:space:]]+[^[:space:];&|]+)*[[:space:]]+((-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)([[:space:]]+[^[:space:];&|]+)*[[:space:]]+(/(Users|home|etc|usr|bin|sbin|var|opt|System|Library|Applications|Volumes|private|root|boot|lib|dev|proc)(/\*{0,2})?)([[:space:];&|)`]|$)|(/(Users|home|etc|usr|bin|sbin|var|opt|System|Library|Applications|Volumes|private|root|boot|lib|dev|proc)(/\*{0,2})?)([[:space:]]+[^[:space:];&|]+)*[[:space:]]+(-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)([[:space:];&|)`]|$)) Catastrophic delete blocked — recursive rm of a system directory
66
- (^|[;&|(`])[[:space:]]*((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:]]+)*(\\|/(usr/)?bin/)?find([[:space:]]+-[HLP])*([[:space:]]+[^-[:space:];&|][^[:space:];&|]*)*[[:space:]]+(/+\*{0,2}|(~[A-Za-z_][A-Za-z0-9_.-]*|~|\$HOME|\$\{HOME\})(/\*{0,2})?|/(Users|home|etc|usr|bin|sbin|var|opt|System|Library|Applications|Volumes|private|root|boot|lib|dev|proc)(/\*{0,2})?)([[:space:]]+([^-[:space:];&|][^[:space:];&|]*|-(mount|xdev|depth|noleaf|follow|ignore_readdir_race)|-(type|maxdepth|mindepth)[[:space:]]+[^[:space:];&|]+))*[[:space:]]+(-delete([[:space:];&|)`]|$)|-exec(dir)?[[:space:]]+(\\|/(usr/)?bin/)?rm([[:space:];&|)`]|$)) Catastrophic delete blocked — find -delete or -exec rm from the root, home or a system directory with no name, path or time predicate to scope it
76
+ (^|[;&|(`])[[:space:]]*((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:]]+)*(\\|/(usr/)?bin/)?rm([[:space:]]+[^[:space:];&|]+)*[[:space:]]+((-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)([[:space:]]+[^[:space:];&|]+)*[[:space:]]+(/+\*{0,2})([[:space:];&|)`]|$)|(/+\*{0,2})([[:space:]]+[^[:space:];&|]+)*[[:space:]]+(-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)([[:space:];&|)`]|$)) Catastrophic delete blocked — recursive rm of the root directory (/)
77
+ (^|[;&|(`])[[:space:]]*((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:]]+)*(\\|/(usr/)?bin/)?rm([[:space:]]+[^[:space:];&|]+)*[[:space:]]+((-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)([[:space:]]+[^[:space:];&|]+)*[[:space:]]+((~[A-Za-z_][A-Za-z0-9_.-]*|~|\$HOME|\$\{HOME\})(/\*{0,2})?)([[:space:];&|)`]|$)|((~[A-Za-z_][A-Za-z0-9_.-]*|~|\$HOME|\$\{HOME\})(/\*{0,2})?)([[:space:]]+[^[:space:];&|]+)*[[:space:]]+(-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)([[:space:];&|)`]|$)) Catastrophic delete blocked — recursive rm of the home directory
78
+ (^|[;&|(`])[[:space:]]*((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:]]+)*(\\|/(usr/)?bin/)?rm([[:space:]]+[^[:space:];&|]+)*[[:space:]]+((-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)([[:space:]]+[^[:space:];&|]+)*[[:space:]]+(/(Users|home|etc|usr|bin|sbin|var|opt|System|Library|Applications|Volumes|private|root|boot|lib|dev|proc)(/\*{0,2})?)([[:space:];&|)`]|$)|(/(Users|home|etc|usr|bin|sbin|var|opt|System|Library|Applications|Volumes|private|root|boot|lib|dev|proc)(/\*{0,2})?)([[:space:]]+[^[:space:];&|]+)*[[:space:]]+(-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)([[:space:];&|)`]|$)) Catastrophic delete blocked — recursive rm of a system directory
79
+ (^|[;&|(`])[[:space:]]*((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:]]+)*(\\|/(usr/)?bin/)?find([[:space:]]+-[HLP])*([[:space:]]+[^-[:space:];&|][^[:space:];&|]*)*[[:space:]]+(/+\*{0,2}|(~[A-Za-z_][A-Za-z0-9_.-]*|~|\$HOME|\$\{HOME\})(/\*{0,2})?|/(Users|home|etc|usr|bin|sbin|var|opt|System|Library|Applications|Volumes|private|root|boot|lib|dev|proc)(/\*{0,2})?)([[:space:]]+([^-[:space:];&|][^[:space:];&|]*|-(mount|xdev|depth|noleaf|follow|ignore_readdir_race)|-(type|maxdepth|mindepth)[[:space:]]+[^[:space:];&|]+))*[[:space:]]+(-delete([[:space:];&|)`]|$)|-exec(dir)?[[:space:]]+(\\|/(usr/)?bin/)?rm([[:space:];&|)`]|$)) Catastrophic delete blocked — find -delete or -exec rm from the root, home or a system directory with no name, path or time predicate to scope it
67
80
 
68
81
  # ── SQL destructive statements (no anchor — must catch inside psql heredocs) ──
82
+ #@exempt-next worker-dropdb
69
83
  DROP[[:space:]]+(TABLE|DATABASE|SCHEMA)[[:space:]] Destructive DDL blocked — confirm with user before dropping
70
84
  DELETE[[:space:]]+FROM[[:space:]]+[[:alnum:]_]+[[:space:]]*; Unscoped DELETE blocked — this would affect all rows, add a WHERE clause
71
85
  TRUNCATE[[:space:]]+TABLE Irreversible truncate blocked — confirm with user before truncating
@@ -6,7 +6,7 @@
6
6
  # example its pattern file is missing). It exists only to be sourced, in a subshell with all output
7
7
  # discarded, from inside a branch where the policy already fails open:
8
8
  #
9
- # ( . "$(dirname "$0")/capture-failure.sh"; doflow_capture_failure pre-bash-guard patterns-missing ) >/dev/null 2>&1 || true
9
+ # ( . "<policy-dir>/capture-failure.sh"; doflow_capture_failure pre-bash-guard patterns-missing ) >/dev/null 2>&1 || true
10
10
  #
11
11
  # The subshell keeps anything this file does away from the policy, which runs with `set -uo pipefail`,
12
12
  # does not source lib.sh and would change exit codes under `set -e`. So this file:
@@ -15,28 +15,61 @@
15
15
  # - reads no stdin and writes nothing to stdout or stderr;
16
16
  # - never rotates the file (rotation is done by the Node writers only, DEC-019);
17
17
  # - never blocks, never changes the policy's decision, and swallows every error.
18
+
19
+ _DOFLOW_SRC=${BASH_SOURCE[0]//\\//}; case $_DOFLOW_SRC in */*) _DOFLOW_CAPTURE_FAILURE_DIR=${_DOFLOW_SRC%/*} ;; *) _DOFLOW_CAPTURE_FAILURE_DIR=. ;; esac
18
20
  # The line holds only the policy name and the point name from DoFlow's own code, the working
19
21
  # directory, the harness name and the install version: never the tool call, its arguments or stdin.
20
- # It is a no-op when no failure home can be resolved (HOME unset, or XDG_CONFIG_HOME relative) and
21
- # while capture is off (the `off` sentinel file wins; DOFLOW_FAILURE_CAPTURE=off|0|false|no also
22
- # turns it off), the same rules the Node writer follows (IC-011, IC-014).
22
+ # It is a no-op when there is no config home (neither XDG_CONFIG_HOME nor HOME is set to an absolute
23
+ # path; doflow_config_home below) and while capture is off (DOFLOW_FAILURE_CAPTURE=off|0|false|no,
24
+ # the `off` sentinel file, or a `failure.capture` value other than "on" in <config home>/settings.json,
25
+ # read by doflow_lex_capture below), the same rules the Node writer follows (IC-011, IC-014, IC-005).
26
+ # The two marked blocks are byte-identical to the ones in bin/doflow-run.
27
+
28
+ # BEGIN doflow-config-home v1 (rule: src/runtime/config-home.js; checked by test/guards/settings.test.js)
29
+ doflow_config_home() {
30
+ DOFLOW_HOME=""
31
+ case "${XDG_CONFIG_HOME:-}" in
32
+ /*) DOFLOW_HOME="${XDG_CONFIG_HOME}/doflow"; return 0 ;;
33
+ [A-Za-z]:[\\/]*) case "${OSTYPE:-}" in msys*|cygwin*|win32*) DOFLOW_HOME="${XDG_CONFIG_HOME}/doflow"; return 0 ;; esac ;;
34
+ esac
35
+ case "${HOME:-}" in
36
+ /*) DOFLOW_HOME="${HOME}/.config/doflow" ;;
37
+ [A-Za-z]:[\\/]*) case "${OSTYPE:-}" in msys*|cygwin*|win32*) DOFLOW_HOME="${HOME}/.config/doflow" ;; esac ;;
38
+ esac
39
+ return 0
40
+ }
41
+ # END doflow-config-home v1
42
+
43
+ # BEGIN doflow-settings-lexical v1 (rule: src/runtime/settings/lexical.js; checked by test/guards/settings.test.js)
44
+ doflow_lex_capture() {
45
+ local f="${1:-}" text="" rest="" d=""
46
+ local re='"failure\.capture"[[:space:]]*:[[:space:]]*("[^"]*"|[^,}"[:space:]]*)'
47
+ DOFLOW_LEX_CAPTURE=""
48
+ if [ ! -e "$f" ]; then
49
+ # Missing is no setting, unless the folder holding it cannot be searched (Node: EACCES is off).
50
+ d="${f%/*}"
51
+ if [ -n "$d" ] && [ "$d" != "$f" ] && [ -d "$d" ] && [ ! -x "$d" ]; then DOFLOW_LEX_CAPTURE="off"; fi
52
+ return 0
53
+ fi
54
+ if [ ! -f "$f" ] || [ ! -r "$f" ]; then DOFLOW_LEX_CAPTURE="off"; return 0; fi
55
+ IFS= read -r -d '' text < "$f" || true
56
+ rest="$text"
57
+ while [[ $rest =~ $re ]]; do
58
+ if [ "${BASH_REMATCH[1]}" = '"on"' ]; then DOFLOW_LEX_CAPTURE="${DOFLOW_LEX_CAPTURE:-on}"; else DOFLOW_LEX_CAPTURE="off"; fi
59
+ rest="${rest#*"${BASH_REMATCH[0]}"}"
60
+ done
61
+ return 0
62
+ }
63
+ # END doflow-settings-lexical v1
23
64
 
24
65
  doflow_capture_failure() {
25
66
  local policy="${1:-}" point="${2:-}"
26
67
  local LC_ALL=C # byte-wise lengths and cuts; scoped to this function
27
68
  local home="" setting="" version="" harness="" project="" cwd="" hp="" at="" line="" here="" dir=""
28
69
 
29
- if [ -n "${XDG_CONFIG_HOME:-}" ]; then
30
- case "$XDG_CONFIG_HOME" in
31
- /*) home="$XDG_CONFIG_HOME/doflow/failures" ;;
32
- *) return 0 ;;
33
- esac
34
- else
35
- case "${HOME:-}" in
36
- /*) home="$HOME/.config/doflow/failures" ;;
37
- *) return 0 ;;
38
- esac
39
- fi
70
+ doflow_config_home
71
+ [ -n "$DOFLOW_HOME" ] || return 0
72
+ home="$DOFLOW_HOME/failures"
40
73
 
41
74
  # Case-insensitive, whitespace trimmed at the ends only: the same reading the Node writer has.
42
75
  setting="$(printf '%s' "${DOFLOW_FAILURE_CAPTURE:-}" | tr '[:upper:]' '[:lower:]')" || return 0
@@ -46,6 +79,8 @@ doflow_capture_failure() {
46
79
  off|0|false|no) return 0 ;;
47
80
  esac
48
81
  [ -f "$home/off" ] && return 0
82
+ doflow_lex_capture "$DOFLOW_HOME/settings.json"
83
+ [ "$DOFLOW_LEX_CAPTURE" = off ] && return 0
49
84
 
50
85
  [[ "$policy" =~ ^[a-z][a-z0-9-]{0,39}$ ]] || policy="unknown"
51
86
  [[ "$point" =~ ^[A-Za-z][A-Za-z0-9._:-]{0,39}$ ]] || return 0
@@ -54,7 +89,7 @@ doflow_capture_failure() {
54
89
  [[ "$harness" =~ ^[A-Za-z0-9._-]{1,40}$ ]] || harness="none"
55
90
 
56
91
  # script_version of the nearest install manifest: walking up from this file, then the global one.
57
- here="$(cd "$(dirname "${BASH_SOURCE[0]}")" 2>/dev/null && pwd -P)" || here=""
92
+ here="$(CDPATH= cd -P -- "$_DOFLOW_CAPTURE_FAILURE_DIR" 2>/dev/null && pwd -P)" || here=""
58
93
  dir="$here"
59
94
  while [ -n "$dir" ] && [ -z "$version" ]; do
60
95
  if [ -f "$dir/.doflow/.install-manifest.json" ]; then
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env bash
2
+ # IC-001 (feature 116): shared PreCompact text builder; sourced, not executed.
3
+
4
+ doflow_compact_text() {
5
+ local CWD="${1:-}"
6
+ local preserve branch sha uncommitted recent text
7
+
8
+ preserve='Preserve: decisions made, files modified, open questions, planned next steps, acceptance criteria and pending verification of the active task.'
9
+
10
+ if [[ -n "$CWD" ]] && is_git_worktree "$CWD"; then
11
+ branch=$(git_branch_of "$CWD" || echo "")
12
+ sha=$(git_short_sha_of "$CWD" || echo "")
13
+ uncommitted=$(git_uncommitted_count_of "$CWD" || echo "0")
14
+ recent=$(run_with_timeout 1 -- git -C "$CWD" log --format="%h %s" -2 2>/dev/null \
15
+ | awk 'NR>1{printf " | "}{printf "%s",$0}' || echo "")
16
+
17
+ text=$(printf '%s\nInclude in compact summary:\n- git branch: %s sha: %s\n- uncommitted files: %s\n- cwd: %s\n- recent commits: %s' \
18
+ "$preserve" "${branch:-unknown}" "${sha:-unknown}" "$uncommitted" "${CWD:-unknown}" "${recent:-none}")
19
+ else
20
+ text=$(printf '%s\nInclude in compact summary:\n- cwd: %s (not a git repository)' \
21
+ "$preserve" "${CWD:-unknown}")
22
+ fi
23
+
24
+ # The cap is 490 BYTES in every locale: LC_ALL=C makes the substring and length below count bytes,
25
+ # where a UTF-8 locale would count characters. A cut can then end inside a multi-byte character, so
26
+ # a trailing partial UTF-8 sequence is dropped (FU-tsq77m).
27
+ local LC_ALL=C cut i b need
28
+ cut="${text:0:490}"
29
+ i=$(( ${#cut} - 1 ))
30
+ # Walk back over at most three continuation bytes (0x80-0xBF) to the byte that starts the last character.
31
+ while [[ $i -ge 1 && $(( ${#cut} - i )) -le 3 ]]; do
32
+ printf -v b '%d' "'${cut:i:1}"
33
+ b=$(( b & 255 )) # the shell may print a byte above 127 as a negative number (bash 3.2) or a code unit
34
+ [[ $b -ge 128 && $b -lt 192 ]] || break
35
+ i=$(( i - 1 ))
36
+ done
37
+ printf -v b '%d' "'${cut:i:1}"
38
+ b=$(( b & 255 ))
39
+ need=1
40
+ [[ $b -ge 192 ]] && need=2
41
+ [[ $b -ge 224 ]] && need=3
42
+ [[ $b -ge 240 ]] && need=4
43
+ [[ $(( ${#cut} - i )) -lt $need ]] && cut="${cut:0:i}"
44
+ DF_COMPACT_TEXT="$cut"
45
+ return 0
46
+ }
@@ -0,0 +1,78 @@
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
3
+ # contract.sh — shared decision and envelope contract for canonical policies.
4
+ #
5
+ # This file is sourced by policy scripts. It sets no shell options, installs no traps, reads no
6
+ # stdin, and keeps decision recording independent of jq.
7
+
8
+ doflow_decision_init() {
9
+ DF_POLICY="${1:-}"
10
+ DF_POLICY_VERSION="${2:-0}"
11
+ doflow_decide allow allow
12
+ }
13
+
14
+ doflow_decide() {
15
+ local LC_ALL=C
16
+ local outcome="${1:-allow}" code="${2:-invalid-code}" fd="${DOFLOW_DECISION_FD:-}"
17
+ case "$code" in
18
+ [a-z]*)
19
+ case "$code" in
20
+ *[!a-z0-9:._-]*) code=invalid-code ;;
21
+ *) [ "${#code}" -le 40 ] || code=invalid-code ;;
22
+ esac
23
+ ;;
24
+ *) code=invalid-code ;;
25
+ esac
26
+ case "$fd" in
27
+ 3|4|5|6|7|8|9)
28
+ # A caller may set the descriptor name without passing an open fd. Keep
29
+ # that broken handoff silent and harmless, including under `set -e`.
30
+ { printf '\036{"v":1,"policy":"%s","policyVersion":%s,"outcome":"%s","code":"%s"}\n' \
31
+ "${DF_POLICY:-}" "${DF_POLICY_VERSION:-0}" "$outcome" "$code" >&"$fd"; } 2>/dev/null || :
32
+ ;;
33
+ esac
34
+ }
35
+
36
+ doflow_read_envelope() {
37
+ local assignments
38
+ if [ -z "${INPUT:-}" ]; then
39
+ DF_ENV_CLASS=missing
40
+ DF_ENV_CODE=envelope-missing
41
+ DF_ENV_HARNESS=
42
+ DF_ENV_EVENT=
43
+ DF_ENV_OP=
44
+ DF_ENV_TOOL=
45
+ DF_ENV_PATHS=
46
+ DF_ENV_COMMAND=
47
+ DF_ENV_CWD=
48
+ DF_ENV_PATCH=
49
+ DF_ENV_ROOT=
50
+ DF_ENV_TASK=
51
+ return 0
52
+ fi
53
+ assignments=$(printf '%s' "${INPUT:-}" | jq -sr '
54
+ (.[0]) as $root |
55
+ ($root | if type == "object" then . else {} end) as $object |
56
+ ($object.doflow_event // {}) as $event |
57
+ ($event | if type == "object" then . else {} end) as $fields |
58
+ def field($key): ($fields[$key] // "") | if type == "string" then . else "" end;
59
+ def shellvar($name; $value): ($name + "=" + ($value | @sh));
60
+ (if ($root | type) != "object" or (($event | type) != "object") then "missing"
61
+ elif ($event | has("version") | not) then "missing"
62
+ elif ($event.version | type) == "number" and $event.version == 1 then "v1"
63
+ else "unsupported" end) as $class |
64
+ [shellvar("DF_ENV_CLASS"; $class),
65
+ shellvar("DF_ENV_CODE"; (if $class == "missing" then "envelope-missing" else "" end)),
66
+ shellvar("DF_ENV_HARNESS"; field("harness")),
67
+ shellvar("DF_ENV_EVENT"; field("event")),
68
+ shellvar("DF_ENV_OP"; field("operation")),
69
+ shellvar("DF_ENV_TOOL"; field("tool")),
70
+ shellvar("DF_ENV_PATHS"; ([$fields.paths[]? | select(type == "string")] | join("\n"))),
71
+ shellvar("DF_ENV_COMMAND"; field("command")),
72
+ shellvar("DF_ENV_CWD"; ($object.cwd // "") | if type=="string" then . else "" end),
73
+ shellvar("DF_ENV_PATCH"; field("patch")),
74
+ shellvar("DF_ENV_ROOT"; field("projectRoot")),
75
+ shellvar("DF_ENV_TASK"; field("taskId"))][]
76
+ ' 2>/dev/null) || return 1
77
+ eval "$assignments"
78
+ }