@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
@@ -0,0 +1,415 @@
1
+ 'use strict';
2
+
3
+ // TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
4
+
5
+ /**
6
+ * Process identity, liveness, bounded group spawn and kill escalation (feature 081, IC-022 and
7
+ * IC-021 step 3; DEC-023, DEC-043, DEC-050).
8
+ *
9
+ * A managed process is recorded as `{ pid, pgid, start }`, `start` the `ps` `lstart` text. A pid
10
+ * alone is not an identity: it is reused once the process is reaped, so liveness also compares the
11
+ * start time. Every `ps` call runs under `LC_ALL=C` and `TZ=UTC`, so the start a supervisor records
12
+ * and the one a status or cancel from another shell reads are the same text whatever each
13
+ * process's own time zone or locale (DEC-043).
14
+ *
15
+ * Zombies are reported apart from survivors: they have exited and only await their parent.
16
+ * On win32 there is no `ps` and no process group, so liveness and cleanup are `unknown` (DEC-008).
17
+ */
18
+
19
+ const fs = require('node:fs');
20
+ const { execFileSync, spawn } = require('node:child_process');
21
+
22
+ /** Grace between SIGTERM and SIGKILL (DEC-023). */
23
+ const KILL_GRACE_MS = 3000;
24
+ /** How long survivors of SIGKILL are polled for before they are listed. */
25
+ const KILL_FINAL_MS = 1000;
26
+ /** Grace waits are polled, never slept in one block (RK7). */
27
+ const POLL_MS = 100;
28
+ /** Bound on every `ps` call. */
29
+ const PS_TIMEOUT_MS = 2000;
30
+
31
+ const SNAPSHOT_FIELDS = ['pid', 'ppid', 'pgid', 'stat'];
32
+ const GROUP_FIELDS = ['pid', 'pgid', 'stat'];
33
+ const MONTHS = {
34
+ Jan: 0, Feb: 1, Mar: 2, Apr: 3, May: 4, Jun: 5, Jul: 6, Aug: 7, Sep: 8, Oct: 9, Nov: 10, Dec: 11,
35
+ };
36
+
37
+ /** `ps` by absolute path where one exists, so a worker's or a test's `PATH` never decides which
38
+ * binary reports process identity. */
39
+ const PS_BIN = ['/bin/ps', '/usr/bin/ps'].find((candidate) => {
40
+ try {
41
+ fs.accessSync(candidate, fs.constants.X_OK);
42
+ return true;
43
+ } catch {
44
+ return false;
45
+ }
46
+ }) || 'ps';
47
+
48
+ /** The one environment every `ps` call runs under (DEC-043). */
49
+ function psEnv() {
50
+ return { ...process.env, LC_ALL: 'C', TZ: 'UTC' };
51
+ }
52
+
53
+ /**
54
+ * Runs `ps` with `args`. Returns its stdout, or null when `ps` could not run or exceeded its bound.
55
+ * A `ps` that ran and exited non-zero (`-p` naming no live pid) returns what it printed.
56
+ */
57
+ function runPs(args ) {
58
+ try {
59
+ return execFileSync(PS_BIN, args, {
60
+ encoding: 'utf8',
61
+ env: psEnv(),
62
+ timeout: PS_TIMEOUT_MS,
63
+ stdio: ['ignore', 'pipe', 'ignore'],
64
+ });
65
+ } catch (error ) {
66
+ if (typeof error?.status === 'number' && !error.signal) return String(error.stdout || '');
67
+ return null;
68
+ }
69
+ }
70
+
71
+ /** Collapses the column padding `ps` puts inside and after `lstart`. */
72
+ function normaliseStart(text ) {
73
+ if (typeof text !== 'string') return null;
74
+ const value = text.trim().replace(/\s+/g, ' ');
75
+ return value || null;
76
+ }
77
+
78
+ /** Milliseconds for an `lstart` text read under `LC_ALL=C` and `TZ=UTC`, or null. */
79
+ function startMs(text ) {
80
+ const value = normaliseStart(text);
81
+ const match = value && /^[A-Za-z]{3} ([A-Za-z]{3}) (\d{1,2}) (\d{2}):(\d{2}):(\d{2}) (\d{4})$/.exec(value);
82
+ if (!match || !(match[1] in MONTHS)) return null;
83
+ return Date.UTC(Number(match[6]), MONTHS[match[1]], Number(match[2]), Number(match[3]), Number(match[4]), Number(match[5]));
84
+ }
85
+
86
+ /**
87
+ * Parses a `ps -o <fields...>,lstart=` listing. `lstart` is last, so the rest of each line after
88
+ * the leading fields is the start. Lines whose numeric fields do not parse are skipped.
89
+ */
90
+ function parseListing(text , fields = SNAPSHOT_FIELDS) {
91
+ const rows = [];
92
+ for (const line of String(text || '').split('\n')) {
93
+ const tokens = line.trim().split(/\s+/);
94
+ if (tokens.length <= fields.length) continue;
95
+ const row = {};
96
+ let valid = true;
97
+ fields.forEach((field, index) => {
98
+ if (field === 'stat') {
99
+ row.stat = tokens[index];
100
+ return;
101
+ }
102
+ const number = Number(tokens[index]);
103
+ if (!Number.isInteger(number)) valid = false;
104
+ row[field] = number;
105
+ });
106
+ if (!valid) continue;
107
+ row.start = normaliseStart(tokens.slice(fields.length).join(' '));
108
+ rows.push(row);
109
+ }
110
+ return rows;
111
+ }
112
+
113
+ const isZombie = (row ) => typeof row?.stat === 'string' && row.stat.startsWith('Z');
114
+
115
+ /** `ps -o lstart= -p <pid>`, trimmed, or null when the pid is not listed or `ps` failed. */
116
+ function procStart(pid , { run = runPs } = {}) {
117
+ if (!Number.isInteger(pid) || pid <= 0) return null;
118
+ const out = run(['-o', 'lstart=', '-p', String(pid)]);
119
+ return out == null ? null : normaliseStart(out);
120
+ }
121
+
122
+ /**
123
+ * True when the recorded process still runs: the pid answers signal 0, its start equals the
124
+ * recorded one and it is not a zombie. False on `ESRCH`, a different start or a zombie. `unknown`
125
+ * when the start was not recorded, `ps` failed, or on win32 while the pid answers.
126
+ */
127
+ function alive(proc , { run = runPs, kill = process.kill.bind(process), platform = process.platform } = {}) {
128
+ if (!proc || !Number.isInteger(proc.pid) || proc.pid <= 0) return 'unknown';
129
+ try {
130
+ kill(proc.pid, 0);
131
+ } catch (error ) {
132
+ if (error?.code === 'ESRCH') return false;
133
+ if (error?.code !== 'EPERM') return 'unknown';
134
+ }
135
+ if (platform === 'win32' || normaliseStart(proc.start) == null) return 'unknown';
136
+ const out = run(['-o', 'stat=,lstart=', '-p', String(proc.pid)]);
137
+ if (out == null) return 'unknown';
138
+ const [row] = parseListing(out, ['stat']);
139
+ if (!row) return false;
140
+ if (isZombie(row)) return false;
141
+ return row.start === normaliseStart(proc.start);
142
+ }
143
+
144
+ /** Members of `rows` in group `pgid` that started at or after the leader, and whether the group id
145
+ * now belongs to a different leader. */
146
+ function selectGroup(rows , pgid , leaderStart ) {
147
+ const leader = normaliseStart(leaderStart);
148
+ const leaderMs = startMs(leader);
149
+ const foreignLeader = leader != null && rows.some((row) => row.pid === pgid && row.start !== leader);
150
+ const members = rows.filter((row) => {
151
+ if (row.pgid !== pgid) return false;
152
+ if (leaderMs == null) return true;
153
+ const ms = startMs(row.start);
154
+ return ms == null || ms >= leaderMs;
155
+ });
156
+ return { members, foreignLeader };
157
+ }
158
+
159
+ /**
160
+ * One `ps -A -o pid=,pgid=,stat=,lstart=` listing: the processes carrying `pgid` that started at or
161
+ * after `leaderStart`, split into `live` and `zombies`. Null when `ps` failed.
162
+ */
163
+ function groupMembers(pgid , leaderStart , { run = runPs } = {}) {
164
+ const out = run(['-A', '-o', 'pid=,pgid=,stat=,lstart=']);
165
+ if (out == null) return null;
166
+ const { members, foreignLeader } = selectGroup(parseListing(out, GROUP_FIELDS), pgid, leaderStart);
167
+ return {
168
+ live: members.filter((row) => !isZombie(row)),
169
+ zombies: members.filter(isZombie),
170
+ foreignLeader,
171
+ };
172
+ }
173
+
174
+ /**
175
+ * True when the group still has a live member. False when it has none, or when the listing shows
176
+ * a leader with the group's id and another start: POSIX does not reuse a pid while a group with
177
+ * that id exists, so the worker's group has ended. `unknown` when `ps` failed or on win32.
178
+ */
179
+ function groupAlive(pgid , leaderStart , { run = runPs, platform = process.platform } = {}) {
180
+ if (platform === 'win32') return 'unknown';
181
+ const group = groupMembers(pgid, leaderStart, { run });
182
+ if (group == null) return 'unknown';
183
+ if (group.foreignLeader) return false;
184
+ return group.live.length > 0;
185
+ }
186
+
187
+ /** The recorded process, its group and its ppid descendants, from one listing. */
188
+ function treeFromRows(rows , proc ) {
189
+ const { members, foreignLeader } = selectGroup(rows, proc.pgid, proc.start);
190
+ const byPid = new Map ();
191
+ if (!foreignLeader) for (const row of members) byPid.set(row.pid, row);
192
+ const recorded = normaliseStart(proc.start);
193
+ const root = rows.find((row) => row.pid === proc.pid && (recorded == null || row.start === recorded));
194
+ if (root) {
195
+ const queue = [root];
196
+ byPid.set(root.pid, root);
197
+ while (queue.length) {
198
+ const parent = queue.shift();
199
+ for (const row of rows) {
200
+ if (row.ppid !== parent.pid || byPid.has(row.pid) || row.pid === parent.pid) continue;
201
+ byPid.set(row.pid, row);
202
+ queue.push(row);
203
+ }
204
+ }
205
+ }
206
+ return { members: [...byPid.values()], foreignLeader };
207
+ }
208
+
209
+ /**
210
+ * One `ps -A -o pid=,ppid=,pgid=,stat=,lstart=` snapshot of every process in the recorded group or
211
+ * descended from its pid by ppid, each with its start. Null when `ps` failed.
212
+ */
213
+ function snapshotTree(proc , { run = runPs } = {}) {
214
+ const out = run(['-A', '-o', 'pid=,ppid=,pgid=,stat=,lstart=']);
215
+ if (out == null) return null;
216
+ return treeFromRows(parseListing(out, SNAPSHOT_FIELDS), proc);
217
+ }
218
+
219
+ /** Which tracked processes (and live members of the group) remain, from one listing. */
220
+ function remaining(tracked , proc , groupOwned , run ) {
221
+ const out = run(['-A', '-o', 'pid=,ppid=,pgid=,stat=,lstart=']);
222
+ if (out == null) return null;
223
+ const rows = parseListing(out, SNAPSHOT_FIELDS);
224
+ const byPid = new Map(rows.map((row) => [row.pid, row]));
225
+ const live = new Set ();
226
+ const zombies = new Set ();
227
+ for (const entry of tracked) {
228
+ const row = byPid.get(entry.pid);
229
+ if (!row || (entry.start != null && row.start !== entry.start)) continue;
230
+ (isZombie(row) ? zombies : live).add(row.pid);
231
+ }
232
+ if (groupOwned) {
233
+ const { members, foreignLeader } = selectGroup(rows, proc.pgid, proc.start);
234
+ if (!foreignLeader) for (const row of members) (isZombie(row) ? zombies : live).add(row.pid);
235
+ }
236
+ for (const pid of live) zombies.delete(pid);
237
+ return { live: [...live].sort((a, b) => a - b), zombies: [...zombies].sort((a, b) => a - b) };
238
+ }
239
+
240
+ const defaultSleep = (ms ) => new Promise((resolve) => setTimeout(resolve, ms));
241
+
242
+ function signal(kill , target , name ) {
243
+ try {
244
+ kill(target, name);
245
+ } catch {
246
+ // ESRCH: already gone. EPERM: not ours to end; the final listing reports it if it remains.
247
+ }
248
+ }
249
+
250
+ /**
251
+ * IC-021 step 3: SIGTERM to the group and to each snapshot pid outside it, poll every 100 ms up to
252
+ * `KILL_GRACE_MS`, SIGKILL to the group and every remaining snapshot pid, poll up to
253
+ * `KILL_FINAL_MS`. Returns `{ status, reason, survivors, zombies }`: `verified` with no survivors,
254
+ * `survivors` with them, `unknown` when `ps` failed or on win32. A group id that now names another
255
+ * leader is never signalled.
256
+ *
257
+ * No-snapshot rule (DEC-060): when the first snapshot's `ps` fails, or the recorded leader start is
258
+ * null, no group is signalled. The recorded pid is signalled only while `alive(proc)` is `true`, and
259
+ * the result is `unknown` with reason `ps-failed` or `no-leader-start`; a pid still confirmed alive
260
+ * after SIGKILL is listed in `survivors`.
261
+ */
262
+ async function escalate({
263
+ proc,
264
+ platform = process.platform,
265
+ run = runPs,
266
+ kill = process.kill.bind(process),
267
+ sleep = defaultSleep,
268
+ now = Date.now,
269
+ graceMs = KILL_GRACE_MS,
270
+ finalMs = KILL_FINAL_MS,
271
+ } ) {
272
+ if (platform === 'win32') {
273
+ signal(kill, proc.pid, 'SIGTERM');
274
+ return { status: 'unknown', reason: 'win32', survivors: [], zombies: [] };
275
+ }
276
+ const leaderStart = normaliseStart(proc.start);
277
+ const snapshot = leaderStart == null ? null : snapshotTree(proc, { run });
278
+ if (snapshot == null) {
279
+ const isAlive = () => alive(proc, { run, kill, platform }) === true;
280
+ const waitGone = async (budgetMs ) => {
281
+ const until = now() + budgetMs;
282
+ let live = isAlive();
283
+ while (live && now() < until) {
284
+ await sleep(POLL_MS);
285
+ live = isAlive();
286
+ }
287
+ return live;
288
+ };
289
+ let live = isAlive();
290
+ if (live) {
291
+ signal(kill, proc.pid, 'SIGTERM');
292
+ live = await waitGone(graceMs);
293
+ if (live) {
294
+ signal(kill, proc.pid, 'SIGKILL');
295
+ live = await waitGone(finalMs);
296
+ }
297
+ }
298
+ return { status: 'unknown', reason: leaderStart == null ? 'no-leader-start' : 'ps-failed', survivors: live ? [proc.pid] : [], zombies: [] };
299
+ }
300
+ const groupOwned = !snapshot.foreignLeader;
301
+ const tracked = snapshot.members.map((row ) => ({ pid: row.pid, start: row.start, pgid: row.pgid }));
302
+ // A snapshot lists the recorded pid only when its start still matches; a pid now naming another
303
+ // process is never signalled.
304
+
305
+ const wait = async (budgetMs ) => {
306
+ const until = now() + budgetMs;
307
+ let state = remaining(tracked, proc, groupOwned, run);
308
+ while ((state == null || state.live.length > 0) && now() < until) {
309
+ await sleep(POLL_MS);
310
+ state = remaining(tracked, proc, groupOwned, run);
311
+ }
312
+ return state;
313
+ };
314
+
315
+ if (groupOwned) signal(kill, -proc.pgid, 'SIGTERM');
316
+ for (const entry of tracked) if (!groupOwned || entry.pgid !== proc.pgid) signal(kill, entry.pid, 'SIGTERM');
317
+ let state = await wait(graceMs);
318
+
319
+ if (state == null || state.live.length > 0) {
320
+ if (groupOwned) signal(kill, -proc.pgid, 'SIGKILL');
321
+ const left = state == null ? tracked.map((entry ) => entry.pid) : state.live;
322
+ for (const pid of left) signal(kill, pid, 'SIGKILL');
323
+ state = await wait(finalMs);
324
+ }
325
+
326
+ if (state == null) return { status: 'unknown', reason: 'ps-failed', survivors: [], zombies: [] };
327
+ return {
328
+ status: state.live.length ? 'survivors' : 'verified',
329
+ reason: null,
330
+ survivors: state.live,
331
+ zombies: state.zombies,
332
+ };
333
+ }
334
+
335
+ /**
336
+ * Spawns `binary` in its own process group (darwin and linux), reads stdout up to `maxStdout`
337
+ * bytes and ends the whole tree by `escalate` when `timeoutMs` passes. Resolves
338
+ * `{ status, code, signal, error, stdout, truncated, proc, cleanup }`, `status` one of `exited`,
339
+ * `timeout` or `error` (a spawn failure, `error` its code).
340
+ */
341
+ function spawnBounded({ binary, argv = [], cwd, env, timeoutMs, maxStdout = 65536, platform = process.platform } ) {
342
+ return new Promise((resolve) => {
343
+ let child ;
344
+ try {
345
+ child = spawn(binary, argv, { cwd, env, detached: platform !== 'win32', stdio: ['ignore', 'pipe', 'pipe'] });
346
+ } catch (error ) {
347
+ resolve({ status: 'error', code: null, signal: null, error: error?.code || 'spawn-failed', stdout: '', truncated: false, proc: null, cleanup: null });
348
+ return;
349
+ }
350
+ const chunks = [];
351
+ let size = 0;
352
+ let truncated = false;
353
+ let settled = false;
354
+ let timedOut = false;
355
+ let cleanup = null;
356
+ let cleanupDone = Promise.resolve();
357
+ const proc = { pid: child.pid, pgid: child.pid, start: platform === 'win32' ? null : procStart(child.pid) };
358
+
359
+ child.stdout.on('data', (chunk ) => {
360
+ if (size >= maxStdout) {
361
+ truncated = true;
362
+ return;
363
+ }
364
+ const room = maxStdout - size;
365
+ if (chunk.length > room) truncated = true;
366
+ const kept = chunk.subarray(0, room);
367
+ chunks.push(kept);
368
+ size += kept.length;
369
+ });
370
+ child.stderr.resume();
371
+
372
+ const finish = (result ) => {
373
+ if (settled) return;
374
+ settled = true;
375
+ clearTimeout(timer);
376
+ child.stdout.destroy();
377
+ child.stderr.destroy();
378
+ resolve({ ...result, stdout: Buffer.concat(chunks).toString('utf8'), truncated, proc: child.pid ? proc : null, cleanup });
379
+ };
380
+
381
+ const timer = setTimeout(() => {
382
+ timedOut = true;
383
+ cleanupDone = escalate({ proc, platform }).then((outcome) => {
384
+ cleanup = outcome;
385
+ });
386
+ }, timeoutMs);
387
+
388
+ child.on('error', (error ) => {
389
+ cleanupDone.then(() => finish({ status: timedOut ? 'timeout' : 'error', code: null, signal: null, error: error?.code || 'spawn-failed' }));
390
+ });
391
+ child.on('exit', (code , signalName ) => {
392
+ clearTimeout(timer);
393
+ // Grandchildren may hold the pipes open; read what is buffered, then settle without waiting
394
+ // for every holder to close them.
395
+ setTimeout(() => cleanupDone.then(() => finish({ status: timedOut ? 'timeout' : 'exited', code, signal: signalName, error: null })), 0);
396
+ });
397
+ });
398
+ }
399
+
400
+ module.exports = {
401
+ KILL_GRACE_MS,
402
+ KILL_FINAL_MS,
403
+ POLL_MS,
404
+ PS_TIMEOUT_MS,
405
+ psEnv,
406
+ procStart,
407
+ alive,
408
+ parseListing,
409
+ startMs,
410
+ groupMembers,
411
+ groupAlive,
412
+ snapshotTree,
413
+ escalate,
414
+ spawnBounded,
415
+ };
@@ -0,0 +1,43 @@
1
+ 'use strict';
2
+
3
+ // Feature 106 IC-002: recognise declared quota signals in a bounded, masked report summary.
4
+ //
5
+ // `matchQuota(summary, signals)` reads at most the first 2,000 UTF-8 bytes of `summary` (cut without
6
+ // splitting a character), lowercases both sides and searches literal substrings, no regular
7
+ // expression. It returns the first matching signal in declaration order, as declared, or null. The
8
+ // signals are registry data (a harness's `structuredOutput.quotaSignals`): this module names no
9
+ // harness, provider or model. Pure: no I/O.
10
+
11
+ const QUOTA_SCAN_BYTES = 2000;
12
+
13
+ /** `text` cut to at most `max` UTF-8 bytes without splitting a character. */
14
+ function cutBytes(text , max ) {
15
+ const bytes = Buffer.from(text, 'utf8');
16
+ if (bytes.length <= max) return text;
17
+ let end = max;
18
+ while (end > 0 && (bytes[end] & 0xc0) === 0x80) end--; // back off a continuation byte
19
+ return bytes.subarray(0, end).toString('utf8');
20
+ }
21
+
22
+ function matchQuota(summary , signals ) {
23
+ if (typeof summary !== 'string' || !summary || !Array.isArray(signals) || signals.length === 0) return null;
24
+ const text = cutBytes(summary, QUOTA_SCAN_BYTES).toLowerCase();
25
+ for (const signal of signals) {
26
+ if (typeof signal === 'string' && text.includes(signal.toLowerCase())) return signal;
27
+ }
28
+ return null;
29
+ }
30
+
31
+ /**
32
+ * IC-002: the `report.quota` a finished run's report carries, or null. Only a `failure` report can
33
+ * carry it, and only when its summary matches the descriptor's `structuredOutput.quotaSignals`;
34
+ * `success`, `denied` and `unparsed` never do.
35
+ */
36
+ function quotaOf(report , descriptor ) {
37
+ if (!report || report.status !== 'failure') return null;
38
+ const quotaSignals = descriptor?.structuredOutput?.quotaSignals;
39
+ const signal = matchQuota(report.summary ?? null, quotaSignals?.signals);
40
+ return signal ? { signal, basis: quotaSignals.basis } : null;
41
+ }
42
+
43
+ module.exports = { matchQuota, quotaOf, QUOTA_SCAN_BYTES };