@khoavu882/doflow 2.0.0-beta.4 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (497) hide show
  1. package/README.md +12 -11
  2. package/bin/doflow.js +21 -11
  3. package/core/.antigravity-plugin/plugin.json +1 -3
  4. package/core/.claude-plugin/plugin.json +1 -1
  5. package/core/.codex-plugin/plugin.json +1 -1
  6. package/core/.plugin/plugin.json +1 -1
  7. package/core/harnesses/claude/hooks/lib.sh +43 -24
  8. package/core/harnesses/claude/hooks/mcp-tool-guard.sh +7 -17
  9. package/core/harnesses/claude/hooks/permission-audit.sh +3 -0
  10. package/core/harnesses/claude/hooks/post-compact.sh +35 -32
  11. package/core/harnesses/claude/hooks/post-edit-lint.sh +2 -0
  12. package/core/harnesses/claude/hooks/post-shell-observe.sh +48 -0
  13. package/core/harnesses/claude/hooks/post-tool-failure.sh +3 -0
  14. package/core/harnesses/claude/hooks/pre-bash-guard.sh +7 -17
  15. package/core/harnesses/claude/hooks/pre-compact.sh +23 -27
  16. package/core/harnesses/claude/hooks/pre-implement-gate.sh +7 -18
  17. package/core/harnesses/claude/hooks/session-end.sh +23 -10
  18. package/core/harnesses/claude/hooks/session-start.sh +5 -3
  19. package/core/harnesses/claude/hooks/skill-config-audit.sh +3 -0
  20. package/core/harnesses/claude/hooks/stop-check.sh +5 -3
  21. package/core/harnesses/claude/hooks/subagent-audit.sh +5 -5
  22. package/core/harnesses/claude/hooks/user-prompt-submit.sh +5 -3
  23. package/core/harnesses/claude/settings/settings.json +18 -0
  24. package/core/harnesses/claude/statusline/doflow-statusline.sh +1 -0
  25. package/core/harnesses/codex/agents/core-implementer.toml +1 -0
  26. package/core/harnesses/codex/agents/delivery-lead.toml +10 -0
  27. package/core/harnesses/codex/agents/quality-guardian.toml +1 -1
  28. package/core/harnesses/codex/agents/research-writer.toml +1 -1
  29. package/core/harnesses/codex/agents/reviewer.toml +10 -0
  30. package/core/harnesses/codex/agents/spec-analyst.toml +1 -1
  31. package/core/harnesses/codex/agents/system-architect.toml +1 -1
  32. package/core/harnesses/codex/hooks/lib.sh +44 -25
  33. package/core/harnesses/codex/hooks/mcp-tool-guard.sh +7 -15
  34. package/core/harnesses/codex/hooks/post-edit-lint.impl.sh +3 -1
  35. package/core/harnesses/codex/hooks/pre-bash-guard.sh +7 -15
  36. package/core/harnesses/codex/hooks/pre-compact.impl.sh +29 -26
  37. package/core/harnesses/codex/hooks/pre-implement-gate.sh +7 -16
  38. package/core/harnesses/codex/hooks/session-end.impl.sh +23 -10
  39. package/core/harnesses/codex/hooks/session-start.sh +5 -3
  40. package/core/harnesses/codex/hooks/stop-check.sh +6 -5
  41. package/core/harnesses/codex/hooks/subagent-audit.sh +18 -6
  42. package/core/harnesses/codex/hooks/user-prompt-submit.sh +5 -4
  43. package/core/harnesses/copilot/hooks/doflow-lib.sh +93 -0
  44. package/core/harnesses/copilot/hooks/doflow-mcp-tool-guard.sh +9 -0
  45. package/core/harnesses/copilot/hooks/doflow-pre-bash-guard.sh +9 -0
  46. package/core/harnesses/copilot/hooks/doflow-pre-implement-gate.sh +9 -0
  47. package/core/harnesses/copilot/hooks/doflow-session-start.sh +9 -0
  48. package/core/harnesses/copilot/hooks/doflow-stop-check.sh +9 -0
  49. package/core/harnesses/copilot/hooks/doflow-subagent-start.sh +9 -0
  50. package/core/harnesses/copilot/hooks/doflow-subagent-stop.sh +9 -0
  51. package/core/harnesses/copilot/hooks/doflow.json +57 -0
  52. package/core/harnesses/kiro/agents/doflow.json +1 -1
  53. package/core/harnesses/kiro/hooks/lib.sh +26 -6
  54. package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +21 -5
  55. package/core/harnesses/kiro/hooks/pre-bash-guard.sh +21 -5
  56. package/core/harnesses/kiro/hooks/pre-implement-gate.sh +21 -5
  57. package/core/harnesses/kiro/hooks/session-start.sh +12 -5
  58. package/core/harnesses/kiro/hooks/stop-check.sh +22 -30
  59. package/core/harnesses/pi/extensions/doflow-guard.js +167 -0
  60. package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +27 -12
  61. package/core/harnesses/shared/hooks/adapters/antigravity.js +14 -57
  62. package/core/harnesses/shared/hooks/bridge/bridge.sh +277 -0
  63. package/core/harnesses/shared/hooks/bridge/envelope.jq +129 -0
  64. package/core/harnesses/shared/hooks/bridge/observe-model.sh +75 -0
  65. package/core/harnesses/shared/hooks/bridge/respond.sh +70 -0
  66. package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +28 -14
  67. package/core/harnesses/shared/hooks/policies/capture-failure.sh +51 -16
  68. package/core/harnesses/shared/hooks/policies/compact-text.sh +46 -0
  69. package/core/harnesses/shared/hooks/policies/contract.sh +78 -0
  70. package/core/harnesses/shared/hooks/policies/deny-json.sh +35 -9
  71. package/core/harnesses/shared/hooks/policies/emission-rules.sh +37 -0
  72. package/core/harnesses/shared/hooks/policies/emission.jq +209 -0
  73. package/core/harnesses/shared/hooks/policies/emission.sh +383 -0
  74. package/core/harnesses/shared/hooks/policies/identity.sh +177 -0
  75. package/core/harnesses/shared/hooks/policies/lib.sh +65 -40
  76. package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +48 -35
  77. package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +284 -33
  78. package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +180 -110
  79. package/core/harnesses/shared/hooks/policies/session-context.sh +66 -7
  80. package/core/harnesses/shared/hooks/policies/stop-check.sh +61 -23
  81. package/core/harnesses/shared/hooks/policies/subagent-audit.sh +39 -6
  82. package/core/harnesses/shared/hooks/policies/summary-store.sh +347 -0
  83. package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +97 -159
  84. package/core/harnesses/shared/hooks/stream-hook-runner.js +114 -187
  85. package/core/harnesses/shared/locator/doflow-run +2 -3
  86. package/core/registry/advice.json +3 -3
  87. package/core/registry/assets.json +72 -80
  88. package/core/registry/capabilities.json +12 -0
  89. package/core/registry/contracts.json +60 -33
  90. package/core/registry/contribution-points.json +32 -0
  91. package/core/registry/gates.json +32 -0
  92. package/core/registry/guidance-caps.json +40 -0
  93. package/core/registry/harnesses.json +1542 -277
  94. package/core/registry/lifecycle.json +2837 -37
  95. package/core/registry/mcp.json +5 -1
  96. package/core/registry/models.json +7 -1
  97. package/core/registry/package-templates/README.md +19 -0
  98. package/core/registry/package-templates/agent/files/agent.md +9 -0
  99. package/core/registry/package-templates/agent/template.json +4 -0
  100. package/core/registry/package-templates/capability-provider/files/provider.json +9 -0
  101. package/core/registry/package-templates/capability-provider/template.json +11 -0
  102. package/core/registry/package-templates/debate-preset/files/preset.json +26 -0
  103. package/core/registry/package-templates/debate-preset/template.json +4 -0
  104. package/core/registry/package-templates/review-preset/files/preset.json +30 -0
  105. package/core/registry/package-templates/review-preset/template.json +4 -0
  106. package/core/registry/package-templates/skill/files/SKILL.md +8 -0
  107. package/core/registry/package-templates/skill/template.json +4 -0
  108. package/core/registry/package-templates/worker/files/worker.json +92 -0
  109. package/core/registry/package-templates/worker/template.json +11 -0
  110. package/core/registry/packages/doflow/doflow-package.json +15 -0
  111. package/core/registry/packages/doflow/presets/debate-3.json +26 -0
  112. package/core/registry/protocols.json +61 -0
  113. package/core/registry/routes.json +17 -0
  114. package/core/registry/rules.json +431 -0
  115. package/core/registry/settings.json +276 -0
  116. package/core/registry/storage.json +701 -30
  117. package/core/registry/telemetry.json +236 -0
  118. package/core/registry/transcripts.json +142 -0
  119. package/core/registry/verification.json +76 -1
  120. package/core/registry/workflows.json +55 -0
  121. package/core/shared/agent-specs/core-implementer.md +3 -0
  122. package/core/shared/agent-specs/delivery-lead.md +68 -0
  123. package/core/shared/agent-specs/quality-guardian.md +5 -1
  124. package/core/shared/agent-specs/research-writer.md +10 -3
  125. package/core/shared/agent-specs/reviewer.md +53 -0
  126. package/core/shared/agent-specs/spec-analyst.md +5 -6
  127. package/core/shared/agent-specs/system-architect.md +5 -1
  128. package/core/shared/guidance/DOFLOW_CORE.md +1 -5
  129. package/core/shared/guidance/modes/MODE_DeepResearch.md +1 -1
  130. package/core/shared/guidance/modes/MODE_Orchestration.md +4 -3
  131. package/core/shared/guidance/modes/MODE_Task_Management.md +14 -14
  132. package/core/shared/guidance/pointers/codex.md +1 -4
  133. package/core/shared/guidance/references/ARTIFACT_FORMAT.md +30 -16
  134. package/core/shared/guidance/references/ARTIFACT_VOICE.md +3 -2
  135. package/core/shared/guidance/references/CONSTITUTION_BASE.md +17 -15
  136. package/core/shared/guidance/references/DIAGRAM_GUIDE.md +1 -1
  137. package/core/shared/guidance/references/DOFLOW_CHAIN.md +17 -19
  138. package/core/shared/guidance/references/EVIDENCE_LEDGER.md +7 -8
  139. package/core/shared/guidance/references/MODEL_SELECTION.md +5 -3
  140. package/core/shared/guidance/references/ORCHESTRATION.md +1 -2
  141. package/core/shared/guidance/rules/RULES.md +45 -0
  142. package/core/shared/scripts/doflow/bash/do-args.sh +37 -0
  143. package/core/shared/scripts/doflow/bash/do-exec-paths.sh +4 -0
  144. package/core/shared/scripts/doflow/bash/do-git-state.sh +15 -4
  145. package/core/shared/scripts/doflow/bash/do-parallel-check.sh +4 -0
  146. package/core/shared/scripts/doflow/bash/do-paths.sh +40 -2
  147. package/core/shared/scripts/doflow/bash/do-prereqs.sh +2 -1
  148. package/core/shared/scripts/doflow/bash/do-review-package.sh +4 -0
  149. package/core/shared/scripts/doflow/bash/do-task-brief.sh +58 -16
  150. package/core/shared/scripts/doflow/bash/render-audit.sh +26 -2
  151. package/core/shared/scripts/doflow/bash/render-diagrams.sh +1 -0
  152. package/core/shared/scripts/doflow/bash/render-puml.sh +2 -1
  153. package/core/shared/scripts/doflow/bash/validate-artifacts.sh +48 -4
  154. package/core/shared/scripts/doflow/bin/doflow-run +154 -35
  155. package/core/shared/skills/do/SKILL.md +12 -10
  156. package/core/shared/skills/do/references/advice_calls.md +26 -0
  157. package/core/shared/skills/do/references/pm_routing.md +1 -2
  158. package/core/shared/skills/do/references/program_mode.md +31 -0
  159. package/core/shared/skills/do-brainstorm/SKILL.md +20 -74
  160. package/core/shared/skills/do-brainstorm/references/handoff_branches.md +35 -0
  161. package/core/shared/skills/do-brainstorm/references/intent_seed.md +21 -0
  162. package/core/shared/skills/do-brainstorm/references/round_log.md +17 -0
  163. package/core/shared/skills/do-code-review/SKILL.md +41 -144
  164. package/core/shared/skills/do-code-review/content-types/markdown.md +24 -2
  165. package/core/shared/skills/do-code-review/references/tools.md +90 -0
  166. package/core/shared/skills/do-code-review/scripts/doc_quality_checker.py +549 -7
  167. package/core/shared/skills/do-design/SKILL.md +24 -23
  168. package/core/shared/skills/do-diagnose/SKILL.md +14 -4
  169. package/core/shared/skills/do-document/SKILL.md +11 -1
  170. package/core/shared/skills/do-document/references/deep_research.md +1 -2
  171. package/core/shared/skills/do-document/references/embedded-research.md +18 -6
  172. package/core/shared/skills/do-document/references/implementation-flow.md +5 -6
  173. package/core/shared/skills/do-execute-plan/SKILL.md +17 -59
  174. package/core/shared/skills/do-execute-plan/references/handoff_record.md +7 -2
  175. package/core/shared/skills/do-execute-plan/references/parallel_dispatch.md +13 -0
  176. package/core/shared/skills/do-execute-plan/references/readiness_gate.md +28 -1
  177. package/core/shared/skills/do-execute-plan/references/run_position.md +13 -1
  178. package/core/shared/skills/do-execute-plan/references/scaffold.md +17 -9
  179. package/core/shared/skills/do-execute-plan/references/state_checkpoint.md +11 -0
  180. package/core/shared/skills/do-execute-plan/references/task_attempts.md +78 -24
  181. package/core/shared/skills/do-flow/SKILL.md +8 -9
  182. package/core/shared/skills/do-flow/references/task_classes.md +2 -2
  183. package/core/shared/skills/do-git/SKILL.md +4 -0
  184. package/core/shared/skills/do-implement/SKILL.md +7 -9
  185. package/core/shared/skills/do-plan/SKILL.md +29 -19
  186. package/core/shared/skills/do-test/SKILL.md +7 -4
  187. package/core/shared/templates/doflow/constitution-template.md +9 -3
  188. package/core/shared/templates/doflow/data-model-template.md +1 -1
  189. package/core/shared/templates/doflow/design-template.md +113 -16
  190. package/core/shared/templates/doflow/plan-template.md +11 -10
  191. package/core/shared/templates/doflow/question-log-template.md +14 -2
  192. package/core/shared/templates/doflow/requirement-template.md +1 -1
  193. package/core/worker-evidence/antigravity/checks.json +14 -0
  194. package/core/worker-evidence/codex/2026-10-18-cli-check.json +107 -0
  195. package/core/worker-evidence/codex/2026-10-18-live-run-edit.json +73 -0
  196. package/core/worker-evidence/codex/2026-10-18-live-run-read-only.json +62 -0
  197. package/core/worker-evidence/codex/2026-10-24-cli-check.json +126 -0
  198. package/core/worker-evidence/codex/checks.json +38 -0
  199. package/package.json +10 -6
  200. package/src/adapters/antigravity/index.js +226 -113
  201. package/src/adapters/claude/index.js +39 -5
  202. package/src/adapters/codex/agents.js +25 -15
  203. package/src/adapters/codex/hooks.js +40 -5
  204. package/src/adapters/codex/index.js +100 -22
  205. package/src/adapters/codex/native-projection.js +6 -2
  206. package/src/adapters/copilot/index.js +58 -23
  207. package/src/adapters/copy-tree.js +294 -48
  208. package/src/adapters/guidance-manifest.js +71 -0
  209. package/src/adapters/guidance-pointer.js +19 -0
  210. package/src/adapters/hook-commands.js +6 -10
  211. package/src/adapters/index.js +1 -0
  212. package/src/adapters/kiro/index.js +38 -13
  213. package/src/adapters/opencode/index.js +8 -8
  214. package/src/adapters/pi/hooks.js +120 -0
  215. package/src/adapters/pi/index.js +48 -16
  216. package/src/adapters/resolve-source.js +26 -2
  217. package/src/cli/commands/install.js +3 -2
  218. package/src/cli/commands/reconcile.js +53 -9
  219. package/src/cli/commands/remove.js +2 -2
  220. package/src/cli/commands/rollback.js +6 -3
  221. package/src/cli/commands/self-update.js +18 -9
  222. package/src/cli/commands/status.js +21 -2
  223. package/src/cli/commands/ui.js +99 -0
  224. package/src/cli/commands/update.js +3 -2
  225. package/src/cli/index.js +338 -33
  226. package/src/cli/runtime-commands.js +243 -52
  227. package/src/cli/shared.js +0 -2
  228. package/src/helper/git.js +1 -1
  229. package/src/helper/guardrails.js +1 -1
  230. package/src/helper/marker-merge.js +31 -13
  231. package/src/helper/package-ids.js +37 -0
  232. package/src/helper/semver.js +94 -0
  233. package/src/install/backup.js +105 -15
  234. package/src/install/context.js +1 -1
  235. package/src/install/targets.js +31 -16
  236. package/src/lifecycle/coverage.js +74 -0
  237. package/src/lifecycle/hook-step.js +61 -0
  238. package/src/lifecycle/hook-tier.js +160 -0
  239. package/src/lifecycle/index.js +434 -86
  240. package/src/lifecycle/retired-harnesses.js +318 -0
  241. package/src/lifecycle/view.js +35 -15
  242. package/src/registry/index.js +686 -32
  243. package/src/registry/worker-ids.js +45 -0
  244. package/src/release/build-package.js +0 -3
  245. package/src/release/docs-blocks.js +492 -0
  246. package/src/release/preflight.js +425 -0
  247. package/src/release/rehearse.js +401 -0
  248. package/src/release/release-workflow.js +733 -0
  249. package/src/release/sync-plugin-versions.js +43 -7
  250. package/src/runtime/advice/bench-gate.js +25 -0
  251. package/src/runtime/advice/cli.js +59 -16
  252. package/src/runtime/advice/client.js +32 -9
  253. package/src/runtime/advice/config.js +98 -133
  254. package/src/runtime/advice/doctor.js +1 -1
  255. package/src/runtime/advice/elevation.js +97 -0
  256. package/src/runtime/advice/endpoint-guard.js +9 -3
  257. package/src/runtime/advice/mode.js +37 -0
  258. package/src/runtime/advice/record.js +18 -5
  259. package/src/runtime/advice/registry.js +4 -1
  260. package/src/runtime/advice/shadow-config.js +229 -0
  261. package/src/runtime/advice/shadow-dispatcher.js +276 -0
  262. package/src/runtime/brief-rules.js +81 -0
  263. package/src/runtime/brief-tools.js +111 -34
  264. package/src/runtime/candidate.js +514 -0
  265. package/src/runtime/capabilities/executor.js +450 -0
  266. package/src/runtime/capabilities/loopback.js +41 -0
  267. package/src/runtime/capabilities/sanitizer.js +88 -0
  268. package/src/runtime/capability-router.js +135 -3
  269. package/src/runtime/checkouts.js +73 -11
  270. package/src/runtime/claims.js +27 -12
  271. package/src/runtime/cli-result.js +11 -4
  272. package/src/runtime/cli.js +221 -277
  273. package/src/runtime/config-home.js +42 -0
  274. package/src/runtime/context/arbitrate.js +767 -0
  275. package/src/runtime/context/budget.js +203 -0
  276. package/src/runtime/context/compose.js +1367 -0
  277. package/src/runtime/context/delivery-store.js +100 -0
  278. package/src/runtime/context/emission-rules.js +118 -0
  279. package/src/runtime/context/identity.js +227 -0
  280. package/src/runtime/context/mandatory.js +594 -0
  281. package/src/runtime/context/pointer.js +142 -0
  282. package/src/runtime/context/primary-command.js +117 -0
  283. package/src/runtime/context/primary.js +344 -0
  284. package/src/runtime/context/summary-store.js +655 -0
  285. package/src/runtime/context-pack.js +1106 -245
  286. package/src/runtime/decision-register.js +13 -4
  287. package/src/runtime/dispatch/admission.js +392 -0
  288. package/src/runtime/dispatch/attempt-end.js +274 -0
  289. package/src/runtime/dispatch/command.js +628 -0
  290. package/src/runtime/dispatch/controls.js +904 -0
  291. package/src/runtime/dispatch/envelope.js +276 -0
  292. package/src/runtime/dispatch/grants.js +310 -0
  293. package/src/runtime/dispatch/integrate.js +617 -0
  294. package/src/runtime/dispatch/loop.js +1835 -0
  295. package/src/runtime/dispatch/store.js +352 -0
  296. package/src/runtime/dispatch/verify-run.js +201 -0
  297. package/src/runtime/evidence-fields.js +39 -0
  298. package/src/runtime/evidence-ledger.js +41 -7
  299. package/src/runtime/failure/capture.js +3 -1
  300. package/src/runtime/failure/cli.js +25 -9
  301. package/src/runtime/failure/home.js +57 -30
  302. package/src/runtime/failure/store.js +7 -2
  303. package/src/runtime/feature-pointer.js +174 -0
  304. package/src/runtime/feature-resolve.js +13 -2
  305. package/src/runtime/fleet/command.js +77 -0
  306. package/src/runtime/fleet/projects.js +178 -0
  307. package/src/runtime/fleet/tree.js +383 -0
  308. package/src/runtime/freshness.js +77 -10
  309. package/src/runtime/git-memo.js +46 -0
  310. package/src/runtime/guidance/ablation.js +427 -0
  311. package/src/runtime/health.js +565 -17
  312. package/src/runtime/history-compactor.js +13 -3
  313. package/src/runtime/implementation-gate.js +277 -22
  314. package/src/runtime/inventory/coverage.js +1 -1
  315. package/src/runtime/inventory/index.js +1 -1
  316. package/src/runtime/inventory/precedence.js +2 -2
  317. package/src/runtime/inventory/siblings.js +6 -4
  318. package/src/runtime/knowledge/code-corpus.js +277 -0
  319. package/src/runtime/knowledge/code-lexical.js +130 -0
  320. package/src/runtime/knowledge/code-retrieval.js +307 -0
  321. package/src/runtime/knowledge/code-validation.js +106 -0
  322. package/src/runtime/knowledge/gp3-gate.js +74 -0
  323. package/src/runtime/knowledge/graph-adapter.js +288 -0
  324. package/src/runtime/knowledge/graph-fallback.js +394 -0
  325. package/src/runtime/knowledge/hybrid.js +136 -8
  326. package/src/runtime/knowledge/retrieval.js +34 -1
  327. package/src/runtime/knowledge/rtk-compress.js +77 -0
  328. package/src/runtime/knowledge/semble-bridge.py +106 -0
  329. package/src/runtime/knowledge/semble-lifecycle.js +114 -0
  330. package/src/runtime/knowledge/semble-provider.js +365 -0
  331. package/src/runtime/lifecycle/cli.js +16 -6
  332. package/src/runtime/lifecycle/event-store.js +8 -6
  333. package/src/runtime/lifecycle/followup.js +6 -1
  334. package/src/runtime/lifecycle/overview.js +24 -1
  335. package/src/runtime/lifecycle/report-store.js +8 -10
  336. package/src/runtime/lifecycle/retention.js +95 -31
  337. package/src/runtime/lifecycle/root.js +9 -1
  338. package/src/runtime/lifecycle/status.js +34 -17
  339. package/src/runtime/lifecycle/store-upkeep.js +56 -7
  340. package/src/runtime/mask.js +134 -7
  341. package/src/runtime/mcp-consultations.js +40 -6
  342. package/src/runtime/model-router.js +79 -7
  343. package/src/runtime/models/catalogue.js +191 -0
  344. package/src/runtime/models/command.js +153 -0
  345. package/src/runtime/off-words.js +12 -0
  346. package/src/runtime/outcome.js +3 -3
  347. package/src/runtime/packages/command.js +1009 -0
  348. package/src/runtime/packages/contributions.js +116 -0
  349. package/src/runtime/packages/digest.js +132 -0
  350. package/src/runtime/packages/fetch.js +182 -0
  351. package/src/runtime/packages/grants.js +192 -0
  352. package/src/runtime/packages/index-file.js +134 -0
  353. package/src/runtime/packages/instruction.js +90 -0
  354. package/src/runtime/packages/manifest.js +331 -0
  355. package/src/runtime/packages/points.js +477 -0
  356. package/src/runtime/packages/programs.js +216 -0
  357. package/src/runtime/packages/scaffold.js +277 -0
  358. package/src/runtime/packages/source.js +90 -0
  359. package/src/runtime/packages/state.js +164 -0
  360. package/src/runtime/packages/store.js +449 -0
  361. package/src/runtime/packages/tar.js +106 -0
  362. package/src/runtime/packages/view.js +406 -0
  363. package/src/runtime/plan-check.js +59 -0
  364. package/src/runtime/plan-content.js +324 -0
  365. package/src/runtime/policy-init.js +118 -0
  366. package/src/runtime/profiles/doctor.js +96 -0
  367. package/src/runtime/profiles/resolve.js +244 -0
  368. package/src/runtime/profiles/rollback.js +254 -0
  369. package/src/runtime/profiles/sampler.js +41 -0
  370. package/src/runtime/readiness-record.js +16 -11
  371. package/src/runtime/readiness.js +127 -16
  372. package/src/runtime/research-request.js +50 -25
  373. package/src/runtime/retrieval-plan.js +402 -73
  374. package/src/runtime/retrieval-record.js +293 -0
  375. package/src/runtime/review/command.js +246 -0
  376. package/src/runtime/review/dominance.js +68 -0
  377. package/src/runtime/review/findings.js +434 -0
  378. package/src/runtime/review/identity.js +255 -0
  379. package/src/runtime/review/protocol-command.js +109 -0
  380. package/src/runtime/review/protocols.js +405 -0
  381. package/src/runtime/review/resolve-protocol.js +302 -0
  382. package/src/runtime/review/start.js +530 -0
  383. package/src/runtime/roadmap/facts.js +479 -0
  384. package/src/runtime/roadmap/gates.js +270 -0
  385. package/src/runtime/roadmap/loop.js +1636 -0
  386. package/src/runtime/roadmap/merge.js +381 -0
  387. package/src/runtime/roadmap/rows.js +1113 -0
  388. package/src/runtime/roadmap/seed.js +951 -0
  389. package/src/runtime/rules-report.js +336 -0
  390. package/src/runtime/scaffold/artifacts.js +63 -9
  391. package/src/runtime/scaffold/generate.js +21 -9
  392. package/src/runtime/settings/autopilot.js +106 -0
  393. package/src/runtime/settings/cli.js +280 -0
  394. package/src/runtime/settings/config-write.js +314 -0
  395. package/src/runtime/settings/files.js +271 -0
  396. package/src/runtime/settings/legacy.js +154 -0
  397. package/src/runtime/settings/lexical.js +56 -0
  398. package/src/runtime/settings/prepared-run.js +269 -0
  399. package/src/runtime/settings/project-layers.js +118 -0
  400. package/src/runtime/settings/refs.js +288 -0
  401. package/src/runtime/settings/registry.js +553 -0
  402. package/src/runtime/settings/resolve.js +713 -0
  403. package/src/runtime/settings/shared.js +37 -0
  404. package/src/runtime/settings/worker-billing.js +107 -0
  405. package/src/runtime/settings/write.js +269 -0
  406. package/src/runtime/slot-command.js +78 -0
  407. package/src/runtime/slots.js +671 -0
  408. package/src/runtime/source-version.js +290 -0
  409. package/src/runtime/state-root.js +217 -0
  410. package/src/runtime/storage/cli.js +12 -5
  411. package/src/runtime/storage/engine.js +9 -2
  412. package/src/runtime/storage/registry.js +14 -1
  413. package/src/runtime/storage/roots.js +66 -16
  414. package/src/runtime/storage/rules.js +82 -10
  415. package/src/runtime/storage/upkeep.js +10 -2
  416. package/src/runtime/storage/work-status.js +245 -26
  417. package/src/runtime/task-attempts.js +589 -69
  418. package/src/runtime/task-classifier.js +47 -11
  419. package/src/runtime/task-graph.js +373 -0
  420. package/src/runtime/task-run.js +778 -0
  421. package/src/runtime/task-scope.js +202 -56
  422. package/src/runtime/task-sources.js +147 -0
  423. package/src/runtime/task-state.js +440 -65
  424. package/src/runtime/team/admission.js +301 -0
  425. package/src/runtime/team/envelope.js +324 -0
  426. package/src/runtime/team/review-guard.js +156 -0
  427. package/src/runtime/trace/analyze.js +427 -0
  428. package/src/runtime/trace/baseline.js +770 -0
  429. package/src/runtime/trace/ledger.js +63 -140
  430. package/src/runtime/trace/otlp-export.js +568 -0
  431. package/src/runtime/trace/otlp-map.js +481 -0
  432. package/src/runtime/trace/spans.js +745 -0
  433. package/src/runtime/trace/telemetry.js +225 -0
  434. package/src/runtime/trace/views.js +1 -0
  435. package/src/runtime/transcripts/command.js +98 -0
  436. package/src/runtime/transcripts/cursor.js +326 -0
  437. package/src/runtime/transcripts/read.js +126 -0
  438. package/src/runtime/transcripts/readers/claude-jsonl.js +74 -0
  439. package/src/runtime/transcripts/readers/codex-rollout.js +70 -0
  440. package/src/runtime/transcripts/readers/copilot-events.js +90 -0
  441. package/src/runtime/transcripts/readers/kiro-jsonl.js +77 -0
  442. package/src/runtime/transcripts/readers/pi-jsonl.js +59 -0
  443. package/src/runtime/transcripts/registry.js +161 -0
  444. package/src/runtime/transcripts/stats.js +161 -0
  445. package/src/runtime/verification/check-key.js +628 -0
  446. package/src/runtime/verification/contract-runner.js +546 -11
  447. package/src/runtime/verification/engine.js +499 -9
  448. package/src/runtime/verification/impact.js +617 -0
  449. package/src/runtime/verification/inflight.js +205 -0
  450. package/src/runtime/verification/registry.js +176 -0
  451. package/src/runtime/verification/reuse.js +184 -0
  452. package/src/runtime/verification/telemetry-emit.js +254 -0
  453. package/src/runtime/verification/test-selection.js +67 -7
  454. package/src/runtime/worker/active.js +90 -0
  455. package/src/runtime/worker/cancel.js +222 -0
  456. package/src/runtime/worker/catalogue.js +122 -0
  457. package/src/runtime/worker/command.js +677 -0
  458. package/src/runtime/worker/evidence.js +288 -0
  459. package/src/runtime/worker/launch.js +288 -0
  460. package/src/runtime/worker/models.js +137 -0
  461. package/src/runtime/worker/parsers.js +218 -0
  462. package/src/runtime/worker/probes/index.js +706 -0
  463. package/src/runtime/worker/process-tree.js +415 -0
  464. package/src/runtime/worker/quota.js +43 -0
  465. package/src/runtime/worker/readiness.js +713 -0
  466. package/src/runtime/worker/run-store.js +166 -0
  467. package/src/runtime/worker/supervisor.js +541 -0
  468. package/src/runtime/worker/worktree-check.js +252 -0
  469. package/src/runtime/workflow-engine.js +5 -1
  470. package/src/runtime/workflow-orchestrator.js +578 -63
  471. package/src/runtime/worktree.js +149 -22
  472. package/src/state/index.js +1 -1
  473. package/src/state/lockfile.js +64 -10
  474. package/src/state/run-lock.js +1 -1
  475. package/types/node-commonjs.d.ts +106 -7
  476. package/core/.antigravity-plugin/hooks.json +0 -39
  477. package/core/harnesses/gemini/hooks/hooks.json +0 -77
  478. package/core/harnesses/gemini/hooks/lib.sh +0 -302
  479. package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +0 -21
  480. package/core/harnesses/gemini/hooks/post-edit-lint.sh +0 -39
  481. package/core/harnesses/gemini/hooks/pre-bash-guard.sh +0 -21
  482. package/core/harnesses/gemini/hooks/pre-compact.sh +0 -38
  483. package/core/harnesses/gemini/hooks/pre-implement-gate.sh +0 -22
  484. package/core/harnesses/gemini/hooks/session-end.impl.sh +0 -89
  485. package/core/harnesses/gemini/hooks/session-end.sh +0 -5
  486. package/core/harnesses/gemini/hooks/session-start.sh +0 -5
  487. package/core/harnesses/gemini/settings/adapter-defaults.json +0 -4
  488. package/core/harnesses/shared/hooks/adapters/arg-fields.js +0 -19
  489. package/core/harnesses/shared/hooks/adapters/gemini.js +0 -114
  490. package/core/harnesses/shared/hooks/hooks.json +0 -47
  491. package/core/shared/guidance/FLAGS.md +0 -25
  492. package/core/shared/guidance/rules/RULE_01_SAFETY.md +0 -25
  493. package/core/shared/guidance/rules/RULE_02_WORKFLOW.md +0 -23
  494. package/core/shared/guidance/rules/RULE_03_QUALITY.md +0 -14
  495. package/core/shared/guidance/rules/RULE_04_QUESTIONS.md +0 -46
  496. package/src/adapters/gemini/hooks.js +0 -210
  497. package/src/adapters/gemini/index.js +0 -390
