devrites 3.0.7 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (472) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +67 -47
  3. package/SECURITY.md +31 -29
  4. package/docs/adr/0001-go-engine-as-control-plane.md +1 -1
  5. package/docs/adr/0002-dual-host-harness.md +1 -1
  6. package/docs/adr/0004-state-schema-phases-sections.md +1 -2
  7. package/docs/adr/0006-clock-seam-and-engine-ci-gates.md +3 -4
  8. package/docs/adr/0009-prebuild-decision-coverage-and-readiness.md +67 -0
  9. package/docs/adr/0010-agent-first-fresh-context-orchestration.md +71 -0
  10. package/docs/adr/0011-define-plan-transition-rights.md +28 -0
  11. package/docs/adr/README.md +3 -0
  12. package/docs/agents/triage-labels.md +7 -7
  13. package/docs/architecture.md +157 -140
  14. package/docs/cli.md +41 -12
  15. package/docs/command-map.md +49 -35
  16. package/docs/engine/agent-contract.md +92 -15
  17. package/docs/engine/commands.md +242 -72
  18. package/docs/engine/state-schema.md +52 -23
  19. package/docs/engine/workspace-schema.md +94 -15
  20. package/docs/extensions.md +1 -1
  21. package/docs/flow.md +80 -50
  22. package/docs/harness-compliance.md +29 -5
  23. package/docs/orchestration.md +107 -79
  24. package/docs/quick-reference.md +7 -3
  25. package/docs/release.md +4 -3
  26. package/docs/skills.md +64 -38
  27. package/docs/usage.md +57 -38
  28. package/engine/cmd/releasepack/main.go +219 -0
  29. package/engine/cmd/releasepack/main_test.go +170 -0
  30. package/engine/commands.go +170 -23
  31. package/engine/git_guard.go +187 -0
  32. package/engine/git_guard_test.go +283 -0
  33. package/engine/hookpolicy.go +53 -55
  34. package/engine/hookpolicy_test.go +91 -1
  35. package/engine/hooks.go +296 -75
  36. package/engine/hooks_events_test.go +276 -6
  37. package/engine/hooks_workspace.go +640 -159
  38. package/engine/internal/devritespaths/paths.go +65 -10
  39. package/engine/internal/devritespaths/paths_test.go +110 -0
  40. package/engine/internal/doctor/doctor.go +153 -23
  41. package/engine/internal/doctor/doctor_test.go +74 -0
  42. package/engine/internal/forge/forge.go +940 -0
  43. package/engine/internal/forge/forge_test.go +576 -0
  44. package/engine/internal/forge/git.go +245 -0
  45. package/engine/internal/forge/liveness_unix.go +48 -0
  46. package/engine/internal/forge/liveness_windows.go +67 -0
  47. package/engine/internal/forge/manifest.go +402 -0
  48. package/engine/internal/gate/gate.go +90 -71
  49. package/engine/internal/gate/gate_test.go +71 -2
  50. package/engine/internal/harness/compliance.go +18 -24
  51. package/engine/internal/harness/harness.go +37 -44
  52. package/engine/internal/harness/harness_test.go +21 -5
  53. package/engine/internal/install/install.go +486 -38
  54. package/engine/internal/install/install_test.go +383 -10
  55. package/engine/internal/iohooks/iohooks.go +350 -59
  56. package/engine/internal/iohooks/iohooks_test.go +421 -1
  57. package/engine/internal/lib/buildreadiness.go +35 -18
  58. package/engine/internal/lib/clarifyreturn.go +91 -0
  59. package/engine/internal/lib/clarifyreturn_test.go +123 -0
  60. package/engine/internal/lib/context.go +54 -15
  61. package/engine/internal/lib/cursor_compat_test.go +8 -0
  62. package/engine/internal/lib/extensions.go +2 -3
  63. package/engine/internal/lib/gitauthority.go +601 -0
  64. package/engine/internal/lib/gitauthority_test.go +346 -0
  65. package/engine/internal/lib/jsonout.go +25 -11
  66. package/engine/internal/lib/jsonout_test.go +16 -0
  67. package/engine/internal/lib/lanes.go +14 -8
  68. package/engine/internal/lib/observability_test.go +358 -0
  69. package/engine/internal/lib/packageexistence.go +151 -35
  70. package/engine/internal/lib/packageexistence_test.go +163 -0
  71. package/engine/internal/lib/progress.go +11 -9
  72. package/engine/internal/lib/provenance.go +462 -0
  73. package/engine/internal/lib/provenance_test.go +154 -0
  74. package/engine/internal/lib/readiness_contract.json +111 -0
  75. package/engine/internal/lib/readinessartifact.go +510 -0
  76. package/engine/internal/lib/readinessartifact_test.go +321 -0
  77. package/engine/internal/lib/reconcile.go +712 -88
  78. package/engine/internal/lib/reconcile_test.go +335 -16
  79. package/engine/internal/lib/recoveryattempts.go +298 -0
  80. package/engine/internal/lib/recoveryattempts_test.go +215 -0
  81. package/engine/internal/lib/resolve.go +23 -19
  82. package/engine/internal/lib/runbook_context_test.go +20 -0
  83. package/engine/internal/lib/session.go +701 -9
  84. package/engine/internal/lib/session_test.go +80 -13
  85. package/engine/internal/lib/testintegrity.go +33 -37
  86. package/engine/internal/lib/testintegrity_test.go +63 -1
  87. package/engine/internal/migrate/migrate.go +81 -1
  88. package/engine/internal/migrate/migrate_test.go +22 -1
  89. package/engine/internal/reason/reason.go +180 -0
  90. package/engine/internal/reason/reason_test.go +35 -0
  91. package/engine/internal/rootfacts/facts.go +466 -0
  92. package/engine/internal/rootfacts/facts_test.go +306 -0
  93. package/engine/internal/safepath/safepath.go +55 -0
  94. package/engine/internal/safepath/safepath_test.go +69 -0
  95. package/engine/internal/safepath/safepath_windows_test.go +26 -0
  96. package/engine/internal/state/clarify_transition.go +165 -0
  97. package/engine/internal/state/clarify_transition_test.go +130 -0
  98. package/engine/internal/state/cmd/workflowmanifest/main.go +12 -8
  99. package/engine/internal/state/cursor.go +58 -0
  100. package/engine/internal/state/cursor_test.go +42 -1
  101. package/engine/internal/state/feature.go +35 -48
  102. package/engine/internal/state/schema.go +90 -37
  103. package/engine/internal/state/snapshot.go +18 -2
  104. package/engine/internal/state/state_test.go +187 -7
  105. package/engine/internal/state/status.go +48 -11
  106. package/engine/internal/state/workflow_manifest.json +231 -50
  107. package/engine/internal/toolpolicy/classifier.go +533 -0
  108. package/engine/internal/toolpolicy/classifier_test.go +424 -0
  109. package/engine/internal/toolpolicy/git.go +616 -0
  110. package/engine/internal/toolpolicy/scanner.go +382 -0
  111. package/engine/main.go +160 -77
  112. package/engine/observability_cli_test.go +52 -0
  113. package/engine/root_routing_test.go +277 -0
  114. package/engine/testdata/golden/TestParityBuildReadiness/arg=approved.golden +1 -1
  115. package/engine/testdata/golden/TestParityBuildReadiness/arg=emptystatus.golden +1 -1
  116. package/engine/testdata/golden/TestParityBuildReadiness/arg=noclarify.golden +1 -0
  117. package/engine/testdata/golden/TestParityBuildReadiness/arg=novet.golden +1 -0
  118. package/engine/testdata/golden/TestParityBuildReadiness/arg=stalevet.golden +1 -0
  119. package/engine/testdata/golden/TestParityBuildReadiness/arg=trailhash.golden +1 -1
  120. package/engine/testdata/golden/TestParityBuildReadiness/arg=trailpipe.golden +1 -1
  121. package/engine/testdata/golden/TestParityBuildReadiness/arg=vetnotready.golden +1 -0
  122. package/engine/testdata/golden/TestParityProgress/arg=allbuilt.golden +1 -1
  123. package/engine/testdata/golden/TestParityProgress/arg=done.golden +1 -1
  124. package/engine/testdata/golden/TestParityProgress/arg=mid.golden +1 -1
  125. package/engine/testdata/golden/TestParityProgress/arg=nophase.golden +1 -1
  126. package/engine/testdata/golden/TestParityProgress/arg=noslice.golden +1 -1
  127. package/engine/testdata/golden/TestParityProgress/arg=plan.golden +1 -1
  128. package/engine/testdata/golden/TestParityProgress/arg=seal.golden +1 -1
  129. package/engine/testdata/golden/TestParityReconcile/check-clean.golden +1 -1
  130. package/engine/testdata/golden/TestParityReconcile/inline-fallback.golden +1 -0
  131. package/engine/testdata/golden/TestParityReconcile/snapshot-no-allowlist.golden +1 -0
  132. package/engine/testdata/golden/TestParityWrightScope/devrites-edit-denied.golden +2 -0
  133. package/engine/testdata/golden/TestParityWrightScope/out-of-scope-enforce-denies.golden +1 -1
  134. package/engine/tests/adr_0011_define_plan_test.go +30 -0
  135. package/engine/tests/budget_test.go +19 -8
  136. package/engine/tests/concurrency_cli_test.go +7 -1
  137. package/engine/tests/doctor_cli_test.go +148 -2
  138. package/engine/tests/forge_cli_test.go +463 -0
  139. package/engine/tests/gate_test.go +63 -8
  140. package/engine/tests/hook_test.go +260 -8
  141. package/engine/tests/hooks_io_test.go +94 -3
  142. package/engine/tests/json_contract_test.go +127 -2
  143. package/engine/tests/migrate_cli_test.go +2 -2
  144. package/engine/tests/parity_buildreadiness_test.go +141 -10
  145. package/engine/tests/parity_learnings_test.go +16 -17
  146. package/engine/tests/parity_reconcile_test.go +20 -17
  147. package/engine/tests/parity_resolve_test.go +12 -11
  148. package/engine/tests/parity_test.go +21 -17
  149. package/pack/.claude/agents/devrites-code-reviewer.md +62 -48
  150. package/pack/.claude/agents/devrites-devex-reviewer.md +69 -53
  151. package/pack/.claude/agents/devrites-doubt-reviewer.md +29 -22
  152. package/pack/.claude/agents/devrites-evidence-scout.md +69 -0
  153. package/pack/.claude/agents/devrites-forge-judge.md +74 -61
  154. package/pack/.claude/agents/devrites-frontend-reviewer.md +48 -39
  155. package/pack/.claude/agents/devrites-performance-reviewer.md +49 -40
  156. package/pack/.claude/agents/devrites-plan-drafter.md +71 -0
  157. package/pack/.claude/agents/devrites-plan-reviewer.md +80 -47
  158. package/pack/.claude/agents/devrites-proof-runner.md +74 -0
  159. package/pack/.claude/agents/devrites-retrospector.md +48 -45
  160. package/pack/.claude/agents/devrites-security-auditor.md +46 -36
  161. package/pack/.claude/agents/devrites-simplifier-reviewer.md +50 -46
  162. package/pack/.claude/agents/devrites-slice-wright.md +153 -165
  163. package/pack/.claude/agents/devrites-spec-reviewer.md +34 -32
  164. package/pack/.claude/agents/devrites-strategy-reviewer.md +63 -34
  165. package/pack/.claude/agents/devrites-test-analyst.md +40 -30
  166. package/pack/.claude/settings.json +2 -1
  167. package/pack/.claude/skills/devrites-audit/SKILL.md +40 -61
  168. package/pack/.claude/skills/devrites-debug-recovery/SKILL.md +24 -13
  169. package/pack/.claude/skills/devrites-debug-recovery/reference/build-the-loop.md +24 -21
  170. package/pack/.claude/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +34 -6
  171. package/pack/.claude/skills/devrites-debug-recovery/reference/instrumentation.md +2 -2
  172. package/pack/.claude/skills/devrites-doubt/SKILL.md +25 -17
  173. package/pack/.claude/skills/devrites-interview/SKILL.md +53 -52
  174. package/pack/.claude/skills/devrites-lib/SKILL.md +11 -9
  175. package/pack/.claude/skills/devrites-lib/reference/intent-map.md +3 -2
  176. package/pack/.claude/skills/devrites-lib/reference/parallel-dispatch.md +82 -137
  177. package/pack/.claude/skills/devrites-lib/reference/reply-contract.md +8 -1
  178. package/pack/.claude/skills/devrites-lib/reference/standards/README.md +38 -55
  179. package/pack/.claude/skills/devrites-lib/reference/standards/afk-hitl.md +53 -27
  180. package/pack/.claude/skills/devrites-lib/reference/standards/agents.md +198 -190
  181. package/pack/.claude/skills/devrites-lib/reference/standards/anti-patterns.md +5 -15
  182. package/pack/.claude/skills/devrites-lib/reference/standards/ci-cd.md +27 -58
  183. package/pack/.claude/skills/devrites-lib/reference/standards/code-review.md +16 -52
  184. package/pack/.claude/skills/devrites-lib/reference/standards/coding-style.md +2 -10
  185. package/pack/.claude/skills/devrites-lib/reference/standards/context-hygiene.md +18 -61
  186. package/pack/.claude/skills/devrites-lib/reference/standards/core.md +23 -18
  187. package/pack/.claude/skills/devrites-lib/reference/standards/deprecation.md +17 -57
  188. package/pack/.claude/skills/devrites-lib/reference/standards/development-workflow.md +4 -33
  189. package/pack/.claude/skills/devrites-lib/reference/standards/git-workflow.md +4 -20
  190. package/pack/.claude/skills/devrites-lib/reference/standards/hooks.md +3 -14
  191. package/pack/.claude/skills/devrites-lib/reference/standards/patterns.md +9 -25
  192. package/pack/.claude/skills/devrites-lib/reference/standards/performance.md +0 -9
  193. package/pack/.claude/skills/devrites-lib/reference/standards/principles.md +1 -3
  194. package/pack/.claude/skills/devrites-lib/reference/standards/security.md +17 -7
  195. package/pack/.claude/skills/devrites-lib/reference/standards/testing.md +1 -27
  196. package/pack/.claude/skills/devrites-lib/reference/standards/tooling.md +46 -50
  197. package/pack/.claude/skills/devrites-lib/reference/workspace-artifact-schema.md +23 -10
  198. package/pack/.claude/skills/devrites-source-driven/SKILL.md +23 -22
  199. package/pack/.claude/skills/rite/SKILL.md +8 -6
  200. package/pack/.claude/skills/rite/reference/menu.md +8 -6
  201. package/pack/.claude/skills/rite-adopt/SKILL.md +33 -37
  202. package/pack/.claude/skills/rite-autocomplete/SKILL.md +33 -28
  203. package/pack/.claude/skills/rite-autocomplete/reference/loop.md +21 -21
  204. package/pack/.claude/skills/rite-autocomplete/reference/stop-conditions.md +15 -6
  205. package/pack/.claude/skills/rite-build/SKILL.md +35 -30
  206. package/pack/.claude/skills/rite-build/reference/afk-discipline.md +38 -38
  207. package/pack/.claude/skills/rite-build/reference/evidence-standard.md +10 -0
  208. package/pack/.claude/skills/rite-build/reference/forge.md +186 -156
  209. package/pack/.claude/skills/rite-build/reference/one-slice-cycle.md +4 -3
  210. package/pack/.claude/skills/rite-build/reference/phase-contract.md +96 -175
  211. package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +129 -134
  212. package/pack/.claude/skills/rite-clarify/SKILL.md +89 -0
  213. package/pack/.claude/skills/rite-clarify/reference/decision-coverage.md +59 -0
  214. package/pack/.claude/skills/rite-converge/SKILL.md +35 -25
  215. package/pack/.claude/skills/rite-define/SKILL.md +57 -32
  216. package/pack/.claude/skills/rite-define/reference/gates.md +16 -15
  217. package/pack/.claude/skills/rite-define/reference/plan-template.md +16 -8
  218. package/pack/.claude/skills/rite-frame/reference/failure-modes.md +22 -24
  219. package/pack/.claude/skills/rite-plan/SKILL.md +44 -16
  220. package/pack/.claude/skills/rite-plan/reference/task-breakdown.md +4 -0
  221. package/pack/.claude/skills/rite-polish/SKILL.md +27 -23
  222. package/pack/.claude/skills/rite-prototype/SKILL.md +25 -26
  223. package/pack/.claude/skills/rite-prove/SKILL.md +27 -17
  224. package/pack/.claude/skills/rite-resolve/SKILL.md +12 -11
  225. package/pack/.claude/skills/rite-resolve/reference/answer-protocol.md +3 -0
  226. package/pack/.claude/skills/rite-review/SKILL.md +37 -28
  227. package/pack/.claude/skills/rite-seal/reference/phase-contract.md +27 -92
  228. package/pack/.claude/skills/rite-seal/reference/risk-and-rollback.md +13 -0
  229. package/pack/.claude/skills/rite-ship/reference/design-memory.md +31 -36
  230. package/pack/.claude/skills/rite-spec/SKILL.md +84 -135
  231. package/pack/.claude/skills/rite-spec/reference/investigation.md +37 -33
  232. package/pack/.claude/skills/rite-spec/reference/question-protocol.md +6 -2
  233. package/pack/.claude/skills/rite-spec/reference/spec-checklists.md +25 -24
  234. package/pack/.claude/skills/rite-spec/reference/spec-template.md +9 -2
  235. package/pack/.claude/skills/rite-spec/reference/state-workspace.md +13 -3
  236. package/pack/.claude/skills/rite-temper/SKILL.md +62 -47
  237. package/pack/.claude/skills/rite-temper/reference/review-dimensions.md +24 -22
  238. package/pack/.claude/skills/rite-vet/SKILL.md +110 -113
  239. package/pack/.claude/skills/rite-vet/reference/artifacts.md +42 -9
  240. package/pack/.claude/skills/rite-vet/reference/review-axes.md +39 -37
  241. package/pack/generated/claude/agents/devrites-code-reviewer.md +62 -48
  242. package/pack/generated/claude/agents/devrites-devex-reviewer.md +69 -53
  243. package/pack/generated/claude/agents/devrites-doubt-reviewer.md +29 -22
  244. package/pack/generated/claude/agents/devrites-evidence-scout.md +69 -0
  245. package/pack/generated/claude/agents/devrites-forge-judge.md +74 -61
  246. package/pack/generated/claude/agents/devrites-frontend-reviewer.md +48 -39
  247. package/pack/generated/claude/agents/devrites-performance-reviewer.md +49 -40
  248. package/pack/generated/claude/agents/devrites-plan-drafter.md +71 -0
  249. package/pack/generated/claude/agents/devrites-plan-reviewer.md +80 -47
  250. package/pack/generated/claude/agents/devrites-proof-runner.md +74 -0
  251. package/pack/generated/claude/agents/devrites-retrospector.md +48 -45
  252. package/pack/generated/claude/agents/devrites-security-auditor.md +46 -36
  253. package/pack/generated/claude/agents/devrites-simplifier-reviewer.md +50 -46
  254. package/pack/generated/claude/agents/devrites-slice-wright.md +153 -165
  255. package/pack/generated/claude/agents/devrites-spec-reviewer.md +34 -32
  256. package/pack/generated/claude/agents/devrites-strategy-reviewer.md +63 -34
  257. package/pack/generated/claude/agents/devrites-test-analyst.md +40 -30
  258. package/pack/generated/claude/settings.json +2 -1
  259. package/pack/generated/claude/skills/devrites-audit/SKILL.md +40 -61
  260. package/pack/generated/claude/skills/devrites-debug-recovery/SKILL.md +24 -13
  261. package/pack/generated/claude/skills/devrites-debug-recovery/reference/build-the-loop.md +24 -21
  262. package/pack/generated/claude/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +34 -6
  263. package/pack/generated/claude/skills/devrites-debug-recovery/reference/instrumentation.md +2 -2
  264. package/pack/generated/claude/skills/devrites-doubt/SKILL.md +25 -17
  265. package/pack/generated/claude/skills/devrites-interview/SKILL.md +53 -52
  266. package/pack/generated/claude/skills/devrites-lib/SKILL.md +11 -9
  267. package/pack/generated/claude/skills/devrites-lib/reference/intent-map.md +3 -2
  268. package/pack/generated/claude/skills/devrites-lib/reference/parallel-dispatch.md +82 -137
  269. package/pack/generated/claude/skills/devrites-lib/reference/reply-contract.md +8 -1
  270. package/pack/generated/claude/skills/devrites-lib/reference/standards/README.md +38 -55
  271. package/pack/generated/claude/skills/devrites-lib/reference/standards/afk-hitl.md +53 -27
  272. package/pack/generated/claude/skills/devrites-lib/reference/standards/agents.md +198 -190
  273. package/pack/generated/claude/skills/devrites-lib/reference/standards/anti-patterns.md +5 -15
  274. package/pack/generated/claude/skills/devrites-lib/reference/standards/ci-cd.md +27 -58
  275. package/pack/generated/claude/skills/devrites-lib/reference/standards/code-review.md +16 -52
  276. package/pack/generated/claude/skills/devrites-lib/reference/standards/coding-style.md +2 -10
  277. package/pack/generated/claude/skills/devrites-lib/reference/standards/context-hygiene.md +18 -61
  278. package/pack/generated/claude/skills/devrites-lib/reference/standards/core.md +23 -18
  279. package/pack/generated/claude/skills/devrites-lib/reference/standards/deprecation.md +17 -57
  280. package/pack/generated/claude/skills/devrites-lib/reference/standards/development-workflow.md +4 -33
  281. package/pack/generated/claude/skills/devrites-lib/reference/standards/git-workflow.md +4 -20
  282. package/pack/generated/claude/skills/devrites-lib/reference/standards/hooks.md +3 -14
  283. package/pack/generated/claude/skills/devrites-lib/reference/standards/patterns.md +9 -25
  284. package/pack/generated/claude/skills/devrites-lib/reference/standards/performance.md +0 -9
  285. package/pack/generated/claude/skills/devrites-lib/reference/standards/principles.md +1 -3
  286. package/pack/generated/claude/skills/devrites-lib/reference/standards/security.md +17 -7
  287. package/pack/generated/claude/skills/devrites-lib/reference/standards/testing.md +1 -27
  288. package/pack/generated/claude/skills/devrites-lib/reference/standards/tooling.md +46 -50
  289. package/pack/generated/claude/skills/devrites-lib/reference/workspace-artifact-schema.md +23 -10
  290. package/pack/generated/claude/skills/devrites-source-driven/SKILL.md +23 -22
  291. package/pack/generated/claude/skills/rite/SKILL.md +8 -6
  292. package/pack/generated/claude/skills/rite/reference/menu.md +8 -6
  293. package/pack/generated/claude/skills/rite-adopt/SKILL.md +33 -37
  294. package/pack/generated/claude/skills/rite-autocomplete/SKILL.md +33 -28
  295. package/pack/generated/claude/skills/rite-autocomplete/reference/loop.md +21 -21
  296. package/pack/generated/claude/skills/rite-autocomplete/reference/stop-conditions.md +15 -6
  297. package/pack/generated/claude/skills/rite-build/SKILL.md +35 -30
  298. package/pack/generated/claude/skills/rite-build/reference/afk-discipline.md +38 -38
  299. package/pack/generated/claude/skills/rite-build/reference/evidence-standard.md +10 -0
  300. package/pack/generated/claude/skills/rite-build/reference/forge.md +186 -156
  301. package/pack/generated/claude/skills/rite-build/reference/one-slice-cycle.md +4 -3
  302. package/pack/generated/claude/skills/rite-build/reference/phase-contract.md +96 -175
  303. package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +129 -134
  304. package/pack/generated/claude/skills/rite-clarify/SKILL.md +89 -0
  305. package/pack/generated/claude/skills/rite-clarify/reference/decision-coverage.md +59 -0
  306. package/pack/generated/claude/skills/rite-converge/SKILL.md +35 -25
  307. package/pack/generated/claude/skills/rite-define/SKILL.md +57 -32
  308. package/pack/generated/claude/skills/rite-define/reference/gates.md +16 -15
  309. package/pack/generated/claude/skills/rite-define/reference/plan-template.md +16 -8
  310. package/pack/generated/claude/skills/rite-frame/reference/failure-modes.md +22 -24
  311. package/pack/generated/claude/skills/rite-plan/SKILL.md +44 -16
  312. package/pack/generated/claude/skills/rite-plan/reference/task-breakdown.md +4 -0
  313. package/pack/generated/claude/skills/rite-polish/SKILL.md +27 -23
  314. package/pack/generated/claude/skills/rite-prototype/SKILL.md +25 -26
  315. package/pack/generated/claude/skills/rite-prove/SKILL.md +27 -17
  316. package/pack/generated/claude/skills/rite-resolve/SKILL.md +12 -11
  317. package/pack/generated/claude/skills/rite-resolve/reference/answer-protocol.md +3 -0
  318. package/pack/generated/claude/skills/rite-review/SKILL.md +37 -28
  319. package/pack/generated/claude/skills/rite-seal/reference/phase-contract.md +27 -92
  320. package/pack/generated/claude/skills/rite-seal/reference/risk-and-rollback.md +13 -0
  321. package/pack/generated/claude/skills/rite-ship/reference/design-memory.md +31 -36
  322. package/pack/generated/claude/skills/rite-spec/SKILL.md +84 -135
  323. package/pack/generated/claude/skills/rite-spec/reference/investigation.md +37 -33
  324. package/pack/generated/claude/skills/rite-spec/reference/question-protocol.md +6 -2
  325. package/pack/generated/claude/skills/rite-spec/reference/spec-checklists.md +25 -24
  326. package/pack/generated/claude/skills/rite-spec/reference/spec-template.md +9 -2
  327. package/pack/generated/claude/skills/rite-spec/reference/state-workspace.md +13 -3
  328. package/pack/generated/claude/skills/rite-temper/SKILL.md +62 -47
  329. package/pack/generated/claude/skills/rite-temper/reference/review-dimensions.md +24 -22
  330. package/pack/generated/claude/skills/rite-vet/SKILL.md +110 -113
  331. package/pack/generated/claude/skills/rite-vet/reference/artifacts.md +42 -9
  332. package/pack/generated/claude/skills/rite-vet/reference/review-axes.md +39 -37
  333. package/pack/generated/codex/AGENTS.md +5 -2
  334. package/pack/generated/codex/agents/devrites-code-reviewer.toml +69 -47
  335. package/pack/generated/codex/agents/devrites-devex-reviewer.toml +75 -51
  336. package/pack/generated/codex/agents/devrites-doubt-reviewer.toml +35 -20
  337. package/pack/generated/codex/agents/devrites-evidence-scout.toml +75 -0
  338. package/pack/generated/codex/agents/devrites-forge-judge.toml +80 -59
  339. package/pack/generated/codex/agents/devrites-frontend-reviewer.toml +54 -37
  340. package/pack/generated/codex/agents/devrites-performance-reviewer.toml +55 -38
  341. package/pack/generated/codex/agents/devrites-plan-drafter.toml +77 -0
  342. package/pack/generated/codex/agents/devrites-plan-reviewer.toml +82 -47
  343. package/pack/generated/codex/agents/devrites-proof-runner.toml +80 -0
  344. package/pack/generated/codex/agents/devrites-retrospector.toml +54 -43
  345. package/pack/generated/codex/agents/devrites-security-auditor.toml +53 -35
  346. package/pack/generated/codex/agents/devrites-simplifier-reviewer.toml +56 -44
  347. package/pack/generated/codex/agents/devrites-slice-wright.toml +159 -163
  348. package/pack/generated/codex/agents/devrites-spec-reviewer.toml +40 -30
  349. package/pack/generated/codex/agents/devrites-strategy-reviewer.toml +65 -34
  350. package/pack/generated/codex/agents/devrites-test-analyst.toml +46 -28
  351. package/pack/generated/codex/hooks.json +4 -14
  352. package/pack/generated/codex/skills/devrites-api-interface/SKILL.md +7 -3
  353. package/pack/generated/codex/skills/devrites-audit/SKILL.md +47 -64
  354. package/pack/generated/codex/skills/devrites-browser-proof/SKILL.md +7 -3
  355. package/pack/generated/codex/skills/devrites-debug-recovery/SKILL.md +31 -16
  356. package/pack/generated/codex/skills/devrites-debug-recovery/reference/build-the-loop.md +24 -21
  357. package/pack/generated/codex/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +34 -6
  358. package/pack/generated/codex/skills/devrites-debug-recovery/reference/instrumentation.md +2 -2
  359. package/pack/generated/codex/skills/devrites-doubt/SKILL.md +32 -20
  360. package/pack/generated/codex/skills/devrites-frontend-craft/SKILL.md +7 -3
  361. package/pack/generated/codex/skills/devrites-interview/SKILL.md +60 -55
  362. package/pack/generated/codex/skills/devrites-lib/SKILL.md +18 -12
  363. package/pack/generated/codex/skills/devrites-lib/reference/intent-map.md +3 -2
  364. package/pack/generated/codex/skills/devrites-lib/reference/parallel-dispatch.md +82 -137
  365. package/pack/generated/codex/skills/devrites-lib/reference/reply-contract.md +8 -1
  366. package/pack/generated/codex/skills/devrites-lib/reference/standards/README.md +38 -55
  367. package/pack/generated/codex/skills/devrites-lib/reference/standards/afk-hitl.md +53 -27
  368. package/pack/generated/codex/skills/devrites-lib/reference/standards/agents.md +198 -190
  369. package/pack/generated/codex/skills/devrites-lib/reference/standards/anti-patterns.md +5 -15
  370. package/pack/generated/codex/skills/devrites-lib/reference/standards/ci-cd.md +27 -58
  371. package/pack/generated/codex/skills/devrites-lib/reference/standards/code-review.md +16 -52
  372. package/pack/generated/codex/skills/devrites-lib/reference/standards/coding-style.md +2 -10
  373. package/pack/generated/codex/skills/devrites-lib/reference/standards/context-hygiene.md +18 -61
  374. package/pack/generated/codex/skills/devrites-lib/reference/standards/core.md +23 -18
  375. package/pack/generated/codex/skills/devrites-lib/reference/standards/deprecation.md +17 -57
  376. package/pack/generated/codex/skills/devrites-lib/reference/standards/development-workflow.md +4 -33
  377. package/pack/generated/codex/skills/devrites-lib/reference/standards/git-workflow.md +4 -20
  378. package/pack/generated/codex/skills/devrites-lib/reference/standards/hooks.md +3 -14
  379. package/pack/generated/codex/skills/devrites-lib/reference/standards/patterns.md +9 -25
  380. package/pack/generated/codex/skills/devrites-lib/reference/standards/performance.md +0 -9
  381. package/pack/generated/codex/skills/devrites-lib/reference/standards/principles.md +1 -3
  382. package/pack/generated/codex/skills/devrites-lib/reference/standards/security.md +17 -7
  383. package/pack/generated/codex/skills/devrites-lib/reference/standards/testing.md +1 -27
  384. package/pack/generated/codex/skills/devrites-lib/reference/standards/tooling.md +46 -50
  385. package/pack/generated/codex/skills/devrites-lib/reference/workspace-artifact-schema.md +23 -10
  386. package/pack/generated/codex/skills/devrites-prose-craft/SKILL.md +7 -3
  387. package/pack/generated/codex/skills/devrites-refresh-indexes/SKILL.md +7 -3
  388. package/pack/generated/codex/skills/devrites-source-driven/SKILL.md +29 -24
  389. package/pack/generated/codex/skills/devrites-ux-shape/SKILL.md +7 -3
  390. package/pack/generated/codex/skills/rite/SKILL.md +19 -13
  391. package/pack/generated/codex/skills/rite/reference/menu.md +8 -6
  392. package/pack/generated/codex/skills/rite-adopt/SKILL.md +40 -40
  393. package/pack/generated/codex/skills/rite-autocomplete/SKILL.md +40 -31
  394. package/pack/generated/codex/skills/rite-autocomplete/reference/loop.md +21 -21
  395. package/pack/generated/codex/skills/rite-autocomplete/reference/stop-conditions.md +15 -6
  396. package/pack/generated/codex/skills/rite-build/SKILL.md +42 -33
  397. package/pack/generated/codex/skills/rite-build/reference/afk-discipline.md +38 -38
  398. package/pack/generated/codex/skills/rite-build/reference/evidence-standard.md +10 -0
  399. package/pack/generated/codex/skills/rite-build/reference/forge.md +186 -156
  400. package/pack/generated/codex/skills/rite-build/reference/one-slice-cycle.md +4 -3
  401. package/pack/generated/codex/skills/rite-build/reference/phase-contract.md +96 -175
  402. package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +129 -134
  403. package/pack/generated/codex/skills/rite-clarify/SKILL.md +105 -0
  404. package/pack/generated/codex/skills/rite-clarify/reference/decision-coverage.md +59 -0
  405. package/pack/generated/codex/skills/rite-converge/SKILL.md +42 -28
  406. package/pack/generated/codex/skills/rite-customize/SKILL.md +7 -3
  407. package/pack/generated/codex/skills/rite-define/SKILL.md +64 -35
  408. package/pack/generated/codex/skills/rite-define/reference/gates.md +16 -15
  409. package/pack/generated/codex/skills/rite-define/reference/plan-template.md +16 -8
  410. package/pack/generated/codex/skills/rite-doctor/SKILL.md +7 -3
  411. package/pack/generated/codex/skills/rite-dogfood/SKILL.md +7 -3
  412. package/pack/generated/codex/skills/rite-explain/SKILL.md +7 -3
  413. package/pack/generated/codex/skills/rite-frame/SKILL.md +7 -3
  414. package/pack/generated/codex/skills/rite-frame/reference/failure-modes.md +22 -24
  415. package/pack/generated/codex/skills/rite-handoff/SKILL.md +7 -3
  416. package/pack/generated/codex/skills/rite-learn/SKILL.md +7 -3
  417. package/pack/generated/codex/skills/rite-plan/SKILL.md +51 -19
  418. package/pack/generated/codex/skills/rite-plan/reference/task-breakdown.md +5 -1
  419. package/pack/generated/codex/skills/rite-polish/SKILL.md +34 -26
  420. package/pack/generated/codex/skills/rite-pov/SKILL.md +7 -3
  421. package/pack/generated/codex/skills/rite-pr-feedback/SKILL.md +7 -3
  422. package/pack/generated/codex/skills/rite-pressure-test/SKILL.md +7 -3
  423. package/pack/generated/codex/skills/rite-prototype/SKILL.md +32 -29
  424. package/pack/generated/codex/skills/rite-prove/SKILL.md +34 -20
  425. package/pack/generated/codex/skills/rite-quick/SKILL.md +7 -3
  426. package/pack/generated/codex/skills/rite-resolve/SKILL.md +19 -14
  427. package/pack/generated/codex/skills/rite-resolve/reference/answer-protocol.md +3 -0
  428. package/pack/generated/codex/skills/rite-review/SKILL.md +44 -31
  429. package/pack/generated/codex/skills/rite-seal/SKILL.md +7 -3
  430. package/pack/generated/codex/skills/rite-seal/reference/phase-contract.md +27 -92
  431. package/pack/generated/codex/skills/rite-seal/reference/risk-and-rollback.md +13 -0
  432. package/pack/generated/codex/skills/rite-ship/SKILL.md +7 -3
  433. package/pack/generated/codex/skills/rite-ship/reference/design-memory.md +31 -36
  434. package/pack/generated/codex/skills/rite-spec/SKILL.md +91 -138
  435. package/pack/generated/codex/skills/rite-spec/reference/investigation.md +37 -33
  436. package/pack/generated/codex/skills/rite-spec/reference/question-protocol.md +6 -2
  437. package/pack/generated/codex/skills/rite-spec/reference/spec-checklists.md +25 -24
  438. package/pack/generated/codex/skills/rite-spec/reference/spec-template.md +9 -2
  439. package/pack/generated/codex/skills/rite-spec/reference/state-workspace.md +13 -3
  440. package/pack/generated/codex/skills/rite-status/SKILL.md +7 -3
  441. package/pack/generated/codex/skills/rite-temper/SKILL.md +69 -50
  442. package/pack/generated/codex/skills/rite-temper/reference/review-dimensions.md +24 -22
  443. package/pack/generated/codex/skills/rite-vet/SKILL.md +117 -116
  444. package/pack/generated/codex/skills/rite-vet/reference/artifacts.md +42 -9
  445. package/pack/generated/codex/skills/rite-vet/reference/review-axes.md +40 -38
  446. package/pack/generated/codex/skills/rite-zoom-out/SKILL.md +7 -3
  447. package/package.json +1 -1
  448. package/scripts/build-release-tarball.sh +32 -15
  449. package/scripts/check-authority-drift.py +125 -0
  450. package/scripts/check-instruction-size-baseline.mjs +19 -11
  451. package/scripts/check-invocation-integrity.py +2 -0
  452. package/scripts/codex-generate.sh +69 -33
  453. package/scripts/grade-feature.sh +121 -40
  454. package/scripts/live-hosts/agent-result.schema.json +230 -0
  455. package/scripts/live-hosts/claude.sh +87 -0
  456. package/scripts/live-hosts/codex.sh +81 -0
  457. package/scripts/live-hosts/common.sh +113 -0
  458. package/scripts/live-hosts/fake-host.py +264 -0
  459. package/scripts/live-hosts/host-transport.py +287 -0
  460. package/scripts/release-check.sh +5 -1
  461. package/scripts/run-agent-contract-evals.py +1380 -0
  462. package/scripts/run-behavioral-evals.sh +24 -30
  463. package/scripts/run-evals.sh +1 -5
  464. package/scripts/run-live-behavioral-evals.py +1274 -144
  465. package/scripts/run-outcome-evals.sh +414 -88
  466. package/scripts/run-tests.mjs +30 -2
  467. package/scripts/skills-inventory.mjs +1 -1
  468. package/scripts/validate-workflow-security.py +39 -20
  469. package/scripts/validate-workspace-schema.py +362 -10
  470. package/scripts/validate.sh +21 -15
  471. package/engine/testdata/golden/TestParityWrightScope/devrites-edit-allowed.golden +0 -1
  472. /package/engine/testdata/golden/{TestParityReconcile/check-no-claimed.golden → TestParityBuildReadiness/arg=clarifyopen.golden} +0 -0
