devrites 3.2.28 → 4.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 (897) hide show
  1. package/CHANGELOG.md +16 -144
  2. package/NOTICE.md +6 -5
  3. package/README.md +187 -95
  4. package/SECURITY.md +103 -79
  5. package/bin/devrites.mjs +140 -24
  6. package/docs/adr/0003-gate-model-hitl-pause.md +1 -1
  7. package/docs/adr/0013-native-codex-agent-orchestration.md +74 -0
  8. package/docs/adr/0014-native-host-hook-boundary.md +64 -0
  9. package/docs/adr/0015-read-only-root-native-orchestration.md +96 -0
  10. package/docs/adr/0016-codex-writer-fail-closed.md +53 -0
  11. package/docs/adr/0017-native-codex-writer-agent.md +63 -0
  12. package/docs/adr/0018-native-sandbox-instruction-writer-boundary.md +66 -0
  13. package/docs/adr/0019-native-boundary-with-deterministic-gates.md +48 -0
  14. package/docs/adr/0020-thin-engine-native-orchestration-boundary.md +54 -0
  15. package/docs/adr/0021-observable-workspace-compatibility.md +49 -0
  16. package/docs/adr/0022-native-orchestration-thin-engine.md +90 -0
  17. package/docs/adr/0023-native-workspace-reads-line-output.md +71 -0
  18. package/docs/adr/0024-native-policy-offline-installer-boundary.md +111 -0
  19. package/docs/adr/0025-evidence-gated-workspace-upgrades.md +64 -0
  20. package/docs/adr/0026-content-bound-proof-and-bounded-inputs.md +169 -0
  21. package/docs/adr/0027-content-bound-build-readiness.md +77 -0
  22. package/docs/adr/README.md +26 -11
  23. package/docs/agents/domain.md +2 -1
  24. package/docs/agents/issue-driven-rites.md +1 -1
  25. package/docs/architecture.md +191 -103
  26. package/docs/candidate-integrity.md +138 -0
  27. package/docs/capability-surface-selection.md +15 -5
  28. package/docs/cli.md +139 -92
  29. package/docs/command-map.md +88 -63
  30. package/docs/engine/commands.md +115 -507
  31. package/docs/engine/state-schema.md +94 -113
  32. package/docs/engine/workspace-schema.md +169 -229
  33. package/docs/flow.md +113 -68
  34. package/docs/harness-compliance.md +29 -56
  35. package/docs/markdown-instruction-upgrade-2026-08-02.md +302 -0
  36. package/docs/orchestration.md +136 -149
  37. package/docs/porting-to-a-new-harness.md +22 -7
  38. package/docs/quick-reference.md +21 -8
  39. package/docs/release.md +47 -4
  40. package/docs/skills.md +111 -72
  41. package/docs/templates/CRITIC_REVIEW_PACKET_TEMPLATE.md +2 -0
  42. package/docs/upstream-workflow-benchmark-2026-08-01.md +655 -0
  43. package/docs/usage.md +99 -65
  44. package/engine/Makefile +1 -2
  45. package/engine/cmd/releasepack/main.go +45 -25
  46. package/engine/cmd/releasepack/main_test.go +69 -0
  47. package/engine/commands.go +8 -258
  48. package/engine/harnessmatrix_test.go +28 -41
  49. package/engine/internal/devritespaths/paths.go +12 -28
  50. package/engine/internal/fsutil/copy.go +8 -0
  51. package/engine/internal/gate/gate.go +104 -105
  52. package/engine/internal/gate/gate_test.go +94 -84
  53. package/engine/internal/gate/readiness_binding.go +203 -0
  54. package/engine/internal/gate/readiness_binding_test.go +271 -0
  55. package/engine/internal/gitenv/env.go +46 -0
  56. package/engine/internal/gitenv/env_test.go +48 -0
  57. package/engine/internal/hostpack/hostpack.go +16 -20
  58. package/engine/internal/hostpack/hostpack_test.go +6 -6
  59. package/engine/internal/install/install.go +289 -511
  60. package/engine/internal/install/install_test.go +218 -306
  61. package/engine/internal/lib/candidate.go +475 -0
  62. package/engine/internal/lib/candidate_test.go +503 -0
  63. package/engine/internal/lib/closeout.go +17 -21
  64. package/engine/internal/lib/closeout_test.go +23 -0
  65. package/engine/internal/lib/cursor_compat_test.go +12 -75
  66. package/engine/internal/lib/evidencefresh.go +58 -47
  67. package/engine/internal/lib/evidencefresh_test.go +120 -0
  68. package/engine/internal/lib/resolve.go +13 -35
  69. package/engine/internal/lib/resolve_remediation_test.go +5 -17
  70. package/engine/internal/lib/secretscan.go +432 -0
  71. package/engine/internal/lib/secretscan_test.go +555 -0
  72. package/engine/internal/lib/util.go +24 -65
  73. package/engine/internal/lib/util_test.go +44 -0
  74. package/engine/internal/markdowntext/markdowntext.go +108 -0
  75. package/engine/internal/markdowntext/markdowntext_test.go +58 -0
  76. package/engine/internal/markdowntext/testdata/structural.json +27 -0
  77. package/engine/internal/reason/reason.go +2 -123
  78. package/engine/internal/reason/reason_test.go +4 -2
  79. package/engine/internal/rootfacts/facts.go +6 -8
  80. package/engine/internal/rootfacts/facts_test.go +20 -0
  81. package/engine/internal/state/cmd/workflowmanifest/main.go +10 -12
  82. package/engine/internal/state/cursor.go +53 -14
  83. package/engine/internal/state/cursor_test.go +57 -0
  84. package/engine/internal/state/feature.go +64 -154
  85. package/engine/internal/state/schema.go +24 -50
  86. package/engine/internal/state/state_test.go +151 -140
  87. package/engine/internal/state/status.go +4 -17
  88. package/engine/internal/state/workflow_manifest.json +9 -89
  89. package/engine/main.go +73 -502
  90. package/engine/root_routing_test.go +183 -212
  91. package/engine/testdata/fixtures/basic/devrites-root/{features/auth-tokens/feature.md → work/auth-tokens/README.md} +0 -1
  92. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/architecture.md +3 -0
  93. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/assumptions.md +3 -0
  94. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/brief.md +3 -0
  95. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/decision-coverage.md +3 -0
  96. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/eng-review.md +3 -0
  97. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/questions.md +3 -0
  98. package/engine/testdata/fixtures/basic/devrites-root/{features/auth-tokens/status.md → work/auth-tokens/state.md} +3 -0
  99. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/test-plan.md +3 -0
  100. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/traceability.md +3 -0
  101. package/engine/testdata/fixtures/basic/devrites-root/{features/search-ranking/feature.md → work/search-ranking/README.md} +0 -1
  102. package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/assumptions.md +3 -0
  103. package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/brief.md +3 -0
  104. package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/decisions.md +3 -0
  105. package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/questions.md +3 -0
  106. package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/state.md +2 -0
  107. package/engine/testdata/golden/TestParityCloseOut/active_pointed_elsewhere.golden +1 -1
  108. package/engine/testdata/golden/TestParityCloseOut/active_slug_cleared.golden +1 -1
  109. package/engine/tests/adr_0003_gate_exit_code_test.go +2 -2
  110. package/engine/tests/adr_0011_define_plan_test.go +2 -2
  111. package/engine/tests/adr_0027_readiness_binding_test.go +106 -0
  112. package/engine/tests/cli_test.go +24 -107
  113. package/engine/tests/gate_test.go +177 -200
  114. package/engine/tests/meta_test.go +5 -35
  115. package/engine/tests/parity_closeout_test.go +12 -12
  116. package/engine/tests/parity_resolve_test.go +6 -16
  117. package/engine/tests/parity_test.go +8 -302
  118. package/install.sh +201 -34
  119. package/pack/.claude/agents/devrites-code-reviewer.md +9 -18
  120. package/pack/.claude/agents/devrites-devex-reviewer.md +10 -18
  121. package/pack/.claude/agents/devrites-doubt-reviewer.md +10 -20
  122. package/pack/.claude/agents/devrites-evidence-scout.md +14 -26
  123. package/pack/.claude/agents/devrites-frontend-reviewer.md +9 -15
  124. package/pack/.claude/agents/devrites-performance-reviewer.md +13 -21
  125. package/pack/.claude/agents/devrites-plan-drafter.md +24 -28
  126. package/pack/.claude/agents/devrites-plan-reviewer.md +30 -47
  127. package/pack/.claude/agents/devrites-proof-runner.md +41 -47
  128. package/pack/.claude/agents/devrites-retrospector.md +34 -79
  129. package/pack/.claude/agents/devrites-security-auditor.md +9 -17
  130. package/pack/.claude/agents/devrites-simplifier-reviewer.md +9 -18
  131. package/pack/.claude/agents/devrites-slice-wright.md +44 -62
  132. package/pack/.claude/agents/devrites-spec-reviewer.md +38 -50
  133. package/pack/.claude/agents/devrites-strategy-reviewer.md +10 -19
  134. package/pack/.claude/agents/devrites-test-analyst.md +22 -21
  135. package/pack/.claude/agents/devrites-upgrade-planner.md +52 -71
  136. package/pack/.claude/settings.json +12 -100
  137. package/pack/.claude/skills/devrites-api-interface/SKILL.md +1 -2
  138. package/pack/.claude/skills/devrites-audit/SKILL.md +6 -9
  139. package/pack/.claude/skills/devrites-browser-proof/SKILL.md +0 -1
  140. package/pack/.claude/skills/devrites-debug-recovery/SKILL.md +21 -27
  141. package/pack/.claude/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +22 -33
  142. package/pack/.claude/skills/devrites-doubt/SKILL.md +4 -6
  143. package/pack/.claude/skills/devrites-frontend-craft/SKILL.md +1 -2
  144. package/pack/.claude/skills/devrites-frontend-craft/reference/design-references.md +2 -1
  145. package/pack/.claude/skills/devrites-interview/SKILL.md +31 -36
  146. package/pack/.claude/skills/devrites-lib/SKILL.md +37 -66
  147. package/pack/.claude/skills/devrites-lib/reference/candidate-integrity.md +51 -0
  148. package/pack/.claude/skills/devrites-lib/reference/intent-map.md +20 -12
  149. package/pack/.claude/skills/devrites-lib/reference/orchestration-profiles.md +22 -0
  150. package/pack/.claude/skills/devrites-lib/reference/parallel-dispatch.md +35 -110
  151. package/pack/.claude/skills/devrites-lib/reference/reply-contract.md +26 -93
  152. package/pack/.claude/skills/devrites-lib/reference/standards/README.md +6 -3
  153. package/pack/.claude/skills/devrites-lib/reference/standards/afk-hitl.md +43 -34
  154. package/pack/.claude/skills/devrites-lib/reference/standards/agents.md +90 -216
  155. package/pack/.claude/skills/devrites-lib/reference/standards/anti-patterns.md +1 -1
  156. package/pack/.claude/skills/devrites-lib/reference/standards/code-review.md +9 -28
  157. package/pack/.claude/skills/devrites-lib/reference/standards/context-hygiene.md +13 -11
  158. package/pack/.claude/skills/devrites-lib/reference/standards/core.md +59 -54
  159. package/pack/.claude/skills/devrites-lib/reference/standards/hooks.md +3 -5
  160. package/pack/.claude/skills/devrites-lib/reference/standards/principles.md +29 -143
  161. package/pack/.claude/skills/devrites-lib/reference/standards/release/ship-checklist.md +5 -6
  162. package/pack/.claude/skills/devrites-lib/reference/standards/security.md +13 -31
  163. package/pack/.claude/skills/devrites-lib/reference/standards/skill-authoring.md +112 -82
  164. package/pack/.claude/skills/devrites-lib/reference/standards/spec-grammar.md +71 -45
  165. package/pack/.claude/skills/devrites-lib/reference/standards/testing.md +22 -11
  166. package/pack/.claude/skills/devrites-lib/reference/standards/tooling.md +15 -24
  167. package/pack/.claude/skills/devrites-lib/reference/workspace-artifact-schema.md +106 -37
  168. package/pack/.claude/skills/devrites-prose-craft/SKILL.md +3 -4
  169. package/pack/.claude/skills/devrites-source-driven/SKILL.md +3 -4
  170. package/pack/.claude/skills/devrites-ux-shape/SKILL.md +3 -3
  171. package/pack/.claude/skills/rite/SKILL.md +12 -13
  172. package/pack/.claude/skills/rite/reference/menu.md +11 -11
  173. package/pack/.claude/skills/rite-adopt/SKILL.md +29 -84
  174. package/pack/.claude/skills/rite-adopt/reference/adoption.md +16 -50
  175. package/pack/.claude/skills/rite-autocomplete/SKILL.md +65 -54
  176. package/pack/.claude/skills/rite-autocomplete/reference/loop.md +24 -17
  177. package/pack/.claude/skills/rite-autocomplete/reference/stop-conditions.md +18 -21
  178. package/pack/.claude/skills/rite-build/SKILL.md +28 -34
  179. package/pack/.claude/skills/rite-build/reference/afk-discipline.md +36 -34
  180. package/pack/.claude/skills/rite-build/reference/anti-patterns.md +5 -19
  181. package/pack/.claude/skills/rite-build/reference/checkpoint-protocol.md +10 -9
  182. package/pack/.claude/skills/rite-build/reference/checkpoint.md +17 -17
  183. package/pack/.claude/skills/rite-build/reference/frontend-trigger.md +3 -3
  184. package/pack/.claude/skills/rite-build/reference/one-slice-cycle.md +11 -14
  185. package/pack/.claude/skills/rite-build/reference/output.md +14 -24
  186. package/pack/.claude/skills/rite-build/reference/phase-contract.md +49 -188
  187. package/pack/.claude/skills/rite-build/reference/spec-drift-guard.md +1 -1
  188. package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +43 -133
  189. package/pack/.claude/skills/rite-clarify/SKILL.md +46 -40
  190. package/pack/.claude/skills/rite-clarify/reference/decision-coverage.md +4 -10
  191. package/pack/.claude/skills/rite-converge/SKILL.md +9 -33
  192. package/pack/.claude/skills/rite-converge/reference/convergence-assessment.md +2 -2
  193. package/pack/.claude/skills/rite-customize/SKILL.md +34 -50
  194. package/pack/.claude/skills/rite-define/SKILL.md +34 -58
  195. package/pack/.claude/skills/rite-define/reference/plan-template.md +15 -0
  196. package/pack/.claude/skills/rite-doctor/SKILL.md +52 -64
  197. package/pack/.claude/skills/rite-dogfood/SKILL.md +1 -2
  198. package/pack/.claude/skills/rite-explain/SKILL.md +1 -20
  199. package/pack/.claude/skills/rite-frame/SKILL.md +1 -7
  200. package/pack/.claude/skills/rite-frame/reference/failure-modes.md +1 -1
  201. package/pack/.claude/skills/rite-handoff/SKILL.md +1 -2
  202. package/pack/.claude/skills/rite-handoff/reference/handoff-template.md +1 -1
  203. package/pack/.claude/skills/rite-learn/SKILL.md +44 -104
  204. package/pack/.claude/skills/rite-plan/SKILL.md +14 -26
  205. package/pack/.claude/skills/rite-plan/reference/task-breakdown.md +4 -1
  206. package/pack/.claude/skills/rite-polish/SKILL.md +20 -28
  207. package/pack/.claude/skills/rite-polish/reference/adr-promotion.md +11 -0
  208. package/pack/{generated/claude/skills/rite-ship → .claude/skills/rite-polish}/reference/design-memory.md +23 -21
  209. package/pack/.claude/skills/rite-polish/reference/ledger.md +62 -0
  210. package/pack/.claude/skills/rite-pov/SKILL.md +28 -32
  211. package/pack/.claude/skills/rite-pr-feedback/SKILL.md +0 -1
  212. package/pack/.claude/skills/rite-pressure-test/SKILL.md +6 -13
  213. package/pack/.claude/skills/rite-prototype/SKILL.md +0 -6
  214. package/pack/.claude/skills/rite-prove/SKILL.md +73 -68
  215. package/pack/.claude/skills/rite-prove/reference/acceptance-proof.md +34 -30
  216. package/pack/.claude/skills/rite-prove/reference/test-command-discovery.md +15 -14
  217. package/pack/.claude/skills/rite-quick/SKILL.md +5 -19
  218. package/pack/.claude/skills/rite-resolve/SKILL.md +4 -21
  219. package/pack/.claude/skills/rite-resolve/reference/answer-protocol.md +2 -2
  220. package/pack/.claude/skills/rite-review/SKILL.md +34 -57
  221. package/pack/.claude/skills/rite-seal/SKILL.md +54 -87
  222. package/pack/.claude/skills/rite-seal/reference/anti-patterns.md +2 -2
  223. package/pack/.claude/skills/rite-seal/reference/final-evidence.md +31 -56
  224. package/pack/.claude/skills/rite-seal/reference/go-no-go.md +27 -51
  225. package/pack/.claude/skills/rite-seal/reference/output.md +2 -4
  226. package/pack/.claude/skills/rite-seal/reference/phase-contract.md +42 -140
  227. package/pack/.claude/skills/rite-seal/reference/risk-and-rollback.md +2 -2
  228. package/pack/.claude/skills/rite-seal/reference/seal-template.md +17 -28
  229. package/pack/.claude/skills/rite-ship/SKILL.md +65 -93
  230. package/pack/.claude/skills/rite-ship/reference/anti-patterns.md +4 -1
  231. package/pack/.claude/skills/rite-ship/reference/close-out.md +4 -12
  232. package/pack/.claude/skills/rite-ship/reference/git-ship.md +86 -48
  233. package/pack/.claude/skills/rite-ship/reference/ship-template.md +22 -16
  234. package/pack/.claude/skills/rite-spec/SKILL.md +62 -95
  235. package/pack/.claude/skills/rite-spec/reference/investigation.md +1 -4
  236. package/pack/.claude/skills/rite-spec/reference/question-protocol.md +21 -20
  237. package/pack/.claude/skills/rite-spec/reference/spec-checklists.md +16 -17
  238. package/pack/.claude/skills/rite-spec/reference/spec-template.md +47 -49
  239. package/pack/.claude/skills/rite-spec/reference/state-workspace.md +28 -13
  240. package/pack/.claude/skills/rite-status/SKILL.md +30 -88
  241. package/pack/.claude/skills/rite-temper/SKILL.md +12 -31
  242. package/pack/.claude/skills/rite-upgrade/SKILL.md +86 -101
  243. package/pack/.claude/skills/rite-vet/SKILL.md +88 -117
  244. package/pack/.claude/skills/rite-vet/reference/artifacts.md +56 -50
  245. package/pack/.claude/skills/rite-vet/reference/cross-model.md +15 -39
  246. package/pack/.claude/skills/rite-vet/reference/depth.md +2 -2
  247. package/pack/.claude/skills/rite-vet/reference/review-axes.md +3 -6
  248. package/pack/.claude/skills/rite-zoom-out/SKILL.md +0 -6
  249. package/pack/generated/README.md +2 -2
  250. package/pack/generated/claude/agents/devrites-code-reviewer.md +9 -18
  251. package/pack/generated/claude/agents/devrites-devex-reviewer.md +10 -18
  252. package/pack/generated/claude/agents/devrites-doubt-reviewer.md +10 -20
  253. package/pack/generated/claude/agents/devrites-evidence-scout.md +14 -26
  254. package/pack/generated/claude/agents/devrites-frontend-reviewer.md +9 -15
  255. package/pack/generated/claude/agents/devrites-performance-reviewer.md +13 -21
  256. package/pack/generated/claude/agents/devrites-plan-drafter.md +24 -28
  257. package/pack/generated/claude/agents/devrites-plan-reviewer.md +30 -47
  258. package/pack/generated/claude/agents/devrites-proof-runner.md +41 -47
  259. package/pack/generated/claude/agents/devrites-retrospector.md +34 -79
  260. package/pack/generated/claude/agents/devrites-security-auditor.md +9 -17
  261. package/pack/generated/claude/agents/devrites-simplifier-reviewer.md +9 -18
  262. package/pack/generated/claude/agents/devrites-slice-wright.md +44 -62
  263. package/pack/generated/claude/agents/devrites-spec-reviewer.md +38 -50
  264. package/pack/generated/claude/agents/devrites-strategy-reviewer.md +10 -19
  265. package/pack/generated/claude/agents/devrites-test-analyst.md +22 -21
  266. package/pack/generated/claude/agents/devrites-upgrade-planner.md +52 -71
  267. package/pack/generated/claude/settings.json +12 -100
  268. package/pack/generated/claude/skills/devrites-api-interface/SKILL.md +1 -2
  269. package/pack/generated/claude/skills/devrites-audit/SKILL.md +6 -9
  270. package/pack/generated/claude/skills/devrites-browser-proof/SKILL.md +0 -1
  271. package/pack/generated/claude/skills/devrites-debug-recovery/SKILL.md +21 -27
  272. package/pack/generated/claude/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +22 -33
  273. package/pack/generated/claude/skills/devrites-doubt/SKILL.md +4 -6
  274. package/pack/generated/claude/skills/devrites-frontend-craft/SKILL.md +1 -2
  275. package/pack/generated/claude/skills/devrites-frontend-craft/reference/design-references.md +2 -1
  276. package/pack/generated/claude/skills/devrites-interview/SKILL.md +31 -36
  277. package/pack/generated/claude/skills/devrites-lib/SKILL.md +37 -66
  278. package/pack/generated/claude/skills/devrites-lib/reference/candidate-integrity.md +51 -0
  279. package/pack/generated/claude/skills/devrites-lib/reference/intent-map.md +20 -12
  280. package/pack/generated/claude/skills/devrites-lib/reference/orchestration-profiles.md +22 -0
  281. package/pack/generated/claude/skills/devrites-lib/reference/parallel-dispatch.md +35 -110
  282. package/pack/generated/claude/skills/devrites-lib/reference/reply-contract.md +26 -93
  283. package/pack/generated/claude/skills/devrites-lib/reference/standards/README.md +6 -3
  284. package/pack/generated/claude/skills/devrites-lib/reference/standards/afk-hitl.md +43 -34
  285. package/pack/generated/claude/skills/devrites-lib/reference/standards/agents.md +90 -216
  286. package/pack/generated/claude/skills/devrites-lib/reference/standards/anti-patterns.md +1 -1
  287. package/pack/generated/claude/skills/devrites-lib/reference/standards/code-review.md +9 -28
  288. package/pack/generated/claude/skills/devrites-lib/reference/standards/context-hygiene.md +13 -11
  289. package/pack/generated/claude/skills/devrites-lib/reference/standards/core.md +59 -54
  290. package/pack/generated/claude/skills/devrites-lib/reference/standards/hooks.md +3 -5
  291. package/pack/generated/claude/skills/devrites-lib/reference/standards/principles.md +29 -143
  292. package/pack/generated/claude/skills/devrites-lib/reference/standards/release/ship-checklist.md +5 -6
  293. package/pack/generated/claude/skills/devrites-lib/reference/standards/security.md +13 -31
  294. package/pack/generated/claude/skills/devrites-lib/reference/standards/skill-authoring.md +112 -82
  295. package/pack/generated/claude/skills/devrites-lib/reference/standards/spec-grammar.md +71 -45
  296. package/pack/generated/claude/skills/devrites-lib/reference/standards/testing.md +22 -11
  297. package/pack/generated/claude/skills/devrites-lib/reference/standards/tooling.md +15 -24
  298. package/pack/generated/claude/skills/devrites-lib/reference/workspace-artifact-schema.md +106 -37
  299. package/pack/generated/claude/skills/devrites-prose-craft/SKILL.md +3 -4
  300. package/pack/generated/claude/skills/devrites-source-driven/SKILL.md +3 -4
  301. package/pack/generated/claude/skills/devrites-ux-shape/SKILL.md +3 -3
  302. package/pack/generated/claude/skills/rite/SKILL.md +12 -13
  303. package/pack/generated/claude/skills/rite/reference/menu.md +11 -11
  304. package/pack/generated/claude/skills/rite-adopt/SKILL.md +29 -84
  305. package/pack/generated/claude/skills/rite-adopt/reference/adoption.md +16 -50
  306. package/pack/generated/claude/skills/rite-autocomplete/SKILL.md +65 -54
  307. package/pack/generated/claude/skills/rite-autocomplete/reference/loop.md +24 -17
  308. package/pack/generated/claude/skills/rite-autocomplete/reference/stop-conditions.md +18 -21
  309. package/pack/generated/claude/skills/rite-build/SKILL.md +28 -34
  310. package/pack/generated/claude/skills/rite-build/reference/afk-discipline.md +36 -34
  311. package/pack/generated/claude/skills/rite-build/reference/anti-patterns.md +5 -19
  312. package/pack/generated/claude/skills/rite-build/reference/checkpoint-protocol.md +10 -9
  313. package/pack/generated/claude/skills/rite-build/reference/checkpoint.md +17 -17
  314. package/pack/generated/claude/skills/rite-build/reference/frontend-trigger.md +3 -3
  315. package/pack/generated/claude/skills/rite-build/reference/one-slice-cycle.md +11 -14
  316. package/pack/generated/claude/skills/rite-build/reference/output.md +14 -24
  317. package/pack/generated/claude/skills/rite-build/reference/phase-contract.md +49 -188
  318. package/pack/generated/claude/skills/rite-build/reference/spec-drift-guard.md +1 -1
  319. package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +43 -133
  320. package/pack/generated/claude/skills/rite-clarify/SKILL.md +46 -40
  321. package/pack/generated/claude/skills/rite-clarify/reference/decision-coverage.md +4 -10
  322. package/pack/generated/claude/skills/rite-converge/SKILL.md +9 -33
  323. package/pack/generated/claude/skills/rite-converge/reference/convergence-assessment.md +2 -2
  324. package/pack/generated/claude/skills/rite-customize/SKILL.md +34 -50
  325. package/pack/generated/claude/skills/rite-define/SKILL.md +34 -58
  326. package/pack/generated/claude/skills/rite-define/reference/plan-template.md +15 -0
  327. package/pack/generated/claude/skills/rite-doctor/SKILL.md +52 -64
  328. package/pack/generated/claude/skills/rite-dogfood/SKILL.md +1 -2
  329. package/pack/generated/claude/skills/rite-explain/SKILL.md +1 -20
  330. package/pack/generated/claude/skills/rite-frame/SKILL.md +1 -7
  331. package/pack/generated/claude/skills/rite-frame/reference/failure-modes.md +1 -1
  332. package/pack/generated/claude/skills/rite-handoff/SKILL.md +1 -2
  333. package/pack/generated/claude/skills/rite-handoff/reference/handoff-template.md +1 -1
  334. package/pack/generated/claude/skills/rite-learn/SKILL.md +44 -104
  335. package/pack/generated/claude/skills/rite-plan/SKILL.md +14 -26
  336. package/pack/generated/claude/skills/rite-plan/reference/task-breakdown.md +4 -1
  337. package/pack/generated/claude/skills/rite-polish/SKILL.md +20 -28
  338. package/pack/generated/claude/skills/rite-polish/reference/adr-promotion.md +11 -0
  339. package/pack/generated/{codex/skills/rite-ship → claude/skills/rite-polish}/reference/design-memory.md +23 -21
  340. package/pack/generated/claude/skills/rite-polish/reference/ledger.md +62 -0
  341. package/pack/generated/claude/skills/rite-pov/SKILL.md +28 -32
  342. package/pack/generated/claude/skills/rite-pr-feedback/SKILL.md +0 -1
  343. package/pack/generated/claude/skills/rite-pressure-test/SKILL.md +6 -13
  344. package/pack/generated/claude/skills/rite-prototype/SKILL.md +0 -6
  345. package/pack/generated/claude/skills/rite-prove/SKILL.md +73 -68
  346. package/pack/generated/claude/skills/rite-prove/reference/acceptance-proof.md +34 -30
  347. package/pack/generated/claude/skills/rite-prove/reference/test-command-discovery.md +15 -14
  348. package/pack/generated/claude/skills/rite-quick/SKILL.md +5 -19
  349. package/pack/generated/claude/skills/rite-resolve/SKILL.md +4 -21
  350. package/pack/generated/claude/skills/rite-resolve/reference/answer-protocol.md +2 -2
  351. package/pack/generated/claude/skills/rite-review/SKILL.md +34 -57
  352. package/pack/generated/claude/skills/rite-seal/SKILL.md +54 -87
  353. package/pack/generated/claude/skills/rite-seal/reference/anti-patterns.md +2 -2
  354. package/pack/generated/claude/skills/rite-seal/reference/final-evidence.md +31 -56
  355. package/pack/generated/claude/skills/rite-seal/reference/go-no-go.md +27 -51
  356. package/pack/generated/claude/skills/rite-seal/reference/output.md +2 -4
  357. package/pack/generated/claude/skills/rite-seal/reference/phase-contract.md +42 -140
  358. package/pack/generated/claude/skills/rite-seal/reference/risk-and-rollback.md +2 -2
  359. package/pack/generated/claude/skills/rite-seal/reference/seal-template.md +17 -28
  360. package/pack/generated/claude/skills/rite-ship/SKILL.md +65 -93
  361. package/pack/generated/claude/skills/rite-ship/reference/anti-patterns.md +4 -1
  362. package/pack/generated/claude/skills/rite-ship/reference/close-out.md +4 -12
  363. package/pack/generated/claude/skills/rite-ship/reference/git-ship.md +86 -48
  364. package/pack/generated/claude/skills/rite-ship/reference/ship-template.md +22 -16
  365. package/pack/generated/claude/skills/rite-spec/SKILL.md +62 -95
  366. package/pack/generated/claude/skills/rite-spec/reference/investigation.md +1 -4
  367. package/pack/generated/claude/skills/rite-spec/reference/question-protocol.md +21 -20
  368. package/pack/generated/claude/skills/rite-spec/reference/spec-checklists.md +16 -17
  369. package/pack/generated/claude/skills/rite-spec/reference/spec-template.md +47 -49
  370. package/pack/generated/claude/skills/rite-spec/reference/state-workspace.md +28 -13
  371. package/pack/generated/claude/skills/rite-status/SKILL.md +30 -88
  372. package/pack/generated/claude/skills/rite-temper/SKILL.md +12 -31
  373. package/pack/generated/claude/skills/rite-upgrade/SKILL.md +86 -101
  374. package/pack/generated/claude/skills/rite-vet/SKILL.md +88 -117
  375. package/pack/generated/claude/skills/rite-vet/reference/artifacts.md +56 -50
  376. package/pack/generated/claude/skills/rite-vet/reference/cross-model.md +15 -39
  377. package/pack/generated/claude/skills/rite-vet/reference/depth.md +2 -2
  378. package/pack/generated/claude/skills/rite-vet/reference/review-axes.md +3 -6
  379. package/pack/generated/claude/skills/rite-zoom-out/SKILL.md +0 -6
  380. package/pack/generated/codex/AGENTS.md +5 -4
  381. package/pack/generated/codex/agents/devrites-code-reviewer.toml +8 -27
  382. package/pack/generated/codex/agents/devrites-devex-reviewer.toml +9 -27
  383. package/pack/generated/codex/agents/devrites-doubt-reviewer.toml +9 -29
  384. package/pack/generated/codex/agents/devrites-evidence-scout.toml +14 -36
  385. package/pack/generated/codex/agents/devrites-frontend-reviewer.toml +8 -24
  386. package/pack/generated/codex/agents/devrites-performance-reviewer.toml +12 -30
  387. package/pack/generated/codex/agents/devrites-plan-drafter.toml +24 -38
  388. package/pack/generated/codex/agents/devrites-plan-reviewer.toml +30 -57
  389. package/pack/generated/codex/agents/devrites-proof-runner.toml +41 -57
  390. package/pack/generated/codex/agents/devrites-retrospector.toml +34 -89
  391. package/pack/generated/codex/agents/devrites-security-auditor.toml +8 -26
  392. package/pack/generated/codex/agents/devrites-simplifier-reviewer.toml +8 -27
  393. package/pack/generated/codex/agents/devrites-slice-wright.toml +44 -71
  394. package/pack/generated/codex/agents/devrites-spec-reviewer.toml +38 -60
  395. package/pack/generated/codex/agents/devrites-strategy-reviewer.toml +9 -28
  396. package/pack/generated/codex/agents/devrites-test-analyst.toml +21 -30
  397. package/pack/generated/codex/agents/devrites-upgrade-planner.toml +52 -81
  398. package/pack/generated/codex/config.toml +7 -0
  399. package/pack/generated/codex/skills/devrites-api-interface/SKILL.md +1 -21
  400. package/pack/generated/codex/skills/devrites-audit/SKILL.md +6 -28
  401. package/pack/generated/codex/skills/devrites-browser-proof/SKILL.md +1 -21
  402. package/pack/generated/codex/skills/devrites-debug-recovery/SKILL.md +21 -46
  403. package/pack/generated/codex/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +22 -33
  404. package/pack/generated/codex/skills/devrites-doubt/SKILL.md +4 -25
  405. package/pack/generated/codex/skills/devrites-frontend-craft/SKILL.md +1 -21
  406. package/pack/generated/codex/skills/devrites-frontend-craft/reference/design-references.md +2 -1
  407. package/pack/generated/codex/skills/devrites-interview/SKILL.md +32 -56
  408. package/pack/generated/codex/skills/devrites-lib/SKILL.md +38 -86
  409. package/pack/generated/codex/skills/devrites-lib/reference/candidate-integrity.md +51 -0
  410. package/pack/generated/codex/skills/devrites-lib/reference/intent-map.md +20 -12
  411. package/pack/generated/codex/skills/devrites-lib/reference/orchestration-profiles.md +22 -0
  412. package/pack/generated/codex/skills/devrites-lib/reference/parallel-dispatch.md +35 -110
  413. package/pack/generated/codex/skills/devrites-lib/reference/reply-contract.md +26 -93
  414. package/pack/generated/codex/skills/devrites-lib/reference/standards/README.md +6 -3
  415. package/pack/generated/codex/skills/devrites-lib/reference/standards/afk-hitl.md +43 -34
  416. package/pack/generated/codex/skills/devrites-lib/reference/standards/agents.md +90 -216
  417. package/pack/generated/codex/skills/devrites-lib/reference/standards/anti-patterns.md +1 -1
  418. package/pack/generated/codex/skills/devrites-lib/reference/standards/code-review.md +9 -28
  419. package/pack/generated/codex/skills/devrites-lib/reference/standards/context-hygiene.md +13 -11
  420. package/pack/generated/codex/skills/devrites-lib/reference/standards/core.md +59 -54
  421. package/pack/generated/codex/skills/devrites-lib/reference/standards/hooks.md +3 -5
  422. package/pack/generated/codex/skills/devrites-lib/reference/standards/principles.md +29 -143
  423. package/pack/generated/codex/skills/devrites-lib/reference/standards/release/ship-checklist.md +5 -6
  424. package/pack/generated/codex/skills/devrites-lib/reference/standards/security.md +13 -31
  425. package/pack/generated/codex/skills/devrites-lib/reference/standards/skill-authoring.md +112 -82
  426. package/pack/generated/codex/skills/devrites-lib/reference/standards/spec-grammar.md +71 -45
  427. package/pack/generated/codex/skills/devrites-lib/reference/standards/testing.md +22 -11
  428. package/pack/generated/codex/skills/devrites-lib/reference/standards/tooling.md +15 -24
  429. package/pack/generated/codex/skills/devrites-lib/reference/workspace-artifact-schema.md +106 -37
  430. package/pack/generated/codex/skills/devrites-prose-craft/SKILL.md +3 -23
  431. package/pack/generated/codex/skills/devrites-source-driven/SKILL.md +3 -23
  432. package/pack/generated/codex/skills/devrites-ux-shape/SKILL.md +3 -22
  433. package/pack/generated/codex/skills/rite/SKILL.md +12 -32
  434. package/pack/generated/codex/skills/rite/reference/menu.md +11 -11
  435. package/pack/generated/codex/skills/rite-adopt/SKILL.md +29 -103
  436. package/pack/generated/codex/skills/rite-adopt/reference/adoption.md +16 -50
  437. package/pack/generated/codex/skills/rite-autocomplete/SKILL.md +65 -73
  438. package/pack/generated/codex/skills/rite-autocomplete/reference/loop.md +24 -17
  439. package/pack/generated/codex/skills/rite-autocomplete/reference/stop-conditions.md +18 -21
  440. package/pack/generated/codex/skills/rite-build/SKILL.md +28 -53
  441. package/pack/generated/codex/skills/rite-build/reference/afk-discipline.md +36 -34
  442. package/pack/generated/codex/skills/rite-build/reference/anti-patterns.md +5 -19
  443. package/pack/generated/codex/skills/rite-build/reference/checkpoint-protocol.md +10 -9
  444. package/pack/generated/codex/skills/rite-build/reference/checkpoint.md +17 -17
  445. package/pack/generated/codex/skills/rite-build/reference/frontend-trigger.md +3 -3
  446. package/pack/generated/codex/skills/rite-build/reference/one-slice-cycle.md +11 -14
  447. package/pack/generated/codex/skills/rite-build/reference/output.md +14 -24
  448. package/pack/generated/codex/skills/rite-build/reference/phase-contract.md +49 -188
  449. package/pack/generated/codex/skills/rite-build/reference/spec-drift-guard.md +1 -1
  450. package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +43 -133
  451. package/pack/generated/codex/skills/rite-clarify/SKILL.md +46 -59
  452. package/pack/generated/codex/skills/rite-clarify/reference/decision-coverage.md +4 -10
  453. package/pack/generated/codex/skills/rite-converge/SKILL.md +9 -52
  454. package/pack/generated/codex/skills/rite-converge/reference/convergence-assessment.md +2 -2
  455. package/pack/generated/codex/skills/rite-customize/SKILL.md +32 -67
  456. package/pack/generated/codex/skills/rite-define/SKILL.md +34 -77
  457. package/pack/generated/codex/skills/rite-define/reference/plan-template.md +15 -0
  458. package/pack/generated/codex/skills/rite-doctor/SKILL.md +52 -83
  459. package/pack/generated/codex/skills/rite-dogfood/SKILL.md +1 -21
  460. package/pack/generated/codex/skills/rite-explain/SKILL.md +1 -39
  461. package/pack/generated/codex/skills/rite-frame/SKILL.md +1 -26
  462. package/pack/generated/codex/skills/rite-frame/reference/failure-modes.md +1 -1
  463. package/pack/generated/codex/skills/rite-handoff/SKILL.md +1 -21
  464. package/pack/generated/codex/skills/rite-handoff/reference/handoff-template.md +1 -1
  465. package/pack/generated/codex/skills/rite-learn/SKILL.md +44 -123
  466. package/pack/generated/codex/skills/rite-plan/SKILL.md +14 -45
  467. package/pack/generated/codex/skills/rite-plan/reference/task-breakdown.md +4 -1
  468. package/pack/generated/codex/skills/rite-polish/SKILL.md +20 -47
  469. package/pack/generated/codex/skills/rite-polish/reference/adr-promotion.md +11 -0
  470. package/pack/{.claude/skills/rite-ship → generated/codex/skills/rite-polish}/reference/design-memory.md +23 -21
  471. package/pack/generated/codex/skills/rite-polish/reference/ledger.md +62 -0
  472. package/pack/generated/codex/skills/rite-pov/SKILL.md +28 -51
  473. package/pack/generated/codex/skills/rite-pr-feedback/SKILL.md +0 -20
  474. package/pack/generated/codex/skills/rite-pressure-test/SKILL.md +6 -32
  475. package/pack/generated/codex/skills/rite-prototype/SKILL.md +0 -25
  476. package/pack/generated/codex/skills/rite-prove/SKILL.md +73 -87
  477. package/pack/generated/codex/skills/rite-prove/reference/acceptance-proof.md +34 -30
  478. package/pack/generated/codex/skills/rite-prove/reference/test-command-discovery.md +15 -14
  479. package/pack/generated/codex/skills/rite-quick/SKILL.md +5 -38
  480. package/pack/generated/codex/skills/rite-resolve/SKILL.md +4 -40
  481. package/pack/generated/codex/skills/rite-resolve/reference/answer-protocol.md +2 -2
  482. package/pack/generated/codex/skills/rite-review/SKILL.md +34 -76
  483. package/pack/generated/codex/skills/rite-seal/SKILL.md +51 -103
  484. package/pack/generated/codex/skills/rite-seal/reference/anti-patterns.md +2 -2
  485. package/pack/generated/codex/skills/rite-seal/reference/final-evidence.md +31 -56
  486. package/pack/generated/codex/skills/rite-seal/reference/go-no-go.md +27 -51
  487. package/pack/generated/codex/skills/rite-seal/reference/output.md +2 -4
  488. package/pack/generated/codex/skills/rite-seal/reference/phase-contract.md +42 -140
  489. package/pack/generated/codex/skills/rite-seal/reference/risk-and-rollback.md +2 -2
  490. package/pack/generated/codex/skills/rite-seal/reference/seal-template.md +17 -28
  491. package/pack/generated/codex/skills/rite-ship/SKILL.md +65 -112
  492. package/pack/generated/codex/skills/rite-ship/reference/anti-patterns.md +4 -1
  493. package/pack/generated/codex/skills/rite-ship/reference/close-out.md +4 -12
  494. package/pack/generated/codex/skills/rite-ship/reference/git-ship.md +86 -48
  495. package/pack/generated/codex/skills/rite-ship/reference/ship-template.md +22 -16
  496. package/pack/generated/codex/skills/rite-spec/SKILL.md +62 -114
  497. package/pack/generated/codex/skills/rite-spec/reference/investigation.md +1 -4
  498. package/pack/generated/codex/skills/rite-spec/reference/question-protocol.md +21 -20
  499. package/pack/generated/codex/skills/rite-spec/reference/spec-checklists.md +16 -17
  500. package/pack/generated/codex/skills/rite-spec/reference/spec-template.md +47 -49
  501. package/pack/generated/codex/skills/rite-spec/reference/state-workspace.md +28 -13
  502. package/pack/generated/codex/skills/rite-status/SKILL.md +30 -107
  503. package/pack/generated/codex/skills/rite-temper/SKILL.md +12 -50
  504. package/pack/generated/codex/skills/rite-upgrade/SKILL.md +86 -120
  505. package/pack/generated/codex/skills/rite-vet/SKILL.md +88 -136
  506. package/pack/generated/codex/skills/rite-vet/reference/artifacts.md +56 -50
  507. package/pack/generated/codex/skills/rite-vet/reference/cross-model.md +15 -39
  508. package/pack/generated/codex/skills/rite-vet/reference/depth.md +2 -2
  509. package/pack/generated/codex/skills/rite-vet/reference/review-axes.md +3 -6
  510. package/pack/generated/codex/skills/rite-zoom-out/SKILL.md +0 -25
  511. package/package.json +6 -5
  512. package/scripts/build-host-artifacts.sh +5 -9
  513. package/scripts/codex-generate.sh +18 -278
  514. package/scripts/install-lib.sh +121 -34
  515. package/uninstall.sh +19 -17
  516. package/update.sh +26 -18
  517. package/docs/engine/agent-contract.md +0 -172
  518. package/docs/extensions.md +0 -178
  519. package/engine/git_guard.go +0 -187
  520. package/engine/git_guard_test.go +0 -283
  521. package/engine/harnessmatrix.go +0 -60
  522. package/engine/hookpolicy.go +0 -113
  523. package/engine/hookpolicy_test.go +0 -160
  524. package/engine/hooks.go +0 -677
  525. package/engine/hooks_agent_dispatch.go +0 -1726
  526. package/engine/hooks_events_test.go +0 -431
  527. package/engine/hooks_workspace.go +0 -1196
  528. package/engine/internal/doctor/doctor.go +0 -306
  529. package/engine/internal/doctor/doctor_test.go +0 -252
  530. package/engine/internal/forge/forge.go +0 -938
  531. package/engine/internal/forge/forge_test.go +0 -576
  532. package/engine/internal/forge/git.go +0 -245
  533. package/engine/internal/forge/liveness_unix.go +0 -48
  534. package/engine/internal/forge/liveness_windows.go +0 -67
  535. package/engine/internal/forge/manifest.go +0 -402
  536. package/engine/internal/harness/compliance.go +0 -96
  537. package/engine/internal/harness/harness.go +0 -301
  538. package/engine/internal/harness/harness_test.go +0 -124
  539. package/engine/internal/iohooks/detach_other.go +0 -11
  540. package/engine/internal/iohooks/detach_unix.go +0 -11
  541. package/engine/internal/iohooks/iohooks.go +0 -839
  542. package/engine/internal/iohooks/iohooks_test.go +0 -561
  543. package/engine/internal/lib/adoption.go +0 -659
  544. package/engine/internal/lib/adoption_test.go +0 -121
  545. package/engine/internal/lib/analyze.go +0 -232
  546. package/engine/internal/lib/archivesearch.go +0 -125
  547. package/engine/internal/lib/archivesearch_test.go +0 -138
  548. package/engine/internal/lib/budget.go +0 -172
  549. package/engine/internal/lib/buildreadiness.go +0 -111
  550. package/engine/internal/lib/checkacceptance.go +0 -176
  551. package/engine/internal/lib/clarifyreturn.go +0 -91
  552. package/engine/internal/lib/clarifyreturn_test.go +0 -123
  553. package/engine/internal/lib/command_other.go +0 -7
  554. package/engine/internal/lib/command_unix.go +0 -24
  555. package/engine/internal/lib/context.go +0 -280
  556. package/engine/internal/lib/conventions.go +0 -449
  557. package/engine/internal/lib/coverage.go +0 -145
  558. package/engine/internal/lib/dispatchwaive.go +0 -31
  559. package/engine/internal/lib/dispatchwaive_test.go +0 -23
  560. package/engine/internal/lib/doubtcoverage.go +0 -78
  561. package/engine/internal/lib/extensions.go +0 -622
  562. package/engine/internal/lib/extensions_test.go +0 -305
  563. package/engine/internal/lib/footprint.go +0 -239
  564. package/engine/internal/lib/gitauthority.go +0 -601
  565. package/engine/internal/lib/gitauthority_test.go +0 -346
  566. package/engine/internal/lib/jsonout.go +0 -129
  567. package/engine/internal/lib/jsonout_test.go +0 -73
  568. package/engine/internal/lib/lanes.go +0 -112
  569. package/engine/internal/lib/learnings.go +0 -397
  570. package/engine/internal/lib/learnings_inject_test.go +0 -77
  571. package/engine/internal/lib/learnings_remediation_test.go +0 -24
  572. package/engine/internal/lib/ledger.go +0 -311
  573. package/engine/internal/lib/ledger_test.go +0 -186
  574. package/engine/internal/lib/mutationgate.go +0 -89
  575. package/engine/internal/lib/observability_test.go +0 -358
  576. package/engine/internal/lib/overrides.go +0 -164
  577. package/engine/internal/lib/overrides_test.go +0 -112
  578. package/engine/internal/lib/preamble.go +0 -159
  579. package/engine/internal/lib/preamble_questions_test.go +0 -10
  580. package/engine/internal/lib/progress.go +0 -210
  581. package/engine/internal/lib/provenance.go +0 -462
  582. package/engine/internal/lib/provenance_test.go +0 -154
  583. package/engine/internal/lib/readiness_contract.json +0 -118
  584. package/engine/internal/lib/readinessartifact.go +0 -533
  585. package/engine/internal/lib/readinessartifact_test.go +0 -422
  586. package/engine/internal/lib/reconcile.go +0 -1496
  587. package/engine/internal/lib/reconcile_test.go +0 -1071
  588. package/engine/internal/lib/recoveryattempts.go +0 -298
  589. package/engine/internal/lib/recoveryattempts_test.go +0 -215
  590. package/engine/internal/lib/reviewers.go +0 -143
  591. package/engine/internal/lib/reviewers_test.go +0 -45
  592. package/engine/internal/lib/reviewerstats.go +0 -199
  593. package/engine/internal/lib/reviewerstats_test.go +0 -104
  594. package/engine/internal/lib/reviewintegrity.go +0 -143
  595. package/engine/internal/lib/reviewintegrity_test.go +0 -103
  596. package/engine/internal/lib/runbook.go +0 -295
  597. package/engine/internal/lib/runbook_context_test.go +0 -182
  598. package/engine/internal/lib/session.go +0 -1020
  599. package/engine/internal/lib/session_test.go +0 -177
  600. package/engine/internal/lib/specparse.go +0 -125
  601. package/engine/internal/lib/specskeleton.go +0 -79
  602. package/engine/internal/lib/specvalidate.go +0 -391
  603. package/engine/internal/lib/specvalidate_test.go +0 -185
  604. package/engine/internal/lib/stuck.go +0 -206
  605. package/engine/internal/lib/templates/conventions_drift.tmpl +0 -6
  606. package/engine/internal/lib/templates/conventions_ledger_header.md +0 -6
  607. package/engine/internal/lib/templates/conventions_orient_header.txt +0 -1
  608. package/engine/internal/lib/testintegrity.go +0 -199
  609. package/engine/internal/lib/testintegrity_test.go +0 -149
  610. package/engine/internal/lib/tickafk.go +0 -86
  611. package/engine/internal/migrate/migrate.go +0 -279
  612. package/engine/internal/migrate/migrate_test.go +0 -157
  613. package/engine/internal/orient/firstrun.go +0 -50
  614. package/engine/internal/orient/firstrun_test.go +0 -72
  615. package/engine/internal/orient/orient.go +0 -69
  616. package/engine/internal/orient/orient_test.go +0 -25
  617. package/engine/internal/profile/profile.go +0 -284
  618. package/engine/internal/profile/profile_test.go +0 -13
  619. package/engine/internal/state/clarify_transition.go +0 -165
  620. package/engine/internal/state/clarify_transition_test.go +0 -130
  621. package/engine/internal/state/snapshot.go +0 -457
  622. package/engine/internal/toolpolicy/classifier.go +0 -533
  623. package/engine/internal/toolpolicy/classifier_test.go +0 -424
  624. package/engine/internal/toolpolicy/git.go +0 -616
  625. package/engine/internal/toolpolicy/scanner.go +0 -382
  626. package/engine/observability_cli_test.go +0 -52
  627. package/engine/profile_test.go +0 -76
  628. package/engine/subagent_orient_context.txt +0 -8
  629. package/engine/testdata/golden/TestParityA1Guard/devrites-edit-allowed.golden +0 -1
  630. package/engine/testdata/golden/TestParityA1Guard/no-window-silent.golden +0 -1
  631. package/engine/testdata/golden/TestParityA1Guard/non-edit-tool.golden +0 -1
  632. package/engine/testdata/golden/TestParityA1Guard/observe-mode-silent.golden +0 -1
  633. package/engine/testdata/golden/TestParityA1Guard/source-edit-enforce-denies.golden +0 -2
  634. package/engine/testdata/golden/TestParityA1Guard/subagent-allowed.golden +0 -1
  635. package/engine/testdata/golden/TestParityAnalyze/noactive.golden +0 -1
  636. package/engine/testdata/golden/TestParityAnalyze/slug=covered.golden +0 -15
  637. package/engine/testdata/golden/TestParityAnalyze/slug=dangling.golden +0 -16
  638. package/engine/testdata/golden/TestParityAnalyze/slug=ghost.golden +0 -1
  639. package/engine/testdata/golden/TestParityAnalyze/slug=noac.golden +0 -15
  640. package/engine/testdata/golden/TestParityAnalyze/slug=order.golden +0 -17
  641. package/engine/testdata/golden/TestParityAnalyze/slug=orphan.golden +0 -16
  642. package/engine/testdata/golden/TestParityAnalyze/slug=uncovered.golden +0 -16
  643. package/engine/testdata/golden/TestParityBudget/default-active.golden +0 -4
  644. package/engine/testdata/golden/TestParityBudget/extra-md.golden +0 -5
  645. package/engine/testdata/golden/TestParityBudget/no-active.golden +0 -1
  646. package/engine/testdata/golden/TestParityBudget/no-feature.golden +0 -1
  647. package/engine/testdata/golden/TestParityBudget/over-advisory.golden +0 -6
  648. package/engine/testdata/golden/TestParityBudget/over-strict.golden +0 -6
  649. package/engine/testdata/golden/TestParityBudget/within.golden +0 -6
  650. package/engine/testdata/golden/TestParityBuildReadiness/arg=approved.golden +0 -2
  651. package/engine/testdata/golden/TestParityBuildReadiness/arg=approvenone.golden +0 -1
  652. package/engine/testdata/golden/TestParityBuildReadiness/arg=await.golden +0 -1
  653. package/engine/testdata/golden/TestParityBuildReadiness/arg=blocked.golden +0 -1
  654. package/engine/testdata/golden/TestParityBuildReadiness/arg=clarifyopen.golden +0 -1
  655. package/engine/testdata/golden/TestParityBuildReadiness/arg=emptystatus.golden +0 -2
  656. package/engine/testdata/golden/TestParityBuildReadiness/arg=ghost.golden +0 -1
  657. package/engine/testdata/golden/TestParityBuildReadiness/arg=legacycontract.golden +0 -1
  658. package/engine/testdata/golden/TestParityBuildReadiness/arg=noapprove.golden +0 -1
  659. package/engine/testdata/golden/TestParityBuildReadiness/arg=noclarify.golden +0 -1
  660. package/engine/testdata/golden/TestParityBuildReadiness/arg=novet.golden +0 -1
  661. package/engine/testdata/golden/TestParityBuildReadiness/arg=stalevet.golden +0 -1
  662. package/engine/testdata/golden/TestParityBuildReadiness/arg=trailhash.golden +0 -2
  663. package/engine/testdata/golden/TestParityBuildReadiness/arg=trailpipe.golden +0 -2
  664. package/engine/testdata/golden/TestParityBuildReadiness/arg=vetnotready.golden +0 -1
  665. package/engine/testdata/golden/TestParityCheckAcceptance/arg=blank-section.golden +0 -1
  666. package/engine/testdata/golden/TestParityCheckAcceptance/arg=empty.golden +0 -1
  667. package/engine/testdata/golden/TestParityCheckAcceptance/arg=flat-dropped.golden +0 -1
  668. package/engine/testdata/golden/TestParityCheckAcceptance/arg=flat-ok.golden +0 -2
  669. package/engine/testdata/golden/TestParityCheckAcceptance/arg=flat-unchecked.golden +0 -1
  670. package/engine/testdata/golden/TestParityCheckAcceptance/arg=id-gap.golden +0 -1
  671. package/engine/testdata/golden/TestParityCheckAcceptance/arg=id-ok.golden +0 -2
  672. package/engine/testdata/golden/TestParityCheckAcceptance/arg=no-seal.golden +0 -1
  673. package/engine/testdata/golden/TestParityCheckAcceptance/arg=no-section.golden +0 -1
  674. package/engine/testdata/golden/TestParityCheckAcceptance/arg=nonexistent.golden +0 -1
  675. package/engine/testdata/golden/TestParityCheckAcceptance.golden +0 -1
  676. package/engine/testdata/golden/TestParityCoverage/arg=flat.golden +0 -7
  677. package/engine/testdata/golden/TestParityCoverage/arg=full.golden +0 -10
  678. package/engine/testdata/golden/TestParityCoverage/arg=nonexistent.golden +0 -1
  679. package/engine/testdata/golden/TestParityCoverage/arg=nospec.golden +0 -1
  680. package/engine/testdata/golden/TestParityCoverage/arg=speconly.golden +0 -9
  681. package/engine/testdata/golden/TestParityCoverage.golden +0 -1
  682. package/engine/testdata/golden/TestParityCursor/bare.golden +0 -3
  683. package/engine/testdata/golden/TestParityCursor/full-afk.golden +0 -6
  684. package/engine/testdata/golden/TestParityCursor/full-hitl.golden +0 -5
  685. package/engine/testdata/golden/TestParityCursor/no-active.golden +0 -1
  686. package/engine/testdata/golden/TestParityCursor/red.golden +0 -5
  687. package/engine/testdata/golden/TestParityCursor/zerogates.golden +0 -5
  688. package/engine/testdata/golden/TestParityDoubtCoverage/arg=doubted.golden +0 -2
  689. package/engine/testdata/golden/TestParityDoubtCoverage/arg=ghost.golden +0 -2
  690. package/engine/testdata/golden/TestParityDoubtCoverage/arg=missing.golden +0 -3
  691. package/engine/testdata/golden/TestParityDoubtCoverage/arg=nodoubt.golden +0 -4
  692. package/engine/testdata/golden/TestParityDoubtCoverage/arg=nolog.golden +0 -2
  693. package/engine/testdata/golden/TestParityDoubtCoverage/arg=nowright.golden +0 -2
  694. package/engine/testdata/golden/TestParityDoubtCoverage.golden +0 -1
  695. package/engine/testdata/golden/TestParityMutationGate/active-slug.golden +0 -3
  696. package/engine/testdata/golden/TestParityMutationGate/explicit-slug.golden +0 -3
  697. package/engine/testdata/golden/TestParityMutationGate/ghost.golden +0 -1
  698. package/engine/testdata/golden/TestParityOrientSilentPath.golden +0 -1
  699. package/engine/testdata/golden/TestParityReconcile/bad-mode.golden +0 -1
  700. package/engine/testdata/golden/TestParityReconcile/check-clean/snapshot.golden +0 -2
  701. package/engine/testdata/golden/TestParityReconcile/check-clean.golden +0 -2
  702. package/engine/testdata/golden/TestParityReconcile/check-no-base.golden +0 -1
  703. package/engine/testdata/golden/TestParityReconcile/check-no-claimed/snapshot.golden +0 -2
  704. package/engine/testdata/golden/TestParityReconcile/check-violation/snapshot.golden +0 -2
  705. package/engine/testdata/golden/TestParityReconcile/check-violation.golden +0 -1
  706. package/engine/testdata/golden/TestParityReconcile/close-window/snapshot.golden +0 -2
  707. package/engine/testdata/golden/TestParityReconcile/close-window.golden +0 -3
  708. package/engine/testdata/golden/TestParityReconcile/not-git.golden +0 -1
  709. package/engine/testdata/golden/TestParityReconcile/snapshot/snapshot.golden +0 -2
  710. package/engine/testdata/golden/TestParityReconcile/snapshot-no-allowlist.golden +0 -1
  711. package/engine/testdata/golden/TestParityReconcile/snapshot.golden +0 -2
  712. package/engine/testdata/golden/TestParityRedwatch/fail-writes-red.golden +0 -2
  713. package/engine/testdata/golden/TestParityRedwatch/non-bash-tool.golden +0 -1
  714. package/engine/testdata/golden/TestParityRedwatch/non-test-command.golden +0 -1
  715. package/engine/testdata/golden/TestParityRedwatch/pass-clears-red.golden +0 -1
  716. package/engine/testdata/golden/TestParityResolve/next-qid.golden +0 -2
  717. package/engine/testdata/golden/TestParityReviewerReadonly/agent-required-devrites-agent-denies.golden +0 -2
  718. package/engine/testdata/golden/TestParityReviewerReadonly/agent-required-non-devrites-allows.golden +0 -1
  719. package/engine/testdata/golden/TestParityReviewerReadonly/agent-required-subagent-type-only-allows.golden +0 -1
  720. package/engine/testdata/golden/TestParityReviewerReadonly/enforce-deny-git-commit.golden +0 -2
  721. package/engine/testdata/golden/TestParityReviewerReadonly/enforce-deny-mutating.golden +0 -2
  722. package/engine/testdata/golden/TestParityReviewerReadonly/non-bash-tool.golden +0 -1
  723. package/engine/testdata/golden/TestParityReviewerReadonly/non-tool-input.golden +0 -1
  724. package/engine/testdata/golden/TestParityReviewerReadonly/observe-mutating-silent.golden +0 -1
  725. package/engine/testdata/golden/TestParityReviewerReadonly/safe-bash-command.golden +0 -1
  726. package/engine/testdata/golden/TestParitySpecValidate/arg=dup.golden +0 -1
  727. package/engine/testdata/golden/TestParitySpecValidate/arg=empty.golden +0 -1
  728. package/engine/testdata/golden/TestParitySpecValidate/arg=flat.golden +0 -2
  729. package/engine/testdata/golden/TestParitySpecValidate/arg=nonexistent.golden +0 -1
  730. package/engine/testdata/golden/TestParitySpecValidate/arg=noscenario.golden +0 -1
  731. package/engine/testdata/golden/TestParitySpecValidate/arg=noshall.golden +0 -1
  732. package/engine/testdata/golden/TestParitySpecValidate/arg=nothen.golden +0 -1
  733. package/engine/testdata/golden/TestParitySpecValidate/arg=valid/spec.md.golden +0 -2
  734. package/engine/testdata/golden/TestParitySpecValidate/arg=valid.golden +0 -2
  735. package/engine/testdata/golden/TestParitySpecValidate.golden +0 -1
  736. package/engine/testdata/golden/TestParityStatusline/full-afk.golden +0 -2
  737. package/engine/testdata/golden/TestParityStatusline/full-hitl.golden +0 -2
  738. package/engine/testdata/golden/TestParityStatusline/no-active.golden +0 -1
  739. package/engine/testdata/golden/TestParityStatusline/nophase.golden +0 -2
  740. package/engine/testdata/golden/TestParityStatusline/red.golden +0 -2
  741. package/engine/testdata/golden/TestParityStuckCheck/bad-cmd.golden +0 -1
  742. package/engine/testdata/golden/TestParityStuckCheck/custom-window-2.golden +0 -2
  743. package/engine/testdata/golden/TestParityStuckCheck/dominant.golden +0 -2
  744. package/engine/testdata/golden/TestParityStuckCheck/few.golden +0 -2
  745. package/engine/testdata/golden/TestParityStuckCheck/ghost.golden +0 -2
  746. package/engine/testdata/golden/TestParityStuckCheck/identical.golden +0 -2
  747. package/engine/testdata/golden/TestParityStuckCheck/no-cmd.golden +0 -1
  748. package/engine/testdata/golden/TestParityStuckCheck/no-slug.golden +0 -1
  749. package/engine/testdata/golden/TestParityStuckCheck/nolog.golden +0 -2
  750. package/engine/testdata/golden/TestParityStuckCheck/pingpong.golden +0 -2
  751. package/engine/testdata/golden/TestParityStuckCheck/varied.golden +0 -2
  752. package/engine/testdata/golden/TestParityStuckCheck/window-04-identical.golden +0 -2
  753. package/engine/testdata/golden/TestParityStuckCheck/window-2.5-varied4.golden +0 -2
  754. package/engine/testdata/golden/TestParityStuckCheck/window-4x-varied4.golden +0 -2
  755. package/engine/testdata/golden/TestParityStuckCheck/window-leadspace-varied4.golden +0 -2
  756. package/engine/testdata/golden/TestParityStuckCheck/window-plus4-identical.golden +0 -2
  757. package/engine/testdata/golden/TestParitySubagentOrient/devrites-agent-subagent-type-key.golden +0 -2
  758. package/engine/testdata/golden/TestParitySubagentOrient/devrites-agent.golden +0 -2
  759. package/engine/testdata/golden/TestParitySubagentOrient/garbage-input.golden +0 -1
  760. package/engine/testdata/golden/TestParitySubagentOrient/non-devrites-agent.golden +0 -1
  761. package/engine/testdata/golden/TestParityTestIntegrity/assertion-dropped.golden +0 -1
  762. package/engine/testdata/golden/TestParityTestIntegrity/clean.golden +0 -2
  763. package/engine/testdata/golden/TestParityTestIntegrity/deleted.golden +0 -1
  764. package/engine/testdata/golden/TestParityTestIntegrity/ghost-workspace.golden +0 -1
  765. package/engine/testdata/golden/TestParityTestIntegrity/not-git.golden +0 -1
  766. package/engine/testdata/golden/TestParityTestIntegrity/skip-added.golden +0 -1
  767. package/engine/testdata/golden/TestParityTickAfk/budget1/state.md.golden +0 -1
  768. package/engine/testdata/golden/TestParityTickAfk/budget1.golden +0 -2
  769. package/engine/testdata/golden/TestParityTickAfk/budget3/state.md.golden +0 -5
  770. package/engine/testdata/golden/TestParityTickAfk/budget3.golden +0 -2
  771. package/engine/testdata/golden/TestParityTickAfk/bulletPipe/state.md.golden +0 -1
  772. package/engine/testdata/golden/TestParityTickAfk/bulletPipe.golden +0 -2
  773. package/engine/testdata/golden/TestParityTickAfk/missingField/state.md.golden +0 -3
  774. package/engine/testdata/golden/TestParityTickAfk/missingField.golden +0 -2
  775. package/engine/testdata/golden/TestParityTickAfk/missingFile.golden +0 -1
  776. package/engine/testdata/golden/TestParityTickAfk/nonNumeric/state.md.golden +0 -1
  777. package/engine/testdata/golden/TestParityTickAfk/nonNumeric.golden +0 -1
  778. package/engine/testdata/golden/TestParityTickAfk/none/state.md.golden +0 -1
  779. package/engine/testdata/golden/TestParityTickAfk/none.golden +0 -2
  780. package/engine/testdata/golden/TestParityWrightScope/agent-required-non-wright.golden +0 -1
  781. package/engine/testdata/golden/TestParityWrightScope/devrites-edit-denied.golden +0 -2
  782. package/engine/testdata/golden/TestParityWrightScope/in-scope-allowed.golden +0 -1
  783. package/engine/testdata/golden/TestParityWrightScope/non-edit-tool.golden +0 -1
  784. package/engine/testdata/golden/TestParityWrightScope/out-of-scope-enforce-denies.golden +0 -2
  785. package/engine/testdata/golden/TestParityWrightScope/out-of-scope-observe-silent.golden +0 -1
  786. package/engine/testdata/golden/TestSpecSkeleton/missing-some.golden +0 -2
  787. package/engine/testdata/golden/TestSpecSkeleton/present-all.golden +0 -2
  788. package/engine/testdata/golden/TestStuckLogHashParity.golden +0 -1
  789. package/engine/tests/adr_0006_clock_seam_test.go +0 -36
  790. package/engine/tests/budget_test.go +0 -88
  791. package/engine/tests/canonical_ac_ids_test.go +0 -44
  792. package/engine/tests/concurrency_cli_test.go +0 -131
  793. package/engine/tests/conventions_test.go +0 -106
  794. package/engine/tests/doctor_cli_test.go +0 -223
  795. package/engine/tests/forge_cli_test.go +0 -463
  796. package/engine/tests/hook_test.go +0 -2778
  797. package/engine/tests/hooks_io_test.go +0 -309
  798. package/engine/tests/json_contract_test.go +0 -224
  799. package/engine/tests/lib_parity_test.go +0 -930
  800. package/engine/tests/migrate_cli_test.go +0 -195
  801. package/engine/tests/parity_analyze_test.go +0 -55
  802. package/engine/tests/parity_buildreadiness_test.go +0 -199
  803. package/engine/tests/parity_learnings_test.go +0 -89
  804. package/engine/tests/parity_mutationgate_test.go +0 -36
  805. package/engine/tests/parity_reconcile_test.go +0 -94
  806. package/engine/tests/parity_testintegrity_test.go +0 -65
  807. package/engine/tests/parity_tickafk_test.go +0 -83
  808. package/engine/validatepack.go +0 -192
  809. package/engine/validatepack_test.go +0 -79
  810. package/engine/validatepackskills.go +0 -181
  811. package/engine/validatepackskills_test.go +0 -171
  812. package/pack/.claude/agents/devrites-forge-judge.md +0 -118
  813. package/pack/.claude/skills/devrites-lib/reference/model-tiers.md +0 -44
  814. package/pack/.claude/skills/devrites-refresh-indexes/SKILL.md +0 -52
  815. package/pack/.claude/skills/rite-build/reference/evidence-standard.md +0 -45
  816. package/pack/.claude/skills/rite-build/reference/forge.md +0 -210
  817. package/pack/.claude/skills/rite-seal/reference/conventions-ledger.md +0 -60
  818. package/pack/.claude/skills/rite-ship/reference/adr-promotion.md +0 -9
  819. package/pack/.claude/skills/rite-ship/reference/ledger.md +0 -56
  820. package/pack/generated/claude/agents/devrites-forge-judge.md +0 -118
  821. package/pack/generated/claude/skills/devrites-lib/reference/model-tiers.md +0 -44
  822. package/pack/generated/claude/skills/devrites-refresh-indexes/SKILL.md +0 -52
  823. package/pack/generated/claude/skills/rite-build/reference/evidence-standard.md +0 -45
  824. package/pack/generated/claude/skills/rite-build/reference/forge.md +0 -210
  825. package/pack/generated/claude/skills/rite-seal/reference/conventions-ledger.md +0 -60
  826. package/pack/generated/claude/skills/rite-ship/reference/adr-promotion.md +0 -9
  827. package/pack/generated/claude/skills/rite-ship/reference/ledger.md +0 -56
  828. package/pack/generated/codex/agents/devrites-forge-judge.toml +0 -128
  829. package/pack/generated/codex/hooks.json +0 -185
  830. package/pack/generated/codex/skills/devrites-api-interface/agents/openai.yaml +0 -2
  831. package/pack/generated/codex/skills/devrites-audit/agents/openai.yaml +0 -2
  832. package/pack/generated/codex/skills/devrites-browser-proof/agents/openai.yaml +0 -2
  833. package/pack/generated/codex/skills/devrites-debug-recovery/agents/openai.yaml +0 -2
  834. package/pack/generated/codex/skills/devrites-doubt/agents/openai.yaml +0 -2
  835. package/pack/generated/codex/skills/devrites-frontend-craft/agents/openai.yaml +0 -2
  836. package/pack/generated/codex/skills/devrites-interview/agents/openai.yaml +0 -2
  837. package/pack/generated/codex/skills/devrites-lib/reference/model-tiers.md +0 -44
  838. package/pack/generated/codex/skills/devrites-prose-craft/agents/openai.yaml +0 -2
  839. package/pack/generated/codex/skills/devrites-refresh-indexes/SKILL.md +0 -71
  840. package/pack/generated/codex/skills/devrites-refresh-indexes/agents/openai.yaml +0 -2
  841. package/pack/generated/codex/skills/devrites-source-driven/agents/openai.yaml +0 -2
  842. package/pack/generated/codex/skills/devrites-ux-shape/agents/openai.yaml +0 -2
  843. package/pack/generated/codex/skills/rite-build/reference/evidence-standard.md +0 -45
  844. package/pack/generated/codex/skills/rite-build/reference/forge.md +0 -210
  845. package/pack/generated/codex/skills/rite-seal/reference/conventions-ledger.md +0 -60
  846. package/pack/generated/codex/skills/rite-ship/reference/adr-promotion.md +0 -9
  847. package/pack/generated/codex/skills/rite-ship/reference/ledger.md +0 -56
  848. package/scripts/build-binaries.sh +0 -75
  849. package/scripts/build-release-tarball.sh +0 -105
  850. package/scripts/check-authority-drift.py +0 -125
  851. package/scripts/check-cross-refs.py +0 -139
  852. package/scripts/check-generated-skill-budget.mjs +0 -42
  853. package/scripts/check-instruction-size-baseline.mjs +0 -88
  854. package/scripts/check-invocation-integrity.py +0 -115
  855. package/scripts/check-no-global-writes.sh +0 -66
  856. package/scripts/check-no-personal-paths.py +0 -50
  857. package/scripts/check-npm-audit.mjs +0 -110
  858. package/scripts/check-reference-governance.mjs +0 -83
  859. package/scripts/check-reply-contract.sh +0 -64
  860. package/scripts/check-rule-uniqueness.sh +0 -45
  861. package/scripts/devrites-detect.sh +0 -176
  862. package/scripts/grade-feature.sh +0 -204
  863. package/scripts/live-hosts/agent-result.schema.json +0 -231
  864. package/scripts/live-hosts/claude.sh +0 -87
  865. package/scripts/live-hosts/codex.sh +0 -85
  866. package/scripts/live-hosts/common.sh +0 -113
  867. package/scripts/live-hosts/fake-host.py +0 -257
  868. package/scripts/live-hosts/host-transport.py +0 -286
  869. package/scripts/npm-audit-exceptions.json +0 -26
  870. package/scripts/pin.sh +0 -249
  871. package/scripts/release-check.sh +0 -75
  872. package/scripts/run-agent-contract-evals.py +0 -1380
  873. package/scripts/run-behavioral-evals.sh +0 -196
  874. package/scripts/run-evals.sh +0 -132
  875. package/scripts/run-live-behavioral-evals.py +0 -1310
  876. package/scripts/run-outcome-evals.sh +0 -454
  877. package/scripts/run-routing-evals.py +0 -342
  878. package/scripts/run-tests.mjs +0 -232
  879. package/scripts/scan-pack-security.py +0 -209
  880. package/scripts/scan-supply-chain-iocs.py +0 -127
  881. package/scripts/skill-pruning-audit.mjs +0 -121
  882. package/scripts/skills-inventory.mjs +0 -125
  883. package/scripts/supply-chain-iocs.json +0 -11
  884. package/scripts/sync-version.sh +0 -54
  885. package/scripts/validate-agent-composition.py +0 -57
  886. package/scripts/validate-command-parity.py +0 -70
  887. package/scripts/validate-frontmatter.py +0 -218
  888. package/scripts/validate-skill-anatomy.py +0 -139
  889. package/scripts/validate-workflow-security.py +0 -184
  890. package/scripts/validate-workspace-schema.py +0 -800
  891. package/scripts/validate.sh +0 -354
  892. package/scripts/workflow_schema.py +0 -69
  893. /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/auth-tokens/decisions.md +0 -0
  894. /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/auth-tokens/plan.md +0 -0
  895. /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/auth-tokens/spec.md +0 -0
  896. /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/auth-tokens/tasks.md +0 -0
  897. /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/search-ranking/spec.md +0 -0
