@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
@@ -16,29 +16,60 @@
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
18
 
19
- case ${BASH_SOURCE[0]} in */*) _DOFLOW_CAPTURE_FAILURE_DIR=${BASH_SOURCE[0]%/*} ;; *) _DOFLOW_CAPTURE_FAILURE_DIR=. ;; esac
19
+ _DOFLOW_SRC=${BASH_SOURCE[0]//\\//}; case $_DOFLOW_SRC in */*) _DOFLOW_CAPTURE_FAILURE_DIR=${_DOFLOW_SRC%/*} ;; *) _DOFLOW_CAPTURE_FAILURE_DIR=. ;; esac
20
20
  # The line holds only the policy name and the point name from DoFlow's own code, the working
21
21
  # directory, the harness name and the install version: never the tool call, its arguments or stdin.
22
- # It is a no-op when no failure home can be resolved (HOME unset, or XDG_CONFIG_HOME relative) and
23
- # while capture is off (the `off` sentinel file wins; DOFLOW_FAILURE_CAPTURE=off|0|false|no also
24
- # 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
25
64
 
26
65
  doflow_capture_failure() {
27
66
  local policy="${1:-}" point="${2:-}"
28
67
  local LC_ALL=C # byte-wise lengths and cuts; scoped to this function
29
68
  local home="" setting="" version="" harness="" project="" cwd="" hp="" at="" line="" here="" dir=""
30
69
 
31
- if [ -n "${XDG_CONFIG_HOME:-}" ]; then
32
- case "$XDG_CONFIG_HOME" in
33
- /*) home="$XDG_CONFIG_HOME/doflow/failures" ;;
34
- *) return 0 ;;
35
- esac
36
- else
37
- case "${HOME:-}" in
38
- /*) home="$HOME/.config/doflow/failures" ;;
39
- *) return 0 ;;
40
- esac
41
- fi
70
+ doflow_config_home
71
+ [ -n "$DOFLOW_HOME" ] || return 0
72
+ home="$DOFLOW_HOME/failures"
42
73
 
43
74
  # Case-insensitive, whitespace trimmed at the ends only: the same reading the Node writer has.
44
75
  setting="$(printf '%s' "${DOFLOW_FAILURE_CAPTURE:-}" | tr '[:upper:]' '[:lower:]')" || return 0
@@ -48,6 +79,8 @@ doflow_capture_failure() {
48
79
  off|0|false|no) return 0 ;;
49
80
  esac
50
81
  [ -f "$home/off" ] && return 0
82
+ doflow_lex_capture "$DOFLOW_HOME/settings.json"
83
+ [ "$DOFLOW_LEX_CAPTURE" = off ] && return 0
51
84
 
52
85
  [[ "$policy" =~ ^[a-z][a-z0-9-]{0,39}$ ]] || policy="unknown"
53
86
  [[ "$point" =~ ^[A-Za-z][A-Za-z0-9._:-]{0,39}$ ]] || return 0
@@ -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
+ }
@@ -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
  # contract.sh — shared decision and envelope contract for canonical policies.
3
4
  #
4
5
  # This file is sourced by policy scripts. It sets no shell options, installs no traps, reads no
@@ -43,6 +44,7 @@ doflow_read_envelope() {
43
44
  DF_ENV_TOOL=
44
45
  DF_ENV_PATHS=
45
46
  DF_ENV_COMMAND=
47
+ DF_ENV_CWD=
46
48
  DF_ENV_PATCH=
47
49
  DF_ENV_ROOT=
48
50
  DF_ENV_TASK=
@@ -67,6 +69,7 @@ doflow_read_envelope() {
67
69
  shellvar("DF_ENV_TOOL"; field("tool")),
68
70
  shellvar("DF_ENV_PATHS"; ([$fields.paths[]? | select(type == "string")] | join("\n"))),
69
71
  shellvar("DF_ENV_COMMAND"; field("command")),
72
+ shellvar("DF_ENV_CWD"; ($object.cwd // "") | if type=="string" then . else "" end),
70
73
  shellvar("DF_ENV_PATCH"; field("patch")),
71
74
  shellvar("DF_ENV_ROOT"; field("projectRoot")),
72
75
  shellvar("DF_ENV_TASK"; field("taskId"))][]
@@ -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
  # deny-json.sh — shared PreToolUse deny-JSON emitters for front-door dispatchers.
3
4
  #
4
5
  # Deliberately carries no `set` directive (unlike lib.sh, which sets -euo
@@ -10,7 +11,7 @@
10
11
  #
11
12
  # Usage: source "<policy-dir>/deny-json.sh"
12
13
 
13
- case ${BASH_SOURCE[0]} in */*) _DOFLOW_DENY_JSON_DIR=${BASH_SOURCE[0]%/*} ;; *) _DOFLOW_DENY_JSON_DIR=. ;; esac
14
+ _DOFLOW_SRC=${BASH_SOURCE[0]//\\//}; case $_DOFLOW_SRC in */*) _DOFLOW_DENY_JSON_DIR=${_DOFLOW_SRC%/*} ;; *) _DOFLOW_DENY_JSON_DIR=. ;; esac
14
15
 
15
16
  # Escape a string as a JSON string body without external tools. Work bytewise so
16
17
  # every control byte is handled the same way regardless of the caller's locale.
@@ -52,13 +53,3 @@ emit_pretooluse_deny_nested() {
52
53
  printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":%s}}\n' "$(_doflow_json_reason "$reason")"
53
54
  fi
54
55
  }
55
-
56
- # Gemini shape: flat {decision, reason} (packages/core/src/hooks/types.ts).
57
- emit_pretooluse_deny_flat() {
58
- local reason="$1"
59
- if command -v jq >/dev/null 2>&1; then
60
- jq -n --arg reason "$reason" '{decision: "deny", reason: $reason}'
61
- else
62
- printf '{"decision":"deny","reason":%s}\n' "$(_doflow_json_reason "$reason")"
63
- fi
64
- }
@@ -0,0 +1,37 @@
1
+ # Generated by node src/runtime/context/emission-rules.js --write. Do not edit.
2
+ # rules-digest: sha256:3f344067dc5466521e89700b8ca03c5729dfe2e3bc0896629d4e08a5395dd4bb
3
+ DF_RULES_VERSION=1
4
+ DF_RULES_MAX_BYTES_DEFAULT=16384
5
+ DF_RULES_SUMMARY_CAP=4000
6
+ DF_RULES_SUMMARY_READ=65536
7
+ doflow_emission_rule() {
8
+ # $1 = event.segment, $2 = vector: input values joined with ':' in IC-002 order
9
+ case "$1|$2" in
10
+ 'compact-instructions.instructions|main') DF_ACTION=include ;;
11
+ 'compact-instructions.instructions|worker') DF_ACTION=include ;;
12
+ 'compact-meta.meta|main:default-main') DF_ACTION=write ;;
13
+ 'compact-store.summary|main:default-main') DF_ACTION=write ;;
14
+ 'compact-store.summary|main:launched') DF_ACTION=write ;;
15
+ 'compact-store.summary|worker:launched') DF_ACTION=write ;;
16
+ 'delta.pack|true') DF_ACTION=include ;;
17
+ 'launch.grant|fits:false') DF_ACTION=whole ;;
18
+ 'launch.grant|fits:true') DF_ACTION=whole ;;
19
+ 'launch.grant|over:false') DF_ACTION=pointer ;;
20
+ 'launch.pack|false') DF_ACTION=include ;;
21
+ 'launch.slot|false') DF_ACTION=claim ;;
22
+ 'launch.summary|main:false:pending') DF_ACTION=truncate ;;
23
+ 'launch.summary|worker:false:pending') DF_ACTION=truncate ;;
24
+ 'legacy-adopt.legacy|main:default-main:false:false:true') DF_ACTION=adopt ;;
25
+ 'prompt.git|main:false') DF_ACTION=include ;;
26
+ 'prompt.grant|false:fits:false') DF_ACTION=whole ;;
27
+ 'prompt.grant|false:fits:true') DF_ACTION=whole ;;
28
+ 'prompt.grant|false:over:false') DF_ACTION=pointer ;;
29
+ 'prompt.nudge|main:nudge') DF_ACTION=include ;;
30
+ 'prompt.slot|false') DF_ACTION=claim ;;
31
+ 'prompt.summary|main:default-main:false:false:pending') DF_ACTION=truncate ;;
32
+ 'prompt.summary|main:launched:false:false:pending') DF_ACTION=truncate ;;
33
+ 'prompt.summary|worker:launched:false:false:pending') DF_ACTION=truncate ;;
34
+ 'prompt.warning|main:false:false:true') DF_ACTION=include ;;
35
+ *) DF_ACTION=skip ;;
36
+ esac
37
+ }
@@ -0,0 +1,209 @@
1
+ # emission.jq — one-pass renderer of the prompt segments and the hook response (078 IC-002, IC-004
2
+ # step 7). renderEmission() in src/runtime/context/arbitrate.ts is the reference; both must give
3
+ # expected.text, bytes, refused and dropped of test/fixtures/emission/render-corpus.json byte for
4
+ # byte (IC-022, RK7).
5
+ #
6
+ # Invocation (IC-004 step 7), one process:
7
+ # jq -n -r -f emission.jq --argjson plan "$PLAN" \
8
+ # --rawfile git <git-context.json> --rawfile summary <claimed summary> \
9
+ # --rawfile grant <claimed grant> --arg warning "$WARNING" --arg nudge "$NUDGE"
10
+ #
11
+ # All four names are always bound (jq refuses an undefined $name); emission.sh binds $warning with
12
+ # --arg, because the warning claim renames the file and reads it before the render. A file that does not
13
+ # exist is passed as /dev/null and its name listed in plan.absent; an empty existing file is not
14
+ # absent (an empty warning still renders, an empty git capture is "Not a git repository.").
15
+ #
16
+ # plan:
17
+ # actions IC-001 actions by segment; only git, summary, warning, grant and nudge render here
18
+ # maxBytes positive integer, the IC-002 byte budget
19
+ # harness the harness name (DOFLOW_AGENT); sessionTitle is added on every harness but
20
+ # "codex", whose envelope rejects unknown fields (DEC-030)
21
+ # title true when a title may be set (the payload carries no session_title)
22
+ # absent names among git, summary, grant, warning whose file does not exist (default [])
23
+ # summaryBytes the claimed summary file's full size, which can exceed the bounded read; it
24
+ # defaults to the UTF-8 size of $summary
25
+ # mode "response" (default) prints the hook response; "render" prints
26
+ # {text, bytes, refused, dropped, segments}, the renderEmission() result, for parity;
27
+ # "hook" (emission.sh, run with -r) prints one first line holding the dropped
28
+ # segment names joined with tabs (possibly empty), a second line holding the
29
+ # response digest (empty unless plan.digest), then the hook response
30
+ # digest hook mode: true computes response_digest (rider R1, 091); other modes ignore it
31
+ #
32
+ # Response bytes (jq's default pretty print): emission is
33
+ # {"hookSpecificOutput": {"hookEventName": "UserPromptSubmit", "additionalContext": ...}}
34
+ # with sessionTitle on every harness but Codex (DEC-030), when plan.title holds and the git action built a title
35
+ # ("{branch?unknown} — {sha?unknown}", or "no-git" outside a repository; none when the capture
36
+ # file is missing). Nothing rendered prints {} and a newline. Any error exits non-zero and the
37
+ # caller answers with the silent response.
38
+ #
39
+ # Counting (design R1): characters are code points (jq string length and slices), budgets are
40
+ # UTF-8 bytes (utf8bytelength). Portability: jq 1.6+ built with Oniguruma; utf8bytelength is the
41
+ # one builtin newer than 1.5. No index/rindex on strings (byte offsets in 1.6) and no 1.7-only
42
+ # builtin (trim, ltrim, rtrim, abs, pick, toarray, have_decnum). Every regex is anchored
43
+ # with \A or \z, never ^ or $, which Oniguruma reads per line.
44
+
45
+ def summary_cap: 4000;
46
+ def summary_read: 65536;
47
+
48
+ # Trailing newlines removed, as $(...) does.
49
+ def rstrip_nl: sub("\n+\\z"; "");
50
+ def no_cr: split("\r") | join("");
51
+
52
+ # `jq -r '<field> // empty'` in a $(...): null and false are empty, other scalars print as JSON.
53
+ def jq_field:
54
+ if . == null or . == false then ""
55
+ elif type == "string" then rstrip_nl
56
+ else tojson | rstrip_nl
57
+ end;
58
+
59
+ # A summary or grant record: \r removed, trailing newlines dropped, frontmatter keys from the lines
60
+ # between the first two `---` lines (first match wins, spaces after `key:` stripped), body from the
61
+ # first non-empty line after the second `---`, trailing newlines dropped. Fewer than two `---`
62
+ # lines give an empty body.
63
+ def parse_record:
64
+ (no_cr | rstrip_nl | split("\n"))
65
+ | reduce .[] as $line ({fences: 0, started: false, fields: {}, body: []};
66
+ if .fences < 2 and $line == "---" then .fences += 1
67
+ elif .fences == 1 then
68
+ ($line | split(":")) as $parts
69
+ | if ($parts | length) > 1 and ($parts[0] | length) > 0 and (.fields | has($parts[0]) | not)
70
+ then .fields[$parts[0]] = ($parts[1:] | join(":") | sub("\\A +"; ""))
71
+ else . end
72
+ elif .fences >= 2 and (.started or $line != "") then .started = true | .body += [$line]
73
+ else . end)
74
+ | {fields: .fields, body: (.body | join("\n") | rstrip_nl)};
75
+
76
+ # A bracketed header value: `]` removed, then empty when it is "unknown".
77
+ def known: (. // "") | split("]") | join("") | if . == "unknown" then "" else . end;
78
+
79
+ def header_tail:
80
+ (.compacted_at | known) as $at | (.branch | known) as $branch
81
+ | (if $at != "" then ", compacted \($at)" else "" end)
82
+ + (if $branch != "" then " on branch \($branch)" else "" end);
83
+
84
+ # The git capture as an object; anything unparseable or not an object reads as {}.
85
+ def capture($raw):
86
+ (try ($raw | fromjson) catch null) | if type == "object" then . else {} end;
87
+
88
+ def is_repo: (.is_git_repo | jq_field) == "true";
89
+
90
+ def render_git($raw; $missing):
91
+ if $missing then "Git context unavailable for this session."
92
+ else capture($raw)
93
+ | if is_repo | not then "Not a git repository."
94
+ else
95
+ ((if (.commits | type) == "array" then .commits else [] end)
96
+ | map(jq_field) | map(select(. != "")) | .[0:5] | join(" · ")) as $commits
97
+ | (.stash_count | jq_field) as $stash
98
+ | "Git context — branch: \(.branch | jq_field | if . == "" then "unknown" else . end) | \(.sha | jq_field | if . == "" then "unknown" else . end)\n"
99
+ + "Last commits: \(if $commits == "" then "none" else $commits end)\n"
100
+ + "Uncommitted files: \(.uncommitted_count | jq_field | if . == "" then "0" else . end)"
101
+ + (if ($stash | test("\\A[0-9]+\\z")) and ($stash | tonumber) > 0 then " | Stashed: \($stash)" else "" end)
102
+ end
103
+ end;
104
+
105
+ def git_title($raw; $missing):
106
+ if $missing then null
107
+ else capture($raw)
108
+ | if is_repo then
109
+ "\(.branch | jq_field | if . == "" then "unknown" else . end) — \(.sha | jq_field | if . == "" then "unknown" else . end)"
110
+ else "no-git" end
111
+ end;
112
+
113
+ # null when the body is empty: an empty summary renders nothing.
114
+ def render_summary($size):
115
+ parse_record
116
+ | if .body == "" then null
117
+ else (.body | length) as $n
118
+ | (if $n > summary_cap
119
+ then .body[0:summary_cap] + "\n[summary truncated: first \(summary_cap) of \(if $size > summary_read then "at least " else "" end)\($n) characters]"
120
+ else .body end) as $shown
121
+ | "\n\n[Prior session summary\(.fields | header_tail)]\n\($shown)"
122
+ end;
123
+
124
+ def render_grant: "[Granted summary from lineage \(.fields.from_lineage // "")\(.fields | header_tail)]\n\(.body)";
125
+
126
+ def render_pointer($max): "[Granted summary \(.fields.summary_id // "") withheld: \(.body | utf8bytelength) bytes exceed the \($max)-byte context budget]";
127
+
128
+ def join_order: ["git", "summary", "warning", "grant", "nudge"];
129
+
130
+ # IC-002 join: summary drops its leading blank line when nothing precedes it; grant and nudge gain
131
+ # one when something does.
132
+ def join_segments:
133
+ . as $p
134
+ | reduce (join_order[] | select($p[.] != null)) as $s ("";
135
+ if $s == "summary" then (if . == "" then . + ($p[$s] | .[2:]) else . + $p[$s] end)
136
+ elif $s == "grant" or $s == "nudge" then (if . == "" then "" else . + "\n\n" end) + $p[$s]
137
+ else . + $p[$s] end);
138
+
139
+ def render($plan; $in):
140
+ ($plan.actions // {}) as $a
141
+ | $plan.maxBytes as $max
142
+ | if ($max | type) != "number" or $max <= 0 or ($max | floor) != $max then error("plan.maxBytes must be a positive integer") else . end
143
+ | ($plan.absent // []) as $absent
144
+ | def present($k): ($absent | map(select(. == $k)) | length) == 0;
145
+ ({}
146
+ | if $a.git == "include" then .git = render_git($in.git; present("git") | not) else . end
147
+ | if $a.summary == "truncate" and present("summary")
148
+ then ($in.summary | render_summary($plan.summaryBytes // ($in.summary | utf8bytelength))) as $s
149
+ | if $s != null then .summary = $s else . end
150
+ else . end
151
+ | if $a.warning == "include" and present("warning") then .warning = "\n[Prior session warning: \($in.warning | rstrip_nl)]" else . end
152
+ | if $a.nudge == "include" and ($in.nudge | type) == "string" and $in.nudge != "" then .nudge = $in.nudge else . end
153
+ ) as $optional
154
+ | (if ($a.grant == "whole" or $a.grant == "pointer") and present("grant")
155
+ then ($in.grant | parse_record) as $r
156
+ | ($r | render_grant) as $whole
157
+ | if $a.grant == "whole" and ($whole | utf8bytelength) <= $max
158
+ then {selected: {grant: $whole}, refused: false}
159
+ else {selected: {grant: ($r | render_pointer($max))}, refused: true} end
160
+ else {selected: {}, refused: false} end)
161
+ | . + {dropped: []}
162
+ | reduce ("git", "summary", "warning", "nudge") as $s (.;
163
+ if $optional[$s] == null then .
164
+ elif (.selected + {($s): $optional[$s]} | join_segments | utf8bytelength) <= $max then .selected[$s] = $optional[$s]
165
+ else .dropped += [$s] end)
166
+ | (.selected | join_segments) as $text
167
+ | {
168
+ text: (if $text == "" then null else $text end),
169
+ bytes: ($text | utf8bytelength),
170
+ refused: .refused,
171
+ dropped: .dropped,
172
+ segments: [join_order[] as $s | select(.selected[$s] != null) | $s]
173
+ };
174
+
175
+ def response($plan; $in; $result):
176
+ if $result.text == null then {}
177
+ else
178
+ (if $plan.harness != "codex" and $plan.title == true and ($plan.actions // {}).git == "include"
179
+ then git_title($in.git; ($plan.absent // []) | map(select(. == "git")) | length > 0) else null end) as $title
180
+ | {"hookSpecificOutput": ({"hookEventName": "UserPromptSubmit", "additionalContext": $result.text}
181
+ + (if $title != null and $title != "" then {"sessionTitle": $title} else {} end))}
182
+ end;
183
+
184
+ # Rider R1 (091): the response exactly as jq's default pretty print writes it (indent 2, insertion
185
+ # order, the same string escaping as tojson), without the trailing newline. The response shape is
186
+ # fixed: {} or one hookSpecificOutput object of string values.
187
+ def pretty_response:
188
+ if . == {} then "{}"
189
+ else "{\n \"hookSpecificOutput\": {\n"
190
+ + ([.hookSpecificOutput | to_entries[] | " \(.key | tojson): \(.value | tojson)"] | join(",\n"))
191
+ + "\n }\n}"
192
+ end;
193
+
194
+ # 8 lowercase hex of h = (h*31 + c) mod 2^32 over the code points of the pretty response. Every step
195
+ # stays below 2^38, exact in doubles; mod and the nibbles use floor, not %, which casts to int on old jq.
196
+ def response_digest:
197
+ (pretty_response | explode | reduce .[] as $c (0; (. * 31 + $c) | . - ((. / 4294967296) | floor) * 4294967296)) as $h
198
+ | [268435456, 16777216, 1048576, 65536, 4096, 256, 16, 1]
199
+ | map(($h / . | floor) - (($h / . / 16) | floor) * 16)
200
+ | map("0123456789abcdef"[.:. + 1]) | join("");
201
+
202
+ {git: $git, summary: $summary, grant: $grant, warning: $warning, nudge: $nudge} as $in
203
+ | render($plan; $in) as $result
204
+ | ($plan.mode // "response") as $mode
205
+ | if $mode == "render" then $result
206
+ elif $mode == "hook" then
207
+ response($plan; $in; $result) as $response
208
+ | ($result.dropped | join("\t")), (if $plan.digest == true then $response | response_digest else "" end), $response
209
+ else response($plan; $in; $result) end