devrites 3.2.28 → 4.0.1

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 +192 -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 +145 -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 +23 -20
  58. package/engine/internal/hostpack/hostpack_test.go +6 -6
  59. package/engine/internal/install/install.go +321 -510
  60. package/engine/internal/install/install_test.go +346 -306
  61. package/engine/internal/lib/candidate.go +475 -0
  62. package/engine/internal/lib/candidate_test.go +503 -0
  63. package/engine/internal/lib/closeout.go +17 -21
  64. package/engine/internal/lib/closeout_test.go +23 -0
  65. package/engine/internal/lib/cursor_compat_test.go +12 -75
  66. package/engine/internal/lib/evidencefresh.go +58 -47
  67. package/engine/internal/lib/evidencefresh_test.go +120 -0
  68. package/engine/internal/lib/resolve.go +13 -35
  69. package/engine/internal/lib/resolve_remediation_test.go +5 -17
  70. package/engine/internal/lib/secretscan.go +432 -0
  71. package/engine/internal/lib/secretscan_test.go +555 -0
  72. package/engine/internal/lib/util.go +24 -65
  73. package/engine/internal/lib/util_test.go +44 -0
  74. package/engine/internal/markdowntext/markdowntext.go +108 -0
  75. package/engine/internal/markdowntext/markdowntext_test.go +58 -0
  76. package/engine/internal/markdowntext/testdata/structural.json +27 -0
  77. package/engine/internal/reason/reason.go +2 -123
  78. package/engine/internal/reason/reason_test.go +4 -2
  79. package/engine/internal/rootfacts/facts.go +6 -8
  80. package/engine/internal/rootfacts/facts_test.go +20 -0
  81. package/engine/internal/state/cmd/workflowmanifest/main.go +10 -12
  82. package/engine/internal/state/cursor.go +53 -14
  83. package/engine/internal/state/cursor_test.go +57 -0
  84. package/engine/internal/state/feature.go +64 -154
  85. package/engine/internal/state/schema.go +24 -50
  86. package/engine/internal/state/state_test.go +151 -140
  87. package/engine/internal/state/status.go +4 -17
  88. package/engine/internal/state/workflow_manifest.json +9 -89
  89. package/engine/main.go +73 -502
  90. package/engine/root_routing_test.go +183 -212
  91. package/engine/testdata/fixtures/basic/devrites-root/{features/auth-tokens/feature.md → work/auth-tokens/README.md} +0 -1
  92. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/architecture.md +3 -0
  93. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/assumptions.md +3 -0
  94. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/brief.md +3 -0
  95. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/decision-coverage.md +3 -0
  96. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/eng-review.md +3 -0
  97. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/questions.md +3 -0
  98. package/engine/testdata/fixtures/basic/devrites-root/{features/auth-tokens/status.md → work/auth-tokens/state.md} +3 -0
  99. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/test-plan.md +3 -0
  100. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/traceability.md +3 -0
  101. package/engine/testdata/fixtures/basic/devrites-root/{features/search-ranking/feature.md → work/search-ranking/README.md} +0 -1
  102. package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/assumptions.md +3 -0
  103. package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/brief.md +3 -0
  104. package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/decisions.md +3 -0
  105. package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/questions.md +3 -0
  106. package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/state.md +2 -0
  107. package/engine/testdata/golden/TestParityCloseOut/active_pointed_elsewhere.golden +1 -1
  108. package/engine/testdata/golden/TestParityCloseOut/active_slug_cleared.golden +1 -1
  109. package/engine/tests/adr_0003_gate_exit_code_test.go +2 -2
  110. package/engine/tests/adr_0011_define_plan_test.go +2 -2
  111. package/engine/tests/adr_0027_readiness_binding_test.go +106 -0
  112. package/engine/tests/cli_test.go +24 -107
  113. package/engine/tests/gate_test.go +177 -200
  114. package/engine/tests/meta_test.go +5 -35
  115. package/engine/tests/parity_closeout_test.go +12 -12
  116. package/engine/tests/parity_resolve_test.go +6 -16
  117. package/engine/tests/parity_test.go +8 -302
  118. package/install.sh +201 -34
  119. package/pack/.claude/agents/devrites-code-reviewer.md +9 -18
  120. package/pack/.claude/agents/devrites-devex-reviewer.md +10 -18
  121. package/pack/.claude/agents/devrites-doubt-reviewer.md +10 -20
  122. package/pack/.claude/agents/devrites-evidence-scout.md +14 -26
  123. package/pack/.claude/agents/devrites-frontend-reviewer.md +9 -15
  124. package/pack/.claude/agents/devrites-performance-reviewer.md +13 -21
  125. package/pack/.claude/agents/devrites-plan-drafter.md +24 -28
  126. package/pack/.claude/agents/devrites-plan-reviewer.md +30 -47
  127. package/pack/.claude/agents/devrites-proof-runner.md +41 -47
  128. package/pack/.claude/agents/devrites-retrospector.md +34 -79
  129. package/pack/.claude/agents/devrites-security-auditor.md +9 -17
  130. package/pack/.claude/agents/devrites-simplifier-reviewer.md +9 -18
  131. package/pack/.claude/agents/devrites-slice-wright.md +44 -62
  132. package/pack/.claude/agents/devrites-spec-reviewer.md +38 -50
  133. package/pack/.claude/agents/devrites-strategy-reviewer.md +10 -19
  134. package/pack/.claude/agents/devrites-test-analyst.md +22 -21
  135. package/pack/.claude/agents/devrites-upgrade-planner.md +52 -71
  136. package/pack/.claude/settings.json +12 -100
  137. package/pack/.claude/skills/devrites-api-interface/SKILL.md +1 -2
  138. package/pack/.claude/skills/devrites-audit/SKILL.md +6 -9
  139. package/pack/.claude/skills/devrites-browser-proof/SKILL.md +0 -1
  140. package/pack/.claude/skills/devrites-debug-recovery/SKILL.md +21 -27
  141. package/pack/.claude/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +22 -33
  142. package/pack/.claude/skills/devrites-doubt/SKILL.md +4 -6
  143. package/pack/.claude/skills/devrites-frontend-craft/SKILL.md +1 -2
  144. package/pack/.claude/skills/devrites-frontend-craft/reference/design-references.md +2 -1
  145. package/pack/.claude/skills/devrites-interview/SKILL.md +31 -36
  146. package/pack/.claude/skills/devrites-lib/SKILL.md +37 -66
  147. package/pack/.claude/skills/devrites-lib/reference/candidate-integrity.md +51 -0
  148. package/pack/.claude/skills/devrites-lib/reference/intent-map.md +20 -12
  149. package/pack/.claude/skills/devrites-lib/reference/orchestration-profiles.md +22 -0
  150. package/pack/.claude/skills/devrites-lib/reference/parallel-dispatch.md +35 -110
  151. package/pack/.claude/skills/devrites-lib/reference/reply-contract.md +26 -93
  152. package/pack/.claude/skills/devrites-lib/reference/standards/README.md +6 -3
  153. package/pack/.claude/skills/devrites-lib/reference/standards/afk-hitl.md +43 -34
  154. package/pack/.claude/skills/devrites-lib/reference/standards/agents.md +90 -216
  155. package/pack/.claude/skills/devrites-lib/reference/standards/anti-patterns.md +1 -1
  156. package/pack/.claude/skills/devrites-lib/reference/standards/code-review.md +9 -28
  157. package/pack/.claude/skills/devrites-lib/reference/standards/context-hygiene.md +13 -11
  158. package/pack/.claude/skills/devrites-lib/reference/standards/core.md +59 -54
  159. package/pack/.claude/skills/devrites-lib/reference/standards/hooks.md +3 -5
  160. package/pack/.claude/skills/devrites-lib/reference/standards/principles.md +29 -143
  161. package/pack/.claude/skills/devrites-lib/reference/standards/release/ship-checklist.md +5 -6
  162. package/pack/.claude/skills/devrites-lib/reference/standards/security.md +13 -31
  163. package/pack/.claude/skills/devrites-lib/reference/standards/skill-authoring.md +112 -82
  164. package/pack/.claude/skills/devrites-lib/reference/standards/spec-grammar.md +71 -45
  165. package/pack/.claude/skills/devrites-lib/reference/standards/testing.md +22 -11
  166. package/pack/.claude/skills/devrites-lib/reference/standards/tooling.md +15 -24
  167. package/pack/.claude/skills/devrites-lib/reference/workspace-artifact-schema.md +106 -37
  168. package/pack/.claude/skills/devrites-prose-craft/SKILL.md +3 -4
  169. package/pack/.claude/skills/devrites-source-driven/SKILL.md +3 -4
  170. package/pack/.claude/skills/devrites-ux-shape/SKILL.md +3 -3
  171. package/pack/.claude/skills/rite/SKILL.md +12 -13
  172. package/pack/.claude/skills/rite/reference/menu.md +11 -11
  173. package/pack/.claude/skills/rite-adopt/SKILL.md +29 -84
  174. package/pack/.claude/skills/rite-adopt/reference/adoption.md +16 -50
  175. package/pack/.claude/skills/rite-autocomplete/SKILL.md +65 -54
  176. package/pack/.claude/skills/rite-autocomplete/reference/loop.md +24 -17
  177. package/pack/.claude/skills/rite-autocomplete/reference/stop-conditions.md +18 -21
  178. package/pack/.claude/skills/rite-build/SKILL.md +28 -34
  179. package/pack/.claude/skills/rite-build/reference/afk-discipline.md +36 -34
  180. package/pack/.claude/skills/rite-build/reference/anti-patterns.md +5 -19
  181. package/pack/.claude/skills/rite-build/reference/checkpoint-protocol.md +10 -9
  182. package/pack/.claude/skills/rite-build/reference/checkpoint.md +17 -17
  183. package/pack/.claude/skills/rite-build/reference/frontend-trigger.md +3 -3
  184. package/pack/.claude/skills/rite-build/reference/one-slice-cycle.md +11 -14
  185. package/pack/.claude/skills/rite-build/reference/output.md +14 -24
  186. package/pack/.claude/skills/rite-build/reference/phase-contract.md +49 -188
  187. package/pack/.claude/skills/rite-build/reference/spec-drift-guard.md +1 -1
  188. package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +43 -133
  189. package/pack/.claude/skills/rite-clarify/SKILL.md +46 -40
  190. package/pack/.claude/skills/rite-clarify/reference/decision-coverage.md +4 -10
  191. package/pack/.claude/skills/rite-converge/SKILL.md +9 -33
  192. package/pack/.claude/skills/rite-converge/reference/convergence-assessment.md +2 -2
  193. package/pack/.claude/skills/rite-customize/SKILL.md +34 -50
  194. package/pack/.claude/skills/rite-define/SKILL.md +34 -58
  195. package/pack/.claude/skills/rite-define/reference/plan-template.md +15 -0
  196. package/pack/.claude/skills/rite-doctor/SKILL.md +52 -64
  197. package/pack/.claude/skills/rite-dogfood/SKILL.md +1 -2
  198. package/pack/.claude/skills/rite-explain/SKILL.md +1 -20
  199. package/pack/.claude/skills/rite-frame/SKILL.md +1 -7
  200. package/pack/.claude/skills/rite-frame/reference/failure-modes.md +1 -1
  201. package/pack/.claude/skills/rite-handoff/SKILL.md +1 -2
  202. package/pack/.claude/skills/rite-handoff/reference/handoff-template.md +1 -1
  203. package/pack/.claude/skills/rite-learn/SKILL.md +44 -104
  204. package/pack/.claude/skills/rite-plan/SKILL.md +14 -26
  205. package/pack/.claude/skills/rite-plan/reference/task-breakdown.md +4 -1
  206. package/pack/.claude/skills/rite-polish/SKILL.md +20 -28
  207. package/pack/.claude/skills/rite-polish/reference/adr-promotion.md +11 -0
  208. package/pack/{generated/claude/skills/rite-ship → .claude/skills/rite-polish}/reference/design-memory.md +23 -21
  209. package/pack/.claude/skills/rite-polish/reference/ledger.md +62 -0
  210. package/pack/.claude/skills/rite-pov/SKILL.md +28 -32
  211. package/pack/.claude/skills/rite-pr-feedback/SKILL.md +0 -1
  212. package/pack/.claude/skills/rite-pressure-test/SKILL.md +6 -13
  213. package/pack/.claude/skills/rite-prototype/SKILL.md +0 -6
  214. package/pack/.claude/skills/rite-prove/SKILL.md +73 -68
  215. package/pack/.claude/skills/rite-prove/reference/acceptance-proof.md +34 -30
  216. package/pack/.claude/skills/rite-prove/reference/test-command-discovery.md +15 -14
  217. package/pack/.claude/skills/rite-quick/SKILL.md +5 -19
  218. package/pack/.claude/skills/rite-resolve/SKILL.md +4 -21
  219. package/pack/.claude/skills/rite-resolve/reference/answer-protocol.md +2 -2
  220. package/pack/.claude/skills/rite-review/SKILL.md +34 -57
  221. package/pack/.claude/skills/rite-seal/SKILL.md +54 -87
  222. package/pack/.claude/skills/rite-seal/reference/anti-patterns.md +2 -2
  223. package/pack/.claude/skills/rite-seal/reference/final-evidence.md +31 -56
  224. package/pack/.claude/skills/rite-seal/reference/go-no-go.md +27 -51
  225. package/pack/.claude/skills/rite-seal/reference/output.md +2 -4
  226. package/pack/.claude/skills/rite-seal/reference/phase-contract.md +42 -140
  227. package/pack/.claude/skills/rite-seal/reference/risk-and-rollback.md +2 -2
  228. package/pack/.claude/skills/rite-seal/reference/seal-template.md +17 -28
  229. package/pack/.claude/skills/rite-ship/SKILL.md +65 -93
  230. package/pack/.claude/skills/rite-ship/reference/anti-patterns.md +4 -1
  231. package/pack/.claude/skills/rite-ship/reference/close-out.md +4 -12
  232. package/pack/.claude/skills/rite-ship/reference/git-ship.md +86 -48
  233. package/pack/.claude/skills/rite-ship/reference/ship-template.md +22 -16
  234. package/pack/.claude/skills/rite-spec/SKILL.md +62 -95
  235. package/pack/.claude/skills/rite-spec/reference/investigation.md +1 -4
  236. package/pack/.claude/skills/rite-spec/reference/question-protocol.md +21 -20
  237. package/pack/.claude/skills/rite-spec/reference/spec-checklists.md +16 -17
  238. package/pack/.claude/skills/rite-spec/reference/spec-template.md +47 -49
  239. package/pack/.claude/skills/rite-spec/reference/state-workspace.md +28 -13
  240. package/pack/.claude/skills/rite-status/SKILL.md +30 -88
  241. package/pack/.claude/skills/rite-temper/SKILL.md +12 -31
  242. package/pack/.claude/skills/rite-upgrade/SKILL.md +86 -101
  243. package/pack/.claude/skills/rite-vet/SKILL.md +88 -117
  244. package/pack/.claude/skills/rite-vet/reference/artifacts.md +56 -50
  245. package/pack/.claude/skills/rite-vet/reference/cross-model.md +15 -39
  246. package/pack/.claude/skills/rite-vet/reference/depth.md +2 -2
  247. package/pack/.claude/skills/rite-vet/reference/review-axes.md +3 -6
  248. package/pack/.claude/skills/rite-zoom-out/SKILL.md +0 -6
  249. package/pack/generated/README.md +2 -2
  250. package/pack/generated/claude/agents/devrites-code-reviewer.md +9 -18
  251. package/pack/generated/claude/agents/devrites-devex-reviewer.md +10 -18
  252. package/pack/generated/claude/agents/devrites-doubt-reviewer.md +10 -20
  253. package/pack/generated/claude/agents/devrites-evidence-scout.md +14 -26
  254. package/pack/generated/claude/agents/devrites-frontend-reviewer.md +9 -15
  255. package/pack/generated/claude/agents/devrites-performance-reviewer.md +13 -21
  256. package/pack/generated/claude/agents/devrites-plan-drafter.md +24 -28
  257. package/pack/generated/claude/agents/devrites-plan-reviewer.md +30 -47
  258. package/pack/generated/claude/agents/devrites-proof-runner.md +41 -47
  259. package/pack/generated/claude/agents/devrites-retrospector.md +34 -79
  260. package/pack/generated/claude/agents/devrites-security-auditor.md +9 -17
  261. package/pack/generated/claude/agents/devrites-simplifier-reviewer.md +9 -18
  262. package/pack/generated/claude/agents/devrites-slice-wright.md +44 -62
  263. package/pack/generated/claude/agents/devrites-spec-reviewer.md +38 -50
  264. package/pack/generated/claude/agents/devrites-strategy-reviewer.md +10 -19
  265. package/pack/generated/claude/agents/devrites-test-analyst.md +22 -21
  266. package/pack/generated/claude/agents/devrites-upgrade-planner.md +52 -71
  267. package/pack/generated/claude/settings.json +12 -100
  268. package/pack/generated/claude/skills/devrites-api-interface/SKILL.md +1 -2
  269. package/pack/generated/claude/skills/devrites-audit/SKILL.md +6 -9
  270. package/pack/generated/claude/skills/devrites-browser-proof/SKILL.md +0 -1
  271. package/pack/generated/claude/skills/devrites-debug-recovery/SKILL.md +21 -27
  272. package/pack/generated/claude/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +22 -33
  273. package/pack/generated/claude/skills/devrites-doubt/SKILL.md +4 -6
  274. package/pack/generated/claude/skills/devrites-frontend-craft/SKILL.md +1 -2
  275. package/pack/generated/claude/skills/devrites-frontend-craft/reference/design-references.md +2 -1
  276. package/pack/generated/claude/skills/devrites-interview/SKILL.md +31 -36
  277. package/pack/generated/claude/skills/devrites-lib/SKILL.md +37 -66
  278. package/pack/generated/claude/skills/devrites-lib/reference/candidate-integrity.md +51 -0
  279. package/pack/generated/claude/skills/devrites-lib/reference/intent-map.md +20 -12
  280. package/pack/generated/claude/skills/devrites-lib/reference/orchestration-profiles.md +22 -0
  281. package/pack/generated/claude/skills/devrites-lib/reference/parallel-dispatch.md +35 -110
  282. package/pack/generated/claude/skills/devrites-lib/reference/reply-contract.md +26 -93
  283. package/pack/generated/claude/skills/devrites-lib/reference/standards/README.md +6 -3
  284. package/pack/generated/claude/skills/devrites-lib/reference/standards/afk-hitl.md +43 -34
  285. package/pack/generated/claude/skills/devrites-lib/reference/standards/agents.md +90 -216
  286. package/pack/generated/claude/skills/devrites-lib/reference/standards/anti-patterns.md +1 -1
  287. package/pack/generated/claude/skills/devrites-lib/reference/standards/code-review.md +9 -28
  288. package/pack/generated/claude/skills/devrites-lib/reference/standards/context-hygiene.md +13 -11
  289. package/pack/generated/claude/skills/devrites-lib/reference/standards/core.md +59 -54
  290. package/pack/generated/claude/skills/devrites-lib/reference/standards/hooks.md +3 -5
  291. package/pack/generated/claude/skills/devrites-lib/reference/standards/principles.md +29 -143
  292. package/pack/generated/claude/skills/devrites-lib/reference/standards/release/ship-checklist.md +5 -6
  293. package/pack/generated/claude/skills/devrites-lib/reference/standards/security.md +13 -31
  294. package/pack/generated/claude/skills/devrites-lib/reference/standards/skill-authoring.md +112 -82
  295. package/pack/generated/claude/skills/devrites-lib/reference/standards/spec-grammar.md +71 -45
  296. package/pack/generated/claude/skills/devrites-lib/reference/standards/testing.md +22 -11
  297. package/pack/generated/claude/skills/devrites-lib/reference/standards/tooling.md +15 -24
  298. package/pack/generated/claude/skills/devrites-lib/reference/workspace-artifact-schema.md +106 -37
  299. package/pack/generated/claude/skills/devrites-prose-craft/SKILL.md +3 -4
  300. package/pack/generated/claude/skills/devrites-source-driven/SKILL.md +3 -4
  301. package/pack/generated/claude/skills/devrites-ux-shape/SKILL.md +3 -3
  302. package/pack/generated/claude/skills/rite/SKILL.md +12 -13
  303. package/pack/generated/claude/skills/rite/reference/menu.md +11 -11
  304. package/pack/generated/claude/skills/rite-adopt/SKILL.md +29 -84
  305. package/pack/generated/claude/skills/rite-adopt/reference/adoption.md +16 -50
  306. package/pack/generated/claude/skills/rite-autocomplete/SKILL.md +65 -54
  307. package/pack/generated/claude/skills/rite-autocomplete/reference/loop.md +24 -17
  308. package/pack/generated/claude/skills/rite-autocomplete/reference/stop-conditions.md +18 -21
  309. package/pack/generated/claude/skills/rite-build/SKILL.md +28 -34
  310. package/pack/generated/claude/skills/rite-build/reference/afk-discipline.md +36 -34
  311. package/pack/generated/claude/skills/rite-build/reference/anti-patterns.md +5 -19
  312. package/pack/generated/claude/skills/rite-build/reference/checkpoint-protocol.md +10 -9
  313. package/pack/generated/claude/skills/rite-build/reference/checkpoint.md +17 -17
  314. package/pack/generated/claude/skills/rite-build/reference/frontend-trigger.md +3 -3
  315. package/pack/generated/claude/skills/rite-build/reference/one-slice-cycle.md +11 -14
  316. package/pack/generated/claude/skills/rite-build/reference/output.md +14 -24
  317. package/pack/generated/claude/skills/rite-build/reference/phase-contract.md +49 -188
  318. package/pack/generated/claude/skills/rite-build/reference/spec-drift-guard.md +1 -1
  319. package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +43 -133
  320. package/pack/generated/claude/skills/rite-clarify/SKILL.md +46 -40
  321. package/pack/generated/claude/skills/rite-clarify/reference/decision-coverage.md +4 -10
  322. package/pack/generated/claude/skills/rite-converge/SKILL.md +9 -33
  323. package/pack/generated/claude/skills/rite-converge/reference/convergence-assessment.md +2 -2
  324. package/pack/generated/claude/skills/rite-customize/SKILL.md +34 -50
  325. package/pack/generated/claude/skills/rite-define/SKILL.md +34 -58
  326. package/pack/generated/claude/skills/rite-define/reference/plan-template.md +15 -0
  327. package/pack/generated/claude/skills/rite-doctor/SKILL.md +52 -64
  328. package/pack/generated/claude/skills/rite-dogfood/SKILL.md +1 -2
  329. package/pack/generated/claude/skills/rite-explain/SKILL.md +1 -20
  330. package/pack/generated/claude/skills/rite-frame/SKILL.md +1 -7
  331. package/pack/generated/claude/skills/rite-frame/reference/failure-modes.md +1 -1
  332. package/pack/generated/claude/skills/rite-handoff/SKILL.md +1 -2
  333. package/pack/generated/claude/skills/rite-handoff/reference/handoff-template.md +1 -1
  334. package/pack/generated/claude/skills/rite-learn/SKILL.md +44 -104
  335. package/pack/generated/claude/skills/rite-plan/SKILL.md +14 -26
  336. package/pack/generated/claude/skills/rite-plan/reference/task-breakdown.md +4 -1
  337. package/pack/generated/claude/skills/rite-polish/SKILL.md +20 -28
  338. package/pack/generated/claude/skills/rite-polish/reference/adr-promotion.md +11 -0
  339. package/pack/generated/{codex/skills/rite-ship → claude/skills/rite-polish}/reference/design-memory.md +23 -21
  340. package/pack/generated/claude/skills/rite-polish/reference/ledger.md +62 -0
  341. package/pack/generated/claude/skills/rite-pov/SKILL.md +28 -32
  342. package/pack/generated/claude/skills/rite-pr-feedback/SKILL.md +0 -1
  343. package/pack/generated/claude/skills/rite-pressure-test/SKILL.md +6 -13
  344. package/pack/generated/claude/skills/rite-prototype/SKILL.md +0 -6
  345. package/pack/generated/claude/skills/rite-prove/SKILL.md +73 -68
  346. package/pack/generated/claude/skills/rite-prove/reference/acceptance-proof.md +34 -30
  347. package/pack/generated/claude/skills/rite-prove/reference/test-command-discovery.md +15 -14
  348. package/pack/generated/claude/skills/rite-quick/SKILL.md +5 -19
  349. package/pack/generated/claude/skills/rite-resolve/SKILL.md +4 -21
  350. package/pack/generated/claude/skills/rite-resolve/reference/answer-protocol.md +2 -2
  351. package/pack/generated/claude/skills/rite-review/SKILL.md +34 -57
  352. package/pack/generated/claude/skills/rite-seal/SKILL.md +54 -87
  353. package/pack/generated/claude/skills/rite-seal/reference/anti-patterns.md +2 -2
  354. package/pack/generated/claude/skills/rite-seal/reference/final-evidence.md +31 -56
  355. package/pack/generated/claude/skills/rite-seal/reference/go-no-go.md +27 -51
  356. package/pack/generated/claude/skills/rite-seal/reference/output.md +2 -4
  357. package/pack/generated/claude/skills/rite-seal/reference/phase-contract.md +42 -140
  358. package/pack/generated/claude/skills/rite-seal/reference/risk-and-rollback.md +2 -2
  359. package/pack/generated/claude/skills/rite-seal/reference/seal-template.md +17 -28
  360. package/pack/generated/claude/skills/rite-ship/SKILL.md +65 -93
  361. package/pack/generated/claude/skills/rite-ship/reference/anti-patterns.md +4 -1
  362. package/pack/generated/claude/skills/rite-ship/reference/close-out.md +4 -12
  363. package/pack/generated/claude/skills/rite-ship/reference/git-ship.md +86 -48
  364. package/pack/generated/claude/skills/rite-ship/reference/ship-template.md +22 -16
  365. package/pack/generated/claude/skills/rite-spec/SKILL.md +62 -95
  366. package/pack/generated/claude/skills/rite-spec/reference/investigation.md +1 -4
  367. package/pack/generated/claude/skills/rite-spec/reference/question-protocol.md +21 -20
  368. package/pack/generated/claude/skills/rite-spec/reference/spec-checklists.md +16 -17
  369. package/pack/generated/claude/skills/rite-spec/reference/spec-template.md +47 -49
  370. package/pack/generated/claude/skills/rite-spec/reference/state-workspace.md +28 -13
  371. package/pack/generated/claude/skills/rite-status/SKILL.md +30 -88
  372. package/pack/generated/claude/skills/rite-temper/SKILL.md +12 -31
  373. package/pack/generated/claude/skills/rite-upgrade/SKILL.md +86 -101
  374. package/pack/generated/claude/skills/rite-vet/SKILL.md +88 -117
  375. package/pack/generated/claude/skills/rite-vet/reference/artifacts.md +56 -50
  376. package/pack/generated/claude/skills/rite-vet/reference/cross-model.md +15 -39
  377. package/pack/generated/claude/skills/rite-vet/reference/depth.md +2 -2
  378. package/pack/generated/claude/skills/rite-vet/reference/review-axes.md +3 -6
  379. package/pack/generated/claude/skills/rite-zoom-out/SKILL.md +0 -6
  380. package/pack/generated/codex/AGENTS.md +5 -4
  381. package/pack/generated/codex/agents/devrites-code-reviewer.toml +8 -27
  382. package/pack/generated/codex/agents/devrites-devex-reviewer.toml +9 -27
  383. package/pack/generated/codex/agents/devrites-doubt-reviewer.toml +9 -29
  384. package/pack/generated/codex/agents/devrites-evidence-scout.toml +14 -36
  385. package/pack/generated/codex/agents/devrites-frontend-reviewer.toml +8 -24
  386. package/pack/generated/codex/agents/devrites-performance-reviewer.toml +12 -30
  387. package/pack/generated/codex/agents/devrites-plan-drafter.toml +24 -38
  388. package/pack/generated/codex/agents/devrites-plan-reviewer.toml +30 -57
  389. package/pack/generated/codex/agents/devrites-proof-runner.toml +41 -57
  390. package/pack/generated/codex/agents/devrites-retrospector.toml +34 -89
  391. package/pack/generated/codex/agents/devrites-security-auditor.toml +8 -26
  392. package/pack/generated/codex/agents/devrites-simplifier-reviewer.toml +8 -27
  393. package/pack/generated/codex/agents/devrites-slice-wright.toml +44 -71
  394. package/pack/generated/codex/agents/devrites-spec-reviewer.toml +38 -60
  395. package/pack/generated/codex/agents/devrites-strategy-reviewer.toml +9 -28
  396. package/pack/generated/codex/agents/devrites-test-analyst.toml +21 -30
  397. package/pack/generated/codex/agents/devrites-upgrade-planner.toml +52 -81
  398. package/pack/generated/codex/config.toml +7 -0
  399. package/pack/generated/codex/skills/devrites-api-interface/SKILL.md +1 -21
  400. package/pack/generated/codex/skills/devrites-audit/SKILL.md +6 -28
  401. package/pack/generated/codex/skills/devrites-browser-proof/SKILL.md +1 -21
  402. package/pack/generated/codex/skills/devrites-debug-recovery/SKILL.md +21 -46
  403. package/pack/generated/codex/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +22 -33
  404. package/pack/generated/codex/skills/devrites-doubt/SKILL.md +4 -25
  405. package/pack/generated/codex/skills/devrites-frontend-craft/SKILL.md +1 -21
  406. package/pack/generated/codex/skills/devrites-frontend-craft/reference/design-references.md +2 -1
  407. package/pack/generated/codex/skills/devrites-interview/SKILL.md +32 -56
  408. package/pack/generated/codex/skills/devrites-lib/SKILL.md +38 -86
  409. package/pack/generated/codex/skills/devrites-lib/reference/candidate-integrity.md +51 -0
  410. package/pack/generated/codex/skills/devrites-lib/reference/intent-map.md +20 -12
  411. package/pack/generated/codex/skills/devrites-lib/reference/orchestration-profiles.md +22 -0
  412. package/pack/generated/codex/skills/devrites-lib/reference/parallel-dispatch.md +35 -110
  413. package/pack/generated/codex/skills/devrites-lib/reference/reply-contract.md +26 -93
  414. package/pack/generated/codex/skills/devrites-lib/reference/standards/README.md +6 -3
  415. package/pack/generated/codex/skills/devrites-lib/reference/standards/afk-hitl.md +43 -34
  416. package/pack/generated/codex/skills/devrites-lib/reference/standards/agents.md +90 -216
  417. package/pack/generated/codex/skills/devrites-lib/reference/standards/anti-patterns.md +1 -1
  418. package/pack/generated/codex/skills/devrites-lib/reference/standards/code-review.md +9 -28
  419. package/pack/generated/codex/skills/devrites-lib/reference/standards/context-hygiene.md +13 -11
  420. package/pack/generated/codex/skills/devrites-lib/reference/standards/core.md +59 -54
  421. package/pack/generated/codex/skills/devrites-lib/reference/standards/hooks.md +3 -5
  422. package/pack/generated/codex/skills/devrites-lib/reference/standards/principles.md +29 -143
  423. package/pack/generated/codex/skills/devrites-lib/reference/standards/release/ship-checklist.md +5 -6
  424. package/pack/generated/codex/skills/devrites-lib/reference/standards/security.md +13 -31
  425. package/pack/generated/codex/skills/devrites-lib/reference/standards/skill-authoring.md +112 -82
  426. package/pack/generated/codex/skills/devrites-lib/reference/standards/spec-grammar.md +71 -45
  427. package/pack/generated/codex/skills/devrites-lib/reference/standards/testing.md +22 -11
  428. package/pack/generated/codex/skills/devrites-lib/reference/standards/tooling.md +15 -24
  429. package/pack/generated/codex/skills/devrites-lib/reference/workspace-artifact-schema.md +106 -37
  430. package/pack/generated/codex/skills/devrites-prose-craft/SKILL.md +3 -23
  431. package/pack/generated/codex/skills/devrites-source-driven/SKILL.md +3 -23
  432. package/pack/generated/codex/skills/devrites-ux-shape/SKILL.md +3 -22
  433. package/pack/generated/codex/skills/rite/SKILL.md +12 -32
  434. package/pack/generated/codex/skills/rite/reference/menu.md +11 -11
  435. package/pack/generated/codex/skills/rite-adopt/SKILL.md +29 -103
  436. package/pack/generated/codex/skills/rite-adopt/reference/adoption.md +16 -50
  437. package/pack/generated/codex/skills/rite-autocomplete/SKILL.md +65 -73
  438. package/pack/generated/codex/skills/rite-autocomplete/reference/loop.md +24 -17
  439. package/pack/generated/codex/skills/rite-autocomplete/reference/stop-conditions.md +18 -21
  440. package/pack/generated/codex/skills/rite-build/SKILL.md +28 -53
  441. package/pack/generated/codex/skills/rite-build/reference/afk-discipline.md +36 -34
  442. package/pack/generated/codex/skills/rite-build/reference/anti-patterns.md +5 -19
  443. package/pack/generated/codex/skills/rite-build/reference/checkpoint-protocol.md +10 -9
  444. package/pack/generated/codex/skills/rite-build/reference/checkpoint.md +17 -17
  445. package/pack/generated/codex/skills/rite-build/reference/frontend-trigger.md +3 -3
  446. package/pack/generated/codex/skills/rite-build/reference/one-slice-cycle.md +11 -14
  447. package/pack/generated/codex/skills/rite-build/reference/output.md +14 -24
  448. package/pack/generated/codex/skills/rite-build/reference/phase-contract.md +49 -188
  449. package/pack/generated/codex/skills/rite-build/reference/spec-drift-guard.md +1 -1
  450. package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +43 -133
  451. package/pack/generated/codex/skills/rite-clarify/SKILL.md +46 -59
  452. package/pack/generated/codex/skills/rite-clarify/reference/decision-coverage.md +4 -10
  453. package/pack/generated/codex/skills/rite-converge/SKILL.md +9 -52
  454. package/pack/generated/codex/skills/rite-converge/reference/convergence-assessment.md +2 -2
  455. package/pack/generated/codex/skills/rite-customize/SKILL.md +32 -67
  456. package/pack/generated/codex/skills/rite-define/SKILL.md +34 -77
  457. package/pack/generated/codex/skills/rite-define/reference/plan-template.md +15 -0
  458. package/pack/generated/codex/skills/rite-doctor/SKILL.md +52 -83
  459. package/pack/generated/codex/skills/rite-dogfood/SKILL.md +1 -21
  460. package/pack/generated/codex/skills/rite-explain/SKILL.md +1 -39
  461. package/pack/generated/codex/skills/rite-frame/SKILL.md +1 -26
  462. package/pack/generated/codex/skills/rite-frame/reference/failure-modes.md +1 -1
  463. package/pack/generated/codex/skills/rite-handoff/SKILL.md +1 -21
  464. package/pack/generated/codex/skills/rite-handoff/reference/handoff-template.md +1 -1
  465. package/pack/generated/codex/skills/rite-learn/SKILL.md +44 -123
  466. package/pack/generated/codex/skills/rite-plan/SKILL.md +14 -45
  467. package/pack/generated/codex/skills/rite-plan/reference/task-breakdown.md +4 -1
  468. package/pack/generated/codex/skills/rite-polish/SKILL.md +20 -47
  469. package/pack/generated/codex/skills/rite-polish/reference/adr-promotion.md +11 -0
  470. package/pack/{.claude/skills/rite-ship → generated/codex/skills/rite-polish}/reference/design-memory.md +23 -21
  471. package/pack/generated/codex/skills/rite-polish/reference/ledger.md +62 -0
  472. package/pack/generated/codex/skills/rite-pov/SKILL.md +28 -51
  473. package/pack/generated/codex/skills/rite-pr-feedback/SKILL.md +0 -20
  474. package/pack/generated/codex/skills/rite-pressure-test/SKILL.md +6 -32
  475. package/pack/generated/codex/skills/rite-prototype/SKILL.md +0 -25
  476. package/pack/generated/codex/skills/rite-prove/SKILL.md +73 -87
  477. package/pack/generated/codex/skills/rite-prove/reference/acceptance-proof.md +34 -30
  478. package/pack/generated/codex/skills/rite-prove/reference/test-command-discovery.md +15 -14
  479. package/pack/generated/codex/skills/rite-quick/SKILL.md +5 -38
  480. package/pack/generated/codex/skills/rite-resolve/SKILL.md +4 -40
  481. package/pack/generated/codex/skills/rite-resolve/reference/answer-protocol.md +2 -2
  482. package/pack/generated/codex/skills/rite-review/SKILL.md +34 -76
  483. package/pack/generated/codex/skills/rite-seal/SKILL.md +51 -103
  484. package/pack/generated/codex/skills/rite-seal/reference/anti-patterns.md +2 -2
  485. package/pack/generated/codex/skills/rite-seal/reference/final-evidence.md +31 -56
  486. package/pack/generated/codex/skills/rite-seal/reference/go-no-go.md +27 -51
  487. package/pack/generated/codex/skills/rite-seal/reference/output.md +2 -4
  488. package/pack/generated/codex/skills/rite-seal/reference/phase-contract.md +42 -140
  489. package/pack/generated/codex/skills/rite-seal/reference/risk-and-rollback.md +2 -2
  490. package/pack/generated/codex/skills/rite-seal/reference/seal-template.md +17 -28
  491. package/pack/generated/codex/skills/rite-ship/SKILL.md +65 -112
  492. package/pack/generated/codex/skills/rite-ship/reference/anti-patterns.md +4 -1
  493. package/pack/generated/codex/skills/rite-ship/reference/close-out.md +4 -12
  494. package/pack/generated/codex/skills/rite-ship/reference/git-ship.md +86 -48
  495. package/pack/generated/codex/skills/rite-ship/reference/ship-template.md +22 -16
  496. package/pack/generated/codex/skills/rite-spec/SKILL.md +62 -114
  497. package/pack/generated/codex/skills/rite-spec/reference/investigation.md +1 -4
  498. package/pack/generated/codex/skills/rite-spec/reference/question-protocol.md +21 -20
  499. package/pack/generated/codex/skills/rite-spec/reference/spec-checklists.md +16 -17
  500. package/pack/generated/codex/skills/rite-spec/reference/spec-template.md +47 -49
  501. package/pack/generated/codex/skills/rite-spec/reference/state-workspace.md +28 -13
  502. package/pack/generated/codex/skills/rite-status/SKILL.md +30 -107
  503. package/pack/generated/codex/skills/rite-temper/SKILL.md +12 -50
  504. package/pack/generated/codex/skills/rite-upgrade/SKILL.md +86 -120
  505. package/pack/generated/codex/skills/rite-vet/SKILL.md +88 -136
  506. package/pack/generated/codex/skills/rite-vet/reference/artifacts.md +56 -50
  507. package/pack/generated/codex/skills/rite-vet/reference/cross-model.md +15 -39
  508. package/pack/generated/codex/skills/rite-vet/reference/depth.md +2 -2
  509. package/pack/generated/codex/skills/rite-vet/reference/review-axes.md +3 -6
  510. package/pack/generated/codex/skills/rite-zoom-out/SKILL.md +0 -25
  511. package/package.json +6 -5
  512. package/scripts/build-host-artifacts.sh +5 -9
  513. package/scripts/codex-generate.sh +18 -278
  514. package/scripts/install-lib.sh +121 -34
  515. package/uninstall.sh +19 -17
  516. package/update.sh +26 -18
  517. package/docs/engine/agent-contract.md +0 -172
  518. package/docs/extensions.md +0 -178
  519. package/engine/git_guard.go +0 -187
  520. package/engine/git_guard_test.go +0 -283
  521. package/engine/harnessmatrix.go +0 -60
  522. package/engine/hookpolicy.go +0 -113
  523. package/engine/hookpolicy_test.go +0 -160
  524. package/engine/hooks.go +0 -677
  525. package/engine/hooks_agent_dispatch.go +0 -1726
  526. package/engine/hooks_events_test.go +0 -431
  527. package/engine/hooks_workspace.go +0 -1196
  528. package/engine/internal/doctor/doctor.go +0 -306
  529. package/engine/internal/doctor/doctor_test.go +0 -252
  530. package/engine/internal/forge/forge.go +0 -938
  531. package/engine/internal/forge/forge_test.go +0 -576
  532. package/engine/internal/forge/git.go +0 -245
  533. package/engine/internal/forge/liveness_unix.go +0 -48
  534. package/engine/internal/forge/liveness_windows.go +0 -67
  535. package/engine/internal/forge/manifest.go +0 -402
  536. package/engine/internal/harness/compliance.go +0 -96
  537. package/engine/internal/harness/harness.go +0 -301
  538. package/engine/internal/harness/harness_test.go +0 -124
  539. package/engine/internal/iohooks/detach_other.go +0 -11
  540. package/engine/internal/iohooks/detach_unix.go +0 -11
  541. package/engine/internal/iohooks/iohooks.go +0 -839
  542. package/engine/internal/iohooks/iohooks_test.go +0 -561
  543. package/engine/internal/lib/adoption.go +0 -659
  544. package/engine/internal/lib/adoption_test.go +0 -121
  545. package/engine/internal/lib/analyze.go +0 -232
  546. package/engine/internal/lib/archivesearch.go +0 -125
  547. package/engine/internal/lib/archivesearch_test.go +0 -138
  548. package/engine/internal/lib/budget.go +0 -172
  549. package/engine/internal/lib/buildreadiness.go +0 -111
  550. package/engine/internal/lib/checkacceptance.go +0 -176
  551. package/engine/internal/lib/clarifyreturn.go +0 -91
  552. package/engine/internal/lib/clarifyreturn_test.go +0 -123
  553. package/engine/internal/lib/command_other.go +0 -7
  554. package/engine/internal/lib/command_unix.go +0 -24
  555. package/engine/internal/lib/context.go +0 -280
  556. package/engine/internal/lib/conventions.go +0 -449
  557. package/engine/internal/lib/coverage.go +0 -145
  558. package/engine/internal/lib/dispatchwaive.go +0 -31
  559. package/engine/internal/lib/dispatchwaive_test.go +0 -23
  560. package/engine/internal/lib/doubtcoverage.go +0 -78
  561. package/engine/internal/lib/extensions.go +0 -622
  562. package/engine/internal/lib/extensions_test.go +0 -305
  563. package/engine/internal/lib/footprint.go +0 -239
  564. package/engine/internal/lib/gitauthority.go +0 -601
  565. package/engine/internal/lib/gitauthority_test.go +0 -346
  566. package/engine/internal/lib/jsonout.go +0 -129
  567. package/engine/internal/lib/jsonout_test.go +0 -73
  568. package/engine/internal/lib/lanes.go +0 -112
  569. package/engine/internal/lib/learnings.go +0 -397
  570. package/engine/internal/lib/learnings_inject_test.go +0 -77
  571. package/engine/internal/lib/learnings_remediation_test.go +0 -24
  572. package/engine/internal/lib/ledger.go +0 -311
  573. package/engine/internal/lib/ledger_test.go +0 -186
  574. package/engine/internal/lib/mutationgate.go +0 -89
  575. package/engine/internal/lib/observability_test.go +0 -358
  576. package/engine/internal/lib/overrides.go +0 -164
  577. package/engine/internal/lib/overrides_test.go +0 -112
  578. package/engine/internal/lib/preamble.go +0 -159
  579. package/engine/internal/lib/preamble_questions_test.go +0 -10
  580. package/engine/internal/lib/progress.go +0 -210
  581. package/engine/internal/lib/provenance.go +0 -462
  582. package/engine/internal/lib/provenance_test.go +0 -154
  583. package/engine/internal/lib/readiness_contract.json +0 -118
  584. package/engine/internal/lib/readinessartifact.go +0 -533
  585. package/engine/internal/lib/readinessartifact_test.go +0 -422
  586. package/engine/internal/lib/reconcile.go +0 -1496
  587. package/engine/internal/lib/reconcile_test.go +0 -1071
  588. package/engine/internal/lib/recoveryattempts.go +0 -298
  589. package/engine/internal/lib/recoveryattempts_test.go +0 -215
  590. package/engine/internal/lib/reviewers.go +0 -143
  591. package/engine/internal/lib/reviewers_test.go +0 -45
  592. package/engine/internal/lib/reviewerstats.go +0 -199
  593. package/engine/internal/lib/reviewerstats_test.go +0 -104
  594. package/engine/internal/lib/reviewintegrity.go +0 -143
  595. package/engine/internal/lib/reviewintegrity_test.go +0 -103
  596. package/engine/internal/lib/runbook.go +0 -295
  597. package/engine/internal/lib/runbook_context_test.go +0 -182
  598. package/engine/internal/lib/session.go +0 -1020
  599. package/engine/internal/lib/session_test.go +0 -177
  600. package/engine/internal/lib/specparse.go +0 -125
  601. package/engine/internal/lib/specskeleton.go +0 -79
  602. package/engine/internal/lib/specvalidate.go +0 -391
  603. package/engine/internal/lib/specvalidate_test.go +0 -185
  604. package/engine/internal/lib/stuck.go +0 -206
  605. package/engine/internal/lib/templates/conventions_drift.tmpl +0 -6
  606. package/engine/internal/lib/templates/conventions_ledger_header.md +0 -6
  607. package/engine/internal/lib/templates/conventions_orient_header.txt +0 -1
  608. package/engine/internal/lib/testintegrity.go +0 -199
  609. package/engine/internal/lib/testintegrity_test.go +0 -149
  610. package/engine/internal/lib/tickafk.go +0 -86
  611. package/engine/internal/migrate/migrate.go +0 -279
  612. package/engine/internal/migrate/migrate_test.go +0 -157
  613. package/engine/internal/orient/firstrun.go +0 -50
  614. package/engine/internal/orient/firstrun_test.go +0 -72
  615. package/engine/internal/orient/orient.go +0 -69
  616. package/engine/internal/orient/orient_test.go +0 -25
  617. package/engine/internal/profile/profile.go +0 -284
  618. package/engine/internal/profile/profile_test.go +0 -13
  619. package/engine/internal/state/clarify_transition.go +0 -165
  620. package/engine/internal/state/clarify_transition_test.go +0 -130
  621. package/engine/internal/state/snapshot.go +0 -457
  622. package/engine/internal/toolpolicy/classifier.go +0 -533
  623. package/engine/internal/toolpolicy/classifier_test.go +0 -424
  624. package/engine/internal/toolpolicy/git.go +0 -616
  625. package/engine/internal/toolpolicy/scanner.go +0 -382
  626. package/engine/observability_cli_test.go +0 -52
  627. package/engine/profile_test.go +0 -76
  628. package/engine/subagent_orient_context.txt +0 -8
  629. package/engine/testdata/golden/TestParityA1Guard/devrites-edit-allowed.golden +0 -1
  630. package/engine/testdata/golden/TestParityA1Guard/no-window-silent.golden +0 -1
  631. package/engine/testdata/golden/TestParityA1Guard/non-edit-tool.golden +0 -1
  632. package/engine/testdata/golden/TestParityA1Guard/observe-mode-silent.golden +0 -1
  633. package/engine/testdata/golden/TestParityA1Guard/source-edit-enforce-denies.golden +0 -2
  634. package/engine/testdata/golden/TestParityA1Guard/subagent-allowed.golden +0 -1
  635. package/engine/testdata/golden/TestParityAnalyze/noactive.golden +0 -1
  636. package/engine/testdata/golden/TestParityAnalyze/slug=covered.golden +0 -15
  637. package/engine/testdata/golden/TestParityAnalyze/slug=dangling.golden +0 -16
  638. package/engine/testdata/golden/TestParityAnalyze/slug=ghost.golden +0 -1
  639. package/engine/testdata/golden/TestParityAnalyze/slug=noac.golden +0 -15
  640. package/engine/testdata/golden/TestParityAnalyze/slug=order.golden +0 -17
  641. package/engine/testdata/golden/TestParityAnalyze/slug=orphan.golden +0 -16
  642. package/engine/testdata/golden/TestParityAnalyze/slug=uncovered.golden +0 -16
  643. package/engine/testdata/golden/TestParityBudget/default-active.golden +0 -4
  644. package/engine/testdata/golden/TestParityBudget/extra-md.golden +0 -5
  645. package/engine/testdata/golden/TestParityBudget/no-active.golden +0 -1
  646. package/engine/testdata/golden/TestParityBudget/no-feature.golden +0 -1
  647. package/engine/testdata/golden/TestParityBudget/over-advisory.golden +0 -6
  648. package/engine/testdata/golden/TestParityBudget/over-strict.golden +0 -6
  649. package/engine/testdata/golden/TestParityBudget/within.golden +0 -6
  650. package/engine/testdata/golden/TestParityBuildReadiness/arg=approved.golden +0 -2
  651. package/engine/testdata/golden/TestParityBuildReadiness/arg=approvenone.golden +0 -1
  652. package/engine/testdata/golden/TestParityBuildReadiness/arg=await.golden +0 -1
  653. package/engine/testdata/golden/TestParityBuildReadiness/arg=blocked.golden +0 -1
  654. package/engine/testdata/golden/TestParityBuildReadiness/arg=clarifyopen.golden +0 -1
  655. package/engine/testdata/golden/TestParityBuildReadiness/arg=emptystatus.golden +0 -2
  656. package/engine/testdata/golden/TestParityBuildReadiness/arg=ghost.golden +0 -1
  657. package/engine/testdata/golden/TestParityBuildReadiness/arg=legacycontract.golden +0 -1
  658. package/engine/testdata/golden/TestParityBuildReadiness/arg=noapprove.golden +0 -1
  659. package/engine/testdata/golden/TestParityBuildReadiness/arg=noclarify.golden +0 -1
  660. package/engine/testdata/golden/TestParityBuildReadiness/arg=novet.golden +0 -1
  661. package/engine/testdata/golden/TestParityBuildReadiness/arg=stalevet.golden +0 -1
  662. package/engine/testdata/golden/TestParityBuildReadiness/arg=trailhash.golden +0 -2
  663. package/engine/testdata/golden/TestParityBuildReadiness/arg=trailpipe.golden +0 -2
  664. package/engine/testdata/golden/TestParityBuildReadiness/arg=vetnotready.golden +0 -1
  665. package/engine/testdata/golden/TestParityCheckAcceptance/arg=blank-section.golden +0 -1
  666. package/engine/testdata/golden/TestParityCheckAcceptance/arg=empty.golden +0 -1
  667. package/engine/testdata/golden/TestParityCheckAcceptance/arg=flat-dropped.golden +0 -1
  668. package/engine/testdata/golden/TestParityCheckAcceptance/arg=flat-ok.golden +0 -2
  669. package/engine/testdata/golden/TestParityCheckAcceptance/arg=flat-unchecked.golden +0 -1
  670. package/engine/testdata/golden/TestParityCheckAcceptance/arg=id-gap.golden +0 -1
  671. package/engine/testdata/golden/TestParityCheckAcceptance/arg=id-ok.golden +0 -2
  672. package/engine/testdata/golden/TestParityCheckAcceptance/arg=no-seal.golden +0 -1
  673. package/engine/testdata/golden/TestParityCheckAcceptance/arg=no-section.golden +0 -1
  674. package/engine/testdata/golden/TestParityCheckAcceptance/arg=nonexistent.golden +0 -1
  675. package/engine/testdata/golden/TestParityCheckAcceptance.golden +0 -1
  676. package/engine/testdata/golden/TestParityCoverage/arg=flat.golden +0 -7
  677. package/engine/testdata/golden/TestParityCoverage/arg=full.golden +0 -10
  678. package/engine/testdata/golden/TestParityCoverage/arg=nonexistent.golden +0 -1
  679. package/engine/testdata/golden/TestParityCoverage/arg=nospec.golden +0 -1
  680. package/engine/testdata/golden/TestParityCoverage/arg=speconly.golden +0 -9
  681. package/engine/testdata/golden/TestParityCoverage.golden +0 -1
  682. package/engine/testdata/golden/TestParityCursor/bare.golden +0 -3
  683. package/engine/testdata/golden/TestParityCursor/full-afk.golden +0 -6
  684. package/engine/testdata/golden/TestParityCursor/full-hitl.golden +0 -5
  685. package/engine/testdata/golden/TestParityCursor/no-active.golden +0 -1
  686. package/engine/testdata/golden/TestParityCursor/red.golden +0 -5
  687. package/engine/testdata/golden/TestParityCursor/zerogates.golden +0 -5
  688. package/engine/testdata/golden/TestParityDoubtCoverage/arg=doubted.golden +0 -2
  689. package/engine/testdata/golden/TestParityDoubtCoverage/arg=ghost.golden +0 -2
  690. package/engine/testdata/golden/TestParityDoubtCoverage/arg=missing.golden +0 -3
  691. package/engine/testdata/golden/TestParityDoubtCoverage/arg=nodoubt.golden +0 -4
  692. package/engine/testdata/golden/TestParityDoubtCoverage/arg=nolog.golden +0 -2
  693. package/engine/testdata/golden/TestParityDoubtCoverage/arg=nowright.golden +0 -2
  694. package/engine/testdata/golden/TestParityDoubtCoverage.golden +0 -1
  695. package/engine/testdata/golden/TestParityMutationGate/active-slug.golden +0 -3
  696. package/engine/testdata/golden/TestParityMutationGate/explicit-slug.golden +0 -3
  697. package/engine/testdata/golden/TestParityMutationGate/ghost.golden +0 -1
  698. package/engine/testdata/golden/TestParityOrientSilentPath.golden +0 -1
  699. package/engine/testdata/golden/TestParityReconcile/bad-mode.golden +0 -1
  700. package/engine/testdata/golden/TestParityReconcile/check-clean/snapshot.golden +0 -2
  701. package/engine/testdata/golden/TestParityReconcile/check-clean.golden +0 -2
  702. package/engine/testdata/golden/TestParityReconcile/check-no-base.golden +0 -1
  703. package/engine/testdata/golden/TestParityReconcile/check-no-claimed/snapshot.golden +0 -2
  704. package/engine/testdata/golden/TestParityReconcile/check-violation/snapshot.golden +0 -2
  705. package/engine/testdata/golden/TestParityReconcile/check-violation.golden +0 -1
  706. package/engine/testdata/golden/TestParityReconcile/close-window/snapshot.golden +0 -2
  707. package/engine/testdata/golden/TestParityReconcile/close-window.golden +0 -3
  708. package/engine/testdata/golden/TestParityReconcile/not-git.golden +0 -1
  709. package/engine/testdata/golden/TestParityReconcile/snapshot/snapshot.golden +0 -2
  710. package/engine/testdata/golden/TestParityReconcile/snapshot-no-allowlist.golden +0 -1
  711. package/engine/testdata/golden/TestParityReconcile/snapshot.golden +0 -2
  712. package/engine/testdata/golden/TestParityRedwatch/fail-writes-red.golden +0 -2
  713. package/engine/testdata/golden/TestParityRedwatch/non-bash-tool.golden +0 -1
  714. package/engine/testdata/golden/TestParityRedwatch/non-test-command.golden +0 -1
  715. package/engine/testdata/golden/TestParityRedwatch/pass-clears-red.golden +0 -1
  716. package/engine/testdata/golden/TestParityResolve/next-qid.golden +0 -2
  717. package/engine/testdata/golden/TestParityReviewerReadonly/agent-required-devrites-agent-denies.golden +0 -2
  718. package/engine/testdata/golden/TestParityReviewerReadonly/agent-required-non-devrites-allows.golden +0 -1
  719. package/engine/testdata/golden/TestParityReviewerReadonly/agent-required-subagent-type-only-allows.golden +0 -1
  720. package/engine/testdata/golden/TestParityReviewerReadonly/enforce-deny-git-commit.golden +0 -2
  721. package/engine/testdata/golden/TestParityReviewerReadonly/enforce-deny-mutating.golden +0 -2
  722. package/engine/testdata/golden/TestParityReviewerReadonly/non-bash-tool.golden +0 -1
  723. package/engine/testdata/golden/TestParityReviewerReadonly/non-tool-input.golden +0 -1
  724. package/engine/testdata/golden/TestParityReviewerReadonly/observe-mutating-silent.golden +0 -1
  725. package/engine/testdata/golden/TestParityReviewerReadonly/safe-bash-command.golden +0 -1
  726. package/engine/testdata/golden/TestParitySpecValidate/arg=dup.golden +0 -1
  727. package/engine/testdata/golden/TestParitySpecValidate/arg=empty.golden +0 -1
  728. package/engine/testdata/golden/TestParitySpecValidate/arg=flat.golden +0 -2
  729. package/engine/testdata/golden/TestParitySpecValidate/arg=nonexistent.golden +0 -1
  730. package/engine/testdata/golden/TestParitySpecValidate/arg=noscenario.golden +0 -1
  731. package/engine/testdata/golden/TestParitySpecValidate/arg=noshall.golden +0 -1
  732. package/engine/testdata/golden/TestParitySpecValidate/arg=nothen.golden +0 -1
  733. package/engine/testdata/golden/TestParitySpecValidate/arg=valid/spec.md.golden +0 -2
  734. package/engine/testdata/golden/TestParitySpecValidate/arg=valid.golden +0 -2
  735. package/engine/testdata/golden/TestParitySpecValidate.golden +0 -1
  736. package/engine/testdata/golden/TestParityStatusline/full-afk.golden +0 -2
  737. package/engine/testdata/golden/TestParityStatusline/full-hitl.golden +0 -2
  738. package/engine/testdata/golden/TestParityStatusline/no-active.golden +0 -1
  739. package/engine/testdata/golden/TestParityStatusline/nophase.golden +0 -2
  740. package/engine/testdata/golden/TestParityStatusline/red.golden +0 -2
  741. package/engine/testdata/golden/TestParityStuckCheck/bad-cmd.golden +0 -1
  742. package/engine/testdata/golden/TestParityStuckCheck/custom-window-2.golden +0 -2
  743. package/engine/testdata/golden/TestParityStuckCheck/dominant.golden +0 -2
  744. package/engine/testdata/golden/TestParityStuckCheck/few.golden +0 -2
  745. package/engine/testdata/golden/TestParityStuckCheck/ghost.golden +0 -2
  746. package/engine/testdata/golden/TestParityStuckCheck/identical.golden +0 -2
  747. package/engine/testdata/golden/TestParityStuckCheck/no-cmd.golden +0 -1
  748. package/engine/testdata/golden/TestParityStuckCheck/no-slug.golden +0 -1
  749. package/engine/testdata/golden/TestParityStuckCheck/nolog.golden +0 -2
  750. package/engine/testdata/golden/TestParityStuckCheck/pingpong.golden +0 -2
  751. package/engine/testdata/golden/TestParityStuckCheck/varied.golden +0 -2
  752. package/engine/testdata/golden/TestParityStuckCheck/window-04-identical.golden +0 -2
  753. package/engine/testdata/golden/TestParityStuckCheck/window-2.5-varied4.golden +0 -2
  754. package/engine/testdata/golden/TestParityStuckCheck/window-4x-varied4.golden +0 -2
  755. package/engine/testdata/golden/TestParityStuckCheck/window-leadspace-varied4.golden +0 -2
  756. package/engine/testdata/golden/TestParityStuckCheck/window-plus4-identical.golden +0 -2
  757. package/engine/testdata/golden/TestParitySubagentOrient/devrites-agent-subagent-type-key.golden +0 -2
  758. package/engine/testdata/golden/TestParitySubagentOrient/devrites-agent.golden +0 -2
  759. package/engine/testdata/golden/TestParitySubagentOrient/garbage-input.golden +0 -1
  760. package/engine/testdata/golden/TestParitySubagentOrient/non-devrites-agent.golden +0 -1
  761. package/engine/testdata/golden/TestParityTestIntegrity/assertion-dropped.golden +0 -1
  762. package/engine/testdata/golden/TestParityTestIntegrity/clean.golden +0 -2
  763. package/engine/testdata/golden/TestParityTestIntegrity/deleted.golden +0 -1
  764. package/engine/testdata/golden/TestParityTestIntegrity/ghost-workspace.golden +0 -1
  765. package/engine/testdata/golden/TestParityTestIntegrity/not-git.golden +0 -1
  766. package/engine/testdata/golden/TestParityTestIntegrity/skip-added.golden +0 -1
  767. package/engine/testdata/golden/TestParityTickAfk/budget1/state.md.golden +0 -1
  768. package/engine/testdata/golden/TestParityTickAfk/budget1.golden +0 -2
  769. package/engine/testdata/golden/TestParityTickAfk/budget3/state.md.golden +0 -5
  770. package/engine/testdata/golden/TestParityTickAfk/budget3.golden +0 -2
  771. package/engine/testdata/golden/TestParityTickAfk/bulletPipe/state.md.golden +0 -1
  772. package/engine/testdata/golden/TestParityTickAfk/bulletPipe.golden +0 -2
  773. package/engine/testdata/golden/TestParityTickAfk/missingField/state.md.golden +0 -3
  774. package/engine/testdata/golden/TestParityTickAfk/missingField.golden +0 -2
  775. package/engine/testdata/golden/TestParityTickAfk/missingFile.golden +0 -1
  776. package/engine/testdata/golden/TestParityTickAfk/nonNumeric/state.md.golden +0 -1
  777. package/engine/testdata/golden/TestParityTickAfk/nonNumeric.golden +0 -1
  778. package/engine/testdata/golden/TestParityTickAfk/none/state.md.golden +0 -1
  779. package/engine/testdata/golden/TestParityTickAfk/none.golden +0 -2
  780. package/engine/testdata/golden/TestParityWrightScope/agent-required-non-wright.golden +0 -1
  781. package/engine/testdata/golden/TestParityWrightScope/devrites-edit-denied.golden +0 -2
  782. package/engine/testdata/golden/TestParityWrightScope/in-scope-allowed.golden +0 -1
  783. package/engine/testdata/golden/TestParityWrightScope/non-edit-tool.golden +0 -1
  784. package/engine/testdata/golden/TestParityWrightScope/out-of-scope-enforce-denies.golden +0 -2
  785. package/engine/testdata/golden/TestParityWrightScope/out-of-scope-observe-silent.golden +0 -1
  786. package/engine/testdata/golden/TestSpecSkeleton/missing-some.golden +0 -2
  787. package/engine/testdata/golden/TestSpecSkeleton/present-all.golden +0 -2
  788. package/engine/testdata/golden/TestStuckLogHashParity.golden +0 -1
  789. package/engine/tests/adr_0006_clock_seam_test.go +0 -36
  790. package/engine/tests/budget_test.go +0 -88
  791. package/engine/tests/canonical_ac_ids_test.go +0 -44
  792. package/engine/tests/concurrency_cli_test.go +0 -131
  793. package/engine/tests/conventions_test.go +0 -106
  794. package/engine/tests/doctor_cli_test.go +0 -223
  795. package/engine/tests/forge_cli_test.go +0 -463
  796. package/engine/tests/hook_test.go +0 -2778
  797. package/engine/tests/hooks_io_test.go +0 -309
  798. package/engine/tests/json_contract_test.go +0 -224
  799. package/engine/tests/lib_parity_test.go +0 -930
  800. package/engine/tests/migrate_cli_test.go +0 -195
  801. package/engine/tests/parity_analyze_test.go +0 -55
  802. package/engine/tests/parity_buildreadiness_test.go +0 -199
  803. package/engine/tests/parity_learnings_test.go +0 -89
  804. package/engine/tests/parity_mutationgate_test.go +0 -36
  805. package/engine/tests/parity_reconcile_test.go +0 -94
  806. package/engine/tests/parity_testintegrity_test.go +0 -65
  807. package/engine/tests/parity_tickafk_test.go +0 -83
  808. package/engine/validatepack.go +0 -192
  809. package/engine/validatepack_test.go +0 -79
  810. package/engine/validatepackskills.go +0 -181
  811. package/engine/validatepackskills_test.go +0 -171
  812. package/pack/.claude/agents/devrites-forge-judge.md +0 -118
  813. package/pack/.claude/skills/devrites-lib/reference/model-tiers.md +0 -44
  814. package/pack/.claude/skills/devrites-refresh-indexes/SKILL.md +0 -52
  815. package/pack/.claude/skills/rite-build/reference/evidence-standard.md +0 -45
  816. package/pack/.claude/skills/rite-build/reference/forge.md +0 -210
  817. package/pack/.claude/skills/rite-seal/reference/conventions-ledger.md +0 -60
  818. package/pack/.claude/skills/rite-ship/reference/adr-promotion.md +0 -9
  819. package/pack/.claude/skills/rite-ship/reference/ledger.md +0 -56
  820. package/pack/generated/claude/agents/devrites-forge-judge.md +0 -118
  821. package/pack/generated/claude/skills/devrites-lib/reference/model-tiers.md +0 -44
  822. package/pack/generated/claude/skills/devrites-refresh-indexes/SKILL.md +0 -52
  823. package/pack/generated/claude/skills/rite-build/reference/evidence-standard.md +0 -45
  824. package/pack/generated/claude/skills/rite-build/reference/forge.md +0 -210
  825. package/pack/generated/claude/skills/rite-seal/reference/conventions-ledger.md +0 -60
  826. package/pack/generated/claude/skills/rite-ship/reference/adr-promotion.md +0 -9
  827. package/pack/generated/claude/skills/rite-ship/reference/ledger.md +0 -56
  828. package/pack/generated/codex/agents/devrites-forge-judge.toml +0 -128
  829. package/pack/generated/codex/hooks.json +0 -185
  830. package/pack/generated/codex/skills/devrites-api-interface/agents/openai.yaml +0 -2
  831. package/pack/generated/codex/skills/devrites-audit/agents/openai.yaml +0 -2
  832. package/pack/generated/codex/skills/devrites-browser-proof/agents/openai.yaml +0 -2
  833. package/pack/generated/codex/skills/devrites-debug-recovery/agents/openai.yaml +0 -2
  834. package/pack/generated/codex/skills/devrites-doubt/agents/openai.yaml +0 -2
  835. package/pack/generated/codex/skills/devrites-frontend-craft/agents/openai.yaml +0 -2
  836. package/pack/generated/codex/skills/devrites-interview/agents/openai.yaml +0 -2
  837. package/pack/generated/codex/skills/devrites-lib/reference/model-tiers.md +0 -44
  838. package/pack/generated/codex/skills/devrites-prose-craft/agents/openai.yaml +0 -2
  839. package/pack/generated/codex/skills/devrites-refresh-indexes/SKILL.md +0 -71
  840. package/pack/generated/codex/skills/devrites-refresh-indexes/agents/openai.yaml +0 -2
  841. package/pack/generated/codex/skills/devrites-source-driven/agents/openai.yaml +0 -2
  842. package/pack/generated/codex/skills/devrites-ux-shape/agents/openai.yaml +0 -2
  843. package/pack/generated/codex/skills/rite-build/reference/evidence-standard.md +0 -45
  844. package/pack/generated/codex/skills/rite-build/reference/forge.md +0 -210
  845. package/pack/generated/codex/skills/rite-seal/reference/conventions-ledger.md +0 -60
  846. package/pack/generated/codex/skills/rite-ship/reference/adr-promotion.md +0 -9
  847. package/pack/generated/codex/skills/rite-ship/reference/ledger.md +0 -56
  848. package/scripts/build-binaries.sh +0 -75
  849. package/scripts/build-release-tarball.sh +0 -105
  850. package/scripts/check-authority-drift.py +0 -125
  851. package/scripts/check-cross-refs.py +0 -139
  852. package/scripts/check-generated-skill-budget.mjs +0 -42
  853. package/scripts/check-instruction-size-baseline.mjs +0 -88
  854. package/scripts/check-invocation-integrity.py +0 -115
  855. package/scripts/check-no-global-writes.sh +0 -66
  856. package/scripts/check-no-personal-paths.py +0 -50
  857. package/scripts/check-npm-audit.mjs +0 -110
  858. package/scripts/check-reference-governance.mjs +0 -83
  859. package/scripts/check-reply-contract.sh +0 -64
  860. package/scripts/check-rule-uniqueness.sh +0 -45
  861. package/scripts/devrites-detect.sh +0 -176
  862. package/scripts/grade-feature.sh +0 -204
  863. package/scripts/live-hosts/agent-result.schema.json +0 -231
  864. package/scripts/live-hosts/claude.sh +0 -87
  865. package/scripts/live-hosts/codex.sh +0 -85
  866. package/scripts/live-hosts/common.sh +0 -113
  867. package/scripts/live-hosts/fake-host.py +0 -257
  868. package/scripts/live-hosts/host-transport.py +0 -286
  869. package/scripts/npm-audit-exceptions.json +0 -26
  870. package/scripts/pin.sh +0 -249
  871. package/scripts/release-check.sh +0 -75
  872. package/scripts/run-agent-contract-evals.py +0 -1380
  873. package/scripts/run-behavioral-evals.sh +0 -196
  874. package/scripts/run-evals.sh +0 -132
  875. package/scripts/run-live-behavioral-evals.py +0 -1310
  876. package/scripts/run-outcome-evals.sh +0 -454
  877. package/scripts/run-routing-evals.py +0 -342
  878. package/scripts/run-tests.mjs +0 -232
  879. package/scripts/scan-pack-security.py +0 -209
  880. package/scripts/scan-supply-chain-iocs.py +0 -127
  881. package/scripts/skill-pruning-audit.mjs +0 -121
  882. package/scripts/skills-inventory.mjs +0 -125
  883. package/scripts/supply-chain-iocs.json +0 -11
  884. package/scripts/sync-version.sh +0 -54
  885. package/scripts/validate-agent-composition.py +0 -57
  886. package/scripts/validate-command-parity.py +0 -70
  887. package/scripts/validate-frontmatter.py +0 -218
  888. package/scripts/validate-skill-anatomy.py +0 -139
  889. package/scripts/validate-workflow-security.py +0 -184
  890. package/scripts/validate-workspace-schema.py +0 -800
  891. package/scripts/validate.sh +0 -354
  892. package/scripts/workflow_schema.py +0 -69
  893. /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/auth-tokens/decisions.md +0 -0
  894. /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/auth-tokens/plan.md +0 -0
  895. /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/auth-tokens/spec.md +0 -0
  896. /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/auth-tokens/tasks.md +0 -0
  897. /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/search-ranking/spec.md +0 -0