@@ -1,1726 +0,0 @@
1
- package main
2
-
3
- import (
4
- "bufio"
5
- "bytes"
6
- "crypto/sha256"
7
- "encoding/hex"
8
- "encoding/json"
9
- "fmt"
10
- "io"
11
- "io/fs"
12
- "os"
13
- "path/filepath"
14
- "regexp"
15
- "sort"
16
- "strconv"
17
- "strings"
18
- "time"
19
-
20
- "github.com/devrites/devrites/internal/harness"
21
- "github.com/devrites/devrites/internal/lib"
22
- "github.com/devrites/devrites/internal/orient"
23
- "github.com/devrites/devrites/internal/safepath"
24
- )
25
-
26
- const (
27
- agentDispatchStateVersion = "devrites-agent-dispatch/v1"
28
- agentDispatchStateDir = "devrites-agent-dispatch-v1"
29
- agentDispatchMetadataRole = "devrites-skill-contract-error"
30
- agentDispatchSkillGuard = "devrites-skill-dispatch-guard"
31
- maxCodexRolloutLine = 8 << 20
32
- v2HiddenAgentTypeRule = "GPT-5.6 V2 may omit agent_type from the visible tool schema while the runtime accepts it. Schema omission alone is not capability loss, V1 detection, or HITL: send agent_type anyway. Use a generic child only after the runtime explicitly identifies MultiAgent V1. If the named V2 call rejects agent_type, stop before any generic/default spawn; never use a default child."
33
- )
34
-
35
- var (
36
- devritesAgentPathRe = regexp.MustCompile(`\.codex/agents/(devrites-[a-z0-9-]+)\.toml`)
37
- devritesAgentNameRe = regexp.MustCompile(`^devrites-[a-z0-9-]+$`)
38
- skillInvocationRe = regexp.MustCompile(`(?i)(?:^|[^a-z0-9_./-])(?:\$|/)([a-z][a-z0-9-]*)\b`)
39
- reconcileCloseRe = regexp.MustCompile(`(?m)(?:^|[;&|]\s*)(?:rtk\s+)?(?:[A-Za-z0-9_./-]+/)?devrites-engine\s+reconcile\s+close\b`)
40
- dispatchWaiveRe = regexp.MustCompile(`^\s*(?:rtk\s+)?(?:[A-Za-z0-9_./-]+/)?devrites-engine\s+dispatch-waive\s+([a-z-]+)\s*$`)
41
- )
42
-
43
- type agentDispatchHookInput struct {
44
- HookEventName string `json:"hook_event_name"`
45
- SessionID string `json:"session_id"`
46
- TurnID string `json:"turn_id"`
47
- ToolName string `json:"tool_name"`
48
- ToolUseID string `json:"tool_use_id"`
49
- AgentType string `json:"agent_type"`
50
- AgentID string `json:"agent_id"`
51
- Prompt string `json:"prompt"`
52
- StopHookActive bool `json:"stop_hook_active"`
53
- LastAssistantMessage string `json:"last_assistant_message"`
54
- ToolResponse string `json:"-"`
55
- ToolInputRaw json.RawMessage `json:"-"`
56
- ToolInput struct {
57
- Command string `json:"command"`
58
- Cmd string `json:"cmd"`
59
- AgentType string `json:"agent_type"`
60
- ForkTurns string `json:"fork_turns"`
61
- Message string `json:"message"`
62
- TaskName string `json:"task_name"`
63
- ReceiverThreadIDs []string `json:"receiver_thread_ids"`
64
- IDs []string `json:"ids"`
65
- AgentIDs []string `json:"agent_ids"`
66
- Target string `json:"target"`
67
- TimeoutMS int `json:"timeout_ms"`
68
- } `json:"tool_input"`
69
- }
70
-
71
- type agentDispatchEvent struct {
72
- Version string `json:"version"`
73
- Event string `json:"event"`
74
- TurnID string `json:"turn_id"`
75
- ToolUseID string `json:"tool_use_id,omitempty"`
76
- AgentID string `json:"agent_id,omitempty"`
77
- AgentType string `json:"agent_type,omitempty"`
78
- Role string `json:"role,omitempty"`
79
- WindowID string `json:"window_id,omitempty"`
80
- ResultSHA256 string `json:"result_sha256,omitempty"`
81
- Reason string `json:"reason,omitempty"`
82
- AtUnixNano int64 `json:"at_unix_nano"`
83
- }
84
-
85
- type agentDispatchAttempt struct {
86
- Role string
87
- AgentType string
88
- ToolUseID string
89
- AgentID string
90
- WindowID string
91
- Started bool
92
- Stopped bool
93
- Waited bool
94
- ResultSHA256 string
95
- }
96
-
97
- type codexRolloutRecord struct {
98
- Timestamp string `json:"timestamp"`
99
- Type string `json:"type"`
100
- Payload struct {
101
- Type string `json:"type"`
102
- SessionID string `json:"session_id"`
103
- ID string `json:"id"`
104
- ParentThreadID string `json:"parent_thread_id"`
105
- CWD string `json:"cwd"`
106
- AgentPath string `json:"agent_path"`
107
- AgentRole string `json:"agent_role"`
108
- Name string `json:"name"`
109
- Arguments string `json:"arguments"`
110
- Author string `json:"author"`
111
- Recipient string `json:"recipient"`
112
- Role string `json:"role"`
113
- LastAgentResult string `json:"last_agent_message"`
114
- Content []struct {
115
- Type string `json:"type"`
116
- Text string `json:"text"`
117
- } `json:"content"`
118
- Metadata struct {
119
- TurnID string `json:"turn_id"`
120
- } `json:"internal_chat_message_metadata_passthrough"`
121
- } `json:"payload"`
122
- }
123
-
124
- type codexV2Spawn struct {
125
- Role string
126
- AgentPath string
127
- SpawnLine int
128
- ResultLine int
129
- SpawnedAt time.Time
130
- Result string
131
- }
132
-
133
- func parseAgentDispatchHookInput(r io.Reader) (agentDispatchHookInput, error) {
134
- var in agentDispatchHookInput
135
- data, err := io.ReadAll(r)
136
- if err != nil {
137
- return in, err
138
- }
139
- if err := json.Unmarshal(data, &in); err != nil {
140
- return in, err
141
- }
142
- var raw struct {
143
- ToolInput json.RawMessage `json:"tool_input"`
144
- ToolResponse json.RawMessage `json:"tool_response"`
145
- }
146
- if err := json.Unmarshal(data, &raw); err != nil {
147
- return in, err
148
- }
149
- in.ToolInputRaw = raw.ToolInput
150
- if len(raw.ToolResponse) > 0 {
151
- if raw.ToolResponse[0] == '"' {
152
- _ = json.Unmarshal(raw.ToolResponse, &in.ToolResponse)
153
- } else {
154
- in.ToolResponse = string(raw.ToolResponse)
155
- }
156
- }
157
- in.HookEventName = strings.TrimSpace(in.HookEventName)
158
- in.SessionID = strings.TrimSpace(in.SessionID)
159
- in.TurnID = strings.TrimSpace(in.TurnID)
160
- in.ToolName = strings.TrimSpace(in.ToolName)
161
- in.ToolUseID = strings.TrimSpace(in.ToolUseID)
162
- in.AgentType = strings.TrimSpace(in.AgentType)
163
- in.AgentID = strings.TrimSpace(in.AgentID)
164
- in.ToolInput.AgentType = strings.TrimSpace(in.ToolInput.AgentType)
165
- in.ToolInput.ForkTurns = strings.TrimSpace(in.ToolInput.ForkTurns)
166
- in.ToolInput.TaskName = strings.TrimSpace(in.ToolInput.TaskName)
167
- return in, nil
168
- }
169
-
170
- func agentDispatchRoot() (string, bool) {
171
- root, err := orient.ResolveRoot(os.Getenv("DEVRITES_ROOT"))
172
- if err != nil {
173
- return "", false
174
- }
175
- return root, true
176
- }
177
-
178
- func agentDispatchLogPath(root, sessionID string) (string, error) {
179
- if strings.TrimSpace(sessionID) == "" {
180
- return "", fmt.Errorf("missing session_id")
181
- }
182
- rootSum := sha256.Sum256([]byte(filepath.Clean(root)))
183
- sessionSum := sha256.Sum256([]byte(sessionID))
184
- dir := filepath.Join(os.TempDir(), agentDispatchStateDir, hex.EncodeToString(rootSum[:]))
185
- if err := os.MkdirAll(dir, 0o700); err != nil {
186
- return "", err
187
- }
188
- if err := os.Chmod(dir, 0o700); err != nil {
189
- return "", err
190
- }
191
- return filepath.Join(dir, hex.EncodeToString(sessionSum[:])+".jsonl"), nil
192
- }
193
-
194
- func appendAgentDispatchEvent(root, sessionID string, event agentDispatchEvent) error {
195
- path, err := agentDispatchLogPath(root, sessionID)
196
- if err != nil {
197
- return err
198
- }
199
- event.Version = agentDispatchStateVersion
200
- event.AtUnixNano = time.Now().UnixNano()
201
- data, err := json.Marshal(event)
202
- if err != nil {
203
- return err
204
- }
205
- data = append(data, '\n')
206
- f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o600)
207
- if err != nil {
208
- return err
209
- }
210
- defer f.Close()
211
- if err := f.Chmod(0o600); err != nil {
212
- return err
213
- }
214
- _, err = f.Write(data)
215
- return err
216
- }
217
-
218
- func readAgentDispatchEvents(root, sessionID string) ([]agentDispatchEvent, error) {
219
- path, err := agentDispatchLogPath(root, sessionID)
220
- if err != nil {
221
- return nil, err
222
- }
223
- data, err := os.ReadFile(path)
224
- if os.IsNotExist(err) {
225
- return nil, nil
226
- }
227
- if err != nil {
228
- return nil, err
229
- }
230
- var events []agentDispatchEvent
231
- for _, line := range bytes.Split(data, []byte{'\n'}) {
232
- if len(bytes.TrimSpace(line)) == 0 {
233
- continue
234
- }
235
- var event agentDispatchEvent
236
- if err := json.Unmarshal(line, &event); err != nil {
237
- return nil, fmt.Errorf("invalid agent dispatch state: %w", err)
238
- }
239
- if event.Version != agentDispatchStateVersion {
240
- return nil, fmt.Errorf("unsupported agent dispatch state %q", event.Version)
241
- }
242
- events = append(events, event)
243
- }
244
- return events, nil
245
- }
246
-
247
- func explicitRolesInText(text string) []string {
248
- matches := devritesAgentPathRe.FindAllStringSubmatch(text, -1)
249
- seen := map[string]struct{}{}
250
- var roles []string
251
- for _, match := range matches {
252
- role := match[1]
253
- if _, ok := seen[role]; ok {
254
- continue
255
- }
256
- seen[role] = struct{}{}
257
- roles = append(roles, role)
258
- }
259
- sort.Strings(roles)
260
- return roles
261
- }
262
-
263
- func requiredAgentRolesFromSkill(path string) ([]string, error) {
264
- info, err := os.Stat(path)
265
- if err != nil {
266
- return nil, err
267
- }
268
- if !info.Mode().IsRegular() {
269
- return nil, fmt.Errorf("skill contract is not a regular file")
270
- }
271
- if info.Size() > 256<<10 {
272
- return nil, fmt.Errorf("skill contract exceeds 256 KiB")
273
- }
274
- data, err := os.ReadFile(path)
275
- if err != nil {
276
- return nil, err
277
- }
278
- lines := strings.Split(strings.ReplaceAll(string(data), "\r\n", "\n"), "\n")
279
- if len(lines) == 0 || strings.TrimSpace(lines[0]) != "---" {
280
- return nil, fmt.Errorf("skill contract is missing frontmatter")
281
- }
282
- value := ""
283
- found := false
284
- closed := false
285
- for _, line := range lines[1:] {
286
- if strings.TrimSpace(line) == "---" {
287
- closed = true
288
- break
289
- }
290
- if !strings.HasPrefix(line, "required-agent-roles:") {
291
- continue
292
- }
293
- if found {
294
- return nil, fmt.Errorf("required-agent-roles is duplicated")
295
- }
296
- found = true
297
- value = strings.TrimSpace(strings.TrimPrefix(line, "required-agent-roles:"))
298
- }
299
- if !closed {
300
- return nil, fmt.Errorf("skill contract has unterminated frontmatter")
301
- }
302
- if !found {
303
- return nil, fmt.Errorf("skill contract is missing required-agent-roles")
304
- }
305
- value = strings.Trim(value, `"'`)
306
- if value == "none" {
307
- return nil, nil
308
- }
309
- if value == "" {
310
- return nil, fmt.Errorf("required-agent-roles is empty")
311
- }
312
- seen := map[string]struct{}{}
313
- var roles []string
314
- for _, item := range strings.Split(value, ",") {
315
- role := strings.TrimSpace(item)
316
- if !devritesAgentNameRe.MatchString(role) {
317
- return nil, fmt.Errorf("invalid required agent role %q", role)
318
- }
319
- if _, ok := seen[role]; ok {
320
- return nil, fmt.Errorf("duplicate required agent role %q", role)
321
- }
322
- seen[role] = struct{}{}
323
- roles = append(roles, role)
324
- }
325
- sort.Strings(roles)
326
- return roles, nil
327
- }
328
-
329
- func requiredRolesInPrompt(root, text string) ([]string, bool, error) {
330
- seen := map[string]struct{}{}
331
- skillInvoked := false
332
- skillsDir := filepath.Join(filepath.Dir(root), ".agents", "skills")
333
- for _, match := range skillInvocationRe.FindAllStringSubmatch(text, -1) {
334
- skill := strings.ToLower(match[1])
335
- path := filepath.Join(skillsDir, skill, "SKILL.md")
336
- if !safepath.WithinResolved(path, skillsDir) {
337
- return nil, false, fmt.Errorf("unsafe skill path for %s", skill)
338
- }
339
- roles, err := requiredAgentRolesFromSkill(path)
340
- if os.IsNotExist(err) {
341
- continue
342
- }
343
- if err != nil {
344
- return nil, false, fmt.Errorf("%s: %w", skill, err)
345
- }
346
- skillInvoked = true
347
- for _, role := range roles {
348
- seen[role] = struct{}{}
349
- }
350
- }
351
- roles := make([]string, 0, len(seen))
352
- for role := range seen {
353
- roles = append(roles, role)
354
- }
355
- sort.Strings(roles)
356
- return roles, skillInvoked, nil
357
- }
358
-
359
- func expectedAgentType(role string) string {
360
- if role == "devrites-slice-wright" {
361
- return "worker"
362
- }
363
- return "explorer"
364
- }
365
-
366
- func agentRoleDeveloperInstructions(root, role string) (string, error) {
367
- agentsDir := filepath.Join(filepath.Dir(root), ".codex", "agents")
368
- path := filepath.Join(agentsDir, role+".toml")
369
- if !safepath.WithinResolved(path, agentsDir) {
370
- return "", fmt.Errorf("unsafe role path")
371
- }
372
- info, err := os.Stat(path)
373
- if err != nil || !info.Mode().IsRegular() {
374
- return "", fmt.Errorf("role contract is not a regular file")
375
- }
376
- if info.Size() > 256<<10 {
377
- return "", fmt.Errorf("role contract exceeds 256 KiB")
378
- }
379
- data, err := os.ReadFile(path)
380
- if err != nil {
381
- return "", err
382
- }
383
- lines := strings.Split(strings.ReplaceAll(string(data), "\r\n", "\n"), "\n")
384
- wantName := `name = "` + role + `"`
385
- nameMatches := false
386
- start := -1
387
- for i, line := range lines {
388
- switch strings.TrimSpace(line) {
389
- case wantName:
390
- nameMatches = true
391
- case "developer_instructions = '''":
392
- start = i + 1
393
- }
394
- if start >= 0 {
395
- break
396
- }
397
- }
398
- if !nameMatches || start < 0 {
399
- return "", fmt.Errorf("role contract name or developer_instructions is invalid")
400
- }
401
- for i := start; i < len(lines); i++ {
402
- if strings.TrimSpace(lines[i]) != "'''" {
403
- continue
404
- }
405
- instructions := strings.TrimSpace(strings.Join(lines[start:i], "\n"))
406
- if instructions == "" {
407
- return "", fmt.Errorf("developer_instructions is empty")
408
- }
409
- return instructions, nil
410
- }
411
- return "", fmt.Errorf("developer_instructions is unterminated")
412
- }
413
-
414
- func currentReconcileWindowID() string {
415
- _, _, dir, ok := resolveWorkspace()
416
- if !ok {
417
- return ""
418
- }
419
- // The wright-start canonical fingerprint is captured by the spawn hook
420
- // before the child starts. It narrows the delta but does not change source
421
- // authorization.
422
- names := []string{".reconcile-base", ".reconcile-allowlist", ".reconcile-devrites"}
423
- h := sha256.New()
424
- for _, name := range names {
425
- data, err := os.ReadFile(filepath.Join(dir, name))
426
- if err != nil {
427
- return ""
428
- }
429
- _, _ = io.WriteString(h, name)
430
- _, _ = h.Write([]byte{0})
431
- _, _ = h.Write(data)
432
- _, _ = h.Write([]byte{0})
433
- }
434
- return hex.EncodeToString(h.Sum(nil))
435
- }
436
-
437
- func dispatchTurnState(events []agentDispatchEvent, turnID string) (map[string]struct{}, []*agentDispatchAttempt) {
438
- armed := map[string]struct{}{}
439
- attemptByTool := map[string]*agentDispatchAttempt{}
440
- attemptByAgent := map[string]*agentDispatchAttempt{}
441
- var attempts []*agentDispatchAttempt
442
- for _, event := range events {
443
- if event.TurnID != turnID {
444
- continue
445
- }
446
- switch event.Event {
447
- case "armed":
448
- armed[event.Role] = struct{}{}
449
- case "waived":
450
- delete(armed, event.Role)
451
- case "pending":
452
- armed[event.Role] = struct{}{}
453
- attempt := &agentDispatchAttempt{
454
- Role: event.Role,
455
- AgentType: event.AgentType,
456
- ToolUseID: event.ToolUseID,
457
- WindowID: event.WindowID,
458
- }
459
- attemptByTool[event.ToolUseID] = attempt
460
- attempts = append(attempts, attempt)
461
- case "started":
462
- attempt := attemptByTool[event.ToolUseID]
463
- if attempt == nil {
464
- attempt = &agentDispatchAttempt{
465
- Role: event.Role,
466
- AgentType: event.AgentType,
467
- ToolUseID: event.ToolUseID,
468
- WindowID: event.WindowID,
469
- }
470
- attempts = append(attempts, attempt)
471
- }
472
- attempt.Started = true
473
- attempt.AgentID = event.AgentID
474
- attemptByAgent[event.AgentID] = attempt
475
- case "waited":
476
- if attempt := attemptByAgent[event.AgentID]; attempt != nil {
477
- attempt.Waited = true
478
- }
479
- case "stopped":
480
- if attempt := attemptByAgent[event.AgentID]; attempt != nil {
481
- attempt.Stopped = true
482
- attempt.ResultSHA256 = event.ResultSHA256
483
- }
484
- case "verified":
485
- attempts = append(attempts, &agentDispatchAttempt{
486
- Role: event.Role,
487
- AgentType: event.AgentType,
488
- AgentID: event.AgentID,
489
- WindowID: event.WindowID,
490
- Started: true,
491
- Stopped: true,
492
- Waited: true,
493
- ResultSHA256: event.ResultSHA256,
494
- })
495
- }
496
- }
497
- return armed, attempts
498
- }
499
-
500
- func durableCodexV2DispatchAttempts(
501
- root, sessionID, turnID string,
502
- armed map[string]struct{},
503
- ) ([]*agentDispatchAttempt, error) {
504
- parentPath, sessionsDir, err := findCodexParentRollout(sessionID)
505
- if err != nil || parentPath == "" {
506
- return nil, err
507
- }
508
- projectRoot := filepath.Dir(root)
509
- spawns, waitLines, err := readCodexV2ParentRollout(parentPath, projectRoot, sessionID, turnID, armed)
510
- if err != nil {
511
- return nil, err
512
- }
513
- var attempts []*agentDispatchAttempt
514
- for _, spawn := range spawns {
515
- role := spawn.Role
516
- var childPath string
517
- var childMeta codexRolloutRecord
518
- if strings.TrimSpace(spawn.Result) != "" {
519
- childPath, childMeta, err = findCodexV2ChildRollout(
520
- sessionsDir, parentPath, projectRoot, sessionID, spawn.AgentPath, "",
521
- )
522
- if err != nil {
523
- return nil, err
524
- }
525
- if childPath != "" {
526
- role = childMeta.Payload.AgentRole
527
- }
528
- }
529
- if !devritesAgentNameRe.MatchString(role) || role == agentDispatchSkillGuard {
530
- if strings.TrimSpace(spawn.Result) != "" {
531
- return nil, fmt.Errorf("codex V2 completed a default or non-DevRites child during a DevRites skill turn; this turn is tainted and cannot be repaired by another dispatch. Do not use the child result or advance workflow state. End this turn, then retry the skill in a fresh turn; %s", conditionalDispatchInstruction())
532
- }
533
- continue
534
- }
535
- armed[role] = struct{}{}
536
- attempt := &agentDispatchAttempt{
537
- Role: role,
538
- AgentType: role,
539
- Started: true,
540
- }
541
- attempts = append(attempts, attempt)
542
- if strings.TrimSpace(spawn.Result) == "" || !lineBetween(waitLines, spawn.SpawnLine, spawn.ResultLine) {
543
- continue
544
- }
545
- if childPath == "" || childMeta.Payload.AgentRole != role {
546
- continue
547
- }
548
- instructions, err := agentRoleDeveloperInstructions(root, role)
549
- if err != nil {
550
- return nil, fmt.Errorf("named role %s is not loadable: %w", role, err)
551
- }
552
- childResult, err := codexChildResult(childPath, instructions)
553
- if err != nil {
554
- return nil, err
555
- }
556
- if childMeta.Payload.ID == "" || strings.TrimSpace(childResult) == "" {
557
- continue
558
- }
559
- attempt.AgentID = childMeta.Payload.ID
560
- attempt.Stopped = true
561
- attempt.Waited = true
562
- if role == "devrites-slice-wright" {
563
- attempt.WindowID = currentReconcileWindowID()
564
- if attempt.WindowID == "" || !reconcileWindowPredates(spawn.SpawnedAt) {
565
- continue
566
- }
567
- }
568
- sum := sha256.Sum256([]byte(childResult))
569
- attempt.ResultSHA256 = hex.EncodeToString(sum[:])
570
- }
571
- return attempts, nil
572
- }
573
-
574
- func findCodexParentRollout(sessionID string) (string, string, error) {
575
- codeHome := strings.TrimSpace(os.Getenv("CODEX_HOME"))
576
- if codeHome == "" {
577
- home, err := os.UserHomeDir()
578
- if err != nil {
579
- return "", "", nil
580
- }
581
- codeHome = filepath.Join(home, ".codex")
582
- }
583
- sessionsDir := filepath.Join(codeHome, "sessions")
584
- wantSuffix := "-" + sessionID + ".jsonl"
585
- var parentPath string
586
- err := filepath.WalkDir(sessionsDir, func(path string, entry fs.DirEntry, walkErr error) error {
587
- if walkErr != nil {
588
- if os.IsNotExist(walkErr) {
589
- return nil
590
- }
591
- return walkErr
592
- }
593
- if entry.IsDir() || !strings.HasSuffix(entry.Name(), wantSuffix) {
594
- return nil
595
- }
596
- parentPath = path
597
- return fs.SkipAll
598
- })
599
- if os.IsNotExist(err) {
600
- return "", sessionsDir, nil
601
- }
602
- return parentPath, sessionsDir, err
603
- }
604
-
605
- func readCodexV2ParentRollout(
606
- path, projectRoot, sessionID, turnID string,
607
- armed map[string]struct{},
608
- ) (map[string]*codexV2Spawn, []int, error) {
609
- file, err := os.Open(path)
610
- if err != nil {
611
- return nil, nil, err
612
- }
613
- defer file.Close()
614
-
615
- spawns := map[string]*codexV2Spawn{}
616
- var waitLines []int
617
- metaSeen := false
618
- _, guarded := armed[agentDispatchSkillGuard]
619
- scanner := bufio.NewScanner(file)
620
- scanner.Buffer(make([]byte, 64<<10), maxCodexRolloutLine)
621
- for lineNumber := 1; scanner.Scan(); lineNumber++ {
622
- line := scanner.Bytes()
623
- if !bytes.Contains(line, []byte(`"session_meta"`)) &&
624
- !bytes.Contains(line, []byte(`"spawn_agent"`)) &&
625
- !bytes.Contains(line, []byte(`"wait_agent"`)) &&
626
- !bytes.Contains(line, []byte(`"agent_message"`)) {
627
- continue
628
- }
629
- var record codexRolloutRecord
630
- if err := json.Unmarshal(line, &record); err != nil {
631
- return nil, nil, fmt.Errorf("invalid Codex parent rollout: %w", err)
632
- }
633
- if record.Type == "session_meta" {
634
- if record.Payload.ID != sessionID || !sameResolvedPath(record.Payload.CWD, projectRoot) {
635
- return nil, nil, fmt.Errorf("codex parent rollout does not match the current session root")
636
- }
637
- metaSeen = true
638
- continue
639
- }
640
- if record.Payload.Metadata.TurnID != turnID {
641
- continue
642
- }
643
- switch {
644
- case record.Payload.Type == "function_call" && record.Payload.Name == "spawn_agent":
645
- var args struct {
646
- AgentType string `json:"agent_type"`
647
- TaskName string `json:"task_name"`
648
- ForkTurns string `json:"fork_turns"`
649
- }
650
- if json.Unmarshal([]byte(record.Payload.Arguments), &args) != nil {
651
- continue
652
- }
653
- _, required := armed[args.AgentType]
654
- if (!required && !guarded) || args.TaskName == "" ||
655
- strings.Contains(args.TaskName, "/") || args.ForkTurns != "none" {
656
- continue
657
- }
658
- spawnedAt, err := time.Parse(time.RFC3339Nano, record.Timestamp)
659
- if err != nil {
660
- return nil, nil, fmt.Errorf("codex spawn timestamp is invalid: %w", err)
661
- }
662
- spawns["/root/"+args.TaskName] = &codexV2Spawn{
663
- Role: args.AgentType,
664
- AgentPath: "/root/" + args.TaskName,
665
- SpawnLine: lineNumber,
666
- SpawnedAt: spawnedAt,
667
- }
668
- case record.Payload.Type == "function_call" && record.Payload.Name == "wait_agent":
669
- waitLines = append(waitLines, lineNumber)
670
- case record.Payload.Type == "agent_message" &&
671
- record.Payload.Recipient == "/root":
672
- spawn := spawns[record.Payload.Author]
673
- if spawn == nil {
674
- continue
675
- }
676
- spawn.Result = rolloutInputText(record.Payload.Content)
677
- spawn.ResultLine = lineNumber
678
- }
679
- }
680
- if err := scanner.Err(); err != nil {
681
- return nil, nil, fmt.Errorf("read Codex parent rollout: %w", err)
682
- }
683
- if !metaSeen {
684
- return nil, nil, fmt.Errorf("codex parent rollout is missing session metadata")
685
- }
686
- return spawns, waitLines, nil
687
- }
688
-
689
- func findCodexV2ChildRollout(
690
- sessionsDir, parentPath, projectRoot, sessionID, agentPath, agentID string,
691
- ) (string, codexRolloutRecord, error) {
692
- var childPath string
693
- var childMeta codexRolloutRecord
694
- selector := agentPath
695
- if selector == "" {
696
- selector = agentID
697
- }
698
- err := filepath.WalkDir(sessionsDir, func(path string, entry fs.DirEntry, walkErr error) error {
699
- if walkErr != nil {
700
- if os.IsNotExist(walkErr) {
701
- return nil
702
- }
703
- return walkErr
704
- }
705
- if entry.IsDir() || path == parentPath || filepath.Ext(path) != ".jsonl" {
706
- return nil
707
- }
708
- record, ok, err := firstCodexRolloutRecord(path)
709
- if err != nil || !ok {
710
- return err
711
- }
712
- if record.Type != "session_meta" ||
713
- record.Payload.ParentThreadID != sessionID ||
714
- (agentPath != "" && record.Payload.AgentPath != agentPath) ||
715
- (agentID != "" && record.Payload.ID != agentID) ||
716
- !sameResolvedPath(record.Payload.CWD, projectRoot) {
717
- return nil
718
- }
719
- if childPath != "" {
720
- return fmt.Errorf("multiple Codex child rollouts match %s", selector)
721
- }
722
- childPath = path
723
- childMeta = record
724
- return nil
725
- })
726
- return childPath, childMeta, err
727
- }
728
-
729
- func firstCodexRolloutRecord(path string) (codexRolloutRecord, bool, error) {
730
- var record codexRolloutRecord
731
- file, err := os.Open(path)
732
- if err != nil {
733
- return record, false, err
734
- }
735
- defer file.Close()
736
- scanner := bufio.NewScanner(file)
737
- scanner.Buffer(make([]byte, 64<<10), maxCodexRolloutLine)
738
- if !scanner.Scan() {
739
- return record, false, scanner.Err()
740
- }
741
- if err := json.Unmarshal(scanner.Bytes(), &record); err != nil {
742
- return record, false, nil
743
- }
744
- return record, true, nil
745
- }
746
-
747
- func codexChildResult(path, instructions string) (string, error) {
748
- file, err := os.Open(path)
749
- if err != nil {
750
- return "", err
751
- }
752
- defer file.Close()
753
- scanner := bufio.NewScanner(file)
754
- scanner.Buffer(make([]byte, 64<<10), maxCodexRolloutLine)
755
- instructionsLoaded := false
756
- result := ""
757
- for scanner.Scan() {
758
- line := scanner.Bytes()
759
- if !bytes.Contains(line, []byte(`"task_complete"`)) &&
760
- !bytes.Contains(line, []byte(`"developer"`)) {
761
- continue
762
- }
763
- var record codexRolloutRecord
764
- if json.Unmarshal(line, &record) != nil {
765
- continue
766
- }
767
- if record.Payload.Type == "message" && record.Payload.Role == "developer" &&
768
- strings.Contains(rolloutInputText(record.Payload.Content), instructions) {
769
- instructionsLoaded = true
770
- }
771
- if record.Payload.Type == "task_complete" &&
772
- strings.TrimSpace(record.Payload.LastAgentResult) != "" {
773
- result = record.Payload.LastAgentResult
774
- }
775
- }
776
- if err := scanner.Err(); err != nil {
777
- return "", err
778
- }
779
- if !instructionsLoaded {
780
- return "", nil
781
- }
782
- return result, nil
783
- }
784
-
785
- func rolloutInputText(content []struct {
786
- Type string `json:"type"`
787
- Text string `json:"text"`
788
- }) string {
789
- var text []string
790
- for _, item := range content {
791
- if item.Type == "input_text" && strings.TrimSpace(item.Text) != "" {
792
- text = append(text, item.Text)
793
- }
794
- }
795
- return strings.Join(text, "\n")
796
- }
797
-
798
- func lineBetween(lines []int, after, before int) bool {
799
- for _, line := range lines {
800
- if line > after && line < before {
801
- return true
802
- }
803
- }
804
- return false
805
- }
806
-
807
- func sameResolvedPath(left, right string) bool {
808
- resolve := func(path string) string {
809
- absolute, err := filepath.Abs(path)
810
- if err == nil {
811
- path = absolute
812
- }
813
- if resolved, err := filepath.EvalSymlinks(path); err == nil {
814
- path = resolved
815
- }
816
- return filepath.Clean(path)
817
- }
818
- return resolve(left) == resolve(right)
819
- }
820
-
821
- func reconcileWindowPredates(at time.Time) bool {
822
- _, _, dir, ok := resolveWorkspace()
823
- if !ok {
824
- return false
825
- }
826
- for _, name := range []string{".reconcile-base", ".reconcile-allowlist", ".reconcile-devrites"} {
827
- info, err := os.Stat(filepath.Join(dir, name))
828
- if err != nil || info.ModTime().After(at) {
829
- return false
830
- }
831
- }
832
- return true
833
- }
834
-
835
- func dispatchAttemptComplete(attempt *agentDispatchAttempt) bool {
836
- return attempt.Started && attempt.Stopped && attempt.Waited && attempt.ResultSHA256 != ""
837
- }
838
-
839
- func roleSatisfied(role, windowID string, attempts []*agentDispatchAttempt) bool {
840
- for _, attempt := range attempts {
841
- if attempt.Role != role || !dispatchAttemptComplete(attempt) {
842
- continue
843
- }
844
- if windowID == "" || attempt.WindowID == windowID {
845
- return true
846
- }
847
- }
848
- return false
849
- }
850
-
851
- func dispatchInstruction(role string) string {
852
- return fmt.Sprintf(
853
- "On MultiAgent V2 call spawn_agent with agent_type=%s, a unique task_name, and fork_turns=\"none\" so Codex loads .codex/agents/%s.toml natively. %s On V1 use agent_type=%s with fork_turns=\"none\" and name that role TOML in the message. Wait for the returned child and use its non-empty result. Do not call wait before spawn_agent and do not synthesize the agent result.",
854
- role, role, v2HiddenAgentTypeRule, expectedAgentType(role),
855
- )
856
- }
857
-
858
- func conditionalDispatchInstruction() string {
859
- return "CONDITIONAL DISPATCH RULE — If this skill reaches a child-agent step, call spawn_agent with the exact named agent_type=devrites-<role> specified by the skill, a unique task_name, and fork_turns=\"none\". " + v2HiddenAgentTypeRule + " Wait for the returned child and use its non-empty result."
860
- }
861
-
862
- func preDispatchStopInstruction() string {
863
- return "PRE-DISPATCH STOP RULE — If a deterministic prerequisite forces STOP before any spawn attempt, do not spawn only to satisfy this receipt. Run exactly one successful standalone devrites-engine dispatch-waive <reason> before replying. Use only the matching observed predicate: no-active-workspace, wrong-phase, readiness-failed, no-eligible-work, or human-gate-before-dispatch. The successful PostToolUse receipt is required; never waive after a spawn attempt."
864
- }
865
-
866
- func incompleteDispatchReason(armed map[string]struct{}, attempts []*agentDispatchAttempt) string {
867
- roles := make([]string, 0, len(armed))
868
- for role := range armed {
869
- if role == agentDispatchSkillGuard {
870
- continue
871
- }
872
- if !roleSatisfied(role, "", attempts) {
873
- roles = append(roles, role)
874
- }
875
- }
876
- if len(roles) == 0 {
877
- return ""
878
- }
879
- sort.Strings(roles)
880
- role := roles[0]
881
- if role == agentDispatchMetadataRole {
882
- return "DevRites could not determine this skill's required agents because its installed required-agent-roles contract is invalid. Reinstall or repair the DevRites skill pack before continuing."
883
- }
884
- reason := fmt.Sprintf("DevRites dispatch for %s is not complete. %s", role, dispatchInstruction(role))
885
- if len(attempts) == 0 {
886
- reason += " " + preDispatchStopInstruction()
887
- }
888
- return reason
889
- }
890
-
891
- func preToolDeny(h harness.Harness, reason string, stdout, stderr io.Writer) int {
892
- out, err := h.PreToolDeny(reason)
893
- if err != nil {
894
- debugf(stderr, "agent-dispatch: %v", err)
895
- return exitOK
896
- }
897
- fmt.Fprintln(stdout, out)
898
- return exitOK
899
- }
900
-
901
- func preToolRewrite(h harness.Harness, toolInput json.RawMessage, message string, stdout, stderr io.Writer) int {
902
- var updated map[string]any
903
- if err := json.Unmarshal(toolInput, &updated); err != nil {
904
- return preToolDeny(h, "DevRites could not bind developer_instructions to the spawn input.", stdout, stderr)
905
- }
906
- updated["message"] = message
907
- out, err := json.Marshal(map[string]any{
908
- "hookSpecificOutput": map[string]any{
909
- "hookEventName": "PreToolUse",
910
- "permissionDecision": "allow",
911
- "updatedInput": updated,
912
- },
913
- })
914
- if err != nil {
915
- return preToolDeny(h, "DevRites could not encode the bound spawn input.", stdout, stderr)
916
- }
917
- fmt.Fprintln(stdout, string(out))
918
- return exitOK
919
- }
920
-
921
- func stopDispatchBlock(h harness.Harness, reason string, stdout, stderr io.Writer) int {
922
- out, err := h.StopBlock(reason)
923
- if err != nil {
924
- debugf(stderr, "agent-dispatch: %v", err)
925
- return exitOK
926
- }
927
- fmt.Fprintln(stdout, out)
928
- return exitOK
929
- }
930
-
931
- func stopDispatchFailure(reason string, stdout, stderr io.Writer) int {
932
- out, err := json.Marshal(map[string]any{
933
- "continue": false,
934
- "stopReason": reason,
935
- "systemMessage": reason,
936
- })
937
- if err != nil {
938
- debugf(stderr, "agent-dispatch: %v", err)
939
- return exitOK
940
- }
941
- fmt.Fprintln(stdout, string(out))
942
- return exitOK
943
- }
944
-
945
- func refreshPendingWrightBoundary(
946
- root string,
947
- in agentDispatchHookInput,
948
- armed map[string]struct{},
949
- attempts []*agentDispatchAttempt,
950
- ) error {
951
- if in.AgentID != "" || in.AgentType != "" || currentReconcileWindowID() == "" {
952
- return nil
953
- }
954
- for _, attempt := range attempts {
955
- if attempt.Role == "devrites-slice-wright" {
956
- return nil
957
- }
958
- }
959
- boundaryArmed := make(map[string]struct{}, len(armed)+1)
960
- for role := range armed {
961
- boundaryArmed[role] = struct{}{}
962
- }
963
- boundaryArmed["devrites-slice-wright"] = struct{}{}
964
- durable, err := durableCodexV2DispatchAttempts(root, in.SessionID, in.TurnID, boundaryArmed)
965
- if err != nil {
966
- return err
967
- }
968
- for _, attempt := range durable {
969
- if attempt.Role == "devrites-slice-wright" {
970
- return nil
971
- }
972
- }
973
- workspaceRoot, slug, _, ok := resolveWorkspace()
974
- if !ok || !sameResolvedPath(workspaceRoot, root) {
975
- return fmt.Errorf("active reconcile window is unavailable")
976
- }
977
- return lib.CaptureReconcileWrightBoundary(root, slug)
978
- }
979
-
980
- func hookAgentDispatch(h harness.Harness, stdin io.Reader, stdout, stderr io.Writer) int {
981
- if h != harness.Codex {
982
- _, _ = io.Copy(io.Discard, stdin)
983
- return exitOK
984
- }
985
- in, err := parseAgentDispatchHookInput(stdin)
986
- if err != nil {
987
- return exitOK
988
- }
989
- root, ok := agentDispatchRoot()
990
- if !ok || in.SessionID == "" || in.TurnID == "" {
991
- return exitOK
992
- }
993
-
994
- switch in.HookEventName {
995
- case "UserPromptSubmit":
996
- roles, skillInvoked, rolesErr := requiredRolesInPrompt(root, in.Prompt)
997
- if rolesErr != nil {
998
- if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
999
- Event: "armed",
1000
- TurnID: in.TurnID,
1001
- Role: agentDispatchMetadataRole,
1002
- }); err != nil {
1003
- return stopDispatchBlock(h, "DevRites could not record the invalid agent-role contract: "+err.Error(), stdout, stderr)
1004
- }
1005
- return stopDispatchBlock(h, "DevRites could not read the required agent roles: "+rolesErr.Error(), stdout, stderr)
1006
- }
1007
- if skillInvoked {
1008
- if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
1009
- Event: "armed",
1010
- TurnID: in.TurnID,
1011
- Role: agentDispatchSkillGuard,
1012
- }); err != nil {
1013
- return stopDispatchBlock(h, "DevRites could not arm the skill dispatch guard: "+err.Error(), stdout, stderr)
1014
- }
1015
- }
1016
- for _, role := range roles {
1017
- if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
1018
- Event: "armed",
1019
- TurnID: in.TurnID,
1020
- Role: role,
1021
- }); err != nil {
1022
- return stopDispatchBlock(h, "DevRites could not arm the required agent dispatch: "+err.Error(), stdout, stderr)
1023
- }
1024
- }
1025
- events, stateErr := readAgentDispatchEvents(root, in.SessionID)
1026
- if stateErr == nil {
1027
- armed, attempts := dispatchTurnState(events, in.TurnID)
1028
- if err := refreshPendingWrightBoundary(root, in, armed, attempts); err != nil {
1029
- debugf(stderr, "agent-dispatch pending wright boundary: %v", err)
1030
- }
1031
- }
1032
- if len(roles) > 0 {
1033
- instructions := make([]string, 0, len(roles))
1034
- for _, role := range roles {
1035
- instructions = append(instructions, dispatchInstruction(role))
1036
- }
1037
- fmt.Fprintf(
1038
- stdout,
1039
- "MANDATORY DISPATCH THIS TURN — At the skill's dispatch step, execute every required child before finishing or claiming completion; a success phrase is not evidence.\n%s\n%s",
1040
- strings.Join(instructions, "\n"),
1041
- preDispatchStopInstruction(),
1042
- )
1043
- }
1044
- if skillInvoked {
1045
- if len(roles) > 0 {
1046
- fmt.Fprintln(stdout)
1047
- }
1048
- fmt.Fprint(stdout, conditionalDispatchInstruction())
1049
- }
1050
- return exitOK
1051
-
1052
- case "PreToolUse":
1053
- return hookAgentDispatchPreTool(h, root, in, stdout, stderr)
1054
-
1055
- case "PostToolUse":
1056
- events, err := readAgentDispatchEvents(root, in.SessionID)
1057
- if err != nil {
1058
- debugf(stderr, "agent-dispatch post-tool state: %v", err)
1059
- return exitOK
1060
- }
1061
- armed, attempts := dispatchTurnState(events, in.TurnID)
1062
- if reason, ok := dispatchWaiverReason(agentDispatchCommand(in)); ok &&
1063
- strings.Contains(in.ToolResponse, "dispatch-waive: accepted "+reason) {
1064
- if err := recordDispatchWaiver(root, in, reason, armed, attempts); err != nil {
1065
- debugf(stderr, "agent-dispatch post-tool waiver: %v", err)
1066
- }
1067
- }
1068
- if isWaitTool(in.ToolName) {
1069
- if waitToolSucceeded(in.ToolResponse) {
1070
- if err := recordCompletedWaits(root, in, attempts); err != nil {
1071
- debugf(stderr, "agent-dispatch post-tool wait: %v", err)
1072
- }
1073
- } else if err := recordCancelledWaits(root, in, attempts); err != nil {
1074
- debugf(stderr, "agent-dispatch post-tool wait cancellation: %v", err)
1075
- }
1076
- }
1077
- if err := refreshPendingWrightBoundary(root, in, armed, attempts); err != nil {
1078
- debugf(stderr, "agent-dispatch post-tool wright boundary: %v", err)
1079
- }
1080
- return exitOK
1081
-
1082
- case "SubagentStop":
1083
- if in.AgentID == "" {
1084
- return exitOK
1085
- }
1086
- attempt, found, lookupErr := boundDispatchAttempt(root, in.SessionID, in.AgentID)
1087
- if lookupErr != nil {
1088
- return stopDispatchBlock(h, "DevRites could not identify the stopped subagent: "+lookupErr.Error(), stdout, stderr)
1089
- }
1090
- if !found {
1091
- return exitOK
1092
- }
1093
- resultSum := ""
1094
- if strings.TrimSpace(in.LastAssistantMessage) != "" {
1095
- sum := sha256.Sum256([]byte(in.LastAssistantMessage))
1096
- resultSum = hex.EncodeToString(sum[:])
1097
- }
1098
- if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
1099
- Event: "stopped",
1100
- TurnID: attempt.TurnID,
1101
- AgentID: in.AgentID,
1102
- AgentType: attempt.AgentType,
1103
- ResultSHA256: resultSum,
1104
- }); err != nil {
1105
- return stopDispatchBlock(h, "DevRites could not record the subagent result: "+err.Error(), stdout, stderr)
1106
- }
1107
- if active, activeErr := activeWaitIntent(root, in.SessionID, attempt.TurnID, in.AgentID, time.Now()); activeErr != nil {
1108
- return stopDispatchBlock(h, "DevRites could not verify the awaited subagent result: "+activeErr.Error(), stdout, stderr)
1109
- } else if active {
1110
- if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
1111
- Event: "waited",
1112
- TurnID: attempt.TurnID,
1113
- AgentID: in.AgentID,
1114
- }); err != nil {
1115
- return stopDispatchBlock(h, "DevRites could not record the awaited subagent result: "+err.Error(), stdout, stderr)
1116
- }
1117
- }
1118
- return exitOK
1119
-
1120
- case "Stop":
1121
- events, err := readAgentDispatchEvents(root, in.SessionID)
1122
- if err != nil {
1123
- reason := "DevRites could not verify the required agent dispatch: " + err.Error()
1124
- if in.StopHookActive {
1125
- return stopDispatchFailure(reason, stdout, stderr)
1126
- }
1127
- return stopDispatchBlock(h, reason, stdout, stderr)
1128
- }
1129
- armed, attempts := dispatchTurnState(events, in.TurnID)
1130
- _, guarded := armed[agentDispatchSkillGuard]
1131
- if reason := incompleteDispatchReason(armed, attempts); reason != "" || guarded && len(attempts) == 0 {
1132
- durable, durableErr := durableCodexV2DispatchAttempts(
1133
- root, in.SessionID, in.TurnID, armed,
1134
- )
1135
- if durableErr != nil {
1136
- reason := "DevRites could not verify the Codex V2 agent dispatch: " + durableErr.Error()
1137
- if in.StopHookActive {
1138
- return stopDispatchFailure(reason, stdout, stderr)
1139
- }
1140
- return stopDispatchBlock(h, reason, stdout, stderr)
1141
- }
1142
- attempts = append(attempts, durable...)
1143
- }
1144
- if reason := incompleteDispatchReason(armed, attempts); reason != "" {
1145
- if in.StopHookActive {
1146
- return stopDispatchFailure(reason, stdout, stderr)
1147
- }
1148
- return stopDispatchBlock(h, reason, stdout, stderr)
1149
- }
1150
- return exitOK
1151
- }
1152
- return exitOK
1153
- }
1154
-
1155
- func hookAgentDispatchPreTool(h harness.Harness, root string, in agentDispatchHookInput, stdout, stderr io.Writer) int {
1156
- events, err := readAgentDispatchEvents(root, in.SessionID)
1157
- if err != nil {
1158
- return preToolDeny(h, "DevRites could not verify agent dispatch state: "+err.Error(), stdout, stderr)
1159
- }
1160
- armed, attempts := dispatchTurnState(events, in.TurnID)
1161
-
1162
- command := agentDispatchCommand(in)
1163
- if isShellTool(in.ToolName) && strings.Contains(command, "dispatch-waive") {
1164
- reason, ok := dispatchWaiverReason(command)
1165
- if !ok || !lib.ValidDispatchWaiver(reason) {
1166
- return preToolDeny(h, "DevRites dispatch waiver must be one exact standalone engine command with an approved reason.", stdout, stderr)
1167
- }
1168
- if len(attempts) != 0 {
1169
- return preToolDeny(h, "DevRites dispatch waiver is only valid before any specialist spawn attempt.", stdout, stderr)
1170
- }
1171
- required := 0
1172
- for role := range armed {
1173
- if strings.HasPrefix(role, "devrites-") && role != agentDispatchSkillGuard && role != agentDispatchMetadataRole {
1174
- required++
1175
- }
1176
- }
1177
- if required == 0 {
1178
- return preToolDeny(h, "DevRites dispatch waiver requires an armed mandatory role in this turn.", stdout, stderr)
1179
- }
1180
- return exitOK
1181
- }
1182
-
1183
- if isAgentDispatchTool(in.ToolName) {
1184
- agentType := in.ToolInput.AgentType
1185
- if agentType == "" {
1186
- roles := explicitRolesInText(in.ToolInput.Message + "\n" + in.ToolInput.TaskName)
1187
- if len(roles) == 1 {
1188
- return preToolDeny(h, "DevRites conditional dispatch requires the exact named specialist. "+dispatchInstruction(roles[0]), stdout, stderr)
1189
- }
1190
- if reason := incompleteDispatchReason(armed, attempts); reason != "" {
1191
- return preToolDeny(h, reason, stdout, stderr)
1192
- }
1193
- if _, guarded := armed[agentDispatchSkillGuard]; guarded {
1194
- return preToolDeny(h, conditionalDispatchInstruction(), stdout, stderr)
1195
- }
1196
- return exitOK
1197
- }
1198
- role := ""
1199
- if strings.HasPrefix(agentType, "devrites-") {
1200
- role = agentType
1201
- } else {
1202
- roles := explicitRolesInText(in.ToolInput.Message)
1203
- if len(roles) == 1 {
1204
- role = roles[0]
1205
- }
1206
- }
1207
- if role == "" {
1208
- if agentType == "explorer" || agentType == "worker" {
1209
- if reason := incompleteDispatchReason(armed, attempts); reason != "" {
1210
- return preToolDeny(h, reason, stdout, stderr)
1211
- }
1212
- if _, guarded := armed[agentDispatchSkillGuard]; !guarded {
1213
- return exitOK // Unrelated generic subagent in a DevRites repository.
1214
- }
1215
- return preToolDeny(h, "DevRites generic dispatch must name exactly one .codex/agents/devrites-<role>.toml contract.", stdout, stderr)
1216
- }
1217
- if reason := incompleteDispatchReason(armed, attempts); reason != "" {
1218
- return preToolDeny(h, reason, stdout, stderr)
1219
- }
1220
- if _, guarded := armed[agentDispatchSkillGuard]; guarded {
1221
- return preToolDeny(h, conditionalDispatchInstruction(), stdout, stderr)
1222
- }
1223
- return exitOK
1224
- }
1225
- instructions, contractErr := agentRoleDeveloperInstructions(root, role)
1226
- if contractErr != nil {
1227
- return preToolDeny(h, "DevRites role contract .codex/agents/"+role+".toml cannot be loaded: "+contractErr.Error()+".", stdout, stderr)
1228
- }
1229
- if in.ToolInput.ForkTurns != "none" {
1230
- return preToolDeny(h, "DevRites agents require fork_turns=\"none\" so the child starts in the role contract rather than inheriting the root type.", stdout, stderr)
1231
- }
1232
- if agentType == "worker" && role != "devrites-slice-wright" {
1233
- return preToolDeny(h, "Generic worker is reserved for devrites-slice-wright; use generic explorer for read-only DevRites roles.", stdout, stderr)
1234
- }
1235
- if agentType == "explorer" && role == "devrites-slice-wright" {
1236
- return preToolDeny(h, "devrites-slice-wright requires the generic worker identity and exact wright allowlist.", stdout, stderr)
1237
- }
1238
- if agentType != "" && agentType != role && agentType != expectedAgentType(role) {
1239
- return preToolDeny(h, fmt.Sprintf("DevRites role %s requires agent_type=%s or its exposed named role. %s", role, expectedAgentType(role), dispatchInstruction(role)), stdout, stderr)
1240
- }
1241
- if in.ToolUseID == "" {
1242
- return preToolDeny(h, "DevRites cannot bind a spawn without tool_use_id.", stdout, stderr)
1243
- }
1244
- windowID := currentReconcileWindowID()
1245
- if role == "devrites-slice-wright" && windowID == "" {
1246
- return preToolDeny(h, "devrites-slice-wright requires an active reconcile snapshot before spawn_agent.", stdout, stderr)
1247
- }
1248
- if role == "devrites-slice-wright" {
1249
- workspaceRoot, slug, _, ok := resolveWorkspace()
1250
- if !ok || !sameResolvedPath(workspaceRoot, root) {
1251
- return preToolDeny(h, "DevRites could not resolve the active wright reconcile window before spawn_agent.", stdout, stderr)
1252
- }
1253
- if err := lib.CaptureReconcileWrightBoundary(root, slug); err != nil {
1254
- return preToolDeny(h, "DevRites could not capture the wright canonical boundary before spawn_agent: "+err.Error(), stdout, stderr)
1255
- }
1256
- }
1257
- if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
1258
- Event: "pending",
1259
- TurnID: in.TurnID,
1260
- ToolUseID: in.ToolUseID,
1261
- AgentType: agentType,
1262
- Role: role,
1263
- WindowID: windowID,
1264
- }); err != nil {
1265
- return preToolDeny(h, "DevRites could not record the spawn attempt: "+err.Error(), stdout, stderr)
1266
- }
1267
- if agentType != role {
1268
- message := fmt.Sprintf(
1269
- "DevRites bound developer_instructions for `%s`:\n\n%s\n\n## Unchanged parent packet\n%s",
1270
- role, instructions, in.ToolInput.Message,
1271
- )
1272
- return preToolRewrite(h, in.ToolInputRaw, message, stdout, stderr)
1273
- }
1274
- return exitOK
1275
- }
1276
-
1277
- switch toolBaseName(in.ToolName) {
1278
- case "wait", "wait_agent":
1279
- if len(armed) == 0 {
1280
- return exitOK
1281
- }
1282
- var started []*agentDispatchAttempt
1283
- for _, attempt := range attempts {
1284
- if attempt.Started {
1285
- started = append(started, attempt)
1286
- }
1287
- }
1288
- if len(started) == 0 {
1289
- reason := incompleteDispatchReason(armed, attempts)
1290
- if reason == "" {
1291
- reason = conditionalDispatchInstruction()
1292
- }
1293
- return preToolDeny(h, reason, stdout, stderr)
1294
- }
1295
- targets := waitTargets(in)
1296
- if len(targets) > 0 {
1297
- known := map[string]*agentDispatchAttempt{}
1298
- for _, attempt := range started {
1299
- known[attempt.AgentID] = attempt
1300
- }
1301
- for _, agentID := range targets {
1302
- if known[agentID] == nil {
1303
- return preToolDeny(h, "DevRites wait target is not a confirmed child from this turn.", stdout, stderr)
1304
- }
1305
- }
1306
- }
1307
- if err := recordWaitIntents(root, in, started); err != nil {
1308
- return preToolDeny(h, "DevRites could not record the wait intent: "+err.Error(), stdout, stderr)
1309
- }
1310
- return exitOK
1311
- }
1312
-
1313
- if isShellTool(in.ToolName) && reconcileCloseRe.MatchString(command) {
1314
- windowID := currentReconcileWindowID()
1315
- if windowID == "" {
1316
- return exitOK
1317
- }
1318
- if !roleSatisfied("devrites-slice-wright", windowID, attempts) {
1319
- durable, durableErr := durableCodexV2DispatchAttempts(
1320
- root, in.SessionID, in.TurnID, armed,
1321
- )
1322
- if durableErr != nil {
1323
- return preToolDeny(h, "DevRites could not verify the Codex V2 wright result: "+durableErr.Error(), stdout, stderr)
1324
- }
1325
- attempts = append(attempts, durable...)
1326
- if !roleSatisfied("devrites-slice-wright", windowID, attempts) {
1327
- return preToolDeny(h, "DevRites reconcile close requires a confirmed, awaited devrites-slice-wright result bound to the active reconcile snapshot.", stdout, stderr)
1328
- }
1329
- for _, attempt := range durable {
1330
- if attempt.Role != "devrites-slice-wright" || attempt.WindowID != windowID ||
1331
- !dispatchAttemptComplete(attempt) {
1332
- continue
1333
- }
1334
- if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
1335
- Event: "verified",
1336
- TurnID: in.TurnID,
1337
- AgentID: attempt.AgentID,
1338
- AgentType: attempt.AgentType,
1339
- Role: attempt.Role,
1340
- WindowID: attempt.WindowID,
1341
- ResultSHA256: attempt.ResultSHA256,
1342
- }); err != nil {
1343
- return preToolDeny(h, "DevRites could not persist the verified Codex V2 wright result: "+err.Error(), stdout, stderr)
1344
- }
1345
- }
1346
- }
1347
- }
1348
- return exitOK
1349
- }
1350
-
1351
- func isWaitTool(toolName string) bool {
1352
- switch toolBaseName(toolName) {
1353
- case "wait", "wait_agent":
1354
- return true
1355
- default:
1356
- return false
1357
- }
1358
- }
1359
-
1360
- func agentDispatchCommand(in agentDispatchHookInput) string {
1361
- if in.ToolInput.Command != "" {
1362
- return in.ToolInput.Command
1363
- }
1364
- return in.ToolInput.Cmd
1365
- }
1366
-
1367
- func dispatchWaiverReason(command string) (string, bool) {
1368
- match := dispatchWaiveRe.FindStringSubmatch(command)
1369
- if len(match) != 2 {
1370
- return "", false
1371
- }
1372
- return match[1], true
1373
- }
1374
-
1375
- func recordDispatchWaiver(
1376
- root string,
1377
- in agentDispatchHookInput,
1378
- reason string,
1379
- armed map[string]struct{},
1380
- attempts []*agentDispatchAttempt,
1381
- ) error {
1382
- if len(attempts) != 0 || !lib.ValidDispatchWaiver(reason) {
1383
- return fmt.Errorf("waiver is not valid after a spawn attempt")
1384
- }
1385
- recorded := 0
1386
- for role := range armed {
1387
- if !strings.HasPrefix(role, "devrites-") || role == agentDispatchSkillGuard || role == agentDispatchMetadataRole {
1388
- continue
1389
- }
1390
- if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
1391
- Event: "waived",
1392
- TurnID: in.TurnID,
1393
- Role: role,
1394
- Reason: reason,
1395
- }); err != nil {
1396
- return err
1397
- }
1398
- recorded++
1399
- }
1400
- if recorded == 0 {
1401
- return fmt.Errorf("no armed mandatory role to waive")
1402
- }
1403
- return nil
1404
- }
1405
-
1406
- func waitTargets(in agentDispatchHookInput) []string {
1407
- if len(in.ToolInput.ReceiverThreadIDs) > 0 {
1408
- return in.ToolInput.ReceiverThreadIDs
1409
- }
1410
- if len(in.ToolInput.IDs) > 0 {
1411
- return in.ToolInput.IDs
1412
- }
1413
- return in.ToolInput.AgentIDs
1414
- }
1415
-
1416
- func waitToolSucceeded(response string) bool {
1417
- response = strings.ToLower(strings.TrimSpace(response))
1418
- if response == "" {
1419
- return false
1420
- }
1421
- for _, failure := range []string{
1422
- `"timed_out":true`,
1423
- `"timed_out": true`,
1424
- "timed out",
1425
- "timeout summary",
1426
- "no activity",
1427
- } {
1428
- if strings.Contains(response, failure) {
1429
- return false
1430
- }
1431
- }
1432
- return true
1433
- }
1434
-
1435
- func waitTargetIDs(in agentDispatchHookInput, attempts []*agentDispatchAttempt) []string {
1436
- targets := waitTargets(in)
1437
- if len(targets) > 0 {
1438
- return targets
1439
- }
1440
- targets = make([]string, 0, len(attempts))
1441
- for _, attempt := range attempts {
1442
- if attempt.Started && attempt.AgentID != "" {
1443
- targets = append(targets, attempt.AgentID)
1444
- }
1445
- }
1446
- return targets
1447
- }
1448
-
1449
- func agentWaitTimeout(in agentDispatchHookInput) time.Duration {
1450
- const defaultTimeout = 30 * time.Second
1451
- const maxTimeout = time.Hour
1452
- if in.ToolInput.TimeoutMS <= 0 {
1453
- return defaultTimeout
1454
- }
1455
- timeout := time.Duration(in.ToolInput.TimeoutMS) * time.Millisecond
1456
- if timeout > maxTimeout {
1457
- return maxTimeout
1458
- }
1459
- return timeout
1460
- }
1461
-
1462
- func recordWaitIntents(root string, in agentDispatchHookInput, attempts []*agentDispatchAttempt) error {
1463
- timeoutMS := strconv.FormatInt(agentWaitTimeout(in).Milliseconds(), 10)
1464
- for _, agentID := range waitTargetIDs(in, attempts) {
1465
- if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
1466
- Event: "waiting",
1467
- TurnID: in.TurnID,
1468
- AgentID: agentID,
1469
- Reason: timeoutMS,
1470
- }); err != nil {
1471
- return err
1472
- }
1473
- }
1474
- return nil
1475
- }
1476
-
1477
- func recordCancelledWaits(root string, in agentDispatchHookInput, attempts []*agentDispatchAttempt) error {
1478
- for _, agentID := range waitTargetIDs(in, attempts) {
1479
- if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
1480
- Event: "wait-cancelled",
1481
- TurnID: in.TurnID,
1482
- AgentID: agentID,
1483
- }); err != nil {
1484
- return err
1485
- }
1486
- }
1487
- return nil
1488
- }
1489
-
1490
- func activeWaitIntent(root, sessionID, turnID, agentID string, now time.Time) (bool, error) {
1491
- events, err := readAgentDispatchEvents(root, sessionID)
1492
- if err != nil {
1493
- return false, err
1494
- }
1495
- for i := len(events) - 1; i >= 0; i-- {
1496
- event := events[i]
1497
- if event.TurnID != turnID || event.AgentID != agentID {
1498
- continue
1499
- }
1500
- switch event.Event {
1501
- case "waited", "wait-cancelled":
1502
- return false, nil
1503
- case "waiting":
1504
- timeoutMS, err := strconv.ParseInt(event.Reason, 10, 64)
1505
- if err != nil || timeoutMS <= 0 {
1506
- return false, nil
1507
- }
1508
- deadline := time.Unix(0, event.AtUnixNano).Add(time.Duration(timeoutMS) * time.Millisecond)
1509
- return !now.After(deadline), nil
1510
- }
1511
- }
1512
- return false, nil
1513
- }
1514
-
1515
- func recordCompletedWaits(root string, in agentDispatchHookInput, attempts []*agentDispatchAttempt) error {
1516
- targetSet := map[string]struct{}{}
1517
- for _, agentID := range waitTargets(in) {
1518
- targetSet[agentID] = struct{}{}
1519
- }
1520
- for _, attempt := range attempts {
1521
- if !attempt.Started || attempt.Waited {
1522
- continue
1523
- }
1524
- if len(targetSet) > 0 {
1525
- if _, ok := targetSet[attempt.AgentID]; !ok {
1526
- continue
1527
- }
1528
- }
1529
- if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
1530
- Event: "waited",
1531
- TurnID: in.TurnID,
1532
- AgentID: attempt.AgentID,
1533
- }); err != nil {
1534
- return err
1535
- }
1536
- }
1537
- return nil
1538
- }
1539
-
1540
- func bindAgentDispatchStart(root string, in agentDispatchHookInput) (string, bool, error) {
1541
- events, err := readAgentDispatchEvents(root, in.SessionID)
1542
- if err != nil {
1543
- return "", false, err
1544
- }
1545
- started := map[string]struct{}{}
1546
- for _, event := range events {
1547
- if event.Event == "started" {
1548
- if event.AgentID == in.AgentID {
1549
- if event.AgentType != in.AgentType || event.Role == "" {
1550
- return "", false, fmt.Errorf("subagent %s is already bound to a different dispatch", in.AgentID)
1551
- }
1552
- return event.Role, true, nil
1553
- }
1554
- started[event.ToolUseID] = struct{}{}
1555
- }
1556
- }
1557
- var candidates []agentDispatchEvent
1558
- for i := len(events) - 1; i >= 0; i-- {
1559
- event := events[i]
1560
- if event.Event != "pending" || event.AgentType != in.AgentType {
1561
- continue
1562
- }
1563
- if _, ok := started[event.ToolUseID]; ok {
1564
- continue
1565
- }
1566
- if strings.HasPrefix(in.AgentType, "devrites-") && event.Role != in.AgentType {
1567
- continue
1568
- }
1569
- candidates = append(candidates, event)
1570
- }
1571
- if len(candidates) == 0 {
1572
- return bindDurableCodexV2Start(root, in, events)
1573
- }
1574
- if len(candidates) != 1 {
1575
- return "", false, fmt.Errorf("ambiguous subagent start: %d pending %s dispatches lack a parent tool-use binding", len(candidates), in.AgentType)
1576
- }
1577
- event := candidates[0]
1578
- if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
1579
- Event: "started",
1580
- TurnID: event.TurnID,
1581
- ToolUseID: event.ToolUseID,
1582
- AgentID: in.AgentID,
1583
- AgentType: in.AgentType,
1584
- Role: event.Role,
1585
- WindowID: event.WindowID,
1586
- }); err != nil {
1587
- return "", false, err
1588
- }
1589
- return event.Role, true, nil
1590
- }
1591
-
1592
- func bindDurableCodexV2Start(
1593
- root string, in agentDispatchHookInput, events []agentDispatchEvent,
1594
- ) (string, bool, error) {
1595
- if !devritesAgentNameRe.MatchString(in.AgentType) || in.AgentType == agentDispatchSkillGuard {
1596
- return "", false, nil
1597
- }
1598
- parentPath, sessionsDir, err := findCodexParentRollout(in.SessionID)
1599
- if err != nil || parentPath == "" {
1600
- return "", false, err
1601
- }
1602
- projectRoot := filepath.Dir(root)
1603
- childPath, childMeta, err := findCodexV2ChildRollout(
1604
- sessionsDir, parentPath, projectRoot, in.SessionID, "", in.AgentID,
1605
- )
1606
- if err != nil || childPath == "" || childMeta.Payload.AgentRole != in.AgentType {
1607
- return "", false, err
1608
- }
1609
-
1610
- turns := map[string]struct{}{}
1611
- for _, event := range events {
1612
- if event.TurnID != "" {
1613
- turns[event.TurnID] = struct{}{}
1614
- }
1615
- }
1616
- type binding struct {
1617
- turnID, windowID string
1618
- }
1619
- var matches []binding
1620
- for turnID := range turns {
1621
- armed, _ := dispatchTurnState(events, turnID)
1622
- _, required := armed[in.AgentType]
1623
- _, guarded := armed[agentDispatchSkillGuard]
1624
- if !required && !guarded {
1625
- continue
1626
- }
1627
- spawns, _, err := readCodexV2ParentRollout(
1628
- parentPath, projectRoot, in.SessionID, turnID, armed,
1629
- )
1630
- if err != nil {
1631
- return "", false, err
1632
- }
1633
- spawn := spawns[childMeta.Payload.AgentPath]
1634
- if spawn == nil || spawn.Role != in.AgentType {
1635
- continue
1636
- }
1637
- windowID := ""
1638
- if in.AgentType == "devrites-slice-wright" {
1639
- windowID = currentReconcileWindowID()
1640
- if windowID == "" || !reconcileWindowPredates(spawn.SpawnedAt) {
1641
- continue
1642
- }
1643
- }
1644
- matches = append(matches, binding{turnID: turnID, windowID: windowID})
1645
- }
1646
- if len(matches) == 0 {
1647
- return "", false, nil
1648
- }
1649
- if len(matches) != 1 {
1650
- return "", false, fmt.Errorf(
1651
- "ambiguous durable subagent start: %d parent spawns match %s",
1652
- len(matches), in.AgentID,
1653
- )
1654
- }
1655
- match := matches[0]
1656
- if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
1657
- Event: "started",
1658
- TurnID: match.turnID,
1659
- ToolUseID: "codex-v2:" + in.AgentID,
1660
- AgentID: in.AgentID,
1661
- AgentType: in.AgentType,
1662
- Role: in.AgentType,
1663
- WindowID: match.windowID,
1664
- }); err != nil {
1665
- return "", false, err
1666
- }
1667
- return in.AgentType, true, nil
1668
- }
1669
-
1670
- type boundAgentDispatchAttempt struct {
1671
- TurnID string
1672
- AgentType string
1673
- Role string
1674
- }
1675
-
1676
- func boundDispatchAttempt(root, sessionID, agentID string) (boundAgentDispatchAttempt, bool, error) {
1677
- events, err := readAgentDispatchEvents(root, sessionID)
1678
- if err != nil {
1679
- return boundAgentDispatchAttempt{}, false, err
1680
- }
1681
- for i := len(events) - 1; i >= 0; i-- {
1682
- event := events[i]
1683
- if event.Event == "started" && event.AgentID == agentID {
1684
- return boundAgentDispatchAttempt{
1685
- TurnID: event.TurnID,
1686
- AgentType: event.AgentType,
1687
- Role: event.Role,
1688
- }, true, nil
1689
- }
1690
- }
1691
- return boundAgentDispatchAttempt{}, false, nil
1692
- }
1693
-
1694
- type devritesAgentGuardBinding struct {
1695
- Kind devritesAgentKind
1696
- Role string
1697
- }
1698
-
1699
- func devritesAgentBindingForGuard(h harness.Harness, in harness.GuardInput) devritesAgentGuardBinding {
1700
- kind := devritesAgent(in.AgentType)
1701
- binding := devritesAgentGuardBinding{Kind: kind, Role: declaredDevritesRole(in.AgentType)}
1702
- generic := in.AgentType == "" || in.AgentType == "explorer" || in.AgentType == "worker"
1703
- if h != harness.Codex || !generic ||
1704
- os.Getenv("DEVRITES_CODEX_GENERIC_AGENT_COMPAT") != "1" ||
1705
- in.SessionID == "" || in.AgentID == "" {
1706
- return binding
1707
- }
1708
- root, ok := agentDispatchRoot()
1709
- if !ok {
1710
- return devritesAgentGuardBinding{Kind: devritesAgentInvalid}
1711
- }
1712
- attempt, found, err := boundDispatchAttempt(root, in.SessionID, in.AgentID)
1713
- if err != nil {
1714
- return devritesAgentGuardBinding{Kind: devritesAgentInvalid}
1715
- }
1716
- if !found {
1717
- return binding
1718
- }
1719
- if attempt.Role == "devrites-slice-wright" {
1720
- return devritesAgentGuardBinding{Kind: devritesAgentGenericWright, Role: attempt.Role}
1721
- }
1722
- if strings.HasPrefix(attempt.Role, "devrites-") {
1723
- return devritesAgentGuardBinding{Kind: devritesAgentReadonly, Role: attempt.Role}
1724
- }
1725
- return devritesAgentGuardBinding{Kind: devritesAgentInvalid}
1726
- }