@@ -0,0 +1,134 @@
1
+ 'use strict';
2
+
3
+ /** IC-023: a bounded, closed, local index. Reading and selecting write and spawn nothing. */
4
+ const fs = require('node:fs');
5
+ const { isPackageId } = require('../../helper/package-ids.js');
6
+ const { parseSemVer, compareSemVer, parseHostRange, satisfiesHostRange } = require('../../helper/semver.js');
7
+
8
+ const MAX_BYTES = 4 * 1024 * 1024;
9
+ const object = (value ) => value !== null && typeof value === 'object' && !Array.isArray(value);
10
+ const exact = (value ) => typeof value === 'string' && parseSemVer(value) !== null;
11
+ const text = (value ) => typeof value === 'string' && value.length > 0;
12
+ const problem = (field , code , message , allowed ) => ({
13
+ package: null, field, code, severity: 'error', message, allowed,
14
+ });
15
+
16
+ function closed(value , allowed , field , findings ) {
17
+ if (!object(value)) {
18
+ findings.push(problem(field, 'bad-type', 'must be an object', allowed));
19
+ return false;
20
+ }
21
+ for (const name of Object.keys(value)) {
22
+ if (!allowed.includes(name)) findings.push(problem(`${field}.${name}`, 'unknown-key', 'unknown index field', allowed));
23
+ }
24
+ return true;
25
+ }
26
+
27
+ /** Shared with the plan reader: only safe, fully pinned remote records are accepted. */
28
+ function pinnedSourceFindings(source , field = 'source') {
29
+ const findings = [];
30
+ if (!object(source)) return [problem(field, 'bad-type', 'must be a pinned source object', 'git or npm')];
31
+ if (source.type === 'git') {
32
+ closed(source, ['type', 'url', 'commit', 'path'], field, findings);
33
+ let validUrl = false;
34
+ if (text(source.url) && source.url.length <= 512 && !/[\s\u0000-\u001f\u007f]/u.test(source.url)) {
35
+ try {
36
+ const url = new URL(source.url);
37
+ validUrl = url.protocol === 'https:' && Boolean(url.host) && !url.username && !url.password && !url.hash;
38
+ } catch { /* invalid URL */ }
39
+ }
40
+ if (!validUrl) findings.push(problem(`${field}.url`, 'bad-type', 'must be an https URL without credentials or a fragment', 'an https URL of at most 512 characters'));
41
+ if (typeof source.commit !== 'string' || !/^[0-9a-fA-F]{40}$/u.test(source.commit)) {
42
+ findings.push(problem(`${field}.commit`, 'source-unpinned', 'git source needs a full commit', '40 hexadecimal characters'));
43
+ }
44
+ if (Object.hasOwn(source, 'path') && (!text(source.path) || source.path.length > 512
45
+ || source.path.startsWith('/') || source.path.startsWith('-') || source.path.includes('\\')
46
+ || source.path.split('/').some((part ) => part === '..' || part === '')
47
+ || /[\s\u0000-\u001f\u007f]/u.test(source.path))) {
48
+ findings.push(problem(`${field}.path`, 'bad-type', 'must be a safe relative subdirectory', 'a relative path without .., backslashes, whitespace or a leading -'));
49
+ }
50
+ } else if (source.type === 'npm') {
51
+ closed(source, ['type', 'name', 'version', 'integrity'], field, findings);
52
+ if (!text(source.name) || source.name.length > 214 || source.name.startsWith('-')
53
+ || !/^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/u.test(source.name)) {
54
+ findings.push(problem(`${field}.name`, 'bad-type', 'invalid npm package name', 'an npm name that does not start with -'));
55
+ }
56
+ if (!exact(source.version)) findings.push(problem(`${field}.version`, 'source-unpinned', 'npm source needs an exact version', 'an exact semver'));
57
+ if (typeof source.integrity !== 'string' || !/^sha512-[A-Za-z0-9+/]+={0,2}$/u.test(source.integrity)) {
58
+ findings.push(problem(`${field}.integrity`, 'source-unpinned', 'npm source needs sha512 integrity', 'sha512-<base64>'));
59
+ }
60
+ } else {
61
+ findings.push(problem(`${field}.type`, 'bad-type', 'index sources must be remote and pinned', ['git', 'npm']));
62
+ }
63
+ return findings;
64
+ }
65
+
66
+ function readIndex(file , { fsImpl = fs } = {}) {
67
+ const fail = (field , code , message , allowed ) => ({
68
+ ok: false, findings: [problem(field, code, message, allowed)],
69
+ });
70
+ let raw ;
71
+ try {
72
+ if (fsImpl.statSync(file).size > MAX_BYTES) return fail('index', 'too-large', 'index exceeds 4 MiB', MAX_BYTES);
73
+ raw = String(fsImpl.readFileSync(file, 'utf8'));
74
+ if (Buffer.byteLength(raw, 'utf8') > MAX_BYTES) return fail('index', 'too-large', 'index exceeds 4 MiB', MAX_BYTES);
75
+ } catch {
76
+ return fail('index', 'bad-type', 'index cannot be read', 'a readable local JSON file');
77
+ }
78
+ let index ;
79
+ try { index = JSON.parse(raw); } catch { return fail('index', 'bad-type', 'index is not JSON', 'a JSON object'); }
80
+ const findings = [];
81
+ if (closed(index, ['format', 'packages'], 'index', findings)) {
82
+ if (index.format !== 1) findings.push(problem('index.format', 'bad-type', 'unsupported index format', 1));
83
+ if (!Array.isArray(index.packages)) {
84
+ findings.push(problem('index.packages', 'bad-type', 'must be an array', 'an array of packages'));
85
+ } else {
86
+ const ids = new Set ();
87
+ index.packages.forEach((pkg , p ) => {
88
+ const field = `index.packages[${p}]`;
89
+ if (!closed(pkg, ['id', 'versions'], field, findings)) return;
90
+ if (!isPackageId(pkg.id) || ids.has(pkg.id)) {
91
+ findings.push(problem(`${field}.id`, 'bad-id', 'invalid or duplicate package id', 'a unique IC-003 package id'));
92
+ }
93
+ ids.add(pkg.id);
94
+ if (!Array.isArray(pkg.versions)) {
95
+ findings.push(problem(`${field}.versions`, 'bad-type', 'must be an array', 'an array of versions'));
96
+ return;
97
+ }
98
+ const versions = new Set ();
99
+ pkg.versions.forEach((entry , v ) => {
100
+ const at = `${field}.versions[${v}]`;
101
+ if (!closed(entry, ['version', 'doflow', 'source'], at, findings)) return;
102
+ if (!exact(entry.version) || versions.has(entry.version)) {
103
+ findings.push(problem(`${at}.version`, 'bad-type', 'invalid or duplicate version', 'a unique exact semver'));
104
+ }
105
+ versions.add(entry.version);
106
+ if (!parseHostRange(entry.doflow).ok) {
107
+ findings.push(problem(`${at}.doflow`, 'bad-type', 'invalid host range', 'the IC-016 host range grammar'));
108
+ }
109
+ findings.push(...pinnedSourceFindings(entry.source, `${at}.source`));
110
+ });
111
+ });
112
+ }
113
+ }
114
+ return findings.length ? { ok: false, findings } : { ok: true, index };
115
+ }
116
+
117
+ function pickVersion(index , id , { version, host } ) {
118
+ const pkg = index.packages.find((entry ) => entry.id === id);
119
+ if (!pkg) return { ok: false, findings: [problem('id', 'bad-id', `no index package '${id}'`, index.packages.map((entry ) => entry.id))] };
120
+ if (version !== undefined) {
121
+ const entry = pkg.versions.find((candidate ) => candidate.version === version);
122
+ return entry ? { ok: true, entry } : {
123
+ ok: false, findings: [problem('version', 'bad-id', `no version '${version}' of '${id}'; available versions: ${pkg.versions.map((candidate ) => candidate.version).join(', ')}`, pkg.versions.map((candidate ) => candidate.version))],
124
+ };
125
+ }
126
+ const candidates = pkg.versions.filter((entry ) => parseSemVer(entry.version).prerelease.length === 0
127
+ && satisfiesHostRange(host, entry.doflow));
128
+ candidates.sort((left , right ) => compareSemVer(right.version, left.version));
129
+ return candidates.length ? { ok: true, entry: candidates[0] } : {
130
+ ok: false, findings: [problem('version', 'host-incompatible', `no stable version of '${id}' admits host ${host}`, 'a non-prerelease version compatible with this host')],
131
+ };
132
+ }
133
+
134
+ module.exports = { readIndex, pickVersion, pinnedSourceFindings };
@@ -0,0 +1,90 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const { qualifiedId, projectedName } = require('../../helper/package-ids.js');
6
+
7
+ function readFrontmatter(text ) {
8
+ const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(text);
9
+ if (!match) return null;
10
+ const values = {};
11
+ for (const line of match[1].split(/\r?\n/)) {
12
+ if (line.trim() === '') continue;
13
+ const item = /^([A-Za-z][A-Za-z0-9_-]*):[ \t]*(.*)$/.exec(line);
14
+ if (!item || Object.hasOwn(values, item[1])) return null;
15
+ let value = item[2].trim();
16
+ if (value.startsWith('"')) {
17
+ if (value.length < 2 || !value.endsWith('"')) return null;
18
+ value = value.slice(1, -1);
19
+ } else if (value.endsWith('"')) return null;
20
+ values[item[1]] = value;
21
+ }
22
+ return values;
23
+ }
24
+
25
+ function finding(field , code , message , allowed ) {
26
+ return { field, code, message, allowed };
27
+ }
28
+
29
+ function judgeInstruction(validatorKey , localId , realPath , { packageId, fsImpl = fs } ) {
30
+ const findings = [];
31
+ const qualified = qualifiedId(packageId, localId);
32
+ const name = qualified ? projectedName(qualified) : null;
33
+ if (!name) return [finding('.name', 'contribution-invalid', 'the projected instruction name cannot be derived', 'a valid package and local id')];
34
+
35
+ if (validatorKey === 'skill-v1') {
36
+ let skillFile ;
37
+ try {
38
+ skillFile = path.join(realPath, 'SKILL.md');
39
+ if (!fsImpl.statSync(skillFile).isFile()) {
40
+ return [finding('.file', 'file-missing', 'SKILL.md is not a regular file in the skill directory', 'a regular SKILL.md file')];
41
+ }
42
+ } catch {
43
+ return [finding('.file', 'file-missing', 'SKILL.md is missing from the skill directory', 'a regular SKILL.md file')];
44
+ }
45
+ if (path.basename(realPath) !== name) {
46
+ findings.push(finding('.dir', 'name-mismatch', `skill directory name must be '${name}'`, name));
47
+ }
48
+ let metadata ;
49
+ try {
50
+ metadata = readFrontmatter(fsImpl.readFileSync(skillFile, 'utf8'));
51
+ } catch {
52
+ metadata = null;
53
+ }
54
+ if (!metadata) findings.push(finding('', 'contribution-invalid', 'SKILL.md must begin with valid name/value frontmatter', 'frontmatter between --- lines'));
55
+ else if (!metadata.name) findings.push(finding('.name', 'contribution-invalid', 'skill frontmatter name is required', name));
56
+ else if (metadata.name !== name) findings.push(finding('.name', 'name-mismatch', `skill frontmatter name must be '${name}'`, name));
57
+ return findings;
58
+ }
59
+
60
+ if (validatorKey === 'agent-v1') {
61
+ let metadata ;
62
+ try {
63
+ metadata = readFrontmatter(fsImpl.readFileSync(realPath, 'utf8'));
64
+ } catch {
65
+ metadata = null;
66
+ }
67
+ if (!metadata) return [finding('', 'contribution-invalid', 'agent file must begin with valid name/value frontmatter', 'frontmatter between --- lines')];
68
+ if (!metadata.name) findings.push(finding('.name', 'contribution-invalid', 'agent frontmatter name is required', name));
69
+ else if (metadata.name !== name) findings.push(finding('.name', 'name-mismatch', `agent frontmatter name must be '${name}'`, name));
70
+ if (typeof metadata.description !== 'string' || metadata.description.length < 1 || metadata.description.length > 160) {
71
+ findings.push(finding('.description', 'contribution-invalid', 'agent description must be 1 to 160 characters', '1 to 160 characters'));
72
+ }
73
+ if (metadata.access !== 'read-only' && metadata.access !== 'write') {
74
+ findings.push(finding('.access', 'contribution-invalid', "agent access must be 'read-only' or 'write'", ['read-only', 'write']));
75
+ }
76
+ return findings;
77
+ }
78
+
79
+ return [finding('', 'contribution-invalid', `unknown instruction validator '${validatorKey}'`, ['skill-v1', 'agent-v1'])];
80
+ }
81
+
82
+ function skillValidator(localId , realPath , context = {}) {
83
+ return judgeInstruction('skill-v1', localId, realPath, context).map((item ) => item.message);
84
+ }
85
+
86
+ function agentValidator(localId , realPath , context = {}) {
87
+ return judgeInstruction('agent-v1', localId, realPath, context).map((item ) => item.message);
88
+ }
89
+
90
+ module.exports = { judgeInstruction, skillValidator, agentValidator };
@@ -0,0 +1,331 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * The package manifest checker (122 IC-001, IC-003, IC-004). `checkManifest` judges one `doflow-package.json`
5
+ * (text or parsed) and returns IC-004 findings; with `root` it also reads and validates each contribution
6
+ * file through the point's validator. `checkPackage` adds the IC-005 tree rules and the digests for a package
7
+ * directory. Neither throws on content and neither writes. Collisions with recorded packages are not judged
8
+ * here: no records exist before the lifecycle verbs (Phase B).
9
+ */
10
+
11
+ const fs = require('node:fs');
12
+ const path = require('node:path');
13
+ const { parseSemVer, parseHostRange, satisfiesHostRange } = require('../../helper/semver.js');
14
+ const { isPackageId, isLocalId, isReservedPackageId, qualifiedId } = require('../../helper/package-ids.js');
15
+ const { REPO_ROOT } = require('../../helper/repo-root.js');
16
+ const { loadPoints, judgeContribution, POINT_VALIDATORS } = require('./points.js');
17
+ const { judgeInstruction } = require('./instruction.js');
18
+ const { walkTree, digestFiles, capabilityDigest, SEGMENT, SEGMENT_GRAMMAR } = require('./digest.js');
19
+ const { duplicateKeys, loadProtocols, lensIds, coreTiers } = require('../review/protocols.js');
20
+ const { dominates } = require('../review/dominance.js');
21
+ const { programFindings } = require('./programs.js');
22
+
23
+ const MANIFEST_FILE = 'doflow-package.json';
24
+ const MANIFEST_MAX_BYTES = 64 * 1024;
25
+ const TOP_KEYS = ['manifest', 'id', 'version', 'summary', 'doflow', 'programs', 'contributions'];
26
+ const ENTRY_KEYS = ['point', 'version', 'id', 'file', 'dir', 'programs'];
27
+ const PRESET_KIND = { 'review-preset-v1': 'review', 'debate-preset-v1': 'debate' };
28
+ const PACKAGE_ID_GRAMMAR = '2 to 16 characters of a-z, 0-9 and single hyphens, starting with a letter and ending with a letter or digit';
29
+ const LOCAL_ID_GRAMMAR = '2 to 15 characters of a-z, 0-9 and single hyphens, starting with a letter and ending with a letter or digit';
30
+ const BARE_ID = /^[a-z][a-z0-9-]*$/;
31
+
32
+ const isObject = (value ) => value !== null && typeof value === 'object' && !Array.isArray(value);
33
+
34
+ /**
35
+ * Names of the core agents: `all`, and `readOnly` (`access: read-only`, the seats of INV-2, which covers review only).
36
+ * Read from core/registry/workflows.json `agents.access`, which an installed runtime ships (it has no core/shared);
37
+ * G63 holds that map equal to the spec frontmatter.
38
+ */
39
+ function coreAgents(repoRoot , fsImpl ) {
40
+ const found = { all: [] , readOnly: [] };
41
+ try {
42
+ const access = JSON.parse(String(fsImpl.readFileSync(path.join(repoRoot, 'core', 'registry', 'workflows.json'), 'utf8'))).agents.access;
43
+ found.all = Object.keys(access).sort();
44
+ found.readOnly = found.all.filter((name) => access[name] === 'read-only');
45
+ } catch { /* an unreadable map leaves no agent usable; the validator then names that */ }
46
+ return found;
47
+ }
48
+
49
+ /** `allowed` for a point validator's message: the part after "must be (one of)", else the whole message. */
50
+ function allowedOf(message ) {
51
+ const list = message.match(/must be one of (.+)$/);
52
+ if (list) return list[1].split(', ');
53
+ const rest = message.match(/must (?:be|match|not exceed) (.+)$/);
54
+ return rest ? rest[1] : message;
55
+ }
56
+
57
+ /**
58
+ * Check a manifest. `input` is the manifest text (preferred: duplicate keys and the size limit are only
59
+ * visible there) or a parsed object. Options: `pointsRegistry` (a loaded point registry, required),
60
+ * `hostVersion` (an unreadable one is `host-incompatible`: the range cannot be shown to hold), `bundled` (ids of the first-party manifests, which alone may use reserved and bare ids),
61
+ * `root` (the package directory, to read contribution files), `protocols` (the core protocol registry;
62
+ * default: the installed one), `repoRoot`, `fsImpl`, `hostExempt` (a person `exempt` grant matched, IC-008: a host
63
+ * outside the range is then a `host-incompatible` warning, never an error). Returns the IC-004 findings.
64
+ */
65
+ function checkManifest(input , options = {}) {
66
+ const { pointsRegistry, hostVersion, root, repoRoot = REPO_ROOT, fsImpl = fs } = options;
67
+ const bundled = (options.bundled ?? []).map((b ) => (typeof b === 'string' ? b : b?.id));
68
+ const findings = [];
69
+ let packageId = null;
70
+ const report = (field , code , message , allowed , severity = 'error') =>
71
+ findings.push({ package: packageId, field, code, severity, message, allowed });
72
+ try {
73
+ check();
74
+ } catch (error ) {
75
+ report('manifest', 'bad-type', `the manifest could not be checked: ${error?.message || String(error)}`, 'a JSON object');
76
+ }
77
+ return findings;
78
+
79
+ function check() {
80
+ const forbidden = pointsRegistry.forbiddenFields;
81
+ let manifest = input;
82
+ if (typeof input === 'string') {
83
+ if (Buffer.byteLength(input) > MANIFEST_MAX_BYTES) {
84
+ report(MANIFEST_FILE, 'too-large', `the manifest is larger than ${MANIFEST_MAX_BYTES} bytes`, `at most ${MANIFEST_MAX_BYTES} bytes`);
85
+ return;
86
+ }
87
+ try { manifest = JSON.parse(input); } catch (error ) {
88
+ report(MANIFEST_FILE, 'bad-type', `the manifest is not valid JSON: ${error.message}`, 'a JSON object');
89
+ return;
90
+ }
91
+ for (const message of duplicateKeys(input)) report(message.replace(/^.* in /, ''), 'duplicate-key', message, 'each key at most once');
92
+ }
93
+ if (!isObject(manifest)) { report('manifest', 'bad-type', 'the manifest must be a JSON object', 'a JSON object'); return; }
94
+ if (typeof manifest.id === 'string') packageId = manifest.id;
95
+ const isBundled = typeof manifest.id === 'string' && bundled.includes(manifest.id);
96
+
97
+ for (const key of Object.keys(manifest)) {
98
+ if (TOP_KEYS.includes(key)) continue;
99
+ if (forbidden.includes(key)) report(key, 'forbidden-field', `'${key}' is decided by DoFlow or the person, never by a package`, 'omit the field');
100
+ else report(key, 'unknown-key', `'${key}' is not a manifest field`, TOP_KEYS);
101
+ }
102
+
103
+ if (!Object.hasOwn(manifest, 'manifest')) report('manifest', 'bad-type', 'manifest is required', [1]);
104
+ else if (manifest.manifest !== 1) report('manifest', 'unsupported-manifest', `manifest format ${JSON.stringify(manifest.manifest)} is not supported`, [1]);
105
+
106
+ if (!isPackageId(manifest.id)) report('id', 'bad-id', `package id ${JSON.stringify(manifest.id)} breaks the grammar`, PACKAGE_ID_GRAMMAR);
107
+ else if (isReservedPackageId(manifest.id) && !isBundled) {
108
+ report('id', 'reserved-id', `package id '${manifest.id}' is reserved`, 'an id that is not doflow, do, core, user, project, local, env, auto, package, packages and does not start with do- or doflow-');
109
+ }
110
+
111
+ if (typeof manifest.version !== 'string' || manifest.version.length > 64 || parseSemVer(manifest.version) === null) {
112
+ report('version', 'bad-type', `version ${JSON.stringify(manifest.version)} is not a semver 2.0.0 string of at most 64 characters`, 'an exact semver 2.0.0 version');
113
+ }
114
+ if (Object.hasOwn(manifest, 'summary')
115
+ && (typeof manifest.summary !== 'string' || manifest.summary.length > 120 || /[\u0000-\u001f\u007f<>`]/.test(manifest.summary))) {
116
+ report('summary', 'bad-type', 'summary is not plain text of at most 120 characters', 'at most 120 characters, no control characters, < > or backticks');
117
+ }
118
+
119
+ if (!Object.hasOwn(manifest, 'doflow')) report('doflow', 'bad-type', 'doflow (the host range) is required', 'a host range');
120
+ else {
121
+ const range = parseHostRange(manifest.doflow);
122
+ if (!range.ok) report('doflow', range.error.code, range.error.message, range.error.allowed);
123
+ else if (!hostVersion) {
124
+ report('doflow', 'host-incompatible', 'the running DoFlow version cannot be read', manifest.doflow);
125
+ } else if (!satisfiesHostRange(hostVersion, range)) {
126
+ report('doflow', 'host-incompatible', `DoFlow ${hostVersion} is outside the range ${manifest.doflow}`, manifest.doflow, options.hostExempt === true ? 'warning' : 'error');
127
+ }
128
+ }
129
+
130
+ const contributions = manifest.contributions;
131
+ let executable = false;
132
+ if (!Array.isArray(contributions) || contributions.length === 0 || contributions.length > 64) {
133
+ report('contributions', 'bad-type', 'contributions must be a non-empty array of at most 64 entries', 'an array of 1 to 64 contributions');
134
+ } else {
135
+ const seen = new Set ();
136
+ const protocols = options.protocols ?? loadProtocols({ repoRoot }).registry;
137
+ const context = { protocols, agents: coreAgents(repoRoot, fsImpl), manifest };
138
+ contributions.forEach((entry , index ) => {
139
+ const where = `contributions[${index}]`;
140
+ if (!isObject(entry)) { report(where, 'bad-type', 'a contribution must be an object', 'an object'); return; }
141
+ const point = pointsRegistry.points.find((p ) => p.id === entry.point);
142
+ if (point?.tier === 'executable') executable = true;
143
+ checkEntry(entry, index, point, isBundled, seen, context);
144
+ });
145
+ }
146
+ if (Object.hasOwn(manifest, 'programs') && !executable) {
147
+ report('programs', 'forbidden-field', 'programs belong only to a package that contributes to an executable point', 'omit the field');
148
+ }
149
+ if (executable) {
150
+ findings.push(...programFindings(manifest, { root, pointsRegistry, fsImpl, packageId }));
151
+ }
152
+ }
153
+
154
+ function checkEntry(entry , index , point , isBundled , seen , context ) {
155
+ const where = `contributions[${index}]`;
156
+ for (const key of Object.keys(entry)) {
157
+ if (ENTRY_KEYS.includes(key)) continue;
158
+ if (pointsRegistry.forbiddenFields.includes(key)) report(`${where}.${key}`, 'forbidden-field', `'${key}' is decided by DoFlow or the person, never by a package`, 'omit the field');
159
+ else report(`${where}.${key}`, 'unknown-key', `'${key}' is not a contribution field`, ENTRY_KEYS);
160
+ }
161
+ if (Object.hasOwn(entry, 'programs') && point?.tier !== 'executable') {
162
+ report(`${where}.programs`, 'forbidden-field', 'programs belong only to a contribution on an executable point', 'omit the field');
163
+ }
164
+ if (typeof entry.point !== 'string') { report(`${where}.point`, 'bad-type', 'point must be a string', pointsRegistry.points.map((p ) => p.id)); return; }
165
+ if (!Number.isInteger(entry.version)) { report(`${where}.version`, 'bad-type', 'version must be an integer', point?.versions ?? 'an integer'); return; }
166
+ const judged = judgeContribution(pointsRegistry, entry, { packageId, index });
167
+ if (judged) findings.push(judged);
168
+
169
+ // The id: a local id, or for a first-party manifest any bare built-in id; a bare built-in id elsewhere is reserved.
170
+ const builtin = new Set (Object.keys(context.protocols?.presets ?? {}));
171
+ let qid = null;
172
+ if (typeof entry.id !== 'string' || !(isBundled ? BARE_ID.test(entry.id) : isLocalId(entry.id))) {
173
+ report(`${where}.id`, 'bad-id', `contribution id ${JSON.stringify(entry.id)} breaks the grammar`, LOCAL_ID_GRAMMAR);
174
+ } else if (!isBundled && builtin.has(entry.id)) {
175
+ report(`${where}.id`, 'reserved-id', `'${entry.id}' is a built-in id; only the first-party package uses a bare built-in id`, 'a local id that is not a built-in id');
176
+ } else {
177
+ const key = `${entry.point}\0${entry.id}`;
178
+ if (seen.has(key)) report(`${where}.id`, 'bad-id', `(${entry.point}, ${entry.id}) is contributed twice`, 'each (point, id) at most once');
179
+ seen.add(key);
180
+ qid = isBundled ? entry.id : qualifiedId(packageId , entry.id);
181
+ }
182
+
183
+ const wanted = point?.entry;
184
+ const given = ['file', 'dir'].filter((key) => Object.hasOwn(entry, key));
185
+ if (given.length !== 1 || (wanted && given[0] !== wanted)) {
186
+ report(`${where}.${wanted ?? 'file'}`, 'bad-type', `exactly one of file or dir is required${wanted ? `; point '${point.id}' takes ${wanted}` : ''}`, wanted ?? ['file', 'dir']);
187
+ return;
188
+ }
189
+ const kind = given[0];
190
+ const found = locate(entry[kind], `${where}.${kind}`, kind);
191
+ if (judged || !found || !qid) return;
192
+ if (point?.validator === 'skill-v1' || point?.validator === 'agent-v1') {
193
+ if (point.validator === 'agent-v1' && fsImpl.statSync(found).size > pointsRegistry.limits.fileBytes) return;
194
+ const instructionFindings = judgeInstruction(point.validator, entry.id, found, { packageId, fsImpl });
195
+ for (const item of instructionFindings) report(`${qid}${item.field}`, item.code, item.message, item.allowed);
196
+ return;
197
+ }
198
+ if (kind !== 'file') return;
199
+ validateFile(found, entry, point, qid, context);
200
+ }
201
+
202
+ /** Lexical and realpath checks on a manifest path; returns the real path when it is usable. */
203
+ function locate(rel , field , kind ) {
204
+ const limits = pointsRegistry.limits;
205
+ if (typeof rel !== 'string' || rel.length === 0) { report(field, 'bad-type', 'the path must be a non-empty string', 'a POSIX path relative to the package root'); return null; }
206
+ if (rel.startsWith('/') || rel.split('/').includes('..')) { report(field, 'path-escapes-root', `${rel} leaves the package root`, 'a path inside the package root'); return null; }
207
+ if (rel.length > limits.pathLength || rel.split('/').some((s ) => !SEGMENT.test(s))) {
208
+ report(field, 'bad-type', `${rel} breaks the path rules`, `${SEGMENT_GRAMMAR} per segment, at most ${limits.pathLength} characters`);
209
+ return null;
210
+ }
211
+ if (!root) return null;
212
+ let real ;
213
+ let rootReal ;
214
+ try {
215
+ rootReal = fsImpl.realpathSync(root);
216
+ real = fsImpl.realpathSync(path.join(root, rel));
217
+ } catch {
218
+ report(field, 'file-missing', `${rel} does not exist`, `an existing ${kind === 'dir' ? 'directory' : 'file'}`);
219
+ return null;
220
+ }
221
+ if (real !== rootReal && !real.startsWith(rootReal + path.sep)) {
222
+ report(field, 'path-escapes-root', `${rel} resolves outside the package root`, 'a path inside the package root');
223
+ return null;
224
+ }
225
+ const stat = fsImpl.statSync(real);
226
+ if (kind === 'file' ? !stat.isFile() : !stat.isDirectory()) {
227
+ report(field, 'file-missing', `${rel} is not a ${kind === 'dir' ? 'directory' : 'regular file'}`, `an existing ${kind === 'dir' ? 'directory' : 'file'}`);
228
+ return null;
229
+ }
230
+ return real;
231
+ }
232
+
233
+ /** Read one contribution file and run the point's validator, then comparability for a preset. */
234
+ function validateFile(real , entry , point , qid , context ) {
235
+ if (fsImpl.statSync(real).size > pointsRegistry.limits.fileBytes) return; // the tree rules report too-large
236
+ const text = fsImpl.readFileSync(real, 'utf8');
237
+ const invalid = (field , message ) => report(field, 'contribution-invalid', message, allowedOf(message));
238
+ let parsed ;
239
+ try { parsed = JSON.parse(text); } catch (error ) { invalid(qid, `${entry.file} is not valid JSON: ${error.message}`); return; }
240
+ for (const message of duplicateKeys(text)) report(`${qid}.${message.replace(/^.* in /, '')}`, 'duplicate-key', message, 'each key at most once');
241
+ const validator = POINT_VALIDATORS[point.validator];
242
+ const kind = PRESET_KIND[point.validator];
243
+ const protocols = context.protocols;
244
+ if (!validator) return;
245
+ if (kind && !protocols) { invalid(qid, 'the core protocol registry is unreadable, so a preset cannot be checked'); return; }
246
+ let errors ;
247
+ let comparable = null;
248
+ if (kind) {
249
+ const judged = judgePackagePreset(point.validator, entry.id, parsed, { protocols, agents: context.agents });
250
+ if (judged.depth) report(`${qid}.depth`, 'forbidden-field', 'depth is not allowed in a package preset (the depth vocabulary stays core)', 'omit the field');
251
+ errors = judged.errors;
252
+ comparable = judged.comparable;
253
+ } else {
254
+ errors = validator(entry.id, parsed, { ...context, entry, packageId });
255
+ }
256
+ const prefix = `presets.${entry.id}`;
257
+ for (const message of errors) {
258
+ const token = message.split(' ')[0];
259
+ invalid(token.startsWith(prefix) ? `${qid}${token.slice(prefix.length)}` : qid, message);
260
+ }
261
+ if (errors.length > 0 || !kind) return;
262
+ if (!comparable) {
263
+ report(qid, 'incomparable', `${qid} dominates no core ${kind} preset, so it cannot be ordered against them`, `a ${kind} preset that is at least as strict as one core ${kind} preset`);
264
+ }
265
+ }
266
+ }
267
+
268
+ /**
269
+ * One contributed preset in package mode (IC-014): the validator `check` runs on a package file, shared with the
270
+ * runtime seam (`contributionsFor`) so the two cannot disagree. `validatorKey` is the point's validator
271
+ * (`review-preset-v1` or `debate-preset-v1`), `protocols` the core registry, `agents` the `coreAgents` result.
272
+ * Returns `{ kind, depth, errors, comparable }`: `depth` says the preset carried the refused `depth` field (judged
273
+ * without it, as `check` reports it separately), `errors` are the validator messages, and `comparable` is whether
274
+ * it dominates at least one core preset of its kind (null while there are errors).
275
+ */
276
+ function judgePackagePreset(validatorKey , localId , parsed , { protocols, agents } ) {
277
+ const kind = PRESET_KIND[validatorKey];
278
+ const depth = isObject(parsed) && 'depth' in parsed;
279
+ let body = parsed;
280
+ if (depth) { body = { ...parsed }; delete body.depth; }
281
+ const tiers = coreTiers();
282
+ const errors = POINT_VALIDATORS[validatorKey](localId, body, {
283
+ mode: 'package', lensIds: lensIds(protocols), hardRoundCap: protocols.hardRoundCap, tiers, agents: kind === 'review' ? agents.readOnly : agents.all,
284
+ });
285
+ const comparable = errors.length > 0 ? null : Object.values (protocols.presets).some((core) => core?.kind === kind
286
+ && dominates(body, core, { tiers, synthesizerStrength: protocols.synthesizerStrength }));
287
+ return { kind, depth, errors, comparable };
288
+ }
289
+
290
+ /**
291
+ * Check a package directory: the manifest, the IC-005 tree rules and, when there is no error, both digests.
292
+ * Returns `{ ok, findings, contentDigest?, capabilityDigest? }`; never throws on content, writes nothing.
293
+ */
294
+ function checkPackage(dir , options = {}) {
295
+ const { fsImpl = fs, repoRoot = REPO_ROOT } = options;
296
+ const loaded = options.pointsRegistry ? { ok: true, registry: options.pointsRegistry } : loadPoints({ repoRoot });
297
+ if (!loaded.ok) {
298
+ return { ok: false, findings: [{ package: null, field: 'points', code: 'state-unreadable', severity: 'error', message: (loaded ).errors.join('; '), allowed: 'a readable point registry' }] };
299
+ }
300
+ const pointsRegistry = loaded.registry;
301
+ const fail = (code , message , allowed ) => ({
302
+ ok: false, findings: [{ package: null, field: MANIFEST_FILE, code, severity: 'error', message, allowed }],
303
+ });
304
+ let text ;
305
+ let walked ;
306
+ try {
307
+ if (!fsImpl.statSync(dir).isDirectory()) return fail('file-missing', `${dir} is not a directory`, 'a package directory');
308
+ walked = walkTree(dir, pointsRegistry.limits, { fsImpl });
309
+ const manifestFile = walked.files.find((f ) => f.rel === MANIFEST_FILE);
310
+ if (!manifestFile) return { ok: false, findings: [...walked.findings, { package: null, field: MANIFEST_FILE, code: 'file-missing', severity: 'error', message: `${MANIFEST_FILE} is missing`, allowed: `a ${MANIFEST_FILE} at the package root` }] };
311
+ if (manifestFile.size > MANIFEST_MAX_BYTES) return { ok: false, findings: [...walked.findings, { package: null, field: MANIFEST_FILE, code: 'too-large', severity: 'error', message: `the manifest is larger than ${MANIFEST_MAX_BYTES} bytes`, allowed: `at most ${MANIFEST_MAX_BYTES} bytes` }] };
312
+ text = fsImpl.readFileSync(manifestFile.abs, 'utf8');
313
+ } catch (error ) {
314
+ return fail('file-missing', `cannot read ${dir}: ${error.message}`, 'a readable package directory');
315
+ }
316
+ const findings = checkManifest(text, { ...options, pointsRegistry, root: dir });
317
+ let id = null;
318
+ try { id = JSON.parse(text)?.id ?? null; } catch { /* checkManifest reported it */ }
319
+ for (const finding of walked.findings) findings.push({ ...finding, package: typeof id === 'string' ? id : null });
320
+ const ok = !findings.some((f) => f.severity === 'error');
321
+ const result = { ok, findings };
322
+ if (ok) {
323
+ const manifest = JSON.parse(text);
324
+ const tiers = Object.fromEntries(pointsRegistry.points.map((p ) => [p.id, p.tier]));
325
+ result.contentDigest = digestFiles(walked.files, fsImpl);
326
+ result.capabilityDigest = capabilityDigest(manifest, { trustPolicyVersion: pointsRegistry.trustPolicyVersion, tiers });
327
+ }
328
+ return result;
329
+ }
330
+
331
+ module.exports = { checkManifest, checkPackage, judgePackagePreset, coreAgents, MANIFEST_FILE, MANIFEST_MAX_BYTES };