@@ -1,9 +1,7 @@
1
1
  package install
2
2
 
3
3
  import (
4
- "archive/tar"
5
4
  "bytes"
6
- "compress/gzip"
7
5
  "context"
8
6
  "crypto/sha256"
9
7
  "encoding/hex"
@@ -16,7 +14,6 @@ import (
16
14
  "os"
17
15
  "os/exec"
18
16
  "path/filepath"
19
- "reflect"
20
17
  "runtime"
21
18
  "slices"
22
19
  "sort"
@@ -26,14 +23,11 @@ import (
26
23
  "github.com/devrites/devrites/internal/devritespaths"
27
24
  "github.com/devrites/devrites/internal/fsutil"
28
25
  "github.com/devrites/devrites/internal/hostpack"
29
- "github.com/devrites/devrites/internal/iohooks"
30
26
  "github.com/devrites/devrites/internal/safepath"
31
27
  "github.com/devrites/devrites/internal/version"
32
28
  )
33
29
 
34
30
  const ManifestName = devritespaths.ManifestName
35
- const defaultRepo = "ViktorsBaikers/DevRites"
36
- const githubBaseURL = "https://github.com"
37
31
 
38
32
  type Mode string
39
33
 
@@ -57,8 +51,6 @@ type Options struct {
57
51
  KeepBinary bool
58
52
  AliasMode string
59
53
  UpdateCheck bool
60
- UpdateTo string
61
- AllowPre bool
62
54
  Stdout io.Writer
63
55
  Stderr io.Writer
64
56
  }
@@ -73,18 +65,18 @@ type stats struct {
73
65
  }
74
66
 
75
67
  type runner struct {
76
- opts Options
77
- target string
78
- payload string
79
- payloadFS fs.FS
80
- source string
81
- manifest []string
82
- records map[string]string
83
- prev map[string]managedRecord
84
- preflight map[string]pathSnapshot
85
- stats stats
86
- releaseBinaryTag string
87
- preparedBinary string
68
+ opts Options
69
+ target string
70
+ payload string
71
+ payloadFS fs.FS
72
+ source string
73
+ manifest []string
74
+ records map[string]string
75
+ prev map[string]managedRecord
76
+ preflight map[string]pathSnapshot
77
+ stats stats
78
+ requiredBinaryTag string
79
+ preparedBinary string
88
80
  }
89
81
 
90
82
  type managedRecord struct {
@@ -162,8 +154,6 @@ func parseArgs(args []string, opts *Options) error {
162
154
  noShortAliases := flags.Bool("no-short-aliases", false, "")
163
155
  shortAliases := flags.String("short-aliases", opts.AliasMode, "")
164
156
  updateCheck := flags.Bool("check", opts.UpdateCheck, "")
165
- allowPre := flags.Bool("pre", opts.AllowPre, "")
166
- updateTo := flags.String("to", opts.UpdateTo, "")
167
157
 
168
158
  if err := flags.Parse(normalized); err != nil {
169
159
  if errors.Is(err, flag.ErrHelp) {
@@ -186,8 +176,6 @@ func parseArgs(args []string, opts *Options) error {
186
176
  opts.WithBinary = !*noBinary
187
177
  opts.KeepBinary = *keepBinary
188
178
  opts.UpdateCheck = *updateCheck
189
- opts.AllowPre = *allowPre
190
- opts.UpdateTo = *updateTo
191
179
  for _, dep := range []struct {
192
180
  set bool
193
181
  name string
@@ -227,7 +215,7 @@ func usage(mode Mode) string {
227
215
  case ModeUninstall:
228
216
  return "usage: devrites-engine uninstall [--target DIR] [--dry-run] [--force] [--keep-binary]\n"
229
217
  case ModeUpdate:
230
- return "usage: devrites-engine update [--target DIR] [--dry-run] [--force] [--check] [--to TAG] [install flags]\n"
218
+ return "usage: devrites-engine update [--target DIR] [--source-dir DIR] [--payload-dir DIR] [--dry-run] [--force] [--check] [install flags]\n"
231
219
  default:
232
220
  return "usage: devrites-engine install [--target DIR] [--dry-run] [--force] [--no-codex] [--no-agents] [--no-skills] [--no-binary] [--short-aliases=all]\n"
233
221
  }
@@ -404,8 +392,8 @@ func (r *runner) install() error {
404
392
  if err := r.mergeMarkerFile(hostpack.CodexAgentsMerge); err != nil {
405
393
  return fmt.Errorf("merge %s: %w", hostpack.CodexAgentsMerge.TargetRel, err)
406
394
  }
407
- if err := r.mergeJSONHooks(hostpack.CodexHooksMerge); err != nil {
408
- return fmt.Errorf("merge %s: %w", hostpack.CodexHooksMerge.TargetRel, err)
395
+ if err := r.mergeCodexConfig(); err != nil {
396
+ return fmt.Errorf("merge %s: %w", hostpack.CodexConfigMerge.TargetRel, err)
409
397
  }
410
398
  }
411
399
  if r.opts.WithSkills {
@@ -435,6 +423,13 @@ func (r *runner) install() error {
435
423
  fmt.Fprintln(r.opts.Stdout, "DevRites installed")
436
424
  }
437
425
  fmt.Fprintf(r.opts.Stdout, " installed: %d overwritten: %d skipped(conflict): %d pruned: %d\n", r.stats.installed, r.stats.overwrote, r.stats.skipped, r.stats.pruned)
426
+ if !r.opts.DryRun && r.opts.WithSkills {
427
+ if r.opts.WithCodex {
428
+ fmt.Fprintln(r.opts.Stdout, "Next: reopen the project, then run /rite (Claude) or $rite (Codex).")
429
+ } else {
430
+ fmt.Fprintln(r.opts.Stdout, "Next: reopen the project, then run /rite.")
431
+ }
432
+ }
438
433
  return nil
439
434
  }
440
435
 
@@ -568,7 +563,7 @@ func (r *runner) desiredInstallPaths() (map[string]bool, error) {
568
563
  }
569
564
  if r.opts.WithSkills && r.opts.WithCodex {
570
565
  out[hostpack.CodexAgentsMerge.MarkerRel] = true
571
- out[hostpack.CodexHooksMerge.MarkerRel] = true
566
+ out[hostpack.CodexConfigMerge.MarkerRel] = true
572
567
  }
573
568
  if r.opts.WithSkills {
574
569
  out[hostpack.ClaudeSettingsMerge.MarkerRel] = true
@@ -581,7 +576,7 @@ func (r *runner) installMergeTargets() []string {
581
576
  if r.opts.WithSkills {
582
577
  out = append(out, hostpack.ClaudeSettingsMerge.TargetRel)
583
578
  if r.opts.WithCodex {
584
- out = append(out, hostpack.CodexAgentsMerge.TargetRel, hostpack.CodexHooksMerge.TargetRel)
579
+ out = append(out, hostpack.CodexAgentsMerge.TargetRel, hostpack.CodexConfigMerge.TargetRel)
585
580
  }
586
581
  }
587
582
  return out
@@ -738,6 +733,20 @@ func (r *runner) installMarker(rel, text string) error {
738
733
  return r.installData([]byte(text+"\n"), rel)
739
734
  }
740
735
 
736
+ func (r *runner) installClaudeSettingsMarker(ownsDefaultMode bool) error {
737
+ ownership := "preexisting"
738
+ if ownsDefaultMode {
739
+ ownership = "added"
740
+ }
741
+ text := hostpack.ClaudeSettingsMerge.MarkerText + "\ndefault-mode=" + ownership
742
+ return r.installMarker(hostpack.ClaudeSettingsMerge.MarkerRel, text)
743
+ }
744
+
745
+ func (r *runner) claudeDefaultModeOwned() bool {
746
+ data, err := os.ReadFile(filepath.Join(r.target, filepath.FromSlash(hostpack.ClaudeSettingsMerge.MarkerRel)))
747
+ return err == nil && strings.Contains(string(data), "\ndefault-mode=added\n")
748
+ }
749
+
741
750
  func (r *runner) mergeMarkerFile(merge hostpack.MarkerMerge) error {
742
751
  block, err := fs.ReadFile(r.payloadFS, merge.PayloadRel)
743
752
  if err != nil {
@@ -775,87 +784,81 @@ func (r *runner) mergeMarkerFile(merge hostpack.MarkerMerge) error {
775
784
  return r.installMarker(merge.MarkerRel, merge.MarkerText)
776
785
  }
777
786
 
778
- func (r *runner) mergeJSONHooks(merge hostpack.JSONMerge) error {
779
- dest := filepath.Join(r.target, filepath.FromSlash(merge.TargetRel))
780
- devrites, err := readJSONFS(r.payloadFS, merge.PayloadRel)
787
+ func (r *runner) mergeCodexConfig() error {
788
+ merge := hostpack.CodexConfigMerge
789
+ block, err := fs.ReadFile(r.payloadFS, merge.PayloadRel)
781
790
  if err != nil {
782
- return fmt.Errorf("load hooks payload: %w", err)
791
+ return fmt.Errorf("read payload %s: %w", merge.PayloadRel, err)
783
792
  }
793
+ dest := filepath.Join(r.target, filepath.FromSlash(merge.TargetRel))
784
794
  if r.opts.DryRun {
785
- fmt.Fprintf(r.opts.Stdout, " [merge] %s\n", merge.DryRunText)
795
+ fmt.Fprintf(r.opts.Stdout, " [merge] %s (prepend DevRites permission block)\n", merge.TargetRel)
786
796
  return r.installMarker(merge.MarkerRel, merge.MarkerText)
787
797
  }
788
798
  if err := r.recheckPath(merge.TargetRel); err != nil {
789
799
  return err
790
800
  }
791
- next := devrites
792
- if current, err := readJSON(dest); err == nil {
793
- if currentStatus, exists := current["statusLine"]; exists && !isDevritesHookCommand(currentStatus) {
794
- if _, wanted := devrites["statusLine"]; wanted {
795
- fmt.Fprintln(r.opts.Stderr, "warning: preserved existing Claude statusLine; DevRites statusline was not installed.")
796
- }
801
+ var current []byte
802
+ if data, readErr := os.ReadFile(dest); readErr == nil {
803
+ current = stripMarkerBlock(data, merge.Begin, merge.End)
804
+ current = stripMarkerBlock(current, "# BEGIN DEVRITES CODEX MCP", "# END DEVRITES CODEX MCP")
805
+ current = stripMarkerBlock(current, "### BEGIN DEVRITES CODEX MCP", "### END DEVRITES CODEX MCP")
806
+ if hasTopLevelTOMLKey(current, "default_permissions") {
807
+ return fmt.Errorf("%s already sets top-level default_permissions; remove that project override before installing the DevRites read-only-root profile", merge.TargetRel)
797
808
  }
798
- next = mergeHooks(stripDevritesHooks(current), devrites)
809
+ } else if !errors.Is(readErr, fs.ErrNotExist) {
810
+ return fmt.Errorf("cannot read %s: %w", merge.TargetRel, readErr)
799
811
  }
800
- data, err := json.MarshalIndent(next, "", " ")
801
- if err != nil {
802
- return fmt.Errorf("encode merged hooks: %w", err)
812
+ next := append([]byte(nil), block...)
813
+ if len(next) == 0 || next[len(next)-1] != '\n' {
814
+ next = append(next, '\n')
803
815
  }
804
- data = append(data, '\n')
805
- if err := fsutil.WriteFileAtomic(dest, data, 0o644); err != nil {
816
+ if len(bytes.TrimSpace(current)) > 0 {
817
+ next = append(next, '\n')
818
+ next = append(next, current...)
819
+ }
820
+ if err := fsutil.WriteFileAtomic(dest, next, 0o644); err != nil {
806
821
  return fmt.Errorf("cannot write %s: %w", merge.TargetRel, err)
807
822
  }
808
823
  return r.installMarker(merge.MarkerRel, merge.MarkerText)
809
824
  }
810
825
 
811
- func (r *runner) seedClaudeSettings() error {
812
- merge := hostpack.ClaudeSettingsMerge
813
- rel := merge.TargetRel
814
- dest := filepath.Join(r.target, rel)
815
- if exists(dest) {
816
- current, err := readJSON(dest)
817
- if err != nil {
818
- return fmt.Errorf("load existing Claude settings: %w", err)
819
- }
820
- devrites, err := readJSONFS(r.payloadFS, merge.PayloadRel)
821
- if err != nil {
822
- return fmt.Errorf("load Claude settings payload: %w", err)
823
- }
824
- _, managed := r.prev[merge.MarkerRel]
825
- if reflect.DeepEqual(current, devrites) {
826
- if managed {
827
- return r.installMarker(merge.MarkerRel, merge.MarkerText)
828
- }
829
- return nil
830
- }
831
- if containsDevritesHook(current) && len(stripDevritesHooks(current)) == 0 && !managed {
832
- if r.opts.DryRun {
833
- fmt.Fprintln(r.opts.Stdout, " [seed] .claude/settings.json (refresh DevRites hooks)")
834
- return nil
835
- }
836
- data, err := fs.ReadFile(r.payloadFS, merge.PayloadRel)
837
- if err != nil {
838
- return fmt.Errorf("read payload claude/settings.json: %w", err)
839
- }
840
- if err := r.recheckPath(rel); err != nil {
841
- return err
842
- }
843
- return fsutil.WriteFileAtomic(dest, data, 0o644)
844
- }
845
- return r.mergeJSONHooks(merge)
826
+ func (r *runner) mergeClaudeSettings(merge hostpack.JSONMerge) error {
827
+ dest := filepath.Join(r.target, filepath.FromSlash(merge.TargetRel))
828
+ devrites, err := readJSONFS(r.payloadFS, merge.PayloadRel)
829
+ if err != nil {
830
+ return fmt.Errorf("load Claude settings payload: %w", err)
846
831
  }
847
- if r.opts.DryRun {
848
- fmt.Fprintln(r.opts.Stdout, " [seed] .claude/settings.json (DevRites hooks - preserved on uninstall/update)")
849
- return nil
832
+ current := map[string]any{}
833
+ if data, readErr := readJSON(dest); readErr == nil {
834
+ current = data
835
+ } else if !errors.Is(readErr, fs.ErrNotExist) {
836
+ return fmt.Errorf("load existing Claude settings: %w", readErr)
850
837
  }
851
- data, err := fs.ReadFile(r.payloadFS, merge.PayloadRel)
838
+ next, ownsDefaultMode, err := mergeClaudeSettingsConfig(current, devrites, r.claudeDefaultModeOwned())
852
839
  if err != nil {
853
- return fmt.Errorf("read payload claude/settings.json: %w", err)
840
+ return err
854
841
  }
855
- if err := r.recheckPath(rel); err != nil {
842
+ if r.opts.DryRun {
843
+ fmt.Fprintf(r.opts.Stdout, " [merge] %s\n", merge.DryRunText)
844
+ return r.installClaudeSettingsMarker(ownsDefaultMode)
845
+ }
846
+ if err := r.recheckPath(merge.TargetRel); err != nil {
856
847
  return err
857
848
  }
858
- return fsutil.WriteFileAtomic(dest, data, 0o644)
849
+ data, err := json.MarshalIndent(next, "", " ")
850
+ if err != nil {
851
+ return fmt.Errorf("encode merged Claude settings: %w", err)
852
+ }
853
+ data = append(data, '\n')
854
+ if err := fsutil.WriteFileAtomic(dest, data, 0o644); err != nil {
855
+ return fmt.Errorf("cannot write %s: %w", merge.TargetRel, err)
856
+ }
857
+ return r.installClaudeSettingsMarker(ownsDefaultMode)
858
+ }
859
+
860
+ func (r *runner) seedClaudeSettings() error {
861
+ return r.mergeClaudeSettings(hostpack.ClaudeSettingsMerge)
859
862
  }
860
863
 
861
864
  func (r *runner) seedDevrites() error {
@@ -922,9 +925,12 @@ func (r *runner) pruneDropped() error {
922
925
  if err := r.recheckPath(merge.TargetRel); err != nil {
923
926
  return err
924
927
  }
925
- if merge.TargetRel == hostpack.CodexHooksMerge.TargetRel || merge.TargetRel == hostpack.ClaudeSettingsMerge.TargetRel {
926
- preserveEmpty := merge.TargetRel == hostpack.ClaudeSettingsMerge.TargetRel
927
- _ = r.stripHooksPath(filepath.Join(r.target, filepath.FromSlash(merge.TargetRel)), preserveEmpty)
928
+ if merge.MarkerRel == hostpack.LegacyCodexHooksMerge.MarkerRel {
929
+ if err := stripHooksPath(filepath.Join(r.target, filepath.FromSlash(merge.TargetRel))); err != nil {
930
+ return fmt.Errorf("strip hooks from %s: %w", merge.TargetRel, err)
931
+ }
932
+ } else if merge.TargetRel == hostpack.ClaudeSettingsMerge.TargetRel {
933
+ _ = r.stripClaudeSettings(filepath.Join(r.target, filepath.FromSlash(merge.TargetRel)), true)
928
934
  } else {
929
935
  _ = stripMarkerPath(filepath.Join(r.target, filepath.FromSlash(merge.TargetRel)), merge.Begin, merge.End)
930
936
  }
@@ -1021,13 +1027,18 @@ func (r *runner) uninstall() error {
1021
1027
  if err := r.recheckPath(merge.TargetRel); err != nil {
1022
1028
  return err
1023
1029
  }
1024
- if merge.TargetRel == hostpack.CodexHooksMerge.TargetRel || merge.TargetRel == hostpack.ClaudeSettingsMerge.TargetRel {
1025
- preserveEmpty := merge.TargetRel == hostpack.ClaudeSettingsMerge.TargetRel
1026
- if err := r.stripHooksPath(filepath.Join(r.target, filepath.FromSlash(merge.TargetRel)), preserveEmpty); err != nil {
1030
+ if merge.MarkerRel == hostpack.LegacyCodexHooksMerge.MarkerRel {
1031
+ if err := stripHooksPath(filepath.Join(r.target, filepath.FromSlash(merge.TargetRel))); err != nil {
1027
1032
  return fmt.Errorf("strip hooks from %s: %w", merge.TargetRel, err)
1028
1033
  }
1029
1034
  continue
1030
1035
  }
1036
+ if merge.TargetRel == hostpack.ClaudeSettingsMerge.TargetRel {
1037
+ if err := r.stripClaudeSettings(filepath.Join(r.target, filepath.FromSlash(merge.TargetRel)), true); err != nil {
1038
+ return fmt.Errorf("strip DevRites settings from %s: %w", merge.TargetRel, err)
1039
+ }
1040
+ continue
1041
+ }
1031
1042
  if err := stripMarkerPath(filepath.Join(r.target, filepath.FromSlash(merge.TargetRel)), merge.Begin, merge.End); err != nil {
1032
1043
  return fmt.Errorf("strip marker block from %s: %w", merge.TargetRel, err)
1033
1044
  }
@@ -1091,7 +1102,7 @@ func (r *runner) uninstall() error {
1091
1102
  return nil
1092
1103
  }
1093
1104
 
1094
- func (r *runner) stripHooksPath(path string, preserveEmpty bool) error {
1105
+ func stripHooksPath(path string) error {
1095
1106
  current, err := readJSON(path)
1096
1107
  if err != nil {
1097
1108
  if errors.Is(err, fs.ErrNotExist) {
@@ -1100,7 +1111,7 @@ func (r *runner) stripHooksPath(path string, preserveEmpty bool) error {
1100
1111
  return fmt.Errorf("load hooks config: %w", err)
1101
1112
  }
1102
1113
  next := stripDevritesHooks(current)
1103
- if len(next) == 0 && !preserveEmpty {
1114
+ if len(next) == 0 {
1104
1115
  return os.Remove(path)
1105
1116
  }
1106
1117
  data, err := json.MarshalIndent(next, "", " ")
@@ -1111,6 +1122,26 @@ func (r *runner) stripHooksPath(path string, preserveEmpty bool) error {
1111
1122
  return fsutil.WriteFileAtomic(path, data, 0o644)
1112
1123
  }
1113
1124
 
1125
+ func (r *runner) stripClaudeSettings(path string, preserveEmpty bool) error {
1126
+ current, err := readJSON(path)
1127
+ if err != nil {
1128
+ if errors.Is(err, fs.ErrNotExist) {
1129
+ return nil
1130
+ }
1131
+ return fmt.Errorf("load Claude settings: %w", err)
1132
+ }
1133
+ next := stripDevritesSettings(current, r.claudeDefaultModeOwned())
1134
+ if len(next) == 0 && !preserveEmpty {
1135
+ return os.Remove(path)
1136
+ }
1137
+ data, err := json.MarshalIndent(next, "", " ")
1138
+ if err != nil {
1139
+ return fmt.Errorf("encode Claude settings: %w", err)
1140
+ }
1141
+ data = append(data, '\n')
1142
+ return fsutil.WriteFileAtomic(path, data, 0o644)
1143
+ }
1144
+
1114
1145
  func runUpdate(opts Options) error {
1115
1146
  target, err := filepath.Abs(opts.Target)
1116
1147
  if err != nil {
@@ -1124,36 +1155,42 @@ func runUpdate(opts Options) error {
1124
1155
  if installed == "" {
1125
1156
  installed = "unknown"
1126
1157
  }
1127
- latestTag, err := resolveUpdateTag(opts)
1158
+ if opts.SourceDir == "" {
1159
+ return fmt.Errorf("update requires --source-dir with the locally acquired source")
1160
+ }
1161
+ source, err := filepath.Abs(opts.SourceDir)
1128
1162
  if err != nil {
1129
- return fmt.Errorf("resolve update tag: %w", err)
1163
+ return fmt.Errorf("resolve source %s: %w", opts.SourceDir, err)
1164
+ }
1165
+ candidate := installedVersion(source)
1166
+ if candidate == "" || candidate == "unknown" {
1167
+ return fmt.Errorf("derive update version from local source %s: package.json has no version", source)
1130
1168
  }
1131
- latest := strings.TrimPrefix(latestTag, "v")
1169
+ candidate = strings.TrimPrefix(candidate, "v")
1132
1170
  fmt.Fprintln(opts.Stdout, "DevRites update")
1133
1171
  fmt.Fprintf(opts.Stdout, " project: %s\n", target)
1134
1172
  fmt.Fprintf(opts.Stdout, " installed: %s\n", installed)
1135
- fmt.Fprintf(opts.Stdout, " latest: %s\n", latest)
1173
+ fmt.Fprintf(opts.Stdout, " candidate: %s\n", candidate)
1136
1174
  if opts.UpdateCheck {
1137
- if installed == latest || strings.TrimPrefix(installed, "v") == latest {
1175
+ if installed == candidate || strings.TrimPrefix(installed, "v") == candidate {
1138
1176
  fmt.Fprintln(opts.Stdout, "up to date.")
1139
1177
  return nil
1140
1178
  }
1141
- return fmt.Errorf("update available: %s -> %s", installed, latest)
1179
+ return fmt.Errorf("update available: %s -> %s", installed, candidate)
1142
1180
  }
1143
- if !opts.Force && (installed == latest || strings.TrimPrefix(installed, "v") == latest) {
1181
+ if !opts.Force && (installed == candidate || strings.TrimPrefix(installed, "v") == candidate) {
1144
1182
  fmt.Fprintln(opts.Stdout, "already up to date (use --force to reinstall).")
1145
1183
  return nil
1146
1184
  }
1147
- source, cleanup, err := acquireUpdateBundle(latestTag, opts.Stdout)
1185
+ if opts.PayloadDir == "" {
1186
+ return fmt.Errorf("update requires --payload-dir with the locally prepared host payload")
1187
+ }
1188
+ payload, err := filepath.Abs(opts.PayloadDir)
1148
1189
  if err != nil {
1149
- return fmt.Errorf("acquire update %s: %w", latestTag, err)
1190
+ return fmt.Errorf("resolve payload %s: %w", opts.PayloadDir, err)
1150
1191
  }
1151
- defer cleanup()
1152
- payload := filepath.Join(source, "pack", "generated")
1153
- if hostpack.ValidatePayload(os.DirFS(payload), true) != nil {
1154
- if err := buildHostArtifacts(source, payload); err != nil {
1155
- return fmt.Errorf("prepare update payload: %w", err)
1156
- }
1192
+ if err := hostpack.ValidatePayload(os.DirFS(payload), true); err != nil {
1193
+ return fmt.Errorf("local update payload under %s is invalid: %w", payload, err)
1157
1194
  }
1158
1195
  installFlags := manifestHeader(mf, "devrites-flags")
1159
1196
  next := DefaultOptions(ModeInstall)
@@ -1175,9 +1212,9 @@ func runUpdate(opts Options) error {
1175
1212
  if err != nil {
1176
1213
  return fmt.Errorf("prepare installer: %w", err)
1177
1214
  }
1178
- r.releaseBinaryTag = latestTag
1215
+ r.requiredBinaryTag = "v" + candidate
1179
1216
  if next.WithBinary && os.Getenv("DEVRITES_NO_BINARY") != "1" && !next.DryRun {
1180
- staged, cleanup, err := r.acquireBinary(latestTag, githubBaseURL)
1217
+ staged, cleanup, err := r.acquireBinary(r.requiredBinaryTag)
1181
1218
  if err != nil {
1182
1219
  return fmt.Errorf("prepare engine binary: %w", err)
1183
1220
  }
@@ -1187,249 +1224,6 @@ func runUpdate(opts Options) error {
1187
1224
  return r.install()
1188
1225
  }
1189
1226
 
1190
- func resolveUpdateTag(opts Options) (string, error) {
1191
- if opts.UpdateTo != "" {
1192
- return normalizeTag(opts.UpdateTo), nil
1193
- }
1194
- if os.Getenv("DEVRITES_UPDATE_BUNDLE") != "" && opts.SourceDir != "" {
1195
- if v := installedVersion(opts.SourceDir); v != "" && v != "unknown" {
1196
- return normalizeTag(v), nil
1197
- }
1198
- }
1199
- repo := os.Getenv("DEVRITES_REPO")
1200
- if repo == "" {
1201
- repo = defaultRepo
1202
- }
1203
- endpoint := "latest"
1204
- if opts.AllowPre {
1205
- endpoint = ""
1206
- }
1207
- url := fmt.Sprintf("https://api.github.com/repos/%s/releases/%s", repo, endpoint)
1208
- if opts.AllowPre {
1209
- var releases []struct {
1210
- TagName string `json:"tag_name"`
1211
- }
1212
- if err := iohooks.FetchJSON(url, &releases); err != nil {
1213
- return "", fmt.Errorf("resolve latest release: %w", err)
1214
- }
1215
- for _, rel := range releases {
1216
- if rel.TagName != "" {
1217
- return normalizeTag(rel.TagName), nil
1218
- }
1219
- }
1220
- return "", fmt.Errorf("resolve latest release: no releases found")
1221
- }
1222
- var release struct {
1223
- TagName string `json:"tag_name"`
1224
- }
1225
- if err := iohooks.FetchJSON(url, &release); err != nil {
1226
- return "", fmt.Errorf("resolve latest release: %w", err)
1227
- }
1228
- if release.TagName == "" {
1229
- return "", fmt.Errorf("resolve latest release: empty tag")
1230
- }
1231
- return normalizeTag(release.TagName), nil
1232
- }
1233
-
1234
- func acquireUpdateBundle(tag string, stdout io.Writer) (string, func(), error) {
1235
- tmp, err := os.MkdirTemp("", "devrites-update-*")
1236
- if err != nil {
1237
- return "", func() {}, fmt.Errorf("create temp dir: %w", err)
1238
- }
1239
- cleanup := func() { _ = os.RemoveAll(tmp) }
1240
- artifact := fmt.Sprintf("devrites-%s.tar.gz", tag)
1241
- tarball := filepath.Join(tmp, artifact)
1242
- if bundle := os.Getenv("DEVRITES_UPDATE_BUNDLE"); bundle != "" {
1243
- if !exists(bundle) {
1244
- cleanup()
1245
- return "", func() {}, fmt.Errorf("DEVRITES_UPDATE_BUNDLE not readable: %s", bundle)
1246
- }
1247
- fmt.Fprintf(stdout, " bundle: %s\n", bundle)
1248
- return extractUpdateBundle(bundle, tmp, cleanup)
1249
- }
1250
-
1251
- repo := os.Getenv("DEVRITES_REPO")
1252
- if repo == "" {
1253
- repo = defaultRepo
1254
- }
1255
- releaseURL := fmt.Sprintf("https://github.com/%s/releases/download/%s/%s", repo, tag, artifact)
1256
- if err := iohooks.DownloadFile(releaseURL, tarball); err == nil {
1257
- sumPath := tarball + ".sha256"
1258
- if err := iohooks.DownloadFile(releaseURL+".sha256", sumPath); err != nil {
1259
- cleanup()
1260
- return "", func() {}, fmt.Errorf("could not verify release artifact %s: missing checksum: %w", artifact, err)
1261
- }
1262
- if err := verifySHA256(tarball, sumPath); err != nil {
1263
- cleanup()
1264
- return "", func() {}, fmt.Errorf("could not verify release artifact %s: %w", artifact, err)
1265
- }
1266
- fmt.Fprintf(stdout, " bundle: %s\n", releaseURL)
1267
- return extractUpdateBundle(tarball, tmp, cleanup)
1268
- }
1269
-
1270
- sourceURL := fmt.Sprintf("https://github.com/%s/archive/refs/tags/%s.tar.gz", repo, tag)
1271
- sourceTarball := filepath.Join(tmp, "src-"+tag+".tar.gz")
1272
- if err := iohooks.DownloadFile(sourceURL, sourceTarball); err != nil {
1273
- cleanup()
1274
- return "", func() {}, fmt.Errorf("download update bundle: %w", err)
1275
- }
1276
- fmt.Fprintf(stdout, " bundle: %s\n", sourceURL)
1277
- return extractUpdateBundle(sourceTarball, tmp, cleanup)
1278
- }
1279
-
1280
- func verifySHA256(path, sumPath string) error {
1281
- data, err := os.ReadFile(sumPath)
1282
- if err != nil {
1283
- return fmt.Errorf("read checksum %s: %w", sumPath, err)
1284
- }
1285
- want := strings.Fields(string(data))
1286
- if len(want) == 0 {
1287
- return fmt.Errorf("empty checksum file")
1288
- }
1289
- body, err := os.ReadFile(path)
1290
- if err != nil {
1291
- return fmt.Errorf("read artifact %s: %w", path, err)
1292
- }
1293
- got := fmt.Sprintf("%x", sha256.Sum256(body))
1294
- if got != want[0] {
1295
- return fmt.Errorf("checksum mismatch: got %s want %s", got, want[0])
1296
- }
1297
- return nil
1298
- }
1299
-
1300
- func extractUpdateBundle(tarball, tmp string, cleanup func()) (string, func(), error) {
1301
- if err := extractTarGz(tarball, tmp); err != nil {
1302
- cleanup()
1303
- return "", func() {}, fmt.Errorf("extract update bundle: %w", err)
1304
- }
1305
- entries, err := os.ReadDir(tmp)
1306
- if err != nil {
1307
- cleanup()
1308
- return "", func() {}, fmt.Errorf("list extracted bundle: %w", err)
1309
- }
1310
- for _, entry := range entries {
1311
- if !entry.IsDir() {
1312
- continue
1313
- }
1314
- dir := filepath.Join(tmp, entry.Name())
1315
- if exists(filepath.Join(dir, "install.sh")) {
1316
- return dir, cleanup, nil
1317
- }
1318
- }
1319
- cleanup()
1320
- return "", func() {}, fmt.Errorf("extracted update bundle has no install.sh")
1321
- }
1322
-
1323
- const (
1324
- maxUpdateEntries = 4096
1325
- maxUpdateEntryBytes = int64(1) << 30
1326
- maxUpdateTotalBytes = int64(2) << 30
1327
- )
1328
-
1329
- func extractTarGz(tarball, dest string) error {
1330
- f, err := os.Open(tarball)
1331
- if err != nil {
1332
- return fmt.Errorf("open update bundle: %w", err)
1333
- }
1334
- defer f.Close()
1335
- gz, err := gzip.NewReader(f)
1336
- if err != nil {
1337
- return fmt.Errorf("decompress %s: %w", tarball, err)
1338
- }
1339
- defer gz.Close()
1340
- tr := tar.NewReader(gz)
1341
- root, err := filepath.Abs(dest)
1342
- if err != nil {
1343
- return fmt.Errorf("resolve %s: %w", dest, err)
1344
- }
1345
- entryCount := 0
1346
- var totalBytes int64
1347
- for {
1348
- hdr, err := tr.Next()
1349
- if errors.Is(err, io.EOF) {
1350
- return nil
1351
- }
1352
- if err != nil {
1353
- return fmt.Errorf("read tar entry: %w", err)
1354
- }
1355
- entryCount++
1356
- if entryCount > maxUpdateEntries {
1357
- return fmt.Errorf("update bundle has too many entries (maximum %d)", maxUpdateEntries)
1358
- }
1359
- if hdr.Size < 0 {
1360
- return fmt.Errorf("entry %s in update bundle has a negative size", hdr.Name)
1361
- }
1362
- if hdr.Size > maxUpdateEntryBytes {
1363
- return fmt.Errorf("entry %s in update bundle exceeds %d bytes", hdr.Name, maxUpdateEntryBytes)
1364
- }
1365
- if hdr.Size > maxUpdateTotalBytes-totalBytes {
1366
- return fmt.Errorf("update bundle exceeds %d total bytes", maxUpdateTotalBytes)
1367
- }
1368
- totalBytes += hdr.Size
1369
- name := filepath.Clean(hdr.Name)
1370
- if name == "." || filepath.IsAbs(name) || strings.HasPrefix(name, ".."+string(os.PathSeparator)) || name == ".." {
1371
- return fmt.Errorf("unsafe path in update bundle: %s", hdr.Name)
1372
- }
1373
- target := filepath.Join(root, name)
1374
- if !strings.HasPrefix(target, root+string(os.PathSeparator)) && target != root {
1375
- return fmt.Errorf("unsafe path in update bundle: %s", hdr.Name)
1376
- }
1377
- switch hdr.Typeflag {
1378
- case tar.TypeDir:
1379
- if err := os.MkdirAll(target, fs.FileMode(hdr.Mode&0o777)); err != nil {
1380
- return fmt.Errorf("create dir %s: %w", name, err)
1381
- }
1382
- case tar.TypeReg:
1383
- if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
1384
- return fmt.Errorf("create dir for %s: %w", name, err)
1385
- }
1386
- out, err := os.OpenFile(target, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, fs.FileMode(hdr.Mode&0o777))
1387
- if err != nil {
1388
- return fmt.Errorf("create %s: %w", name, err)
1389
- }
1390
- // Keep the streaming cap even after validating the header so malformed
1391
- // archives cannot make the extractor trust inconsistent metadata.
1392
- written, copyErr := io.Copy(out, io.LimitReader(tr, maxUpdateEntryBytes+1))
1393
- closeErr := out.Close()
1394
- if copyErr != nil {
1395
- return fmt.Errorf("extract %s: %w", name, copyErr)
1396
- }
1397
- if written > maxUpdateEntryBytes {
1398
- return fmt.Errorf("entry %s in update bundle exceeds %d bytes", hdr.Name, maxUpdateEntryBytes)
1399
- }
1400
- if closeErr != nil {
1401
- return fmt.Errorf("close %s: %w", name, closeErr)
1402
- }
1403
- default:
1404
- continue
1405
- }
1406
- }
1407
- }
1408
-
1409
- func buildHostArtifacts(source, payload string) error {
1410
- script := filepath.Join(source, "scripts", "build-host-artifacts.sh")
1411
- if !exists(script) {
1412
- return fmt.Errorf("generated install payload missing at %s and no builder found at %s", payload, script)
1413
- }
1414
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
1415
- defer cancel()
1416
- cmd := exec.CommandContext(ctx, "bash", script)
1417
- cmd.Dir = source
1418
- cmd.Env = append(os.Environ(), "DEVRITES_HOST_ARTIFACT_DIR="+payload)
1419
- if out, err := cmd.CombinedOutput(); err != nil {
1420
- return fmt.Errorf("build generated install payload: %w: %s", err, strings.TrimSpace(string(out)))
1421
- }
1422
- return nil
1423
- }
1424
-
1425
- func normalizeTag(tag string) string {
1426
- tag = strings.TrimSpace(tag)
1427
- if tag == "" || strings.HasPrefix(tag, "v") {
1428
- return tag
1429
- }
1430
- return "v" + tag
1431
- }
1432
-
1433
1227
  func stripMarkerPath(path, begin, end string) error {
1434
1228
  data, err := os.ReadFile(path)
1435
1229
  if err != nil {
@@ -1438,6 +1232,14 @@ func stripMarkerPath(path, begin, end string) error {
1438
1232
  }
1439
1233
  return fmt.Errorf("read %s: %w", path, err)
1440
1234
  }
1235
+ next := stripMarkerBlock(data, begin, end)
1236
+ if strings.TrimSpace(string(next)) == "" {
1237
+ return os.Remove(path)
1238
+ }
1239
+ return fsutil.WriteFileAtomic(path, next, 0o644)
1240
+ }
1241
+
1242
+ func stripMarkerBlock(data []byte, begin, end string) []byte {
1441
1243
  var out strings.Builder
1442
1244
  inBlock := false
1443
1245
  for _, line := range strings.SplitAfter(string(data), "\n") {
@@ -1455,10 +1257,24 @@ func stripMarkerPath(path, begin, end string) error {
1455
1257
  out.WriteString(line)
1456
1258
  }
1457
1259
  }
1458
- if strings.TrimSpace(out.String()) == "" {
1459
- return os.Remove(path)
1260
+ return []byte(out.String())
1261
+ }
1262
+
1263
+ func hasTopLevelTOMLKey(data []byte, key string) bool {
1264
+ for _, line := range strings.Split(string(data), "\n") {
1265
+ line = strings.TrimSpace(line)
1266
+ if line == "" || strings.HasPrefix(line, "#") {
1267
+ continue
1268
+ }
1269
+ if strings.HasPrefix(line, "[") {
1270
+ return false
1271
+ }
1272
+ name, _, ok := strings.Cut(line, "=")
1273
+ if ok && strings.TrimSpace(name) == key {
1274
+ return true
1275
+ }
1460
1276
  }
1461
- return fsutil.WriteFileAtomic(path, []byte(out.String()), 0o644)
1277
+ return false
1462
1278
  }
1463
1279
 
1464
1280
  func readJSON(path string) (map[string]any, error) {
@@ -1497,27 +1313,6 @@ func isDevritesHookCommand(v any) bool {
1497
1313
  (strings.Contains(command, "printf ") && strings.Contains(command, "DevRites:"))
1498
1314
  }
1499
1315
 
1500
- func containsDevritesHook(v any) bool {
1501
- switch value := v.(type) {
1502
- case map[string]any:
1503
- if isDevritesHookCommand(value) {
1504
- return true
1505
- }
1506
- for _, child := range value {
1507
- if containsDevritesHook(child) {
1508
- return true
1509
- }
1510
- }
1511
- case []any:
1512
- for _, child := range value {
1513
- if containsDevritesHook(child) {
1514
- return true
1515
- }
1516
- }
1517
- }
1518
- return false
1519
- }
1520
-
1521
1316
  func isDevritesHooksComment(comment string) bool {
1522
1317
  return comment == "DevRites hooks" ||
1523
1318
  strings.HasPrefix(comment, "DevRites hooks: every event invokes the global `devrites-engine` engine binary") ||
@@ -1527,6 +1322,11 @@ func isDevritesHooksComment(comment string) bool {
1527
1322
  strings.HasPrefix(comment, "DevRites hooks for Codex. Project hooks load only after")
1528
1323
  }
1529
1324
 
1325
+ func isDevritesSettingsComment(comment string) bool {
1326
+ return isDevritesHooksComment(comment) ||
1327
+ strings.HasPrefix(comment, "DevRites keeps the root orchestration context source-read-only.")
1328
+ }
1329
+
1530
1330
  func stripDevritesHooks(config map[string]any) map[string]any {
1531
1331
  next := map[string]any{}
1532
1332
  for k, v := range config {
@@ -1540,9 +1340,12 @@ func stripDevritesHooks(config map[string]any) map[string]any {
1540
1340
  }
1541
1341
  next[k] = v
1542
1342
  }
1543
- rawHooks, ok := next["hooks"].(map[string]any)
1343
+ rawHooksValue, exists := next["hooks"]
1344
+ if !exists {
1345
+ return next
1346
+ }
1347
+ rawHooks, ok := rawHooksValue.(map[string]any)
1544
1348
  if !ok {
1545
- delete(next, "hooks")
1546
1349
  return next
1547
1350
  }
1548
1351
  hooks := map[string]any{}
@@ -1591,29 +1394,124 @@ func stripDevritesHooks(config map[string]any) map[string]any {
1591
1394
  return next
1592
1395
  }
1593
1396
 
1594
- func mergeHooks(existing, devrites map[string]any) map[string]any {
1595
- hooks, _ := existing["hooks"].(map[string]any)
1596
- if hooks == nil {
1597
- hooks = map[string]any{}
1397
+ func stripDevritesSettings(config map[string]any, removeOwnedDefaultMode bool) map[string]any {
1398
+ next := stripDevritesHooks(config)
1399
+ if comment, ok := next["$comment"].(string); ok && isDevritesSettingsComment(comment) {
1400
+ delete(next, "$comment")
1401
+ }
1402
+
1403
+ rawPermissions, exists := next["permissions"]
1404
+ if !exists {
1405
+ return next
1598
1406
  }
1599
- devHooks, _ := devrites["hooks"].(map[string]any)
1600
- for event, entries := range devHooks {
1601
- arr, _ := hooks[event].([]any)
1602
- devArr, _ := entries.([]any)
1603
- hooks[event] = append(arr, devArr...)
1407
+ permissions, ok := rawPermissions.(map[string]any)
1408
+ if !ok {
1409
+ return next
1604
1410
  }
1605
- existing["hooks"] = hooks
1606
- if statusLine, ok := devrites["statusLine"]; ok {
1607
- if current, exists := existing["statusLine"]; !exists || isDevritesHookCommand(current) {
1608
- existing["statusLine"] = statusLine
1411
+ clean := make(map[string]any, len(permissions))
1412
+ for key, value := range permissions {
1413
+ switch key {
1414
+ case "allow":
1415
+ rules, ok := value.([]any)
1416
+ if !ok {
1417
+ clean[key] = value
1418
+ continue
1419
+ }
1420
+ kept := make([]any, 0, len(rules))
1421
+ for _, rule := range rules {
1422
+ if !isDevritesPermissionRule(rule) {
1423
+ kept = append(kept, rule)
1424
+ }
1425
+ }
1426
+ if len(kept) > 0 {
1427
+ clean[key] = kept
1428
+ }
1429
+ case "defaultMode":
1430
+ if removeOwnedDefaultMode && value == "plan" {
1431
+ continue
1432
+ }
1433
+ clean[key] = value
1434
+ default:
1435
+ clean[key] = value
1436
+ }
1437
+ }
1438
+ if len(clean) == 0 {
1439
+ delete(next, "permissions")
1440
+ } else {
1441
+ next["permissions"] = clean
1442
+ }
1443
+ return next
1444
+ }
1445
+
1446
+ func mergeClaudeSettingsConfig(current, desired map[string]any, defaultModeOwned bool) (map[string]any, bool, error) {
1447
+ next := stripDevritesSettings(current, false)
1448
+ permissions := map[string]any{}
1449
+ if raw, exists := next["permissions"]; exists {
1450
+ var ok bool
1451
+ permissions, ok = raw.(map[string]any)
1452
+ if !ok {
1453
+ return nil, false, fmt.Errorf("existing Claude permissions must be a JSON object")
1454
+ }
1455
+ }
1456
+
1457
+ switch mode, exists := permissions["defaultMode"]; {
1458
+ case !exists:
1459
+ defaultModeOwned = true
1460
+ case mode == "plan":
1461
+ case mode != "plan":
1462
+ return nil, false, fmt.Errorf("existing Claude permissions.defaultMode is %q; DevRites requires plan mode for the read-only root orchestrator", mode)
1463
+ }
1464
+ permissions["defaultMode"] = "plan"
1465
+
1466
+ desiredPermissions, ok := desired["permissions"].(map[string]any)
1467
+ if !ok || desiredPermissions["defaultMode"] != "plan" {
1468
+ return nil, false, fmt.Errorf("DevRites Claude settings payload must declare permissions.defaultMode=plan")
1469
+ }
1470
+ desiredAllow, ok := desiredPermissions["allow"].([]any)
1471
+ if !ok {
1472
+ return nil, false, fmt.Errorf("DevRites Claude settings payload permissions.allow must be an array")
1473
+ }
1474
+ var allow []any
1475
+ if existingAllow, exists := permissions["allow"]; exists {
1476
+ allow, ok = existingAllow.([]any)
1477
+ if !ok {
1478
+ return nil, false, fmt.Errorf("existing Claude permissions.allow must be an array")
1479
+ }
1480
+ }
1481
+ seen := map[string]bool{}
1482
+ for _, rule := range allow {
1483
+ if value, ok := rule.(string); ok {
1484
+ seen[value] = true
1485
+ }
1486
+ }
1487
+ for _, rule := range desiredAllow {
1488
+ value, ok := rule.(string)
1489
+ if !ok {
1490
+ return nil, false, fmt.Errorf("DevRites Claude permission rules must be strings")
1491
+ }
1492
+ if !seen[value] {
1493
+ allow = append(allow, value)
1494
+ seen[value] = true
1609
1495
  }
1610
1496
  }
1611
- return existing
1497
+ permissions["allow"] = allow
1498
+ next["permissions"] = permissions
1499
+ if _, exists := next["$comment"]; !exists {
1500
+ if comment, ok := desired["$comment"].(string); ok {
1501
+ next["$comment"] = comment
1502
+ }
1503
+ }
1504
+ return next, defaultModeOwned, nil
1505
+ }
1506
+
1507
+ func isDevritesPermissionRule(value any) bool {
1508
+ rule, ok := value.(string)
1509
+ return ok && strings.HasPrefix(strings.TrimSpace(rule), "Bash(devrites-engine ")
1612
1510
  }
1613
1511
 
1614
1512
  func (r *runner) installBinary() error {
1615
1513
  if !r.opts.WithBinary || os.Getenv("DEVRITES_NO_BINARY") == "1" {
1616
- fmt.Fprintln(r.opts.Stdout, " engine binary: skipped (--no-binary); hooks fail open.")
1514
+ fmt.Fprintln(r.opts.Stdout, " engine binary: skipped (--no-binary).")
1617
1515
  return nil
1618
1516
  }
1619
1517
  if r.opts.DryRun {
@@ -1632,7 +1530,7 @@ func (r *runner) installBinary() error {
1632
1530
  staged, cleanup := r.preparedBinary, func() {}
1633
1531
  if staged == "" {
1634
1532
  var err error
1635
- staged, cleanup, err = r.acquireBinary(tag, githubBaseURL)
1533
+ staged, cleanup, err = r.acquireBinary(tag)
1636
1534
  if err != nil {
1637
1535
  return r.binaryInstallFailure(err)
1638
1536
  }
@@ -1672,17 +1570,14 @@ func (r *runner) installBinary() error {
1672
1570
  return fmt.Errorf("verify installed engine binary: %w (bad binary removed)", err)
1673
1571
  }
1674
1572
  fmt.Fprintf(r.opts.Stdout, " engine binary: installed %s\n", dest)
1675
- if !binaryReachableFromPATH(dest) {
1676
- fmt.Fprintf(r.opts.Stderr, "warning: engine binary: %s is not on PATH; installed hooks will fail open until devrites-engine is reachable.\n", dest)
1677
- }
1678
1573
  return nil
1679
1574
  }
1680
1575
 
1681
1576
  func (r *runner) binaryTag() string {
1682
- if r.releaseBinaryTag != "" {
1683
- return r.releaseBinaryTag
1577
+ if r.requiredBinaryTag != "" {
1578
+ return r.requiredBinaryTag
1684
1579
  }
1685
- if tag := os.Getenv("DEVRITES_REF"); tag != "" {
1580
+ if tag := os.Getenv("DEVRITES_REF"); semverLike(tag) {
1686
1581
  return tag
1687
1582
  }
1688
1583
  return "v" + strings.TrimPrefix(installedVersion(r.source), "v")
@@ -1737,81 +1632,25 @@ func (r *runner) binaryInstallFailure(err error) error {
1737
1632
  if r.preparedBinary != "" {
1738
1633
  return err
1739
1634
  }
1740
- fmt.Fprintf(r.opts.Stderr, "warning: engine binary: %v - hooks fail open until you install it.\n", err)
1741
- return nil
1742
- }
1743
-
1744
- func (r *runner) acquireBinary(tag, baseURL string) (string, func(), error) {
1745
- if v := os.Getenv("DEVRITES_ENGINE_CLI"); v != "" {
1746
- if exists(v) {
1747
- if r.releaseBinaryTag == "" || strings.TrimPrefix(engineVersion(v), "v") == strings.TrimPrefix(r.releaseBinaryTag, "v") {
1748
- return v, func() {}, nil
1749
- }
1750
- } else {
1751
- return "", func() {}, fmt.Errorf("DEVRITES_ENGINE_CLI points to a missing binary: %s", v)
1752
- }
1753
- }
1754
- tmp, err := os.MkdirTemp("", "devrites-engine-*")
1755
- if err != nil {
1756
- return "", func() {}, fmt.Errorf("create temp dir: %w", err)
1757
- }
1758
- cleanup := func() { _ = os.RemoveAll(tmp) }
1759
- staged := filepath.Join(tmp, engineBinaryName())
1760
- var releaseErr error
1761
- if r.releaseBinaryTag != "" && os.Getenv("DEVRITES_UPDATE_BUNDLE") == "" {
1762
- repo := os.Getenv("DEVRITES_REPO")
1763
- if repo == "" {
1764
- repo = defaultRepo
1765
- }
1766
- downloaded, err := downloadReleaseBinary(baseURL, repo, r.releaseBinaryTag, staged)
1767
- if err == nil {
1768
- return staged, cleanup, nil
1769
- }
1770
- if downloaded {
1771
- return "", cleanup, err
1772
- }
1773
- releaseErr = err
1774
- }
1775
- if r.source != "" && exists(filepath.Join(r.source, "engine", "go.mod")) {
1776
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
1777
- defer cancel()
1778
- cmd := exec.CommandContext(ctx, "go", "build", "-buildvcs=false", "-trimpath", "-ldflags", "-s -w -X github.com/devrites/devrites/internal/version.Version="+tag, "-o", staged, ".")
1779
- cmd.Dir = filepath.Join(r.source, "engine")
1780
- if out, err := cmd.CombinedOutput(); err != nil {
1781
- if releaseErr != nil {
1782
- return "", cleanup, fmt.Errorf("could not download release binary: %v; source fallback failed: %w: %s", releaseErr, err, strings.TrimSpace(string(out)))
1783
- }
1784
- return "", cleanup, fmt.Errorf("could not build from source: %w: %s", err, strings.TrimSpace(string(out)))
1785
- }
1786
- return staged, cleanup, nil
1787
- }
1788
- if releaseErr != nil {
1789
- return "", cleanup, fmt.Errorf("could not obtain a binary: %v; no Go source fallback", releaseErr)
1635
+ if os.Getenv("DEVRITES_ENGINE_CLI") != "" {
1636
+ return err
1790
1637
  }
1791
- return "", cleanup, fmt.Errorf("could not obtain a binary (no DEVRITES_ENGINE_CLI handoff, no Go source)")
1638
+ fmt.Fprintf(r.opts.Stderr, "warning: engine binary: %v; continuing without it.\n", err)
1639
+ return nil
1792
1640
  }
1793
1641
 
1794
- func downloadReleaseBinary(baseURL, repo, tag, dest string) (bool, error) {
1795
- asset := fmt.Sprintf("devrites-%s-%s", runtime.GOOS, runtime.GOARCH)
1796
- if runtime.GOOS == "windows" {
1797
- asset += ".exe"
1798
- }
1799
- url := fmt.Sprintf("%s/%s/releases/download/%s/%s", strings.TrimRight(baseURL, "/"), repo, tag, asset)
1800
- if err := iohooks.DownloadFile(url, dest); err != nil {
1801
- return false, fmt.Errorf("download %s: %w", asset, err)
1802
- }
1803
- sumPath := dest + ".sha256"
1804
- defer os.Remove(sumPath)
1805
- if err := iohooks.DownloadFile(url+".sha256", sumPath); err != nil {
1806
- return true, fmt.Errorf("could not verify release binary %s: missing checksum: %w", asset, err)
1642
+ func (r *runner) acquireBinary(tag string) (string, func(), error) {
1643
+ path := os.Getenv("DEVRITES_ENGINE_CLI")
1644
+ if path == "" {
1645
+ return "", func() {}, fmt.Errorf("no DEVRITES_ENGINE_CLI handoff")
1807
1646
  }
1808
- if err := verifySHA256(dest, sumPath); err != nil {
1809
- return true, fmt.Errorf("could not verify release binary %s: %w", asset, err)
1647
+ if !exists(path) {
1648
+ return "", func() {}, fmt.Errorf("DEVRITES_ENGINE_CLI points to a missing binary: %s", path)
1810
1649
  }
1811
- if err := os.Chmod(dest, 0o755); err != nil && runtime.GOOS != "windows" {
1812
- return true, fmt.Errorf("make release binary executable: %w", err)
1650
+ if _, err := verifyEngineBinary(path, tag, 30*time.Second); err != nil {
1651
+ return "", func() {}, fmt.Errorf("DEVRITES_ENGINE_CLI is incompatible: %w", err)
1813
1652
  }
1814
- return true, nil
1653
+ return path, func() {}, nil
1815
1654
  }
1816
1655
 
1817
1656
  func (r *runner) removeBinary() error {
@@ -1864,36 +1703,6 @@ func binaryCandidates() []string {
1864
1703
  return append(candidates, filepath.Join("/usr/local/bin", engineBinaryName()))
1865
1704
  }
1866
1705
 
1867
- func binaryReachableFromPATH(dest string) bool {
1868
- if _, err := exec.LookPath("devrites-engine"); err == nil {
1869
- return true
1870
- }
1871
- return pathContainsDir(filepath.Dir(dest))
1872
- }
1873
-
1874
- func pathContainsDir(dir string) bool {
1875
- if dir == "" {
1876
- return false
1877
- }
1878
- want, err := filepath.Abs(dir)
1879
- if err != nil {
1880
- want = filepath.Clean(dir)
1881
- }
1882
- for _, entry := range filepath.SplitList(os.Getenv("PATH")) {
1883
- if entry == "" {
1884
- continue
1885
- }
1886
- got, err := filepath.Abs(entry)
1887
- if err != nil {
1888
- got = filepath.Clean(entry)
1889
- }
1890
- if got == want {
1891
- return true
1892
- }
1893
- }
1894
- return false
1895
- }
1896
-
1897
1706
  func engineVersion(path string) string {
1898
1707
  got, err := readEngineVersion(path, 30*time.Second)
1899
1708
  if err != nil {
@@ -2016,14 +1825,16 @@ func manifestHeader(path, key string) string {
2016
1825
  func installedVersion(source string) string {
2017
1826
  if source != "" {
2018
1827
  data, err := os.ReadFile(filepath.Join(source, "package.json"))
2019
- if err == nil {
2020
- var pkg struct {
2021
- Version string `json:"version"`
2022
- }
2023
- if json.Unmarshal(data, &pkg) == nil && pkg.Version != "" {
2024
- return pkg.Version
2025
- }
1828
+ if err != nil {
1829
+ return "unknown"
1830
+ }
1831
+ var pkg struct {
1832
+ Version string `json:"version"`
1833
+ }
1834
+ if json.Unmarshal(data, &pkg) != nil || pkg.Version == "" {
1835
+ return "unknown"
2026
1836
  }
1837
+ return pkg.Version
2027
1838
  }
2028
1839
  if version.Version != "" && version.Version != "dev" {
2029
1840
  return strings.TrimPrefix(version.Version, "v")