package/engine/hooks.go CHANGED
@@ -12,23 +12,24 @@ import (
12
12
 
13
13
  "github.com/devrites/devrites/internal/gate"
14
14
  "github.com/devrites/devrites/internal/harness"
15
+ "github.com/devrites/devrites/internal/lib"
15
16
  "github.com/devrites/devrites/internal/orient"
17
+ drvreason "github.com/devrites/devrites/internal/reason"
16
18
  "github.com/devrites/devrites/internal/state"
17
19
  )
18
20
 
19
- // hookOrient emits the SessionStart orientation for the active feature. It is
20
- // FAIL-OPEN and read-only: on a non-DevRites directory, an unreadable workspace,
21
- // or nothing to say, it stays silent and exits 0 so a session is never wedged.
22
- // The engine's inline shell guard handles a missing binary; this handles a
23
- // present binary run outside a workspace.
21
+ // hookOrient emits SessionStart context for the active feature. It is read only
22
+ // and returns silently when the workspace is missing, unreadable, or has nothing
23
+ // to report. An inline shell guard handles a missing binary; this function
24
+ // handles a present binary outside a workspace.
24
25
  func hookOrient(h harness.Harness, stdin io.Reader, stdout, stderr io.Writer) int {
25
- // The SessionStart payload is drained but unused: orientation is a pure read
26
- // of the workspace files, not a function of the harness's stdin.
26
+ // Orientation depends only on workspace files, so read and discard the
27
+ // SessionStart payload.
27
28
  _, _ = io.Copy(io.Discard, stdin)
28
29
 
29
30
  root, err := orient.ResolveRoot(os.Getenv("DEVRITES_ROOT"))
30
31
  if err != nil {
31
- return exitOK // not a DevRites workspace: silent no-op
32
+ return exitOK // Stay silent outside a DevRites workspace.
32
33
  }
33
34
  text, has, err := orient.Digest(root)
34
35
  if err != nil {
@@ -36,8 +37,8 @@ func hookOrient(h harness.Harness, stdin io.Reader, stdout, stderr io.Writer) in
36
37
  return exitOK
37
38
  }
38
39
  if !has {
39
- // No active feature: on the first-ever such session, orient once with a
40
- // repo-classified starting nudge instead of staying silent.
40
+ // On the first session without an active feature, print a starting hint
41
+ // based on the repository.
41
42
  if text, has = orient.FirstRunDigest(root); !has {
42
43
  return exitOK
43
44
  }
@@ -51,40 +52,59 @@ func hookOrient(h harness.Harness, stdin io.Reader, stdout, stderr io.Writer) in
51
52
  return exitOK
52
53
  }
53
54
 
54
- // hookStopGate refuses to end a turn at a provably inconsistent rest point. It
55
- // mirrors devrites-stop-gate.sh: OBSERVE by default, blocking only when
56
- // DEVRITES_STOP_GATE=enforce; loop-guarded by the harness's stop_hook_active so
57
- // it can never wedge the session; fully fail-open.
55
+ // hookStopGate catches inconsistent state when a turn ends. It mirrors
56
+ // devrites-stop-gate.sh, observes by default, and blocks only when
57
+ // DEVRITES_STOP_GATE=enforce. stop_hook_active prevents a blocking loop, and
58
+ // parsing or workspace errors do not block.
58
59
  func hookStopGate(h harness.Harness, stdin io.Reader, stdout, stderr io.Writer) int {
59
60
  in := h.ParseStopInput(stdin)
61
+ if !in.StopHookActivePresent {
62
+ recordHookGuard(h, "stop-gate", drvreason.HookStopInputInvalid, lib.GuardUnavailable, lib.OutcomeUnavailable)
63
+ return exitOK // Invalid input does not block.
64
+ }
60
65
  if in.StopHookActive {
61
- return exitOK // already re-entered this stop cycle: let it stop
66
+ recordHookGuard(h, "stop-gate", drvreason.HookStopReentry, lib.GuardBypassed, lib.OutcomeBypassed)
67
+ return exitOK // A reentered Stop must finish.
62
68
  }
63
69
  root, err := orient.ResolveRoot(os.Getenv("DEVRITES_ROOT"))
64
70
  if err != nil {
65
71
  return exitOK
66
72
  }
67
73
  res, err := gate.StopGate(root)
68
- if err != nil || !res.Blocked {
74
+ if err != nil {
75
+ recordHookGuard(h, "stop-gate", drvreason.HookStopWorkspaceUnavailable, lib.GuardUnavailable, lib.OutcomeUnavailable)
76
+ return exitOK
77
+ }
78
+ if res.ReasonID == "" {
79
+ recordHookGuard(h, "stop-gate", drvreason.HookStopWorkspaceUnavailable, lib.GuardUnavailable, lib.OutcomeUnavailable)
80
+ return exitOK
81
+ }
82
+ if !res.Blocked {
83
+ strength := lib.GuardObserved
84
+ if hookEnforce("DEVRITES_STOP_GATE") {
85
+ strength = lib.GuardEnforced
86
+ }
87
+ recordHookGuard(h, "stop-gate", res.ReasonID, strength, lib.OutcomePassed)
69
88
  return exitOK
70
89
  }
71
90
  if !hookEnforce("DEVRITES_STOP_GATE") {
72
- // Observe mode: never block. Record a would-block to the feature's
73
- // append-only log (mirroring devrites-stop-gate.sh) so the invariant is
74
- // visible without gating; the append is O_APPEND-atomic across the
75
- // concurrent processes DevRites spawns.
91
+ // Observe mode records the finding without blocking. O_APPEND keeps each
92
+ // log entry intact when DevRites runs concurrent processes.
76
93
  logPath := filepath.Join(resolveWorkspaceDir(root, res.Slug), ".stop-gate.log")
77
94
  if err := state.AppendLog(logPath, "WOULD-BLOCK\t"+res.Reason); err != nil {
78
95
  debugf(stderr, "stop-gate log: %v", err)
79
96
  }
97
+ recordHookGuard(h, "stop-gate", res.ReasonID, lib.GuardObserved, lib.OutcomeObserved, res.EvidenceFiles...)
80
98
  return exitOK
81
99
  }
82
100
  out, err := h.StopBlock(res.Reason)
83
101
  if err != nil {
84
102
  debugf(stderr, "stop-gate: %v", err)
103
+ recordHookGuard(h, "stop-gate", res.ReasonID, lib.GuardUnavailable, lib.OutcomeUnavailable, res.EvidenceFiles...)
85
104
  return exitOK
86
105
  }
87
106
  fmt.Fprintln(stdout, out)
107
+ recordHookGuard(h, "stop-gate", res.ReasonID, lib.GuardEnforced, lib.OutcomeBlocked, res.EvidenceFiles...)
88
108
  return exitOK
89
109
  }
90
110
 
@@ -107,16 +127,14 @@ var allowReadonlySubcommands = map[string]map[string]bool{
107
127
  "reviewer-stats": {"report": true},
108
128
  }
109
129
 
110
- // hookAllow auto-approves the read-only devrites orientation/gate subcommands so
111
- // they stop triggering a permission prompt on every skill run. FAIL-OPEN: it only
112
- // ever EMITS an allow for a structurally exact command shape; on any doubt it
113
- // stays silent, leaving the normal permission flow untouched.
130
+ // hookAllow approves exact read-only DevRites orientation and gate commands.
131
+ // Any other command stays in the harness's normal permission flow.
114
132
  func hookAllow(h harness.Harness, stdin io.Reader, stdout, stderr io.Writer) int {
115
133
  data, err := io.ReadAll(stdin)
116
134
  if err != nil {
117
135
  return exitOK
118
136
  }
119
- // Fast path: a command not mentioning devrites at all is never ours.
137
+ // Ignore commands that do not mention DevRites.
120
138
  if !bytes.Contains(data, []byte("devrites")) {
121
139
  return exitOK
122
140
  }
@@ -133,6 +151,7 @@ func hookAllow(h harness.Harness, stdin io.Reader, stdout, stderr io.Writer) int
133
151
  return exitOK
134
152
  }
135
153
  fmt.Fprintln(stdout, out)
154
+ recordHookGuard(h, "allow", drvreason.HookAllowApproved, lib.GuardEnforced, lib.OutcomeAllowed)
136
155
  return exitOK
137
156
  }
138
157
 
@@ -215,77 +234,283 @@ func safeAllowArg(arg string) bool {
215
234
  // parity oracle green.
216
235
  const reviewerReadonlyDenyReason = "DevRites: reviewers are read-only. This Bash command can mutate or exfiltrate; inspect with Read/Grep/Glob and return findings: do not modify the tree or reach the network. (devrites-reviewer-readonly)"
217
236
 
218
- // reviewerMutateRe matches a Bash command that could mutate the tree or reach the
219
- // network: the deny-list ported verbatim from devrites-reviewer-readonly.sh. A
220
- // fresh-context reviewer reads untrusted source, so a silent write/exfil path is
221
- // a prompt-injection surface (standards/security.md). pack-scan-ignore: this is
222
- // the hook's own defensive deny-list, not a payload.
223
- //
224
- // The pattern is byte-identical to the bash `grep -E`. ONE deliberate divergence:
225
- // this matches against the WHOLE command, while the bash hook's
226
- // `read -r tool cmd agent_type` truncates the command at its first newline and so
227
- // only ever scans line 1. A reviewer running a multi-line Bash whose later lines
228
- // mutate (e.g. `cat x\nsed -i …`) slips past the bash hook but is denied here:
229
- // intentional hardening of a latent bash bug, not a parity regression. (RE2's
230
- // `[[:space:]]` already matches `\n`, so no `(?m)` flag is needed for the `-i$`
231
- // alternative to fire at an interior line break.)
232
- var reviewerMutateRe = regexp.MustCompile(`>>|[^0-9 ]>[^>&]|[[:space:]]-i([[:space:]]|$)|sed[[:space:]]+-i|\brm\b|\bmv\b|\btee\b|\btruncate\b|\bdd\b|chmod|chown|git[[:space:]]+(add|commit|push|reset|checkout|rm|mv|stash|tag|apply)|npm[[:space:]]+(install|i|publish|run)|pnpm[[:space:]]+(install|add)|yarn[[:space:]]+add|pip[[:space:]]+install|curl[[:space:]].*[[:space:]]-o\b|\bwget\b|\bscp\b|\bssh\b|\bnc\b`)
233
-
234
- // hookReviewerReadonly keeps the read-only reviewer subagents read-only: it denies
235
- // a Bash command that could mutate the tree or exfiltrate. It mirrors
236
- // devrites-reviewer-readonly.sh: OBSERVE by default (logs a would-block, allows),
237
- // blocking only when DEVRITES_REVIEWER_RO=enforce; fully fail-open.
237
+ // reviewerMutateRe is the legacy observe-mode deny-list. Active DevRites leaves
238
+ // use the fail-closed safe-command check below instead.
239
+ var reviewerMutateRe = regexp.MustCompile(`(?im)>>|[^0-9 ]>[^>&]|[[:space:]]-i([[:space:]]|$)|sed[[:space:]]+-i|\b(rm|mv|cp|touch|mkdir|rmdir|tee|truncate|dd|chmod|chown|ln|unlink|install|patch)\b|git[[:space:]]+(add|commit|push|reset|checkout|restore|clean|rm|mv|stash|tag|apply|merge|rebase)|npm[[:space:]]+(install|i|ci|publish)|pnpm[[:space:]]+(install|add|publish)|yarn[[:space:]]+(add|install|publish)|pip[[:space:]]+install|go[[:space:]]+(get|install|generate)|cargo[[:space:]]+(add|install|publish)|\b(curl|wget|scp|ssh|nc|socat|telnet)\b`)
240
+
241
+ var shellSegmentRe = regexp.MustCompile(`\|\||&&|[;|\n]`)
242
+
243
+ type devritesAgentKind uint8
244
+
245
+ const (
246
+ devritesAgentNone devritesAgentKind = iota
247
+ devritesAgentReadonly
248
+ devritesAgentWright
249
+ devritesAgentInvalid
250
+ )
251
+
252
+ // devritesAgent identifies the one write-capable leaf without copying the agent
253
+ // roster. DEVRITES_AGENT_RUN supplies identity when a hook payload cannot carry
254
+ // agent_type.
255
+ func devritesAgent(payloadAgent string) devritesAgentKind {
256
+ payloadAgent = strings.TrimSpace(payloadAgent)
257
+ envAgent := strings.TrimSpace(os.Getenv("DEVRITES_ACTIVE_AGENT"))
258
+ declared := os.Getenv("DEVRITES_AGENT_RUN") == "1" || strings.HasPrefix(payloadAgent, "devrites-")
259
+ if !declared {
260
+ return devritesAgentNone
261
+ }
262
+ if payloadAgent != "" && envAgent != "" && payloadAgent != envAgent {
263
+ return devritesAgentInvalid
264
+ }
265
+ agent := payloadAgent
266
+ if agent == "" {
267
+ agent = envAgent
268
+ }
269
+ switch {
270
+ case agent == "devrites-slice-wright":
271
+ return devritesAgentWright
272
+ case strings.HasPrefix(agent, "devrites-") && len(agent) > len("devrites-"):
273
+ return devritesAgentReadonly
274
+ default:
275
+ return devritesAgentInvalid
276
+ }
277
+ }
278
+
279
+ func toolBaseName(tool string) string {
280
+ tool = strings.ToLower(strings.TrimSpace(tool))
281
+ for _, sep := range []string{"::", "__", "/", "."} {
282
+ if i := strings.LastIndex(tool, sep); i >= 0 {
283
+ tool = tool[i+len(sep):]
284
+ }
285
+ }
286
+ return strings.ReplaceAll(tool, "-", "_")
287
+ }
288
+
289
+ func isShellTool(tool string) bool {
290
+ switch toolBaseName(tool) {
291
+ case "bash", "shell", "sh", "exec_command", "run_command":
292
+ return true
293
+ }
294
+ return false
295
+ }
296
+
297
+ func isOpaqueExecutionTool(tool string) bool {
298
+ switch toolBaseName(tool) {
299
+ case "exec", "js", "python", "computer", "computer_use", "write_stdin", "run_code":
300
+ return true
301
+ }
302
+ return false
303
+ }
304
+
305
+ func isAgentDispatchTool(tool string) bool {
306
+ switch toolBaseName(tool) {
307
+ case "agent", "task", "spawn_agent", "delegate", "dispatch_agent", "create_agent":
308
+ return true
309
+ }
310
+ return false
311
+ }
312
+
313
+ // safeReadonlyShellCommand accepts common inspection and proof commands. A
314
+ // read-only leaf can use Read or Grep, or ask the orchestrator to run anything
315
+ // outside this conservative set.
316
+ func safeReadonlyShellCommand(command string) bool {
317
+ command, writeTargets, opaqueRedirect := shellOutputRedirections(command)
318
+ if opaqueRedirect || len(writeTargets) != 0 ||
319
+ strings.Contains(command, "$(") || strings.ContainsRune(command, '`') ||
320
+ reviewerMutateRe.MatchString(command) {
321
+ return false
322
+ }
323
+ segments := shellSegmentRe.Split(command, -1)
324
+ if len(segments) == 0 {
325
+ return false
326
+ }
327
+ for _, segment := range segments {
328
+ if !safeReadonlyShellSegment(segment) {
329
+ return false
330
+ }
331
+ }
332
+ return true
333
+ }
334
+
335
+ func safeReadonlyShellSegment(segment string) bool {
336
+ fields := strings.Fields(strings.TrimSpace(segment))
337
+ for len(fields) > 0 && strings.Contains(fields[0], "=") && !strings.HasPrefix(fields[0], "=") {
338
+ fields = fields[1:]
339
+ }
340
+ for len(fields) > 0 {
341
+ switch filepath.Base(fields[0]) {
342
+ case "rtk", "timeout", "nice":
343
+ fields = fields[1:]
344
+ continue
345
+ case "command":
346
+ if len(fields) == 2 && fields[1] == "-v" {
347
+ return true
348
+ }
349
+ fields = fields[1:]
350
+ continue
351
+ case "env":
352
+ fields = fields[1:]
353
+ for len(fields) > 0 && (strings.HasPrefix(fields[0], "-") || strings.Contains(fields[0], "=")) {
354
+ fields = fields[1:]
355
+ }
356
+ continue
357
+ }
358
+ break
359
+ }
360
+ if len(fields) == 0 {
361
+ return false
362
+ }
363
+
364
+ base := strings.ToLower(filepath.Base(strings.Trim(fields[0], `"'`)))
365
+ args := fields[1:]
366
+ switch base {
367
+ case "true", "false", "pwd", "ls", "cat", "head", "tail", "less", "more",
368
+ "grep", "rg", "wc", "sort", "uniq", "cut", "tr", "stat", "file",
369
+ "readlink", "realpath", "basename", "dirname", "cmp", "diff", "jq", "yq",
370
+ "tree", "du", "df", "printenv", "which", "date", "uname", "id", "whoami",
371
+ "ps", "echo", "printf", "test", "[", "cd":
372
+ return true
373
+ case "find":
374
+ return !hasAnyArg(args, "-delete", "-exec", "-execdir", "-ok", "-okdir", "-fprint", "-fprintf")
375
+ case "sed":
376
+ return !hasArgPrefix(args, "-i")
377
+ case "git":
378
+ return len(args) > 0 && hasAnyArg(args[:1], "diff", "status", "show", "log", "rev-parse", "ls-files", "grep", "blame")
379
+ case "go":
380
+ if len(args) == 0 || !hasAnyArg(args[:1], "test", "vet", "list", "version", "env") {
381
+ return false
382
+ }
383
+ return args[0] != "env" || !hasAnyArg(args[1:], "-w", "-u")
384
+ case "npm", "pnpm", "yarn":
385
+ return safePackageProof(args)
386
+ case "pytest", "rspec":
387
+ return true
388
+ case "python", "python3":
389
+ return len(args) >= 2 && args[0] == "-m" && hasAnyArg(args[1:2], "pytest", "unittest", "compileall")
390
+ case "cargo":
391
+ if len(args) == 0 {
392
+ return false
393
+ }
394
+ if hasAnyArg(args[:1], "test", "check", "clippy") {
395
+ return true
396
+ }
397
+ return args[0] == "fmt" && hasAnyArg(args[1:], "--check")
398
+ case "ruff":
399
+ return len(args) > 0 && args[0] == "check" && !hasAnyArg(args[1:], "--fix")
400
+ case "prettier":
401
+ return hasAnyArg(args, "--check", "--list-different")
402
+ case "eslint":
403
+ return !hasAnyArg(args, "--fix", "--fix-dry-run")
404
+ case "tsc":
405
+ return hasAnyArg(args, "--noEmit")
406
+ case "node":
407
+ return hasAnyArg(args, "--check", "--test", "--version", "-v")
408
+ case "bash", "sh", "zsh":
409
+ return len(args) > 0 && args[0] == "-n"
410
+ case "make":
411
+ return len(args) > 0 && hasAnyArg(args[:1], "test", "check", "lint", "vet")
412
+ case "devrites-engine":
413
+ return len(args) > 0 && safeAllowShape(args[0], args[1:])
414
+ }
415
+ return false
416
+ }
417
+
418
+ func safePackageProof(args []string) bool {
419
+ if len(args) == 0 {
420
+ return false
421
+ }
422
+ if args[0] == "test" {
423
+ return true
424
+ }
425
+ return len(args) > 1 && args[0] == "run" &&
426
+ hasAnyArg(args[1:2], "test", "lint", "check", "typecheck", "type-check")
427
+ }
428
+
429
+ func hasAnyArg(args []string, wants ...string) bool {
430
+ for _, arg := range args {
431
+ for _, want := range wants {
432
+ if arg == want {
433
+ return true
434
+ }
435
+ }
436
+ }
437
+ return false
438
+ }
439
+
440
+ func hasArgPrefix(args []string, prefix string) bool {
441
+ for _, arg := range args {
442
+ if strings.HasPrefix(arg, prefix) {
443
+ return true
444
+ }
445
+ }
446
+ return false
447
+ }
448
+
449
+ // hookReviewerReadonly keeps every DevRites leaf except slice-wright read only.
450
+ // It always enforces declared DevRites runs. Undeclared runs keep the legacy
451
+ // observe mode.
238
452
  func hookReviewerReadonly(h harness.Harness, stdin io.Reader, stdout, stderr io.Writer) int {
239
453
  data, err := io.ReadAll(stdin)
240
454
  if err != nil {
241
455
  return exitOK
242
456
  }
243
- // Fast path: a payload with no tool call is not ours: mirrors the script's
244
- // `case "$input" in *'"tool_name"'*`.
245
457
  if !bytes.Contains(data, []byte(`"tool_name"`)) {
246
458
  return exitOK
247
459
  }
248
- in := h.ParsePreToolInput(bytes.NewReader(data))
460
+ in := h.ParseGuardInput(bytes.NewReader(data))
461
+ kind := devritesAgent(in.AgentType)
249
462
 
250
- // When the surface guarantees subagent identity, scope to devrites reviewers:
251
- // the slice-wright is a sanctioned writer and the main thread / other agents
252
- // are not our concern.
253
- if os.Getenv("DEVRITES_REVIEWER_AGENT_REQUIRED") == "1" {
254
- switch {
255
- case in.AgentType == "" || in.AgentType == "devrites-slice-wright":
463
+ // Do not let a global hook interfere with the main thread or another
464
+ // product's agent when no DevRites run is declared.
465
+ if kind == devritesAgentNone && (in.AgentType != "" || os.Getenv("DEVRITES_REVIEWER_AGENT_REQUIRED") == "1") {
466
+ return exitOK
467
+ }
468
+ if kind == devritesAgentWright {
469
+ if !isAgentDispatchTool(in.ToolName) {
256
470
  return exitOK
257
- case !strings.HasPrefix(in.AgentType, "devrites-"):
471
+ }
472
+ } else {
473
+ active := kind != devritesAgentNone
474
+ unsafe := isEditTool(in.ToolName) || isOpaqueExecutionTool(in.ToolName) || isAgentDispatchTool(in.ToolName)
475
+ if isShellTool(in.ToolName) {
476
+ if in.Command == "" {
477
+ unsafe = active
478
+ } else if active {
479
+ unsafe = !safeReadonlyShellCommand(in.Command)
480
+ } else {
481
+ unsafe = reviewerMutateRe.MatchString(in.Command)
482
+ }
483
+ }
484
+ if !unsafe {
258
485
  return exitOK
259
486
  }
260
487
  }
261
- if in.ToolName != "Bash" || in.Command == "" {
262
- return exitOK
263
- }
264
- if !reviewerMutateRe.MatchString(in.Command) {
265
- return exitOK
266
- }
267
488
 
268
- if hookEnforce("DEVRITES_REVIEWER_RO") {
489
+ active := kind != devritesAgentNone
490
+ if active || hookEnforce("DEVRITES_REVIEWER_RO") {
269
491
  out, err := h.PreToolDeny(reviewerReadonlyDenyReason)
270
492
  if err != nil {
271
493
  debugf(stderr, "reviewer-readonly: %v", err)
272
494
  return exitOK
273
495
  }
274
496
  fmt.Fprintln(stdout, out)
497
+ recordHookGuard(h, "reviewer-readonly", drvreason.HookReviewerReadonlyDenied, lib.GuardEnforced, lib.OutcomeDenied)
275
498
  return exitOK
276
499
  }
277
500
 
278
- // Observe mode: record the would-block to the active feature's append-only log,
279
- // mirroring the stop-gate's log convention, then allow. The append is
280
- // O_APPEND-atomic across the concurrent processes DevRites spawns.
281
501
  if root, err := orient.ResolveRoot(os.Getenv("DEVRITES_ROOT")); err == nil {
282
502
  if slug, _ := orient.ActiveSlug(root); slug != "" {
283
503
  logPath := filepath.Join(resolveWorkspaceDir(root, slug), ".reviewer-ro.log")
284
- if err := state.AppendLog(logPath, "WOULD-BLOCK\t"+head80(in.Command)); err != nil {
504
+ action := in.Command
505
+ if action == "" {
506
+ action = in.ToolName + "\t" + in.FilePath
507
+ }
508
+ if err := state.AppendLog(logPath, "WOULD-BLOCK\t"+head80(action)); err != nil {
285
509
  debugf(stderr, "reviewer-readonly log: %v", err)
286
510
  }
287
511
  }
288
512
  }
513
+ recordHookGuard(h, "reviewer-readonly", drvreason.HookReviewerReadonlyObserved, lib.GuardObserved, lib.OutcomeObserved)
289
514
  return exitOK
290
515
  }
291
516
 
@@ -297,15 +522,13 @@ func hookReviewerReadonly(h harness.Harness, stdin io.Reader, stdout, stderr io.
297
522
  //go:embed subagent_orient_context.txt
298
523
  var subagentOrientContext string
299
524
 
300
- // hookSubagentOrient injects the DevRites discipline into every devrites-* subagent
301
- // at spawn: a spawned subagent starts in a fresh context and neither auto-loads the
302
- // rite-* framework nor discovers skills the way the main thread does. It mirrors
303
- // devrites-subagent-orient.sh: fire only for devrites-* agents, emit a fixed
304
- // context envelope, stay silent + fail-open otherwise.
525
+ // hookSubagentOrient adds DevRites instructions when a devrites-* agent starts.
526
+ // Spawned agents have fresh context and do not load the rite-* framework the
527
+ // same way as the main thread. The output matches devrites-subagent-orient.sh.
305
528
  func hookSubagentOrient(h harness.Harness, stdin io.Reader, stdout, stderr io.Writer) int {
306
529
  agentType := h.SubagentAgentType(stdin)
307
530
  if !strings.HasPrefix(agentType, "devrites-") {
308
- return exitOK // not a DevRites subagent (or no identity): silent no-op
531
+ return exitOK // Stay silent without a DevRites agent identity.
309
532
  }
310
533
  out, err := h.SubagentStartContext(strings.ReplaceAll(subagentOrientContext, "\r\n", "\n"))
311
534
  if err != nil {
@@ -326,9 +549,7 @@ func head80(s string) string {
326
549
  return s
327
550
  }
328
551
 
329
- // debugf writes an operational diagnostic only when DEVRITES_DEBUG is set, so a
330
- // fail-open hook can surface WHY it stayed silent without ever polluting normal
331
- // hook stdout/stderr.
552
+ // debugf writes diagnostics only when DEVRITES_DEBUG is set.
332
553
  func debugf(stderr io.Writer, format string, args ...any) {
333
554
  if os.Getenv("DEVRITES_DEBUG") != "" {
334
555
  fmt.Fprintf(stderr, "devrites: "+format+"\n", args...)