devrites 3.2.27 → 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 +22 -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 -1637
  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 -2732
  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,1637 +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 string,
691
- ) (string, codexRolloutRecord, error) {
692
- var childPath string
693
- var childMeta codexRolloutRecord
694
- err := filepath.WalkDir(sessionsDir, func(path string, entry fs.DirEntry, walkErr error) error {
695
- if walkErr != nil {
696
- if os.IsNotExist(walkErr) {
697
- return nil
698
- }
699
- return walkErr
700
- }
701
- if entry.IsDir() || path == parentPath || filepath.Ext(path) != ".jsonl" {
702
- return nil
703
- }
704
- record, ok, err := firstCodexRolloutRecord(path)
705
- if err != nil || !ok {
706
- return err
707
- }
708
- if record.Type != "session_meta" ||
709
- record.Payload.ParentThreadID != sessionID ||
710
- record.Payload.AgentPath != agentPath ||
711
- !sameResolvedPath(record.Payload.CWD, projectRoot) {
712
- return nil
713
- }
714
- if childPath != "" {
715
- return fmt.Errorf("multiple Codex child rollouts match %s", agentPath)
716
- }
717
- childPath = path
718
- childMeta = record
719
- return nil
720
- })
721
- return childPath, childMeta, err
722
- }
723
-
724
- func firstCodexRolloutRecord(path string) (codexRolloutRecord, bool, error) {
725
- var record codexRolloutRecord
726
- file, err := os.Open(path)
727
- if err != nil {
728
- return record, false, err
729
- }
730
- defer file.Close()
731
- scanner := bufio.NewScanner(file)
732
- scanner.Buffer(make([]byte, 64<<10), maxCodexRolloutLine)
733
- if !scanner.Scan() {
734
- return record, false, scanner.Err()
735
- }
736
- if err := json.Unmarshal(scanner.Bytes(), &record); err != nil {
737
- return record, false, nil
738
- }
739
- return record, true, nil
740
- }
741
-
742
- func codexChildResult(path, instructions string) (string, error) {
743
- file, err := os.Open(path)
744
- if err != nil {
745
- return "", err
746
- }
747
- defer file.Close()
748
- scanner := bufio.NewScanner(file)
749
- scanner.Buffer(make([]byte, 64<<10), maxCodexRolloutLine)
750
- instructionsLoaded := false
751
- result := ""
752
- for scanner.Scan() {
753
- line := scanner.Bytes()
754
- if !bytes.Contains(line, []byte(`"task_complete"`)) &&
755
- !bytes.Contains(line, []byte(`"developer"`)) {
756
- continue
757
- }
758
- var record codexRolloutRecord
759
- if json.Unmarshal(line, &record) != nil {
760
- continue
761
- }
762
- if record.Payload.Type == "message" && record.Payload.Role == "developer" &&
763
- strings.Contains(rolloutInputText(record.Payload.Content), instructions) {
764
- instructionsLoaded = true
765
- }
766
- if record.Payload.Type == "task_complete" &&
767
- strings.TrimSpace(record.Payload.LastAgentResult) != "" {
768
- result = record.Payload.LastAgentResult
769
- }
770
- }
771
- if err := scanner.Err(); err != nil {
772
- return "", err
773
- }
774
- if !instructionsLoaded {
775
- return "", nil
776
- }
777
- return result, nil
778
- }
779
-
780
- func rolloutInputText(content []struct {
781
- Type string `json:"type"`
782
- Text string `json:"text"`
783
- }) string {
784
- var text []string
785
- for _, item := range content {
786
- if item.Type == "input_text" && strings.TrimSpace(item.Text) != "" {
787
- text = append(text, item.Text)
788
- }
789
- }
790
- return strings.Join(text, "\n")
791
- }
792
-
793
- func lineBetween(lines []int, after, before int) bool {
794
- for _, line := range lines {
795
- if line > after && line < before {
796
- return true
797
- }
798
- }
799
- return false
800
- }
801
-
802
- func sameResolvedPath(left, right string) bool {
803
- resolve := func(path string) string {
804
- absolute, err := filepath.Abs(path)
805
- if err == nil {
806
- path = absolute
807
- }
808
- if resolved, err := filepath.EvalSymlinks(path); err == nil {
809
- path = resolved
810
- }
811
- return filepath.Clean(path)
812
- }
813
- return resolve(left) == resolve(right)
814
- }
815
-
816
- func reconcileWindowPredates(at time.Time) bool {
817
- _, _, dir, ok := resolveWorkspace()
818
- if !ok {
819
- return false
820
- }
821
- for _, name := range []string{".reconcile-base", ".reconcile-allowlist", ".reconcile-devrites"} {
822
- info, err := os.Stat(filepath.Join(dir, name))
823
- if err != nil || info.ModTime().After(at) {
824
- return false
825
- }
826
- }
827
- return true
828
- }
829
-
830
- func dispatchAttemptComplete(attempt *agentDispatchAttempt) bool {
831
- return attempt.Started && attempt.Stopped && attempt.Waited && attempt.ResultSHA256 != ""
832
- }
833
-
834
- func roleSatisfied(role, windowID string, attempts []*agentDispatchAttempt) bool {
835
- for _, attempt := range attempts {
836
- if attempt.Role != role || !dispatchAttemptComplete(attempt) {
837
- continue
838
- }
839
- if windowID == "" || attempt.WindowID == windowID {
840
- return true
841
- }
842
- }
843
- return false
844
- }
845
-
846
- func dispatchInstruction(role string) string {
847
- return fmt.Sprintf(
848
- "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.",
849
- role, role, v2HiddenAgentTypeRule, expectedAgentType(role),
850
- )
851
- }
852
-
853
- func conditionalDispatchInstruction() string {
854
- 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."
855
- }
856
-
857
- func preDispatchStopInstruction() string {
858
- 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."
859
- }
860
-
861
- func incompleteDispatchReason(armed map[string]struct{}, attempts []*agentDispatchAttempt) string {
862
- roles := make([]string, 0, len(armed))
863
- for role := range armed {
864
- if role == agentDispatchSkillGuard {
865
- continue
866
- }
867
- if !roleSatisfied(role, "", attempts) {
868
- roles = append(roles, role)
869
- }
870
- }
871
- if len(roles) == 0 {
872
- return ""
873
- }
874
- sort.Strings(roles)
875
- role := roles[0]
876
- if role == agentDispatchMetadataRole {
877
- 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."
878
- }
879
- reason := fmt.Sprintf("DevRites dispatch for %s is not complete. %s", role, dispatchInstruction(role))
880
- if len(attempts) == 0 {
881
- reason += " " + preDispatchStopInstruction()
882
- }
883
- return reason
884
- }
885
-
886
- func preToolDeny(h harness.Harness, reason string, stdout, stderr io.Writer) int {
887
- out, err := h.PreToolDeny(reason)
888
- if err != nil {
889
- debugf(stderr, "agent-dispatch: %v", err)
890
- return exitOK
891
- }
892
- fmt.Fprintln(stdout, out)
893
- return exitOK
894
- }
895
-
896
- func preToolRewrite(h harness.Harness, toolInput json.RawMessage, message string, stdout, stderr io.Writer) int {
897
- var updated map[string]any
898
- if err := json.Unmarshal(toolInput, &updated); err != nil {
899
- return preToolDeny(h, "DevRites could not bind developer_instructions to the spawn input.", stdout, stderr)
900
- }
901
- updated["message"] = message
902
- out, err := json.Marshal(map[string]any{
903
- "hookSpecificOutput": map[string]any{
904
- "hookEventName": "PreToolUse",
905
- "permissionDecision": "allow",
906
- "updatedInput": updated,
907
- },
908
- })
909
- if err != nil {
910
- return preToolDeny(h, "DevRites could not encode the bound spawn input.", stdout, stderr)
911
- }
912
- fmt.Fprintln(stdout, string(out))
913
- return exitOK
914
- }
915
-
916
- func stopDispatchBlock(h harness.Harness, reason string, stdout, stderr io.Writer) int {
917
- out, err := h.StopBlock(reason)
918
- if err != nil {
919
- debugf(stderr, "agent-dispatch: %v", err)
920
- return exitOK
921
- }
922
- fmt.Fprintln(stdout, out)
923
- return exitOK
924
- }
925
-
926
- func stopDispatchFailure(reason string, stdout, stderr io.Writer) int {
927
- out, err := json.Marshal(map[string]any{
928
- "continue": false,
929
- "stopReason": reason,
930
- "systemMessage": reason,
931
- })
932
- if err != nil {
933
- debugf(stderr, "agent-dispatch: %v", err)
934
- return exitOK
935
- }
936
- fmt.Fprintln(stdout, string(out))
937
- return exitOK
938
- }
939
-
940
- func refreshPendingWrightBoundary(
941
- root string,
942
- in agentDispatchHookInput,
943
- armed map[string]struct{},
944
- attempts []*agentDispatchAttempt,
945
- ) error {
946
- if in.AgentID != "" || in.AgentType != "" || currentReconcileWindowID() == "" {
947
- return nil
948
- }
949
- for _, attempt := range attempts {
950
- if attempt.Role == "devrites-slice-wright" {
951
- return nil
952
- }
953
- }
954
- boundaryArmed := make(map[string]struct{}, len(armed)+1)
955
- for role := range armed {
956
- boundaryArmed[role] = struct{}{}
957
- }
958
- boundaryArmed["devrites-slice-wright"] = struct{}{}
959
- durable, err := durableCodexV2DispatchAttempts(root, in.SessionID, in.TurnID, boundaryArmed)
960
- if err != nil {
961
- return err
962
- }
963
- for _, attempt := range durable {
964
- if attempt.Role == "devrites-slice-wright" {
965
- return nil
966
- }
967
- }
968
- workspaceRoot, slug, _, ok := resolveWorkspace()
969
- if !ok || !sameResolvedPath(workspaceRoot, root) {
970
- return fmt.Errorf("active reconcile window is unavailable")
971
- }
972
- return lib.CaptureReconcileWrightBoundary(root, slug)
973
- }
974
-
975
- func hookAgentDispatch(h harness.Harness, stdin io.Reader, stdout, stderr io.Writer) int {
976
- if h != harness.Codex {
977
- _, _ = io.Copy(io.Discard, stdin)
978
- return exitOK
979
- }
980
- in, err := parseAgentDispatchHookInput(stdin)
981
- if err != nil {
982
- return exitOK
983
- }
984
- root, ok := agentDispatchRoot()
985
- if !ok || in.SessionID == "" || in.TurnID == "" {
986
- return exitOK
987
- }
988
-
989
- switch in.HookEventName {
990
- case "UserPromptSubmit":
991
- roles, skillInvoked, rolesErr := requiredRolesInPrompt(root, in.Prompt)
992
- if rolesErr != nil {
993
- if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
994
- Event: "armed",
995
- TurnID: in.TurnID,
996
- Role: agentDispatchMetadataRole,
997
- }); err != nil {
998
- return stopDispatchBlock(h, "DevRites could not record the invalid agent-role contract: "+err.Error(), stdout, stderr)
999
- }
1000
- return stopDispatchBlock(h, "DevRites could not read the required agent roles: "+rolesErr.Error(), stdout, stderr)
1001
- }
1002
- if skillInvoked {
1003
- if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
1004
- Event: "armed",
1005
- TurnID: in.TurnID,
1006
- Role: agentDispatchSkillGuard,
1007
- }); err != nil {
1008
- return stopDispatchBlock(h, "DevRites could not arm the skill dispatch guard: "+err.Error(), stdout, stderr)
1009
- }
1010
- }
1011
- for _, role := range roles {
1012
- if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
1013
- Event: "armed",
1014
- TurnID: in.TurnID,
1015
- Role: role,
1016
- }); err != nil {
1017
- return stopDispatchBlock(h, "DevRites could not arm the required agent dispatch: "+err.Error(), stdout, stderr)
1018
- }
1019
- }
1020
- events, stateErr := readAgentDispatchEvents(root, in.SessionID)
1021
- if stateErr == nil {
1022
- armed, attempts := dispatchTurnState(events, in.TurnID)
1023
- if err := refreshPendingWrightBoundary(root, in, armed, attempts); err != nil {
1024
- debugf(stderr, "agent-dispatch pending wright boundary: %v", err)
1025
- }
1026
- }
1027
- if len(roles) > 0 {
1028
- instructions := make([]string, 0, len(roles))
1029
- for _, role := range roles {
1030
- instructions = append(instructions, dispatchInstruction(role))
1031
- }
1032
- fmt.Fprintf(
1033
- stdout,
1034
- "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",
1035
- strings.Join(instructions, "\n"),
1036
- preDispatchStopInstruction(),
1037
- )
1038
- }
1039
- if skillInvoked {
1040
- if len(roles) > 0 {
1041
- fmt.Fprintln(stdout)
1042
- }
1043
- fmt.Fprint(stdout, conditionalDispatchInstruction())
1044
- }
1045
- return exitOK
1046
-
1047
- case "PreToolUse":
1048
- return hookAgentDispatchPreTool(h, root, in, stdout, stderr)
1049
-
1050
- case "PostToolUse":
1051
- events, err := readAgentDispatchEvents(root, in.SessionID)
1052
- if err != nil {
1053
- debugf(stderr, "agent-dispatch post-tool state: %v", err)
1054
- return exitOK
1055
- }
1056
- armed, attempts := dispatchTurnState(events, in.TurnID)
1057
- if reason, ok := dispatchWaiverReason(agentDispatchCommand(in)); ok &&
1058
- strings.Contains(in.ToolResponse, "dispatch-waive: accepted "+reason) {
1059
- if err := recordDispatchWaiver(root, in, reason, armed, attempts); err != nil {
1060
- debugf(stderr, "agent-dispatch post-tool waiver: %v", err)
1061
- }
1062
- }
1063
- if isWaitTool(in.ToolName) {
1064
- if waitToolSucceeded(in.ToolResponse) {
1065
- if err := recordCompletedWaits(root, in, attempts); err != nil {
1066
- debugf(stderr, "agent-dispatch post-tool wait: %v", err)
1067
- }
1068
- } else if err := recordCancelledWaits(root, in, attempts); err != nil {
1069
- debugf(stderr, "agent-dispatch post-tool wait cancellation: %v", err)
1070
- }
1071
- }
1072
- if err := refreshPendingWrightBoundary(root, in, armed, attempts); err != nil {
1073
- debugf(stderr, "agent-dispatch post-tool wright boundary: %v", err)
1074
- }
1075
- return exitOK
1076
-
1077
- case "SubagentStop":
1078
- if in.AgentID == "" {
1079
- return exitOK
1080
- }
1081
- attempt, found, lookupErr := boundDispatchAttempt(root, in.SessionID, in.AgentID)
1082
- if lookupErr != nil {
1083
- return stopDispatchBlock(h, "DevRites could not identify the stopped subagent: "+lookupErr.Error(), stdout, stderr)
1084
- }
1085
- if !found {
1086
- return exitOK
1087
- }
1088
- resultSum := ""
1089
- if strings.TrimSpace(in.LastAssistantMessage) != "" {
1090
- sum := sha256.Sum256([]byte(in.LastAssistantMessage))
1091
- resultSum = hex.EncodeToString(sum[:])
1092
- }
1093
- if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
1094
- Event: "stopped",
1095
- TurnID: attempt.TurnID,
1096
- AgentID: in.AgentID,
1097
- AgentType: attempt.AgentType,
1098
- ResultSHA256: resultSum,
1099
- }); err != nil {
1100
- return stopDispatchBlock(h, "DevRites could not record the subagent result: "+err.Error(), stdout, stderr)
1101
- }
1102
- if active, activeErr := activeWaitIntent(root, in.SessionID, attempt.TurnID, in.AgentID, time.Now()); activeErr != nil {
1103
- return stopDispatchBlock(h, "DevRites could not verify the awaited subagent result: "+activeErr.Error(), stdout, stderr)
1104
- } else if active {
1105
- if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
1106
- Event: "waited",
1107
- TurnID: attempt.TurnID,
1108
- AgentID: in.AgentID,
1109
- }); err != nil {
1110
- return stopDispatchBlock(h, "DevRites could not record the awaited subagent result: "+err.Error(), stdout, stderr)
1111
- }
1112
- }
1113
- return exitOK
1114
-
1115
- case "Stop":
1116
- events, err := readAgentDispatchEvents(root, in.SessionID)
1117
- if err != nil {
1118
- reason := "DevRites could not verify the required agent dispatch: " + err.Error()
1119
- if in.StopHookActive {
1120
- return stopDispatchFailure(reason, stdout, stderr)
1121
- }
1122
- return stopDispatchBlock(h, reason, stdout, stderr)
1123
- }
1124
- armed, attempts := dispatchTurnState(events, in.TurnID)
1125
- _, guarded := armed[agentDispatchSkillGuard]
1126
- if reason := incompleteDispatchReason(armed, attempts); reason != "" || guarded && len(attempts) == 0 {
1127
- durable, durableErr := durableCodexV2DispatchAttempts(
1128
- root, in.SessionID, in.TurnID, armed,
1129
- )
1130
- if durableErr != nil {
1131
- reason := "DevRites could not verify the Codex V2 agent dispatch: " + durableErr.Error()
1132
- if in.StopHookActive {
1133
- return stopDispatchFailure(reason, stdout, stderr)
1134
- }
1135
- return stopDispatchBlock(h, reason, stdout, stderr)
1136
- }
1137
- attempts = append(attempts, durable...)
1138
- }
1139
- if reason := incompleteDispatchReason(armed, attempts); reason != "" {
1140
- if in.StopHookActive {
1141
- return stopDispatchFailure(reason, stdout, stderr)
1142
- }
1143
- return stopDispatchBlock(h, reason, stdout, stderr)
1144
- }
1145
- return exitOK
1146
- }
1147
- return exitOK
1148
- }
1149
-
1150
- func hookAgentDispatchPreTool(h harness.Harness, root string, in agentDispatchHookInput, stdout, stderr io.Writer) int {
1151
- events, err := readAgentDispatchEvents(root, in.SessionID)
1152
- if err != nil {
1153
- return preToolDeny(h, "DevRites could not verify agent dispatch state: "+err.Error(), stdout, stderr)
1154
- }
1155
- armed, attempts := dispatchTurnState(events, in.TurnID)
1156
-
1157
- command := agentDispatchCommand(in)
1158
- if isShellTool(in.ToolName) && strings.Contains(command, "dispatch-waive") {
1159
- reason, ok := dispatchWaiverReason(command)
1160
- if !ok || !lib.ValidDispatchWaiver(reason) {
1161
- return preToolDeny(h, "DevRites dispatch waiver must be one exact standalone engine command with an approved reason.", stdout, stderr)
1162
- }
1163
- if len(attempts) != 0 {
1164
- return preToolDeny(h, "DevRites dispatch waiver is only valid before any specialist spawn attempt.", stdout, stderr)
1165
- }
1166
- required := 0
1167
- for role := range armed {
1168
- if strings.HasPrefix(role, "devrites-") && role != agentDispatchSkillGuard && role != agentDispatchMetadataRole {
1169
- required++
1170
- }
1171
- }
1172
- if required == 0 {
1173
- return preToolDeny(h, "DevRites dispatch waiver requires an armed mandatory role in this turn.", stdout, stderr)
1174
- }
1175
- return exitOK
1176
- }
1177
-
1178
- if isAgentDispatchTool(in.ToolName) {
1179
- agentType := in.ToolInput.AgentType
1180
- if agentType == "" {
1181
- roles := explicitRolesInText(in.ToolInput.Message + "\n" + in.ToolInput.TaskName)
1182
- if len(roles) == 1 {
1183
- return preToolDeny(h, "DevRites conditional dispatch requires the exact named specialist. "+dispatchInstruction(roles[0]), stdout, stderr)
1184
- }
1185
- if reason := incompleteDispatchReason(armed, attempts); reason != "" {
1186
- return preToolDeny(h, reason, stdout, stderr)
1187
- }
1188
- if _, guarded := armed[agentDispatchSkillGuard]; guarded {
1189
- return preToolDeny(h, conditionalDispatchInstruction(), stdout, stderr)
1190
- }
1191
- return exitOK
1192
- }
1193
- role := ""
1194
- if strings.HasPrefix(agentType, "devrites-") {
1195
- role = agentType
1196
- } else {
1197
- roles := explicitRolesInText(in.ToolInput.Message)
1198
- if len(roles) == 1 {
1199
- role = roles[0]
1200
- }
1201
- }
1202
- if role == "" {
1203
- if agentType == "explorer" || agentType == "worker" {
1204
- if reason := incompleteDispatchReason(armed, attempts); reason != "" {
1205
- return preToolDeny(h, reason, stdout, stderr)
1206
- }
1207
- if _, guarded := armed[agentDispatchSkillGuard]; !guarded {
1208
- return exitOK // Unrelated generic subagent in a DevRites repository.
1209
- }
1210
- return preToolDeny(h, "DevRites generic dispatch must name exactly one .codex/agents/devrites-<role>.toml contract.", stdout, stderr)
1211
- }
1212
- if reason := incompleteDispatchReason(armed, attempts); reason != "" {
1213
- return preToolDeny(h, reason, stdout, stderr)
1214
- }
1215
- if _, guarded := armed[agentDispatchSkillGuard]; guarded {
1216
- return preToolDeny(h, conditionalDispatchInstruction(), stdout, stderr)
1217
- }
1218
- return exitOK
1219
- }
1220
- instructions, contractErr := agentRoleDeveloperInstructions(root, role)
1221
- if contractErr != nil {
1222
- return preToolDeny(h, "DevRites role contract .codex/agents/"+role+".toml cannot be loaded: "+contractErr.Error()+".", stdout, stderr)
1223
- }
1224
- if in.ToolInput.ForkTurns != "none" {
1225
- 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)
1226
- }
1227
- if agentType == "worker" && role != "devrites-slice-wright" {
1228
- return preToolDeny(h, "Generic worker is reserved for devrites-slice-wright; use generic explorer for read-only DevRites roles.", stdout, stderr)
1229
- }
1230
- if agentType == "explorer" && role == "devrites-slice-wright" {
1231
- return preToolDeny(h, "devrites-slice-wright requires the generic worker identity and exact wright allowlist.", stdout, stderr)
1232
- }
1233
- if agentType != "" && agentType != role && agentType != expectedAgentType(role) {
1234
- 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)
1235
- }
1236
- if in.ToolUseID == "" {
1237
- return preToolDeny(h, "DevRites cannot bind a spawn without tool_use_id.", stdout, stderr)
1238
- }
1239
- windowID := currentReconcileWindowID()
1240
- if role == "devrites-slice-wright" && windowID == "" {
1241
- return preToolDeny(h, "devrites-slice-wright requires an active reconcile snapshot before spawn_agent.", stdout, stderr)
1242
- }
1243
- if role == "devrites-slice-wright" {
1244
- workspaceRoot, slug, _, ok := resolveWorkspace()
1245
- if !ok || !sameResolvedPath(workspaceRoot, root) {
1246
- return preToolDeny(h, "DevRites could not resolve the active wright reconcile window before spawn_agent.", stdout, stderr)
1247
- }
1248
- if err := lib.CaptureReconcileWrightBoundary(root, slug); err != nil {
1249
- return preToolDeny(h, "DevRites could not capture the wright canonical boundary before spawn_agent: "+err.Error(), stdout, stderr)
1250
- }
1251
- }
1252
- if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
1253
- Event: "pending",
1254
- TurnID: in.TurnID,
1255
- ToolUseID: in.ToolUseID,
1256
- AgentType: agentType,
1257
- Role: role,
1258
- WindowID: windowID,
1259
- }); err != nil {
1260
- return preToolDeny(h, "DevRites could not record the spawn attempt: "+err.Error(), stdout, stderr)
1261
- }
1262
- if agentType != role {
1263
- message := fmt.Sprintf(
1264
- "DevRites bound developer_instructions for `%s`:\n\n%s\n\n## Unchanged parent packet\n%s",
1265
- role, instructions, in.ToolInput.Message,
1266
- )
1267
- return preToolRewrite(h, in.ToolInputRaw, message, stdout, stderr)
1268
- }
1269
- return exitOK
1270
- }
1271
-
1272
- switch toolBaseName(in.ToolName) {
1273
- case "wait", "wait_agent":
1274
- if len(armed) == 0 {
1275
- return exitOK
1276
- }
1277
- var started []*agentDispatchAttempt
1278
- for _, attempt := range attempts {
1279
- if attempt.Started {
1280
- started = append(started, attempt)
1281
- }
1282
- }
1283
- if len(started) == 0 {
1284
- reason := incompleteDispatchReason(armed, attempts)
1285
- if reason == "" {
1286
- reason = conditionalDispatchInstruction()
1287
- }
1288
- return preToolDeny(h, reason, stdout, stderr)
1289
- }
1290
- targets := waitTargets(in)
1291
- if len(targets) > 0 {
1292
- known := map[string]*agentDispatchAttempt{}
1293
- for _, attempt := range started {
1294
- known[attempt.AgentID] = attempt
1295
- }
1296
- for _, agentID := range targets {
1297
- if known[agentID] == nil {
1298
- return preToolDeny(h, "DevRites wait target is not a confirmed child from this turn.", stdout, stderr)
1299
- }
1300
- }
1301
- }
1302
- if err := recordWaitIntents(root, in, started); err != nil {
1303
- return preToolDeny(h, "DevRites could not record the wait intent: "+err.Error(), stdout, stderr)
1304
- }
1305
- return exitOK
1306
- }
1307
-
1308
- if isShellTool(in.ToolName) && reconcileCloseRe.MatchString(command) {
1309
- windowID := currentReconcileWindowID()
1310
- if windowID == "" {
1311
- return exitOK
1312
- }
1313
- if !roleSatisfied("devrites-slice-wright", windowID, attempts) {
1314
- durable, durableErr := durableCodexV2DispatchAttempts(
1315
- root, in.SessionID, in.TurnID, armed,
1316
- )
1317
- if durableErr != nil {
1318
- return preToolDeny(h, "DevRites could not verify the Codex V2 wright result: "+durableErr.Error(), stdout, stderr)
1319
- }
1320
- attempts = append(attempts, durable...)
1321
- if !roleSatisfied("devrites-slice-wright", windowID, attempts) {
1322
- return preToolDeny(h, "DevRites reconcile close requires a confirmed, awaited devrites-slice-wright result bound to the active reconcile snapshot.", stdout, stderr)
1323
- }
1324
- for _, attempt := range durable {
1325
- if attempt.Role != "devrites-slice-wright" || attempt.WindowID != windowID ||
1326
- !dispatchAttemptComplete(attempt) {
1327
- continue
1328
- }
1329
- if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
1330
- Event: "verified",
1331
- TurnID: in.TurnID,
1332
- AgentID: attempt.AgentID,
1333
- AgentType: attempt.AgentType,
1334
- Role: attempt.Role,
1335
- WindowID: attempt.WindowID,
1336
- ResultSHA256: attempt.ResultSHA256,
1337
- }); err != nil {
1338
- return preToolDeny(h, "DevRites could not persist the verified Codex V2 wright result: "+err.Error(), stdout, stderr)
1339
- }
1340
- }
1341
- }
1342
- }
1343
- return exitOK
1344
- }
1345
-
1346
- func isWaitTool(toolName string) bool {
1347
- switch toolBaseName(toolName) {
1348
- case "wait", "wait_agent":
1349
- return true
1350
- default:
1351
- return false
1352
- }
1353
- }
1354
-
1355
- func agentDispatchCommand(in agentDispatchHookInput) string {
1356
- if in.ToolInput.Command != "" {
1357
- return in.ToolInput.Command
1358
- }
1359
- return in.ToolInput.Cmd
1360
- }
1361
-
1362
- func dispatchWaiverReason(command string) (string, bool) {
1363
- match := dispatchWaiveRe.FindStringSubmatch(command)
1364
- if len(match) != 2 {
1365
- return "", false
1366
- }
1367
- return match[1], true
1368
- }
1369
-
1370
- func recordDispatchWaiver(
1371
- root string,
1372
- in agentDispatchHookInput,
1373
- reason string,
1374
- armed map[string]struct{},
1375
- attempts []*agentDispatchAttempt,
1376
- ) error {
1377
- if len(attempts) != 0 || !lib.ValidDispatchWaiver(reason) {
1378
- return fmt.Errorf("waiver is not valid after a spawn attempt")
1379
- }
1380
- recorded := 0
1381
- for role := range armed {
1382
- if !strings.HasPrefix(role, "devrites-") || role == agentDispatchSkillGuard || role == agentDispatchMetadataRole {
1383
- continue
1384
- }
1385
- if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
1386
- Event: "waived",
1387
- TurnID: in.TurnID,
1388
- Role: role,
1389
- Reason: reason,
1390
- }); err != nil {
1391
- return err
1392
- }
1393
- recorded++
1394
- }
1395
- if recorded == 0 {
1396
- return fmt.Errorf("no armed mandatory role to waive")
1397
- }
1398
- return nil
1399
- }
1400
-
1401
- func waitTargets(in agentDispatchHookInput) []string {
1402
- if len(in.ToolInput.ReceiverThreadIDs) > 0 {
1403
- return in.ToolInput.ReceiverThreadIDs
1404
- }
1405
- if len(in.ToolInput.IDs) > 0 {
1406
- return in.ToolInput.IDs
1407
- }
1408
- return in.ToolInput.AgentIDs
1409
- }
1410
-
1411
- func waitToolSucceeded(response string) bool {
1412
- response = strings.ToLower(strings.TrimSpace(response))
1413
- if response == "" {
1414
- return false
1415
- }
1416
- for _, failure := range []string{
1417
- `"timed_out":true`,
1418
- `"timed_out": true`,
1419
- "timed out",
1420
- "timeout summary",
1421
- "no activity",
1422
- } {
1423
- if strings.Contains(response, failure) {
1424
- return false
1425
- }
1426
- }
1427
- return true
1428
- }
1429
-
1430
- func waitTargetIDs(in agentDispatchHookInput, attempts []*agentDispatchAttempt) []string {
1431
- targets := waitTargets(in)
1432
- if len(targets) > 0 {
1433
- return targets
1434
- }
1435
- targets = make([]string, 0, len(attempts))
1436
- for _, attempt := range attempts {
1437
- if attempt.Started && attempt.AgentID != "" {
1438
- targets = append(targets, attempt.AgentID)
1439
- }
1440
- }
1441
- return targets
1442
- }
1443
-
1444
- func agentWaitTimeout(in agentDispatchHookInput) time.Duration {
1445
- const defaultTimeout = 30 * time.Second
1446
- const maxTimeout = time.Hour
1447
- if in.ToolInput.TimeoutMS <= 0 {
1448
- return defaultTimeout
1449
- }
1450
- timeout := time.Duration(in.ToolInput.TimeoutMS) * time.Millisecond
1451
- if timeout > maxTimeout {
1452
- return maxTimeout
1453
- }
1454
- return timeout
1455
- }
1456
-
1457
- func recordWaitIntents(root string, in agentDispatchHookInput, attempts []*agentDispatchAttempt) error {
1458
- timeoutMS := strconv.FormatInt(agentWaitTimeout(in).Milliseconds(), 10)
1459
- for _, agentID := range waitTargetIDs(in, attempts) {
1460
- if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
1461
- Event: "waiting",
1462
- TurnID: in.TurnID,
1463
- AgentID: agentID,
1464
- Reason: timeoutMS,
1465
- }); err != nil {
1466
- return err
1467
- }
1468
- }
1469
- return nil
1470
- }
1471
-
1472
- func recordCancelledWaits(root string, in agentDispatchHookInput, attempts []*agentDispatchAttempt) error {
1473
- for _, agentID := range waitTargetIDs(in, attempts) {
1474
- if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
1475
- Event: "wait-cancelled",
1476
- TurnID: in.TurnID,
1477
- AgentID: agentID,
1478
- }); err != nil {
1479
- return err
1480
- }
1481
- }
1482
- return nil
1483
- }
1484
-
1485
- func activeWaitIntent(root, sessionID, turnID, agentID string, now time.Time) (bool, error) {
1486
- events, err := readAgentDispatchEvents(root, sessionID)
1487
- if err != nil {
1488
- return false, err
1489
- }
1490
- for i := len(events) - 1; i >= 0; i-- {
1491
- event := events[i]
1492
- if event.TurnID != turnID || event.AgentID != agentID {
1493
- continue
1494
- }
1495
- switch event.Event {
1496
- case "waited", "wait-cancelled":
1497
- return false, nil
1498
- case "waiting":
1499
- timeoutMS, err := strconv.ParseInt(event.Reason, 10, 64)
1500
- if err != nil || timeoutMS <= 0 {
1501
- return false, nil
1502
- }
1503
- deadline := time.Unix(0, event.AtUnixNano).Add(time.Duration(timeoutMS) * time.Millisecond)
1504
- return !now.After(deadline), nil
1505
- }
1506
- }
1507
- return false, nil
1508
- }
1509
-
1510
- func recordCompletedWaits(root string, in agentDispatchHookInput, attempts []*agentDispatchAttempt) error {
1511
- targetSet := map[string]struct{}{}
1512
- for _, agentID := range waitTargets(in) {
1513
- targetSet[agentID] = struct{}{}
1514
- }
1515
- for _, attempt := range attempts {
1516
- if !attempt.Started || attempt.Waited {
1517
- continue
1518
- }
1519
- if len(targetSet) > 0 {
1520
- if _, ok := targetSet[attempt.AgentID]; !ok {
1521
- continue
1522
- }
1523
- }
1524
- if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
1525
- Event: "waited",
1526
- TurnID: in.TurnID,
1527
- AgentID: attempt.AgentID,
1528
- }); err != nil {
1529
- return err
1530
- }
1531
- }
1532
- return nil
1533
- }
1534
-
1535
- func bindAgentDispatchStart(root string, in agentDispatchHookInput) (string, bool, error) {
1536
- events, err := readAgentDispatchEvents(root, in.SessionID)
1537
- if err != nil {
1538
- return "", false, err
1539
- }
1540
- started := map[string]struct{}{}
1541
- for _, event := range events {
1542
- if event.Event == "started" {
1543
- started[event.ToolUseID] = struct{}{}
1544
- }
1545
- }
1546
- var candidates []agentDispatchEvent
1547
- for i := len(events) - 1; i >= 0; i-- {
1548
- event := events[i]
1549
- if event.Event != "pending" || event.AgentType != in.AgentType {
1550
- continue
1551
- }
1552
- if _, ok := started[event.ToolUseID]; ok {
1553
- continue
1554
- }
1555
- if strings.HasPrefix(in.AgentType, "devrites-") && event.Role != in.AgentType {
1556
- continue
1557
- }
1558
- candidates = append(candidates, event)
1559
- }
1560
- if len(candidates) == 0 {
1561
- return "", false, nil
1562
- }
1563
- if len(candidates) != 1 {
1564
- return "", false, fmt.Errorf("ambiguous subagent start: %d pending %s dispatches lack a parent tool-use binding", len(candidates), in.AgentType)
1565
- }
1566
- event := candidates[0]
1567
- if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
1568
- Event: "started",
1569
- TurnID: event.TurnID,
1570
- ToolUseID: event.ToolUseID,
1571
- AgentID: in.AgentID,
1572
- AgentType: in.AgentType,
1573
- Role: event.Role,
1574
- WindowID: event.WindowID,
1575
- }); err != nil {
1576
- return "", false, err
1577
- }
1578
- return event.Role, true, nil
1579
- }
1580
-
1581
- type boundAgentDispatchAttempt struct {
1582
- TurnID string
1583
- AgentType string
1584
- Role string
1585
- }
1586
-
1587
- func boundDispatchAttempt(root, sessionID, agentID string) (boundAgentDispatchAttempt, bool, error) {
1588
- events, err := readAgentDispatchEvents(root, sessionID)
1589
- if err != nil {
1590
- return boundAgentDispatchAttempt{}, false, err
1591
- }
1592
- for i := len(events) - 1; i >= 0; i-- {
1593
- event := events[i]
1594
- if event.Event == "started" && event.AgentID == agentID {
1595
- return boundAgentDispatchAttempt{
1596
- TurnID: event.TurnID,
1597
- AgentType: event.AgentType,
1598
- Role: event.Role,
1599
- }, true, nil
1600
- }
1601
- }
1602
- return boundAgentDispatchAttempt{}, false, nil
1603
- }
1604
-
1605
- type devritesAgentGuardBinding struct {
1606
- Kind devritesAgentKind
1607
- Role string
1608
- }
1609
-
1610
- func devritesAgentBindingForGuard(h harness.Harness, in harness.GuardInput) devritesAgentGuardBinding {
1611
- kind := devritesAgent(in.AgentType)
1612
- binding := devritesAgentGuardBinding{Kind: kind, Role: declaredDevritesRole(in.AgentType)}
1613
- generic := in.AgentType == "" || in.AgentType == "explorer" || in.AgentType == "worker"
1614
- if h != harness.Codex || !generic ||
1615
- os.Getenv("DEVRITES_CODEX_GENERIC_AGENT_COMPAT") != "1" ||
1616
- in.SessionID == "" || in.AgentID == "" {
1617
- return binding
1618
- }
1619
- root, ok := agentDispatchRoot()
1620
- if !ok {
1621
- return devritesAgentGuardBinding{Kind: devritesAgentInvalid}
1622
- }
1623
- attempt, found, err := boundDispatchAttempt(root, in.SessionID, in.AgentID)
1624
- if err != nil {
1625
- return devritesAgentGuardBinding{Kind: devritesAgentInvalid}
1626
- }
1627
- if !found {
1628
- return binding
1629
- }
1630
- if attempt.Role == "devrites-slice-wright" {
1631
- return devritesAgentGuardBinding{Kind: devritesAgentGenericWright, Role: attempt.Role}
1632
- }
1633
- if strings.HasPrefix(attempt.Role, "devrites-") {
1634
- return devritesAgentGuardBinding{Kind: devritesAgentReadonly, Role: attempt.Role}
1635
- }
1636
- return devritesAgentGuardBinding{Kind: devritesAgentInvalid}
1637
- }