@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,434 @@
1
+ 'use strict';
2
+
3
+ // TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
4
+
5
+ /**
6
+ * Review findings record (`review/findings.json` schema 1): the one validator, shared by roadmap
7
+ * gate-b (`src/runtime/roadmap/gates.js`) and the review check verb. Pure: no filesystem, no git,
8
+ * no clock.
9
+ */
10
+
11
+ const HEX_HEAD = /^[0-9a-f]{40}$/;
12
+ const ROW_ID = /^[0-9]{3}([a-z]|-[A-Z])?$/;
13
+ const SEVERITIES = Object.freeze(['blocker', 'should-fix', 'note']);
14
+ const DISPOSITIONS = Object.freeze(['open', 'fixed', 'refuted', 'user-accepted']);
15
+ const ORIGINS = Object.freeze(['introduced', 'pre-existing']);
16
+ const VERIFICATIONS = Object.freeze(['confirmed', 'plausible']);
17
+ const ACCEPTED_AS = Object.freeze(['intentional', 'not-worth-fixing']);
18
+ const ROUND_CAP = 3;
19
+ // Invisible and format characters (zero-width, word joiner, soft hyphen, bidi and tag marks) are
20
+ // stripped and dash variants unified before a verifier id is compared with a seat id (095 C-7, R-1).
21
+ const IGNORABLE = /[\p{Default_Ignorable_Code_Point}\p{Cf}]/gu;
22
+ const DASH = /\p{Pd}/gu;
23
+ const PROTOCOL_DIGEST = /^[0-9a-f]{64}$/;
24
+ const { workerIdFault } = require('../../registry/worker-ids.js');
25
+ const MODEL_ID_RE = /^[A-Za-z0-9][A-Za-z0-9 ._:/@()[\]#+-]*$/;
26
+ const MODEL_ID_MAX = 120;
27
+ const validModelId = (value ) =>
28
+ value === 'unknown' || (typeof value === 'string' && value.length > 0 && value.length <= MODEL_ID_MAX && MODEL_ID_RE.test(value) && !value.endsWith(' '));
29
+ /** The closed top-level key set (IC-001). */
30
+ const RECORD_KEYS = Object.freeze(['schema', 'row', 'slug', 'reviewedHead', 'depth', 'risk', 'protocol', 'rounds', 'author', 'seats', 'converged']);
31
+ /** The optional convergence keys of a converged finding (IC-001); other finding keys stay open. */
32
+ const FINDING_FIELDS = Object.freeze(['origin', 'preExistingAt', 'escapedFrom', 'verification', 'verifiedBy', 'bugClass', 'sweep', 'acceptedAs']);
33
+ /** `family` of an author or a seat: a lower-case family id; `unknown` means not verifiable (119 IC-002). */
34
+ const FAMILY_ID = /^[a-z][a-z0-9-]*$/;
35
+ const object = (x ) => x !== null && typeof x === 'object' && !Array.isArray(x);
36
+ const nonempty = (x ) => typeof x === 'string' && x.trim().length > 0;
37
+ const strings = (x ) => Array.isArray(x) && x.every(nonempty) && new Set(x).size === x.length;
38
+
39
+ /** The risk vocabulary (095b IC-005), read from the verification registry on first use only, so a record without
40
+ * `risk` (and a bench `require` of this file) never loads it. `null` fails closed. */
41
+ let riskVocabulary ;
42
+ function riskLevels() {
43
+ if (riskVocabulary === undefined) {
44
+ try {
45
+ const { loadVerificationRegistry } = require('../verification/registry.js');
46
+ riskVocabulary = Object.keys(loadVerificationRegistry().riskLevels);
47
+ } catch { riskVocabulary = null; }
48
+ }
49
+ return riskVocabulary;
50
+ }
51
+
52
+ /** The protocol registry (119 IC-001), read on first use only, so a record with neither `depth` nor `protocol`
53
+ * never loads it. `null` (a load failure) fails closed. One read serves the preset ids and the depth vocabulary.
54
+ * It is the merged view (122 IC-014) this process resolves under its own environment and config home: gate-b and the
55
+ * check verb never take a package set from the record or from the environment it was made in (NFR-005). A caller may
56
+ * pass another merged registry (`protocols` / `registry` below); that is the only seam. */
57
+ let protocolRegistry ;
58
+ function protocols() {
59
+ if (protocolRegistry === undefined) {
60
+ try {
61
+ const loaded = require('./protocols.js').loadProtocols();
62
+ protocolRegistry = loaded.ok ? loaded.registry : null;
63
+ } catch { protocolRegistry = null; }
64
+ }
65
+ return protocolRegistry;
66
+ }
67
+
68
+ /** The `kind: review` preset ids of a merged view's active set (119 IC-002, 122 IC-014): the effective core order,
69
+ * then the ids active packages contribute. */
70
+ function reviewPresets(registry = protocols()) {
71
+ if (!registry) return null;
72
+ const { presetIds, contributedPresetIds } = require('./protocols.js');
73
+ return [...presetIds(registry, 'review'), ...contributedPresetIds(registry, 'review')];
74
+ }
75
+
76
+ /** The depth vocabulary (L-1, D-4): each review preset's `depth` label with that preset's seat count. */
77
+ function depthSeats() {
78
+ const registry = protocols();
79
+ if (!registry) return null;
80
+ return Object.fromEntries(require('./protocols.js').presetIds(registry, 'review')
81
+ .map((id ) => [registry.presets[id].depth, registry.presets[id].seats.length]));
82
+ }
83
+
84
+ /** The error of a recorded `protocol`, or null. It is exactly `{ id, digest }`: a review preset id of the merged view and
85
+ * a sha256 hex digest (`protocol`). A well-formed package-qualified id that the view does not hold active is
86
+ * `protocol-unavailable` (122 IC-014): its package is not installed, enabled or trusted where the record is checked. */
87
+ function protocolError(value , registry ) {
88
+ if (!object(value)) return 'protocol';
89
+ const keys = Object.keys(value);
90
+ if (keys.length !== 2 || !keys.includes('id') || !keys.includes('digest')) return 'protocol';
91
+ if (typeof value.digest !== 'string' || !PROTOCOL_DIGEST.test(value.digest)) return 'protocol';
92
+ const ids = reviewPresets(registry);
93
+ if (typeof value.id === 'string' && ids !== null && ids.includes(value.id)) return null;
94
+ return ids !== null && require('../../helper/package-ids.js').isQualifiedId(value.id) ? 'protocol-unavailable' : 'protocol';
95
+ }
96
+
97
+ /** C-6: the first project or local candidate of a resolved setting that is `invalid` (a malformed file or a bad value in it).
98
+ * The resolver then falls back to the key's default without a word, so a `review.*` reader treats it as unreadable. */
99
+ function invalidProjectCandidate(resolution ) {
100
+ return (resolution?.candidates ?? []).find((c ) => ['project', 'local'].includes(c.layer) && c.status === 'invalid');
101
+ }
102
+
103
+ /** The preset keys (`review.preset`, `debate.preset`) also refuse an invalid user value: the user file's preset is a
104
+ * floor like the project's, and an unknown id dropped to `auto` or the registry default would lower it without a word. */
105
+ function invalidPresetCandidate(resolution ) {
106
+ return (resolution?.candidates ?? []).find((c ) => ['user', 'project', 'local'].includes(c.layer) && c.status === 'invalid');
107
+ }
108
+
109
+ /** maint-thin: true when `presetId` is the last (loosest) id of the effective core review order; false on no readable view. */
110
+ function loosestReviewPreset(presetId , registry = protocols()) {
111
+ if (!registry) return false;
112
+ const order = require('./protocols.js').presetIds(registry, 'review');
113
+ return order.length > 0 && order[order.length - 1] === presetId;
114
+ }
115
+
116
+ /** True when the record's protocol is looser than `presetId`, the project's `review.preset`: later in the strictest-first
117
+ * core `order.review`, or, for a contributed id on either side, not dominating it (122 IC-014). `auto` needs the diff,
118
+ * which gate-b does not read, so it never compares (C-2). Fails closed: an id outside the active set of `registry` (gate-b's
119
+ * own merged view by default, never the record's), or no readable view, counts as looser. */
120
+ function protocolLooserThanPreset(protocol , presetId , registry = protocols()) {
121
+ if (!object(protocol) || typeof presetId !== 'string' || presetId === 'auto') return false;
122
+ const ids = reviewPresets(registry);
123
+ if (ids === null || !ids.includes(protocol.id) || !ids.includes(presetId)) return true;
124
+ return !require('./resolve-protocol.js').atLeastAsStrict(registry, 'review', protocol.id, presetId);
125
+ }
126
+
127
+ /** 122 IC-024: the `review.run` telemetry `protocol` of a recorded id: a core review preset id as it is, `package` for an
128
+ * id an active package contributes (no third-party name enters telemetry), null for anything else. */
129
+ function telemetryProtocol(id , registry = protocols()) {
130
+ if (typeof id !== 'string' || !registry) return null;
131
+ const { presetIds, contributedPresetIds } = require('./protocols.js');
132
+ if (presetIds(registry, 'review').includes(id)) return id;
133
+ return contributedPresetIds(registry, 'review').includes(id) ? 'package' : null;
134
+ }
135
+
136
+ /** Repo-relative literal path; traversal, globs, absolute paths and alternate separators fail closed. */
137
+ function validPath(value ) {
138
+ return nonempty(value) && !/^[\/~]|[\\\x00-\x1f:*?\[\]{}]/.test(value)
139
+ && !value.split('/').some((part , i , all ) => part === '..' || part === '.' || (part === '' && i !== all.length - 1));
140
+ }
141
+
142
+ /** IC-006 refutation evidence is a locator: `path:line[-line]`, or a test or reproducer file path.
143
+ * Shape only: any repo-relative file path with an extension passes; existence is not checked. */
144
+ function evidenceLocator(value ) {
145
+ if (!nonempty(value) || /\s/.test(value)) return false;
146
+ const at = /^(.+):[1-9][0-9]*(?:-[1-9][0-9]*)?$/.exec(value);
147
+ return at ? validPath(at[1]) && !at[1].endsWith('/') : validPath(value) && /[^/]\.[A-Za-z0-9]+$/.test(value);
148
+ }
149
+
150
+ function normalizedSeatId(value ) {
151
+ return value.normalize('NFKC').replace(IGNORABLE, '').replace(DASH, '-').trim().toUpperCase().toLowerCase();
152
+ }
153
+
154
+ const identityPart = (key , value ) => key === 'agent' ? nonempty(value)
155
+ : key === 'session' ? value === null || nonempty(value)
156
+ : typeof value === 'string' && FAMILY_ID.test(value);
157
+
158
+ /** `author` is `{ agent, session, family }` plus optional `harness` and `model` (119 IC-002, 120 IC-015). */
159
+ function validAuthor(value ) {
160
+ if (!object(value)) return false;
161
+ const keys = Object.keys(value);
162
+ if (!['agent', 'session', 'family'].every((key) => keys.includes(key) && identityPart(key, value[key]))) return false;
163
+ for (const key of keys) {
164
+ if (key === 'agent' || key === 'session' || key === 'family') continue;
165
+ if (key === 'harness') {
166
+ if (workerIdFault(value.harness) !== null) return false;
167
+ } else if (key === 'model') {
168
+ if (!validModelId(value.model)) return false;
169
+ } else {
170
+ return false;
171
+ }
172
+ }
173
+ return true;
174
+ }
175
+
176
+ /** A seat's optional `agent`/`session`/`family`/`harness`/`model`: any may be present, each with the author's shape. */
177
+ const validSeatIdentity = (seat ) =>
178
+ ['agent', 'session', 'family'].every((key) => !Object.hasOwn(seat, key) || identityPart(key, seat[key]))
179
+ && (!Object.hasOwn(seat, 'harness') || workerIdFault(seat.harness) === null)
180
+ && (!Object.hasOwn(seat, 'model') || validModelId(seat.model))
181
+ && (!Object.hasOwn(seat, 'channel') || seat.channel === 'observed' || seat.channel === 'claim');
182
+
183
+
184
+
185
+ function normalizeModel(m ) {
186
+ if (typeof m !== 'string') return '';
187
+ return m.normalize('NFKC').trim().toLowerCase();
188
+ }
189
+
190
+ function stripTrailingQualifier(s ) {
191
+ return s.replace(/\s*(\([^)]*\)|\[[^\]]*\]|#[^\s]*)$/, '').trim();
192
+ }
193
+
194
+ /**
195
+ * The reviewer-independence facts of a record whose shape is already checked (119 L-C2, 120 IC-015).
196
+ * - Author facts: INV-1 `independence:<seat>` always an error (the seat's agent is the author's, or both sessions are non-null and equal);
197
+ * `same-family:<seat>` an error under `refuse`, an advisory under `warn`; `family-unverified:<seat>` an advisory.
198
+ * - INV-8 facts: compared against `authors` whose kind matches `INV-8.authorKinds` (outcome ii: subagent, sequential).
199
+ */
200
+ function identityFacts(data , sameFamily , authors = []) {
201
+ const facts = { errors: [] , advisories: [] };
202
+ if (!Array.isArray(data.seats)) return facts;
203
+
204
+ // 1. Author facts (INV-1, same-family, family-unverified)
205
+ if (Object.hasOwn(data, 'author') && validAuthor(data.author)) {
206
+ const { author } = data;
207
+ for (const seat of data.seats) {
208
+ if (!object(seat) || !nonempty(seat.id) || !validSeatIdentity(seat)) continue;
209
+ if (seat.agent === author.agent || (seat.session != null && author.session != null && seat.session === author.session)) {
210
+ facts.errors.push(`independence:${seat.id}`);
211
+ }
212
+ if (seat.family === undefined || seat.family === 'unknown' || author.family === 'unknown') {
213
+ facts.advisories.push(`family-unverified:${seat.id}`);
214
+ } else if (seat.family === author.family) {
215
+ (sameFamily === 'refuse' ? facts.errors : facts.advisories).push(`same-family:${seat.id}`);
216
+ }
217
+ }
218
+ }
219
+
220
+ // 2. INV-8 facts (IC-015 comparison table)
221
+ const has120Data = (Array.isArray(authors) && authors.length > 0)
222
+ || (Object.hasOwn(data, 'author') && (Boolean(data.author?.harness) || Boolean(data.author?.model)))
223
+ || data.seats.some((s ) => object(s) && (Boolean(s.harness) || Boolean(s.model)));
224
+
225
+ const proto = protocols();
226
+ const inv8 = proto?.invariants?.find((inv ) => inv.id === 'INV-8');
227
+ const authorKinds = Array.isArray(inv8?.authorKinds) ? inv8.authorKinds : ['subagent', 'sequential'];
228
+
229
+ const relevantAuthors = (Array.isArray(authors) ? authors : []).filter(
230
+ (a ) => object(a) && authorKinds.includes(a.kind)
231
+ );
232
+
233
+ for (const seat of data.seats) {
234
+ if (!object(seat) || !nonempty(seat.id) || !validSeatIdentity(seat)) continue;
235
+
236
+ const seatHasHarness = typeof seat.harness === 'string' && seat.harness.length > 0;
237
+ const seatHasModel = typeof seat.model === 'string' && seat.model.length > 0;
238
+
239
+ if (!seatHasHarness && !seatHasModel) {
240
+ if (has120Data) {
241
+ facts.advisories.push(`seat-identity-missing:${seat.id}`);
242
+ }
243
+ continue;
244
+ }
245
+
246
+ if (!seatHasHarness) continue;
247
+
248
+ for (const author of relevantAuthors) {
249
+ if (author.harness !== seat.harness) continue;
250
+
251
+ const normSeat = normalizeModel(seat.model);
252
+ const normAuthor = normalizeModel(author.model);
253
+
254
+ if (normSeat.length > 0 && normSeat !== 'unknown' && normAuthor !== 'unknown' && normSeat === normAuthor) {
255
+ facts.errors.push(`independence-model:${seat.id}`);
256
+ } else if (
257
+ normSeat.length > 0 &&
258
+ normAuthor.length > 0 &&
259
+ normSeat !== 'unknown' &&
260
+ normAuthor !== 'unknown' &&
261
+ stripTrailingQualifier(normSeat).length > 0 &&
262
+ stripTrailingQualifier(normSeat) === stripTrailingQualifier(normAuthor)
263
+ ) {
264
+ facts.advisories.push(`independence-unverified:${seat.id}`);
265
+ } else {
266
+ const seatObserved = seat.channel === 'observed';
267
+ const authorObserved = author.channel === 'observed';
268
+ const seatKnown = normSeat.length > 0 && normSeat !== 'unknown';
269
+ const authorKnown = normAuthor.length > 0 && normAuthor !== 'unknown';
270
+
271
+ if (seatObserved && authorObserved && seatKnown && authorKnown) {
272
+ // independent, nothing reported
273
+ } else {
274
+ facts.advisories.push(`independence-unverified:${seat.id}`);
275
+ }
276
+ }
277
+ }
278
+ }
279
+
280
+ facts.errors = [...new Set(facts.errors)];
281
+ facts.advisories = [...new Set(facts.advisories)].filter(
282
+ (adv) => !facts.errors.some((err) => err === `independence-model:${adv.split(':')[1]}`)
283
+ );
284
+
285
+ return facts;
286
+ }
287
+
288
+ /** Schema checks only. Git ancestry and register-channel backing are separate facts.
289
+ * `sameFamily` (`warn` by default) is the project's `review.sameFamily`: under `refuse` a same-family seat is an error.
290
+ * `preset` (`auto` by default) is the effective `review.preset`: when it is neither `auto` nor the loosest core preset
291
+ * a record without `protocol` is an error (`protocol-missing`) and its rounds and seats are held to that preset (C-2 closure). */
292
+ function validateFindings(
293
+ json ,
294
+ { sameFamily = 'warn', preset = 'auto', authors = [], protocols: registry } = {}
295
+ ) {
296
+ const errors = [];
297
+ let data = json;
298
+ if (typeof json === 'string' || Buffer.isBuffer(json)) {
299
+ try { data = JSON.parse(String(json)); } catch { return { ok: false, errors: ['json'] }; }
300
+ }
301
+ if (!object(data)) return { ok: false, errors: ['object'] };
302
+ if (Object.keys(data).some((key) => !RECORD_KEYS.includes(key))) errors.push('keys');
303
+ if (data.schema !== 1) errors.push('schema');
304
+ if (typeof data.row !== 'string' || !ROW_ID.test(data.row)) errors.push('row');
305
+ if (typeof data.slug !== 'string' || !data.slug.startsWith(`${data.row}-`) || !/^[a-zA-Z0-9-]+$/.test(data.slug)) errors.push('slug');
306
+ if (typeof data.reviewedHead !== 'string' || !HEX_HEAD.test(data.reviewedHead)) errors.push('reviewedHead');
307
+ const depths = Object.hasOwn(data, 'depth') ? depthSeats() : null;
308
+ const risks = Object.hasOwn(data, 'risk') ? riskLevels() : null;
309
+ if (Object.hasOwn(data, 'depth') && !(typeof data.depth === 'string' && depths && Object.hasOwn(depths, data.depth))) errors.push('depth');
310
+ if (Object.hasOwn(data, 'risk') && !(typeof data.risk === 'string' && risks && risks.includes(data.risk))) errors.push('risk');
311
+ const view = () => (registry === undefined ? protocols() : registry);
312
+ const protocolFault = Object.hasOwn(data, 'protocol') ? protocolError(data.protocol, view()) : null;
313
+ if (protocolFault) errors.push(protocolFault);
314
+ if (Object.hasOwn(data, 'rounds') && !(Number.isSafeInteger(data.rounds) && data.rounds >= 1)) errors.push('rounds');
315
+ if (Object.hasOwn(data, 'author') && !validAuthor(data.author)) errors.push('author');
316
+ const seats = Array.isArray(data.seats) ? data.seats : [];
317
+ const converged = Array.isArray(data.converged) ? data.converged : [];
318
+ if (!Array.isArray(data.seats)) errors.push('seats');
319
+ if (!Array.isArray(data.converged)) errors.push('converged');
320
+ const seatIds = new Set ();
321
+ const findingIds = new Set ();
322
+ const seatOf = new Map ();
323
+ for (const seat of seats) {
324
+ if (!object(seat) || !nonempty(seat.id) || seatIds.has(seat.id) || !Array.isArray(seat.findings)) { errors.push('seat'); continue; }
325
+ seatIds.add(seat.id);
326
+ if (!validSeatIdentity(seat)) errors.push(`seat-identity:${seat.id}`);
327
+ for (const finding of seat.findings) {
328
+ if (!object(finding) || !nonempty(finding.id) || findingIds.has(finding.id) || !SEVERITIES.includes(finding.severity)
329
+ || !nonempty(finding.locator) || !nonempty(finding.summary)) { errors.push('seat-finding'); continue; }
330
+ findingIds.add(finding.id);
331
+ seatOf.set(finding.id, seat.id);
332
+ }
333
+ }
334
+ // C-2: the record is held to its own preset, not only to its shape; a record that omits `protocol` is held to the
335
+ // project's non-auto `review.preset` instead (omitting the field must not skip the check).
336
+ const recorded = Object.hasOwn(data, 'protocol');
337
+ // maint-thin: the loosest core preset (the shipped default, review-single) constrains nothing a record could miss,
338
+ // so only a stricter one makes the field mandatory.
339
+ if (!recorded && preset !== 'auto' && !loosestReviewPreset(preset)) errors.push('protocol-missing');
340
+ const held = recorded ? (protocolFault === null ? data.protocol.id : null) : (preset !== 'auto' ? preset : null);
341
+ if (held && Array.isArray(data.seats) && reviewPresets(view())?.includes(held)) {
342
+ const heldPreset = view().presets[held];
343
+ if (Number.isSafeInteger(data.rounds) && data.rounds >= 1 && data.rounds < heldPreset.rounds.min) errors.push('protocol-rounds');
344
+ if (data.seats.length < heldPreset.seats.length) errors.push('protocol-seats');
345
+ }
346
+ for (const error of identityFacts(data, sameFamily === 'refuse' ? 'refuse' : 'warn', authors).errors) errors.push(error);
347
+ const convergedIds = new Set ();
348
+ for (const finding of converged) {
349
+ if (!object(finding) || !nonempty(finding.id) || convergedIds.has(finding.id) || !strings(finding.from)
350
+ || !SEVERITIES.includes(finding.severity) || !DISPOSITIONS.includes(finding.status) || !nonempty(finding.locator)) { errors.push('converged'); continue; }
351
+ convergedIds.add(finding.id);
352
+ if (!finding.from.every((id ) => findingIds.has(id))) errors.push(`from:${finding.id}`);
353
+ if (finding.status === 'fixed' && (typeof finding.fixedIn !== 'string' || !HEX_HEAD.test(finding.fixedIn))) errors.push(`fixedIn:${finding.id}`);
354
+ if (finding.status === 'refuted' && !evidenceLocator(finding.evidence)) errors.push(`evidence:${finding.id}`);
355
+ if (finding.status === 'user-accepted' && (typeof finding.decision !== 'string' || !/^DEC-[0-9]{3,}$/.test(finding.decision))) errors.push(`decision:${finding.id}`);
356
+ for (const error of convergenceErrors(finding, data.row, seatOf)) errors.push(error);
357
+ }
358
+ return { ok: errors.length === 0, errors };
359
+ }
360
+
361
+ /** IC-001's optional convergence fields of one well-formed converged finding, in FINDING_FIELDS order.
362
+ * Presence is `Object.hasOwn`, so a reserved key holding null or '' is outside its vocabulary.
363
+ * `verifiedBy` without `verification` is allowed: IC-001 requires it only when `confirmed`. */
364
+ function convergenceErrors(finding , row , seatOf ) {
365
+ const has = (key ) => Object.hasOwn(finding, key);
366
+ const preExisting = finding.origin === 'pre-existing';
367
+ const bad = [];
368
+ if (has('origin') && !ORIGINS.includes(finding.origin)) bad.push('origin');
369
+ if (preExisting ? typeof finding.preExistingAt !== 'string' || !HEX_HEAD.test(finding.preExistingAt) : has('preExistingAt')) bad.push('preExistingAt');
370
+ if (has('escapedFrom') && (!preExisting || typeof finding.escapedFrom !== 'string' || !ROW_ID.test(finding.escapedFrom) || finding.escapedFrom === row)) bad.push('escapedFrom');
371
+ if (has('verification') && !VERIFICATIONS.includes(finding.verification)) bad.push('verification');
372
+ if (has('verifiedBy') || finding.verification === 'confirmed') {
373
+ const verifiedBy = typeof finding.verifiedBy === 'string' ? normalizedSeatId(finding.verifiedBy) : '';
374
+ if (typeof finding.verifiedBy !== 'string' || !nonempty(finding.verifiedBy)
375
+ || verifiedBy === '' || /[\u0000-\u001f\u007f-\u009f]/u.test(finding.verifiedBy) || /\s/u.test(verifiedBy)
376
+ || finding.from.some((id ) => seatOf.has(id)
377
+ && normalizedSeatId(seatOf.get(id) ) === verifiedBy)) bad.push('verifiedBy');
378
+ }
379
+ if (has('bugClass') && (!nonempty(finding.bugClass) || !has('sweep'))) bad.push('bugClass');
380
+ if (has('sweep') && (!nonempty(finding.sweep) || !has('bugClass'))) bad.push('sweep');
381
+ if (has('acceptedAs') && (finding.status !== 'user-accepted' || !ACCEPTED_AS.includes(finding.acceptedAs))) bad.push('acceptedAs');
382
+ return bad.map((key) => `${key}:${finding.id}`);
383
+ }
384
+
385
+ /** A record that already passed validateFindings, parsed when it came as a string or Buffer. */
386
+ const parsed = (json ) => (typeof json === 'string' || Buffer.isBuffer(json) ? JSON.parse(String(json)) : json);
387
+
388
+ /** IC-004's counts of a valid record; `null` for an invalid one. */
389
+ function countFindings(json ) {
390
+ if (!validateFindings(json).ok) return null;
391
+ const data = parsed(json);
392
+ const converged = data.converged;
393
+ const count = (test ) => converged.filter(test).length;
394
+ return {
395
+ task: data.slug,
396
+ rounds: data.rounds ?? null,
397
+ seats: data.seats.length,
398
+ seat_findings: data.seats.reduce((sum , seat ) => sum + seat.findings.length, 0),
399
+ converged: converged.length,
400
+ blocker: count((f) => f.severity === 'blocker'),
401
+ should_fix: count((f) => f.severity === 'should-fix'),
402
+ note: count((f) => f.severity === 'note'),
403
+ open: count((f) => f.status === 'open'),
404
+ fixed: count((f) => f.status === 'fixed'),
405
+ refuted: count((f) => f.status === 'refuted'),
406
+ user_accepted: count((f) => f.status === 'user-accepted'),
407
+ pre_existing: count((f) => f.origin === 'pre-existing'),
408
+ escaped: count((f) => Object.hasOwn(f, 'escapedFrom')),
409
+ confirmed: count((f) => f.verification === 'confirmed'),
410
+ plausible: count((f) => f.verification === 'plausible'),
411
+ unverified_blockers: count((f) => f.severity === 'blocker' && f.verification !== 'confirmed'),
412
+ bug_classes: new Set(converged.filter((f) => Object.hasOwn(f, 'bugClass')).map((f) => f.bugClass)).size,
413
+ };
414
+ }
415
+
416
+ /** IC-002's advisories of a valid record, sorted; `[]` for an invalid one (its errors say why). */
417
+ function reviewAdvisories(json , { sameFamily = 'warn', authors = [] } = {}) {
418
+ const option = { sameFamily: sameFamily === 'refuse' ? 'refuse' : 'warn', authors } ;
419
+ if (!validateFindings(json, option).ok) return [];
420
+ const data = parsed(json);
421
+ const advisories = identityFacts(data, option.sameFamily, option.authors).advisories;
422
+ for (const finding of data.converged) {
423
+ if (finding.severity === 'blocker' && finding.verification !== 'confirmed') advisories.push(`unverified-blocker:${finding.id}`);
424
+ if (Object.hasOwn(finding, 'bugClass') && finding.status === 'open') advisories.push(`bug-class-open:${finding.id}`);
425
+ }
426
+ if (Object.hasOwn(data, 'rounds') && data.rounds >= ROUND_CAP) advisories.push(`round-cap:${data.rounds}`);
427
+ if (Object.hasOwn(data, 'depth') && data.seats.length < (depthSeats() )[data.depth]) advisories.push(`depth-unmet:${data.depth}`);
428
+ return advisories.sort();
429
+ }
430
+
431
+ module.exports = {
432
+ HEX_HEAD, SEVERITIES, DISPOSITIONS, ORIGINS, VERIFICATIONS, ACCEPTED_AS, ROUND_CAP, RECORD_KEYS, FINDING_FIELDS,
433
+ object, nonempty, strings, validPath, evidenceLocator, validateFindings, countFindings, reviewAdvisories, protocolLooserThanPreset, invalidProjectCandidate, invalidPresetCandidate, telemetryProtocol,
434
+ };