devrites 3.2.27 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (897) hide show
  1. package/CHANGELOG.md +22 -144
  2. package/NOTICE.md +6 -5
  3. package/README.md +187 -95
  4. package/SECURITY.md +103 -79
  5. package/bin/devrites.mjs +140 -24
  6. package/docs/adr/0003-gate-model-hitl-pause.md +1 -1
  7. package/docs/adr/0013-native-codex-agent-orchestration.md +74 -0
  8. package/docs/adr/0014-native-host-hook-boundary.md +64 -0
  9. package/docs/adr/0015-read-only-root-native-orchestration.md +96 -0
  10. package/docs/adr/0016-codex-writer-fail-closed.md +53 -0
  11. package/docs/adr/0017-native-codex-writer-agent.md +63 -0
  12. package/docs/adr/0018-native-sandbox-instruction-writer-boundary.md +66 -0
  13. package/docs/adr/0019-native-boundary-with-deterministic-gates.md +48 -0
  14. package/docs/adr/0020-thin-engine-native-orchestration-boundary.md +54 -0
  15. package/docs/adr/0021-observable-workspace-compatibility.md +49 -0
  16. package/docs/adr/0022-native-orchestration-thin-engine.md +90 -0
  17. package/docs/adr/0023-native-workspace-reads-line-output.md +71 -0
  18. package/docs/adr/0024-native-policy-offline-installer-boundary.md +111 -0
  19. package/docs/adr/0025-evidence-gated-workspace-upgrades.md +64 -0
  20. package/docs/adr/0026-content-bound-proof-and-bounded-inputs.md +169 -0
  21. package/docs/adr/0027-content-bound-build-readiness.md +77 -0
  22. package/docs/adr/README.md +26 -11
  23. package/docs/agents/domain.md +2 -1
  24. package/docs/agents/issue-driven-rites.md +1 -1
  25. package/docs/architecture.md +191 -103
  26. package/docs/candidate-integrity.md +138 -0
  27. package/docs/capability-surface-selection.md +15 -5
  28. package/docs/cli.md +139 -92
  29. package/docs/command-map.md +88 -63
  30. package/docs/engine/commands.md +115 -507
  31. package/docs/engine/state-schema.md +94 -113
  32. package/docs/engine/workspace-schema.md +169 -229
  33. package/docs/flow.md +113 -68
  34. package/docs/harness-compliance.md +29 -56
  35. package/docs/markdown-instruction-upgrade-2026-08-02.md +302 -0
  36. package/docs/orchestration.md +136 -149
  37. package/docs/porting-to-a-new-harness.md +22 -7
  38. package/docs/quick-reference.md +21 -8
  39. package/docs/release.md +47 -4
  40. package/docs/skills.md +111 -72
  41. package/docs/templates/CRITIC_REVIEW_PACKET_TEMPLATE.md +2 -0
  42. package/docs/upstream-workflow-benchmark-2026-08-01.md +655 -0
  43. package/docs/usage.md +99 -65
  44. package/engine/Makefile +1 -2
  45. package/engine/cmd/releasepack/main.go +45 -25
  46. package/engine/cmd/releasepack/main_test.go +69 -0
  47. package/engine/commands.go +8 -258
  48. package/engine/harnessmatrix_test.go +28 -41
  49. package/engine/internal/devritespaths/paths.go +12 -28
  50. package/engine/internal/fsutil/copy.go +8 -0
  51. package/engine/internal/gate/gate.go +104 -105
  52. package/engine/internal/gate/gate_test.go +94 -84
  53. package/engine/internal/gate/readiness_binding.go +203 -0
  54. package/engine/internal/gate/readiness_binding_test.go +271 -0
  55. package/engine/internal/gitenv/env.go +46 -0
  56. package/engine/internal/gitenv/env_test.go +48 -0
  57. package/engine/internal/hostpack/hostpack.go +16 -20
  58. package/engine/internal/hostpack/hostpack_test.go +6 -6
  59. package/engine/internal/install/install.go +289 -511
  60. package/engine/internal/install/install_test.go +218 -306
  61. package/engine/internal/lib/candidate.go +475 -0
  62. package/engine/internal/lib/candidate_test.go +503 -0
  63. package/engine/internal/lib/closeout.go +17 -21
  64. package/engine/internal/lib/closeout_test.go +23 -0
  65. package/engine/internal/lib/cursor_compat_test.go +12 -75
  66. package/engine/internal/lib/evidencefresh.go +58 -47
  67. package/engine/internal/lib/evidencefresh_test.go +120 -0
  68. package/engine/internal/lib/resolve.go +13 -35
  69. package/engine/internal/lib/resolve_remediation_test.go +5 -17
  70. package/engine/internal/lib/secretscan.go +432 -0
  71. package/engine/internal/lib/secretscan_test.go +555 -0
  72. package/engine/internal/lib/util.go +24 -65
  73. package/engine/internal/lib/util_test.go +44 -0
  74. package/engine/internal/markdowntext/markdowntext.go +108 -0
  75. package/engine/internal/markdowntext/markdowntext_test.go +58 -0
  76. package/engine/internal/markdowntext/testdata/structural.json +27 -0
  77. package/engine/internal/reason/reason.go +2 -123
  78. package/engine/internal/reason/reason_test.go +4 -2
  79. package/engine/internal/rootfacts/facts.go +6 -8
  80. package/engine/internal/rootfacts/facts_test.go +20 -0
  81. package/engine/internal/state/cmd/workflowmanifest/main.go +10 -12
  82. package/engine/internal/state/cursor.go +53 -14
  83. package/engine/internal/state/cursor_test.go +57 -0
  84. package/engine/internal/state/feature.go +64 -154
  85. package/engine/internal/state/schema.go +24 -50
  86. package/engine/internal/state/state_test.go +151 -140
  87. package/engine/internal/state/status.go +4 -17
  88. package/engine/internal/state/workflow_manifest.json +9 -89
  89. package/engine/main.go +73 -502
  90. package/engine/root_routing_test.go +183 -212
  91. package/engine/testdata/fixtures/basic/devrites-root/{features/auth-tokens/feature.md → work/auth-tokens/README.md} +0 -1
  92. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/architecture.md +3 -0
  93. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/assumptions.md +3 -0
  94. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/brief.md +3 -0
  95. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/decision-coverage.md +3 -0
  96. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/eng-review.md +3 -0
  97. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/questions.md +3 -0
  98. package/engine/testdata/fixtures/basic/devrites-root/{features/auth-tokens/status.md → work/auth-tokens/state.md} +3 -0
  99. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/test-plan.md +3 -0
  100. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/traceability.md +3 -0
  101. package/engine/testdata/fixtures/basic/devrites-root/{features/search-ranking/feature.md → work/search-ranking/README.md} +0 -1
  102. package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/assumptions.md +3 -0
  103. package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/brief.md +3 -0
  104. package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/decisions.md +3 -0
  105. package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/questions.md +3 -0
  106. package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/state.md +2 -0
  107. package/engine/testdata/golden/TestParityCloseOut/active_pointed_elsewhere.golden +1 -1
  108. package/engine/testdata/golden/TestParityCloseOut/active_slug_cleared.golden +1 -1
  109. package/engine/tests/adr_0003_gate_exit_code_test.go +2 -2
  110. package/engine/tests/adr_0011_define_plan_test.go +2 -2
  111. package/engine/tests/adr_0027_readiness_binding_test.go +106 -0
  112. package/engine/tests/cli_test.go +24 -107
  113. package/engine/tests/gate_test.go +177 -200
  114. package/engine/tests/meta_test.go +5 -35
  115. package/engine/tests/parity_closeout_test.go +12 -12
  116. package/engine/tests/parity_resolve_test.go +6 -16
  117. package/engine/tests/parity_test.go +8 -302
  118. package/install.sh +201 -34
  119. package/pack/.claude/agents/devrites-code-reviewer.md +9 -18
  120. package/pack/.claude/agents/devrites-devex-reviewer.md +10 -18
  121. package/pack/.claude/agents/devrites-doubt-reviewer.md +10 -20
  122. package/pack/.claude/agents/devrites-evidence-scout.md +14 -26
  123. package/pack/.claude/agents/devrites-frontend-reviewer.md +9 -15
  124. package/pack/.claude/agents/devrites-performance-reviewer.md +13 -21
  125. package/pack/.claude/agents/devrites-plan-drafter.md +24 -28
  126. package/pack/.claude/agents/devrites-plan-reviewer.md +30 -47
  127. package/pack/.claude/agents/devrites-proof-runner.md +41 -47
  128. package/pack/.claude/agents/devrites-retrospector.md +34 -79
  129. package/pack/.claude/agents/devrites-security-auditor.md +9 -17
  130. package/pack/.claude/agents/devrites-simplifier-reviewer.md +9 -18
  131. package/pack/.claude/agents/devrites-slice-wright.md +44 -62
  132. package/pack/.claude/agents/devrites-spec-reviewer.md +38 -50
  133. package/pack/.claude/agents/devrites-strategy-reviewer.md +10 -19
  134. package/pack/.claude/agents/devrites-test-analyst.md +22 -21
  135. package/pack/.claude/agents/devrites-upgrade-planner.md +52 -71
  136. package/pack/.claude/settings.json +12 -100
  137. package/pack/.claude/skills/devrites-api-interface/SKILL.md +1 -2
  138. package/pack/.claude/skills/devrites-audit/SKILL.md +6 -9
  139. package/pack/.claude/skills/devrites-browser-proof/SKILL.md +0 -1
  140. package/pack/.claude/skills/devrites-debug-recovery/SKILL.md +21 -27
  141. package/pack/.claude/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +22 -33
  142. package/pack/.claude/skills/devrites-doubt/SKILL.md +4 -6
  143. package/pack/.claude/skills/devrites-frontend-craft/SKILL.md +1 -2
  144. package/pack/.claude/skills/devrites-frontend-craft/reference/design-references.md +2 -1
  145. package/pack/.claude/skills/devrites-interview/SKILL.md +31 -36
  146. package/pack/.claude/skills/devrites-lib/SKILL.md +37 -66
  147. package/pack/.claude/skills/devrites-lib/reference/candidate-integrity.md +51 -0
  148. package/pack/.claude/skills/devrites-lib/reference/intent-map.md +20 -12
  149. package/pack/.claude/skills/devrites-lib/reference/orchestration-profiles.md +22 -0
  150. package/pack/.claude/skills/devrites-lib/reference/parallel-dispatch.md +35 -110
  151. package/pack/.claude/skills/devrites-lib/reference/reply-contract.md +26 -93
  152. package/pack/.claude/skills/devrites-lib/reference/standards/README.md +6 -3
  153. package/pack/.claude/skills/devrites-lib/reference/standards/afk-hitl.md +43 -34
  154. package/pack/.claude/skills/devrites-lib/reference/standards/agents.md +90 -216
  155. package/pack/.claude/skills/devrites-lib/reference/standards/anti-patterns.md +1 -1
  156. package/pack/.claude/skills/devrites-lib/reference/standards/code-review.md +9 -28
  157. package/pack/.claude/skills/devrites-lib/reference/standards/context-hygiene.md +13 -11
  158. package/pack/.claude/skills/devrites-lib/reference/standards/core.md +59 -54
  159. package/pack/.claude/skills/devrites-lib/reference/standards/hooks.md +3 -5
  160. package/pack/.claude/skills/devrites-lib/reference/standards/principles.md +29 -143
  161. package/pack/.claude/skills/devrites-lib/reference/standards/release/ship-checklist.md +5 -6
  162. package/pack/.claude/skills/devrites-lib/reference/standards/security.md +13 -31
  163. package/pack/.claude/skills/devrites-lib/reference/standards/skill-authoring.md +112 -82
  164. package/pack/.claude/skills/devrites-lib/reference/standards/spec-grammar.md +71 -45
  165. package/pack/.claude/skills/devrites-lib/reference/standards/testing.md +22 -11
  166. package/pack/.claude/skills/devrites-lib/reference/standards/tooling.md +15 -24
  167. package/pack/.claude/skills/devrites-lib/reference/workspace-artifact-schema.md +106 -37
  168. package/pack/.claude/skills/devrites-prose-craft/SKILL.md +3 -4
  169. package/pack/.claude/skills/devrites-source-driven/SKILL.md +3 -4
  170. package/pack/.claude/skills/devrites-ux-shape/SKILL.md +3 -3
  171. package/pack/.claude/skills/rite/SKILL.md +12 -13
  172. package/pack/.claude/skills/rite/reference/menu.md +11 -11
  173. package/pack/.claude/skills/rite-adopt/SKILL.md +29 -84
  174. package/pack/.claude/skills/rite-adopt/reference/adoption.md +16 -50
  175. package/pack/.claude/skills/rite-autocomplete/SKILL.md +65 -54
  176. package/pack/.claude/skills/rite-autocomplete/reference/loop.md +24 -17
  177. package/pack/.claude/skills/rite-autocomplete/reference/stop-conditions.md +18 -21
  178. package/pack/.claude/skills/rite-build/SKILL.md +28 -34
  179. package/pack/.claude/skills/rite-build/reference/afk-discipline.md +36 -34
  180. package/pack/.claude/skills/rite-build/reference/anti-patterns.md +5 -19
  181. package/pack/.claude/skills/rite-build/reference/checkpoint-protocol.md +10 -9
  182. package/pack/.claude/skills/rite-build/reference/checkpoint.md +17 -17
  183. package/pack/.claude/skills/rite-build/reference/frontend-trigger.md +3 -3
  184. package/pack/.claude/skills/rite-build/reference/one-slice-cycle.md +11 -14
  185. package/pack/.claude/skills/rite-build/reference/output.md +14 -24
  186. package/pack/.claude/skills/rite-build/reference/phase-contract.md +49 -188
  187. package/pack/.claude/skills/rite-build/reference/spec-drift-guard.md +1 -1
  188. package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +43 -133
  189. package/pack/.claude/skills/rite-clarify/SKILL.md +46 -40
  190. package/pack/.claude/skills/rite-clarify/reference/decision-coverage.md +4 -10
  191. package/pack/.claude/skills/rite-converge/SKILL.md +9 -33
  192. package/pack/.claude/skills/rite-converge/reference/convergence-assessment.md +2 -2
  193. package/pack/.claude/skills/rite-customize/SKILL.md +34 -50
  194. package/pack/.claude/skills/rite-define/SKILL.md +34 -58
  195. package/pack/.claude/skills/rite-define/reference/plan-template.md +15 -0
  196. package/pack/.claude/skills/rite-doctor/SKILL.md +52 -64
  197. package/pack/.claude/skills/rite-dogfood/SKILL.md +1 -2
  198. package/pack/.claude/skills/rite-explain/SKILL.md +1 -20
  199. package/pack/.claude/skills/rite-frame/SKILL.md +1 -7
  200. package/pack/.claude/skills/rite-frame/reference/failure-modes.md +1 -1
  201. package/pack/.claude/skills/rite-handoff/SKILL.md +1 -2
  202. package/pack/.claude/skills/rite-handoff/reference/handoff-template.md +1 -1
  203. package/pack/.claude/skills/rite-learn/SKILL.md +44 -104
  204. package/pack/.claude/skills/rite-plan/SKILL.md +14 -26
  205. package/pack/.claude/skills/rite-plan/reference/task-breakdown.md +4 -1
  206. package/pack/.claude/skills/rite-polish/SKILL.md +20 -28
  207. package/pack/.claude/skills/rite-polish/reference/adr-promotion.md +11 -0
  208. package/pack/{generated/claude/skills/rite-ship → .claude/skills/rite-polish}/reference/design-memory.md +23 -21
  209. package/pack/.claude/skills/rite-polish/reference/ledger.md +62 -0
  210. package/pack/.claude/skills/rite-pov/SKILL.md +28 -32
  211. package/pack/.claude/skills/rite-pr-feedback/SKILL.md +0 -1
  212. package/pack/.claude/skills/rite-pressure-test/SKILL.md +6 -13
  213. package/pack/.claude/skills/rite-prototype/SKILL.md +0 -6
  214. package/pack/.claude/skills/rite-prove/SKILL.md +73 -68
  215. package/pack/.claude/skills/rite-prove/reference/acceptance-proof.md +34 -30
  216. package/pack/.claude/skills/rite-prove/reference/test-command-discovery.md +15 -14
  217. package/pack/.claude/skills/rite-quick/SKILL.md +5 -19
  218. package/pack/.claude/skills/rite-resolve/SKILL.md +4 -21
  219. package/pack/.claude/skills/rite-resolve/reference/answer-protocol.md +2 -2
  220. package/pack/.claude/skills/rite-review/SKILL.md +34 -57
  221. package/pack/.claude/skills/rite-seal/SKILL.md +54 -87
  222. package/pack/.claude/skills/rite-seal/reference/anti-patterns.md +2 -2
  223. package/pack/.claude/skills/rite-seal/reference/final-evidence.md +31 -56
  224. package/pack/.claude/skills/rite-seal/reference/go-no-go.md +27 -51
  225. package/pack/.claude/skills/rite-seal/reference/output.md +2 -4
  226. package/pack/.claude/skills/rite-seal/reference/phase-contract.md +42 -140
  227. package/pack/.claude/skills/rite-seal/reference/risk-and-rollback.md +2 -2
  228. package/pack/.claude/skills/rite-seal/reference/seal-template.md +17 -28
  229. package/pack/.claude/skills/rite-ship/SKILL.md +65 -93
  230. package/pack/.claude/skills/rite-ship/reference/anti-patterns.md +4 -1
  231. package/pack/.claude/skills/rite-ship/reference/close-out.md +4 -12
  232. package/pack/.claude/skills/rite-ship/reference/git-ship.md +86 -48
  233. package/pack/.claude/skills/rite-ship/reference/ship-template.md +22 -16
  234. package/pack/.claude/skills/rite-spec/SKILL.md +62 -95
  235. package/pack/.claude/skills/rite-spec/reference/investigation.md +1 -4
  236. package/pack/.claude/skills/rite-spec/reference/question-protocol.md +21 -20
  237. package/pack/.claude/skills/rite-spec/reference/spec-checklists.md +16 -17
  238. package/pack/.claude/skills/rite-spec/reference/spec-template.md +47 -49
  239. package/pack/.claude/skills/rite-spec/reference/state-workspace.md +28 -13
  240. package/pack/.claude/skills/rite-status/SKILL.md +30 -88
  241. package/pack/.claude/skills/rite-temper/SKILL.md +12 -31
  242. package/pack/.claude/skills/rite-upgrade/SKILL.md +86 -101
  243. package/pack/.claude/skills/rite-vet/SKILL.md +88 -117
  244. package/pack/.claude/skills/rite-vet/reference/artifacts.md +56 -50
  245. package/pack/.claude/skills/rite-vet/reference/cross-model.md +15 -39
  246. package/pack/.claude/skills/rite-vet/reference/depth.md +2 -2
  247. package/pack/.claude/skills/rite-vet/reference/review-axes.md +3 -6
  248. package/pack/.claude/skills/rite-zoom-out/SKILL.md +0 -6
  249. package/pack/generated/README.md +2 -2
  250. package/pack/generated/claude/agents/devrites-code-reviewer.md +9 -18
  251. package/pack/generated/claude/agents/devrites-devex-reviewer.md +10 -18
  252. package/pack/generated/claude/agents/devrites-doubt-reviewer.md +10 -20
  253. package/pack/generated/claude/agents/devrites-evidence-scout.md +14 -26
  254. package/pack/generated/claude/agents/devrites-frontend-reviewer.md +9 -15
  255. package/pack/generated/claude/agents/devrites-performance-reviewer.md +13 -21
  256. package/pack/generated/claude/agents/devrites-plan-drafter.md +24 -28
  257. package/pack/generated/claude/agents/devrites-plan-reviewer.md +30 -47
  258. package/pack/generated/claude/agents/devrites-proof-runner.md +41 -47
  259. package/pack/generated/claude/agents/devrites-retrospector.md +34 -79
  260. package/pack/generated/claude/agents/devrites-security-auditor.md +9 -17
  261. package/pack/generated/claude/agents/devrites-simplifier-reviewer.md +9 -18
  262. package/pack/generated/claude/agents/devrites-slice-wright.md +44 -62
  263. package/pack/generated/claude/agents/devrites-spec-reviewer.md +38 -50
  264. package/pack/generated/claude/agents/devrites-strategy-reviewer.md +10 -19
  265. package/pack/generated/claude/agents/devrites-test-analyst.md +22 -21
  266. package/pack/generated/claude/agents/devrites-upgrade-planner.md +52 -71
  267. package/pack/generated/claude/settings.json +12 -100
  268. package/pack/generated/claude/skills/devrites-api-interface/SKILL.md +1 -2
  269. package/pack/generated/claude/skills/devrites-audit/SKILL.md +6 -9
  270. package/pack/generated/claude/skills/devrites-browser-proof/SKILL.md +0 -1
  271. package/pack/generated/claude/skills/devrites-debug-recovery/SKILL.md +21 -27
  272. package/pack/generated/claude/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +22 -33
  273. package/pack/generated/claude/skills/devrites-doubt/SKILL.md +4 -6
  274. package/pack/generated/claude/skills/devrites-frontend-craft/SKILL.md +1 -2
  275. package/pack/generated/claude/skills/devrites-frontend-craft/reference/design-references.md +2 -1
  276. package/pack/generated/claude/skills/devrites-interview/SKILL.md +31 -36
  277. package/pack/generated/claude/skills/devrites-lib/SKILL.md +37 -66
  278. package/pack/generated/claude/skills/devrites-lib/reference/candidate-integrity.md +51 -0
  279. package/pack/generated/claude/skills/devrites-lib/reference/intent-map.md +20 -12
  280. package/pack/generated/claude/skills/devrites-lib/reference/orchestration-profiles.md +22 -0
  281. package/pack/generated/claude/skills/devrites-lib/reference/parallel-dispatch.md +35 -110
  282. package/pack/generated/claude/skills/devrites-lib/reference/reply-contract.md +26 -93
  283. package/pack/generated/claude/skills/devrites-lib/reference/standards/README.md +6 -3
  284. package/pack/generated/claude/skills/devrites-lib/reference/standards/afk-hitl.md +43 -34
  285. package/pack/generated/claude/skills/devrites-lib/reference/standards/agents.md +90 -216
  286. package/pack/generated/claude/skills/devrites-lib/reference/standards/anti-patterns.md +1 -1
  287. package/pack/generated/claude/skills/devrites-lib/reference/standards/code-review.md +9 -28
  288. package/pack/generated/claude/skills/devrites-lib/reference/standards/context-hygiene.md +13 -11
  289. package/pack/generated/claude/skills/devrites-lib/reference/standards/core.md +59 -54
  290. package/pack/generated/claude/skills/devrites-lib/reference/standards/hooks.md +3 -5
  291. package/pack/generated/claude/skills/devrites-lib/reference/standards/principles.md +29 -143
  292. package/pack/generated/claude/skills/devrites-lib/reference/standards/release/ship-checklist.md +5 -6
  293. package/pack/generated/claude/skills/devrites-lib/reference/standards/security.md +13 -31
  294. package/pack/generated/claude/skills/devrites-lib/reference/standards/skill-authoring.md +112 -82
  295. package/pack/generated/claude/skills/devrites-lib/reference/standards/spec-grammar.md +71 -45
  296. package/pack/generated/claude/skills/devrites-lib/reference/standards/testing.md +22 -11
  297. package/pack/generated/claude/skills/devrites-lib/reference/standards/tooling.md +15 -24
  298. package/pack/generated/claude/skills/devrites-lib/reference/workspace-artifact-schema.md +106 -37
  299. package/pack/generated/claude/skills/devrites-prose-craft/SKILL.md +3 -4
  300. package/pack/generated/claude/skills/devrites-source-driven/SKILL.md +3 -4
  301. package/pack/generated/claude/skills/devrites-ux-shape/SKILL.md +3 -3
  302. package/pack/generated/claude/skills/rite/SKILL.md +12 -13
  303. package/pack/generated/claude/skills/rite/reference/menu.md +11 -11
  304. package/pack/generated/claude/skills/rite-adopt/SKILL.md +29 -84
  305. package/pack/generated/claude/skills/rite-adopt/reference/adoption.md +16 -50
  306. package/pack/generated/claude/skills/rite-autocomplete/SKILL.md +65 -54
  307. package/pack/generated/claude/skills/rite-autocomplete/reference/loop.md +24 -17
  308. package/pack/generated/claude/skills/rite-autocomplete/reference/stop-conditions.md +18 -21
  309. package/pack/generated/claude/skills/rite-build/SKILL.md +28 -34
  310. package/pack/generated/claude/skills/rite-build/reference/afk-discipline.md +36 -34
  311. package/pack/generated/claude/skills/rite-build/reference/anti-patterns.md +5 -19
  312. package/pack/generated/claude/skills/rite-build/reference/checkpoint-protocol.md +10 -9
  313. package/pack/generated/claude/skills/rite-build/reference/checkpoint.md +17 -17
  314. package/pack/generated/claude/skills/rite-build/reference/frontend-trigger.md +3 -3
  315. package/pack/generated/claude/skills/rite-build/reference/one-slice-cycle.md +11 -14
  316. package/pack/generated/claude/skills/rite-build/reference/output.md +14 -24
  317. package/pack/generated/claude/skills/rite-build/reference/phase-contract.md +49 -188
  318. package/pack/generated/claude/skills/rite-build/reference/spec-drift-guard.md +1 -1
  319. package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +43 -133
  320. package/pack/generated/claude/skills/rite-clarify/SKILL.md +46 -40
  321. package/pack/generated/claude/skills/rite-clarify/reference/decision-coverage.md +4 -10
  322. package/pack/generated/claude/skills/rite-converge/SKILL.md +9 -33
  323. package/pack/generated/claude/skills/rite-converge/reference/convergence-assessment.md +2 -2
  324. package/pack/generated/claude/skills/rite-customize/SKILL.md +34 -50
  325. package/pack/generated/claude/skills/rite-define/SKILL.md +34 -58
  326. package/pack/generated/claude/skills/rite-define/reference/plan-template.md +15 -0
  327. package/pack/generated/claude/skills/rite-doctor/SKILL.md +52 -64
  328. package/pack/generated/claude/skills/rite-dogfood/SKILL.md +1 -2
  329. package/pack/generated/claude/skills/rite-explain/SKILL.md +1 -20
  330. package/pack/generated/claude/skills/rite-frame/SKILL.md +1 -7
  331. package/pack/generated/claude/skills/rite-frame/reference/failure-modes.md +1 -1
  332. package/pack/generated/claude/skills/rite-handoff/SKILL.md +1 -2
  333. package/pack/generated/claude/skills/rite-handoff/reference/handoff-template.md +1 -1
  334. package/pack/generated/claude/skills/rite-learn/SKILL.md +44 -104
  335. package/pack/generated/claude/skills/rite-plan/SKILL.md +14 -26
  336. package/pack/generated/claude/skills/rite-plan/reference/task-breakdown.md +4 -1
  337. package/pack/generated/claude/skills/rite-polish/SKILL.md +20 -28
  338. package/pack/generated/claude/skills/rite-polish/reference/adr-promotion.md +11 -0
  339. package/pack/generated/{codex/skills/rite-ship → claude/skills/rite-polish}/reference/design-memory.md +23 -21
  340. package/pack/generated/claude/skills/rite-polish/reference/ledger.md +62 -0
  341. package/pack/generated/claude/skills/rite-pov/SKILL.md +28 -32
  342. package/pack/generated/claude/skills/rite-pr-feedback/SKILL.md +0 -1
  343. package/pack/generated/claude/skills/rite-pressure-test/SKILL.md +6 -13
  344. package/pack/generated/claude/skills/rite-prototype/SKILL.md +0 -6
  345. package/pack/generated/claude/skills/rite-prove/SKILL.md +73 -68
  346. package/pack/generated/claude/skills/rite-prove/reference/acceptance-proof.md +34 -30
  347. package/pack/generated/claude/skills/rite-prove/reference/test-command-discovery.md +15 -14
  348. package/pack/generated/claude/skills/rite-quick/SKILL.md +5 -19
  349. package/pack/generated/claude/skills/rite-resolve/SKILL.md +4 -21
  350. package/pack/generated/claude/skills/rite-resolve/reference/answer-protocol.md +2 -2
  351. package/pack/generated/claude/skills/rite-review/SKILL.md +34 -57
  352. package/pack/generated/claude/skills/rite-seal/SKILL.md +54 -87
  353. package/pack/generated/claude/skills/rite-seal/reference/anti-patterns.md +2 -2
  354. package/pack/generated/claude/skills/rite-seal/reference/final-evidence.md +31 -56
  355. package/pack/generated/claude/skills/rite-seal/reference/go-no-go.md +27 -51
  356. package/pack/generated/claude/skills/rite-seal/reference/output.md +2 -4
  357. package/pack/generated/claude/skills/rite-seal/reference/phase-contract.md +42 -140
  358. package/pack/generated/claude/skills/rite-seal/reference/risk-and-rollback.md +2 -2
  359. package/pack/generated/claude/skills/rite-seal/reference/seal-template.md +17 -28
  360. package/pack/generated/claude/skills/rite-ship/SKILL.md +65 -93
  361. package/pack/generated/claude/skills/rite-ship/reference/anti-patterns.md +4 -1
  362. package/pack/generated/claude/skills/rite-ship/reference/close-out.md +4 -12
  363. package/pack/generated/claude/skills/rite-ship/reference/git-ship.md +86 -48
  364. package/pack/generated/claude/skills/rite-ship/reference/ship-template.md +22 -16
  365. package/pack/generated/claude/skills/rite-spec/SKILL.md +62 -95
  366. package/pack/generated/claude/skills/rite-spec/reference/investigation.md +1 -4
  367. package/pack/generated/claude/skills/rite-spec/reference/question-protocol.md +21 -20
  368. package/pack/generated/claude/skills/rite-spec/reference/spec-checklists.md +16 -17
  369. package/pack/generated/claude/skills/rite-spec/reference/spec-template.md +47 -49
  370. package/pack/generated/claude/skills/rite-spec/reference/state-workspace.md +28 -13
  371. package/pack/generated/claude/skills/rite-status/SKILL.md +30 -88
  372. package/pack/generated/claude/skills/rite-temper/SKILL.md +12 -31
  373. package/pack/generated/claude/skills/rite-upgrade/SKILL.md +86 -101
  374. package/pack/generated/claude/skills/rite-vet/SKILL.md +88 -117
  375. package/pack/generated/claude/skills/rite-vet/reference/artifacts.md +56 -50
  376. package/pack/generated/claude/skills/rite-vet/reference/cross-model.md +15 -39
  377. package/pack/generated/claude/skills/rite-vet/reference/depth.md +2 -2
  378. package/pack/generated/claude/skills/rite-vet/reference/review-axes.md +3 -6
  379. package/pack/generated/claude/skills/rite-zoom-out/SKILL.md +0 -6
  380. package/pack/generated/codex/AGENTS.md +5 -4
  381. package/pack/generated/codex/agents/devrites-code-reviewer.toml +8 -27
  382. package/pack/generated/codex/agents/devrites-devex-reviewer.toml +9 -27
  383. package/pack/generated/codex/agents/devrites-doubt-reviewer.toml +9 -29
  384. package/pack/generated/codex/agents/devrites-evidence-scout.toml +14 -36
  385. package/pack/generated/codex/agents/devrites-frontend-reviewer.toml +8 -24
  386. package/pack/generated/codex/agents/devrites-performance-reviewer.toml +12 -30
  387. package/pack/generated/codex/agents/devrites-plan-drafter.toml +24 -38
  388. package/pack/generated/codex/agents/devrites-plan-reviewer.toml +30 -57
  389. package/pack/generated/codex/agents/devrites-proof-runner.toml +41 -57
  390. package/pack/generated/codex/agents/devrites-retrospector.toml +34 -89
  391. package/pack/generated/codex/agents/devrites-security-auditor.toml +8 -26
  392. package/pack/generated/codex/agents/devrites-simplifier-reviewer.toml +8 -27
  393. package/pack/generated/codex/agents/devrites-slice-wright.toml +44 -71
  394. package/pack/generated/codex/agents/devrites-spec-reviewer.toml +38 -60
  395. package/pack/generated/codex/agents/devrites-strategy-reviewer.toml +9 -28
  396. package/pack/generated/codex/agents/devrites-test-analyst.toml +21 -30
  397. package/pack/generated/codex/agents/devrites-upgrade-planner.toml +52 -81
  398. package/pack/generated/codex/config.toml +7 -0
  399. package/pack/generated/codex/skills/devrites-api-interface/SKILL.md +1 -21
  400. package/pack/generated/codex/skills/devrites-audit/SKILL.md +6 -28
  401. package/pack/generated/codex/skills/devrites-browser-proof/SKILL.md +1 -21
  402. package/pack/generated/codex/skills/devrites-debug-recovery/SKILL.md +21 -46
  403. package/pack/generated/codex/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +22 -33
  404. package/pack/generated/codex/skills/devrites-doubt/SKILL.md +4 -25
  405. package/pack/generated/codex/skills/devrites-frontend-craft/SKILL.md +1 -21
  406. package/pack/generated/codex/skills/devrites-frontend-craft/reference/design-references.md +2 -1
  407. package/pack/generated/codex/skills/devrites-interview/SKILL.md +32 -56
  408. package/pack/generated/codex/skills/devrites-lib/SKILL.md +38 -86
  409. package/pack/generated/codex/skills/devrites-lib/reference/candidate-integrity.md +51 -0
  410. package/pack/generated/codex/skills/devrites-lib/reference/intent-map.md +20 -12
  411. package/pack/generated/codex/skills/devrites-lib/reference/orchestration-profiles.md +22 -0
  412. package/pack/generated/codex/skills/devrites-lib/reference/parallel-dispatch.md +35 -110
  413. package/pack/generated/codex/skills/devrites-lib/reference/reply-contract.md +26 -93
  414. package/pack/generated/codex/skills/devrites-lib/reference/standards/README.md +6 -3
  415. package/pack/generated/codex/skills/devrites-lib/reference/standards/afk-hitl.md +43 -34
  416. package/pack/generated/codex/skills/devrites-lib/reference/standards/agents.md +90 -216
  417. package/pack/generated/codex/skills/devrites-lib/reference/standards/anti-patterns.md +1 -1
  418. package/pack/generated/codex/skills/devrites-lib/reference/standards/code-review.md +9 -28
  419. package/pack/generated/codex/skills/devrites-lib/reference/standards/context-hygiene.md +13 -11
  420. package/pack/generated/codex/skills/devrites-lib/reference/standards/core.md +59 -54
  421. package/pack/generated/codex/skills/devrites-lib/reference/standards/hooks.md +3 -5
  422. package/pack/generated/codex/skills/devrites-lib/reference/standards/principles.md +29 -143
  423. package/pack/generated/codex/skills/devrites-lib/reference/standards/release/ship-checklist.md +5 -6
  424. package/pack/generated/codex/skills/devrites-lib/reference/standards/security.md +13 -31
  425. package/pack/generated/codex/skills/devrites-lib/reference/standards/skill-authoring.md +112 -82
  426. package/pack/generated/codex/skills/devrites-lib/reference/standards/spec-grammar.md +71 -45
  427. package/pack/generated/codex/skills/devrites-lib/reference/standards/testing.md +22 -11
  428. package/pack/generated/codex/skills/devrites-lib/reference/standards/tooling.md +15 -24
  429. package/pack/generated/codex/skills/devrites-lib/reference/workspace-artifact-schema.md +106 -37
  430. package/pack/generated/codex/skills/devrites-prose-craft/SKILL.md +3 -23
  431. package/pack/generated/codex/skills/devrites-source-driven/SKILL.md +3 -23
  432. package/pack/generated/codex/skills/devrites-ux-shape/SKILL.md +3 -22
  433. package/pack/generated/codex/skills/rite/SKILL.md +12 -32
  434. package/pack/generated/codex/skills/rite/reference/menu.md +11 -11
  435. package/pack/generated/codex/skills/rite-adopt/SKILL.md +29 -103
  436. package/pack/generated/codex/skills/rite-adopt/reference/adoption.md +16 -50
  437. package/pack/generated/codex/skills/rite-autocomplete/SKILL.md +65 -73
  438. package/pack/generated/codex/skills/rite-autocomplete/reference/loop.md +24 -17
  439. package/pack/generated/codex/skills/rite-autocomplete/reference/stop-conditions.md +18 -21
  440. package/pack/generated/codex/skills/rite-build/SKILL.md +28 -53
  441. package/pack/generated/codex/skills/rite-build/reference/afk-discipline.md +36 -34
  442. package/pack/generated/codex/skills/rite-build/reference/anti-patterns.md +5 -19
  443. package/pack/generated/codex/skills/rite-build/reference/checkpoint-protocol.md +10 -9
  444. package/pack/generated/codex/skills/rite-build/reference/checkpoint.md +17 -17
  445. package/pack/generated/codex/skills/rite-build/reference/frontend-trigger.md +3 -3
  446. package/pack/generated/codex/skills/rite-build/reference/one-slice-cycle.md +11 -14
  447. package/pack/generated/codex/skills/rite-build/reference/output.md +14 -24
  448. package/pack/generated/codex/skills/rite-build/reference/phase-contract.md +49 -188
  449. package/pack/generated/codex/skills/rite-build/reference/spec-drift-guard.md +1 -1
  450. package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +43 -133
  451. package/pack/generated/codex/skills/rite-clarify/SKILL.md +46 -59
  452. package/pack/generated/codex/skills/rite-clarify/reference/decision-coverage.md +4 -10
  453. package/pack/generated/codex/skills/rite-converge/SKILL.md +9 -52
  454. package/pack/generated/codex/skills/rite-converge/reference/convergence-assessment.md +2 -2
  455. package/pack/generated/codex/skills/rite-customize/SKILL.md +32 -67
  456. package/pack/generated/codex/skills/rite-define/SKILL.md +34 -77
  457. package/pack/generated/codex/skills/rite-define/reference/plan-template.md +15 -0
  458. package/pack/generated/codex/skills/rite-doctor/SKILL.md +52 -83
  459. package/pack/generated/codex/skills/rite-dogfood/SKILL.md +1 -21
  460. package/pack/generated/codex/skills/rite-explain/SKILL.md +1 -39
  461. package/pack/generated/codex/skills/rite-frame/SKILL.md +1 -26
  462. package/pack/generated/codex/skills/rite-frame/reference/failure-modes.md +1 -1
  463. package/pack/generated/codex/skills/rite-handoff/SKILL.md +1 -21
  464. package/pack/generated/codex/skills/rite-handoff/reference/handoff-template.md +1 -1
  465. package/pack/generated/codex/skills/rite-learn/SKILL.md +44 -123
  466. package/pack/generated/codex/skills/rite-plan/SKILL.md +14 -45
  467. package/pack/generated/codex/skills/rite-plan/reference/task-breakdown.md +4 -1
  468. package/pack/generated/codex/skills/rite-polish/SKILL.md +20 -47
  469. package/pack/generated/codex/skills/rite-polish/reference/adr-promotion.md +11 -0
  470. package/pack/{.claude/skills/rite-ship → generated/codex/skills/rite-polish}/reference/design-memory.md +23 -21
  471. package/pack/generated/codex/skills/rite-polish/reference/ledger.md +62 -0
  472. package/pack/generated/codex/skills/rite-pov/SKILL.md +28 -51
  473. package/pack/generated/codex/skills/rite-pr-feedback/SKILL.md +0 -20
  474. package/pack/generated/codex/skills/rite-pressure-test/SKILL.md +6 -32
  475. package/pack/generated/codex/skills/rite-prototype/SKILL.md +0 -25
  476. package/pack/generated/codex/skills/rite-prove/SKILL.md +73 -87
  477. package/pack/generated/codex/skills/rite-prove/reference/acceptance-proof.md +34 -30
  478. package/pack/generated/codex/skills/rite-prove/reference/test-command-discovery.md +15 -14
  479. package/pack/generated/codex/skills/rite-quick/SKILL.md +5 -38
  480. package/pack/generated/codex/skills/rite-resolve/SKILL.md +4 -40
  481. package/pack/generated/codex/skills/rite-resolve/reference/answer-protocol.md +2 -2
  482. package/pack/generated/codex/skills/rite-review/SKILL.md +34 -76
  483. package/pack/generated/codex/skills/rite-seal/SKILL.md +51 -103
  484. package/pack/generated/codex/skills/rite-seal/reference/anti-patterns.md +2 -2
  485. package/pack/generated/codex/skills/rite-seal/reference/final-evidence.md +31 -56
  486. package/pack/generated/codex/skills/rite-seal/reference/go-no-go.md +27 -51
  487. package/pack/generated/codex/skills/rite-seal/reference/output.md +2 -4
  488. package/pack/generated/codex/skills/rite-seal/reference/phase-contract.md +42 -140
  489. package/pack/generated/codex/skills/rite-seal/reference/risk-and-rollback.md +2 -2
  490. package/pack/generated/codex/skills/rite-seal/reference/seal-template.md +17 -28
  491. package/pack/generated/codex/skills/rite-ship/SKILL.md +65 -112
  492. package/pack/generated/codex/skills/rite-ship/reference/anti-patterns.md +4 -1
  493. package/pack/generated/codex/skills/rite-ship/reference/close-out.md +4 -12
  494. package/pack/generated/codex/skills/rite-ship/reference/git-ship.md +86 -48
  495. package/pack/generated/codex/skills/rite-ship/reference/ship-template.md +22 -16
  496. package/pack/generated/codex/skills/rite-spec/SKILL.md +62 -114
  497. package/pack/generated/codex/skills/rite-spec/reference/investigation.md +1 -4
  498. package/pack/generated/codex/skills/rite-spec/reference/question-protocol.md +21 -20
  499. package/pack/generated/codex/skills/rite-spec/reference/spec-checklists.md +16 -17
  500. package/pack/generated/codex/skills/rite-spec/reference/spec-template.md +47 -49
  501. package/pack/generated/codex/skills/rite-spec/reference/state-workspace.md +28 -13
  502. package/pack/generated/codex/skills/rite-status/SKILL.md +30 -107
  503. package/pack/generated/codex/skills/rite-temper/SKILL.md +12 -50
  504. package/pack/generated/codex/skills/rite-upgrade/SKILL.md +86 -120
  505. package/pack/generated/codex/skills/rite-vet/SKILL.md +88 -136
  506. package/pack/generated/codex/skills/rite-vet/reference/artifacts.md +56 -50
  507. package/pack/generated/codex/skills/rite-vet/reference/cross-model.md +15 -39
  508. package/pack/generated/codex/skills/rite-vet/reference/depth.md +2 -2
  509. package/pack/generated/codex/skills/rite-vet/reference/review-axes.md +3 -6
  510. package/pack/generated/codex/skills/rite-zoom-out/SKILL.md +0 -25
  511. package/package.json +6 -5
  512. package/scripts/build-host-artifacts.sh +5 -9
  513. package/scripts/codex-generate.sh +18 -278
  514. package/scripts/install-lib.sh +121 -34
  515. package/uninstall.sh +19 -17
  516. package/update.sh +26 -18
  517. package/docs/engine/agent-contract.md +0 -172
  518. package/docs/extensions.md +0 -178
  519. package/engine/git_guard.go +0 -187
  520. package/engine/git_guard_test.go +0 -283
  521. package/engine/harnessmatrix.go +0 -60
  522. package/engine/hookpolicy.go +0 -113
  523. package/engine/hookpolicy_test.go +0 -160
  524. package/engine/hooks.go +0 -677
  525. package/engine/hooks_agent_dispatch.go +0 -1637
  526. package/engine/hooks_events_test.go +0 -431
  527. package/engine/hooks_workspace.go +0 -1196
  528. package/engine/internal/doctor/doctor.go +0 -306
  529. package/engine/internal/doctor/doctor_test.go +0 -252
  530. package/engine/internal/forge/forge.go +0 -938
  531. package/engine/internal/forge/forge_test.go +0 -576
  532. package/engine/internal/forge/git.go +0 -245
  533. package/engine/internal/forge/liveness_unix.go +0 -48
  534. package/engine/internal/forge/liveness_windows.go +0 -67
  535. package/engine/internal/forge/manifest.go +0 -402
  536. package/engine/internal/harness/compliance.go +0 -96
  537. package/engine/internal/harness/harness.go +0 -301
  538. package/engine/internal/harness/harness_test.go +0 -124
  539. package/engine/internal/iohooks/detach_other.go +0 -11
  540. package/engine/internal/iohooks/detach_unix.go +0 -11
  541. package/engine/internal/iohooks/iohooks.go +0 -839
  542. package/engine/internal/iohooks/iohooks_test.go +0 -561
  543. package/engine/internal/lib/adoption.go +0 -659
  544. package/engine/internal/lib/adoption_test.go +0 -121
  545. package/engine/internal/lib/analyze.go +0 -232
  546. package/engine/internal/lib/archivesearch.go +0 -125
  547. package/engine/internal/lib/archivesearch_test.go +0 -138
  548. package/engine/internal/lib/budget.go +0 -172
  549. package/engine/internal/lib/buildreadiness.go +0 -111
  550. package/engine/internal/lib/checkacceptance.go +0 -176
  551. package/engine/internal/lib/clarifyreturn.go +0 -91
  552. package/engine/internal/lib/clarifyreturn_test.go +0 -123
  553. package/engine/internal/lib/command_other.go +0 -7
  554. package/engine/internal/lib/command_unix.go +0 -24
  555. package/engine/internal/lib/context.go +0 -280
  556. package/engine/internal/lib/conventions.go +0 -449
  557. package/engine/internal/lib/coverage.go +0 -145
  558. package/engine/internal/lib/dispatchwaive.go +0 -31
  559. package/engine/internal/lib/dispatchwaive_test.go +0 -23
  560. package/engine/internal/lib/doubtcoverage.go +0 -78
  561. package/engine/internal/lib/extensions.go +0 -622
  562. package/engine/internal/lib/extensions_test.go +0 -305
  563. package/engine/internal/lib/footprint.go +0 -239
  564. package/engine/internal/lib/gitauthority.go +0 -601
  565. package/engine/internal/lib/gitauthority_test.go +0 -346
  566. package/engine/internal/lib/jsonout.go +0 -129
  567. package/engine/internal/lib/jsonout_test.go +0 -73
  568. package/engine/internal/lib/lanes.go +0 -112
  569. package/engine/internal/lib/learnings.go +0 -397
  570. package/engine/internal/lib/learnings_inject_test.go +0 -77
  571. package/engine/internal/lib/learnings_remediation_test.go +0 -24
  572. package/engine/internal/lib/ledger.go +0 -311
  573. package/engine/internal/lib/ledger_test.go +0 -186
  574. package/engine/internal/lib/mutationgate.go +0 -89
  575. package/engine/internal/lib/observability_test.go +0 -358
  576. package/engine/internal/lib/overrides.go +0 -164
  577. package/engine/internal/lib/overrides_test.go +0 -112
  578. package/engine/internal/lib/preamble.go +0 -159
  579. package/engine/internal/lib/preamble_questions_test.go +0 -10
  580. package/engine/internal/lib/progress.go +0 -210
  581. package/engine/internal/lib/provenance.go +0 -462
  582. package/engine/internal/lib/provenance_test.go +0 -154
  583. package/engine/internal/lib/readiness_contract.json +0 -118
  584. package/engine/internal/lib/readinessartifact.go +0 -533
  585. package/engine/internal/lib/readinessartifact_test.go +0 -422
  586. package/engine/internal/lib/reconcile.go +0 -1496
  587. package/engine/internal/lib/reconcile_test.go +0 -1071
  588. package/engine/internal/lib/recoveryattempts.go +0 -298
  589. package/engine/internal/lib/recoveryattempts_test.go +0 -215
  590. package/engine/internal/lib/reviewers.go +0 -143
  591. package/engine/internal/lib/reviewers_test.go +0 -45
  592. package/engine/internal/lib/reviewerstats.go +0 -199
  593. package/engine/internal/lib/reviewerstats_test.go +0 -104
  594. package/engine/internal/lib/reviewintegrity.go +0 -143
  595. package/engine/internal/lib/reviewintegrity_test.go +0 -103
  596. package/engine/internal/lib/runbook.go +0 -295
  597. package/engine/internal/lib/runbook_context_test.go +0 -182
  598. package/engine/internal/lib/session.go +0 -1020
  599. package/engine/internal/lib/session_test.go +0 -177
  600. package/engine/internal/lib/specparse.go +0 -125
  601. package/engine/internal/lib/specskeleton.go +0 -79
  602. package/engine/internal/lib/specvalidate.go +0 -391
  603. package/engine/internal/lib/specvalidate_test.go +0 -185
  604. package/engine/internal/lib/stuck.go +0 -206
  605. package/engine/internal/lib/templates/conventions_drift.tmpl +0 -6
  606. package/engine/internal/lib/templates/conventions_ledger_header.md +0 -6
  607. package/engine/internal/lib/templates/conventions_orient_header.txt +0 -1
  608. package/engine/internal/lib/testintegrity.go +0 -199
  609. package/engine/internal/lib/testintegrity_test.go +0 -149
  610. package/engine/internal/lib/tickafk.go +0 -86
  611. package/engine/internal/migrate/migrate.go +0 -279
  612. package/engine/internal/migrate/migrate_test.go +0 -157
  613. package/engine/internal/orient/firstrun.go +0 -50
  614. package/engine/internal/orient/firstrun_test.go +0 -72
  615. package/engine/internal/orient/orient.go +0 -69
  616. package/engine/internal/orient/orient_test.go +0 -25
  617. package/engine/internal/profile/profile.go +0 -284
  618. package/engine/internal/profile/profile_test.go +0 -13
  619. package/engine/internal/state/clarify_transition.go +0 -165
  620. package/engine/internal/state/clarify_transition_test.go +0 -130
  621. package/engine/internal/state/snapshot.go +0 -457
  622. package/engine/internal/toolpolicy/classifier.go +0 -533
  623. package/engine/internal/toolpolicy/classifier_test.go +0 -424
  624. package/engine/internal/toolpolicy/git.go +0 -616
  625. package/engine/internal/toolpolicy/scanner.go +0 -382
  626. package/engine/observability_cli_test.go +0 -52
  627. package/engine/profile_test.go +0 -76
  628. package/engine/subagent_orient_context.txt +0 -8
  629. package/engine/testdata/golden/TestParityA1Guard/devrites-edit-allowed.golden +0 -1
  630. package/engine/testdata/golden/TestParityA1Guard/no-window-silent.golden +0 -1
  631. package/engine/testdata/golden/TestParityA1Guard/non-edit-tool.golden +0 -1
  632. package/engine/testdata/golden/TestParityA1Guard/observe-mode-silent.golden +0 -1
  633. package/engine/testdata/golden/TestParityA1Guard/source-edit-enforce-denies.golden +0 -2
  634. package/engine/testdata/golden/TestParityA1Guard/subagent-allowed.golden +0 -1
  635. package/engine/testdata/golden/TestParityAnalyze/noactive.golden +0 -1
  636. package/engine/testdata/golden/TestParityAnalyze/slug=covered.golden +0 -15
  637. package/engine/testdata/golden/TestParityAnalyze/slug=dangling.golden +0 -16
  638. package/engine/testdata/golden/TestParityAnalyze/slug=ghost.golden +0 -1
  639. package/engine/testdata/golden/TestParityAnalyze/slug=noac.golden +0 -15
  640. package/engine/testdata/golden/TestParityAnalyze/slug=order.golden +0 -17
  641. package/engine/testdata/golden/TestParityAnalyze/slug=orphan.golden +0 -16
  642. package/engine/testdata/golden/TestParityAnalyze/slug=uncovered.golden +0 -16
  643. package/engine/testdata/golden/TestParityBudget/default-active.golden +0 -4
  644. package/engine/testdata/golden/TestParityBudget/extra-md.golden +0 -5
  645. package/engine/testdata/golden/TestParityBudget/no-active.golden +0 -1
  646. package/engine/testdata/golden/TestParityBudget/no-feature.golden +0 -1
  647. package/engine/testdata/golden/TestParityBudget/over-advisory.golden +0 -6
  648. package/engine/testdata/golden/TestParityBudget/over-strict.golden +0 -6
  649. package/engine/testdata/golden/TestParityBudget/within.golden +0 -6
  650. package/engine/testdata/golden/TestParityBuildReadiness/arg=approved.golden +0 -2
  651. package/engine/testdata/golden/TestParityBuildReadiness/arg=approvenone.golden +0 -1
  652. package/engine/testdata/golden/TestParityBuildReadiness/arg=await.golden +0 -1
  653. package/engine/testdata/golden/TestParityBuildReadiness/arg=blocked.golden +0 -1
  654. package/engine/testdata/golden/TestParityBuildReadiness/arg=clarifyopen.golden +0 -1
  655. package/engine/testdata/golden/TestParityBuildReadiness/arg=emptystatus.golden +0 -2
  656. package/engine/testdata/golden/TestParityBuildReadiness/arg=ghost.golden +0 -1
  657. package/engine/testdata/golden/TestParityBuildReadiness/arg=legacycontract.golden +0 -1
  658. package/engine/testdata/golden/TestParityBuildReadiness/arg=noapprove.golden +0 -1
  659. package/engine/testdata/golden/TestParityBuildReadiness/arg=noclarify.golden +0 -1
  660. package/engine/testdata/golden/TestParityBuildReadiness/arg=novet.golden +0 -1
  661. package/engine/testdata/golden/TestParityBuildReadiness/arg=stalevet.golden +0 -1
  662. package/engine/testdata/golden/TestParityBuildReadiness/arg=trailhash.golden +0 -2
  663. package/engine/testdata/golden/TestParityBuildReadiness/arg=trailpipe.golden +0 -2
  664. package/engine/testdata/golden/TestParityBuildReadiness/arg=vetnotready.golden +0 -1
  665. package/engine/testdata/golden/TestParityCheckAcceptance/arg=blank-section.golden +0 -1
  666. package/engine/testdata/golden/TestParityCheckAcceptance/arg=empty.golden +0 -1
  667. package/engine/testdata/golden/TestParityCheckAcceptance/arg=flat-dropped.golden +0 -1
  668. package/engine/testdata/golden/TestParityCheckAcceptance/arg=flat-ok.golden +0 -2
  669. package/engine/testdata/golden/TestParityCheckAcceptance/arg=flat-unchecked.golden +0 -1
  670. package/engine/testdata/golden/TestParityCheckAcceptance/arg=id-gap.golden +0 -1
  671. package/engine/testdata/golden/TestParityCheckAcceptance/arg=id-ok.golden +0 -2
  672. package/engine/testdata/golden/TestParityCheckAcceptance/arg=no-seal.golden +0 -1
  673. package/engine/testdata/golden/TestParityCheckAcceptance/arg=no-section.golden +0 -1
  674. package/engine/testdata/golden/TestParityCheckAcceptance/arg=nonexistent.golden +0 -1
  675. package/engine/testdata/golden/TestParityCheckAcceptance.golden +0 -1
  676. package/engine/testdata/golden/TestParityCoverage/arg=flat.golden +0 -7
  677. package/engine/testdata/golden/TestParityCoverage/arg=full.golden +0 -10
  678. package/engine/testdata/golden/TestParityCoverage/arg=nonexistent.golden +0 -1
  679. package/engine/testdata/golden/TestParityCoverage/arg=nospec.golden +0 -1
  680. package/engine/testdata/golden/TestParityCoverage/arg=speconly.golden +0 -9
  681. package/engine/testdata/golden/TestParityCoverage.golden +0 -1
  682. package/engine/testdata/golden/TestParityCursor/bare.golden +0 -3
  683. package/engine/testdata/golden/TestParityCursor/full-afk.golden +0 -6
  684. package/engine/testdata/golden/TestParityCursor/full-hitl.golden +0 -5
  685. package/engine/testdata/golden/TestParityCursor/no-active.golden +0 -1
  686. package/engine/testdata/golden/TestParityCursor/red.golden +0 -5
  687. package/engine/testdata/golden/TestParityCursor/zerogates.golden +0 -5
  688. package/engine/testdata/golden/TestParityDoubtCoverage/arg=doubted.golden +0 -2
  689. package/engine/testdata/golden/TestParityDoubtCoverage/arg=ghost.golden +0 -2
  690. package/engine/testdata/golden/TestParityDoubtCoverage/arg=missing.golden +0 -3
  691. package/engine/testdata/golden/TestParityDoubtCoverage/arg=nodoubt.golden +0 -4
  692. package/engine/testdata/golden/TestParityDoubtCoverage/arg=nolog.golden +0 -2
  693. package/engine/testdata/golden/TestParityDoubtCoverage/arg=nowright.golden +0 -2
  694. package/engine/testdata/golden/TestParityDoubtCoverage.golden +0 -1
  695. package/engine/testdata/golden/TestParityMutationGate/active-slug.golden +0 -3
  696. package/engine/testdata/golden/TestParityMutationGate/explicit-slug.golden +0 -3
  697. package/engine/testdata/golden/TestParityMutationGate/ghost.golden +0 -1
  698. package/engine/testdata/golden/TestParityOrientSilentPath.golden +0 -1
  699. package/engine/testdata/golden/TestParityReconcile/bad-mode.golden +0 -1
  700. package/engine/testdata/golden/TestParityReconcile/check-clean/snapshot.golden +0 -2
  701. package/engine/testdata/golden/TestParityReconcile/check-clean.golden +0 -2
  702. package/engine/testdata/golden/TestParityReconcile/check-no-base.golden +0 -1
  703. package/engine/testdata/golden/TestParityReconcile/check-no-claimed/snapshot.golden +0 -2
  704. package/engine/testdata/golden/TestParityReconcile/check-violation/snapshot.golden +0 -2
  705. package/engine/testdata/golden/TestParityReconcile/check-violation.golden +0 -1
  706. package/engine/testdata/golden/TestParityReconcile/close-window/snapshot.golden +0 -2
  707. package/engine/testdata/golden/TestParityReconcile/close-window.golden +0 -3
  708. package/engine/testdata/golden/TestParityReconcile/not-git.golden +0 -1
  709. package/engine/testdata/golden/TestParityReconcile/snapshot/snapshot.golden +0 -2
  710. package/engine/testdata/golden/TestParityReconcile/snapshot-no-allowlist.golden +0 -1
  711. package/engine/testdata/golden/TestParityReconcile/snapshot.golden +0 -2
  712. package/engine/testdata/golden/TestParityRedwatch/fail-writes-red.golden +0 -2
  713. package/engine/testdata/golden/TestParityRedwatch/non-bash-tool.golden +0 -1
  714. package/engine/testdata/golden/TestParityRedwatch/non-test-command.golden +0 -1
  715. package/engine/testdata/golden/TestParityRedwatch/pass-clears-red.golden +0 -1
  716. package/engine/testdata/golden/TestParityResolve/next-qid.golden +0 -2
  717. package/engine/testdata/golden/TestParityReviewerReadonly/agent-required-devrites-agent-denies.golden +0 -2
  718. package/engine/testdata/golden/TestParityReviewerReadonly/agent-required-non-devrites-allows.golden +0 -1
  719. package/engine/testdata/golden/TestParityReviewerReadonly/agent-required-subagent-type-only-allows.golden +0 -1
  720. package/engine/testdata/golden/TestParityReviewerReadonly/enforce-deny-git-commit.golden +0 -2
  721. package/engine/testdata/golden/TestParityReviewerReadonly/enforce-deny-mutating.golden +0 -2
  722. package/engine/testdata/golden/TestParityReviewerReadonly/non-bash-tool.golden +0 -1
  723. package/engine/testdata/golden/TestParityReviewerReadonly/non-tool-input.golden +0 -1
  724. package/engine/testdata/golden/TestParityReviewerReadonly/observe-mutating-silent.golden +0 -1
  725. package/engine/testdata/golden/TestParityReviewerReadonly/safe-bash-command.golden +0 -1
  726. package/engine/testdata/golden/TestParitySpecValidate/arg=dup.golden +0 -1
  727. package/engine/testdata/golden/TestParitySpecValidate/arg=empty.golden +0 -1
  728. package/engine/testdata/golden/TestParitySpecValidate/arg=flat.golden +0 -2
  729. package/engine/testdata/golden/TestParitySpecValidate/arg=nonexistent.golden +0 -1
  730. package/engine/testdata/golden/TestParitySpecValidate/arg=noscenario.golden +0 -1
  731. package/engine/testdata/golden/TestParitySpecValidate/arg=noshall.golden +0 -1
  732. package/engine/testdata/golden/TestParitySpecValidate/arg=nothen.golden +0 -1
  733. package/engine/testdata/golden/TestParitySpecValidate/arg=valid/spec.md.golden +0 -2
  734. package/engine/testdata/golden/TestParitySpecValidate/arg=valid.golden +0 -2
  735. package/engine/testdata/golden/TestParitySpecValidate.golden +0 -1
  736. package/engine/testdata/golden/TestParityStatusline/full-afk.golden +0 -2
  737. package/engine/testdata/golden/TestParityStatusline/full-hitl.golden +0 -2
  738. package/engine/testdata/golden/TestParityStatusline/no-active.golden +0 -1
  739. package/engine/testdata/golden/TestParityStatusline/nophase.golden +0 -2
  740. package/engine/testdata/golden/TestParityStatusline/red.golden +0 -2
  741. package/engine/testdata/golden/TestParityStuckCheck/bad-cmd.golden +0 -1
  742. package/engine/testdata/golden/TestParityStuckCheck/custom-window-2.golden +0 -2
  743. package/engine/testdata/golden/TestParityStuckCheck/dominant.golden +0 -2
  744. package/engine/testdata/golden/TestParityStuckCheck/few.golden +0 -2
  745. package/engine/testdata/golden/TestParityStuckCheck/ghost.golden +0 -2
  746. package/engine/testdata/golden/TestParityStuckCheck/identical.golden +0 -2
  747. package/engine/testdata/golden/TestParityStuckCheck/no-cmd.golden +0 -1
  748. package/engine/testdata/golden/TestParityStuckCheck/no-slug.golden +0 -1
  749. package/engine/testdata/golden/TestParityStuckCheck/nolog.golden +0 -2
  750. package/engine/testdata/golden/TestParityStuckCheck/pingpong.golden +0 -2
  751. package/engine/testdata/golden/TestParityStuckCheck/varied.golden +0 -2
  752. package/engine/testdata/golden/TestParityStuckCheck/window-04-identical.golden +0 -2
  753. package/engine/testdata/golden/TestParityStuckCheck/window-2.5-varied4.golden +0 -2
  754. package/engine/testdata/golden/TestParityStuckCheck/window-4x-varied4.golden +0 -2
  755. package/engine/testdata/golden/TestParityStuckCheck/window-leadspace-varied4.golden +0 -2
  756. package/engine/testdata/golden/TestParityStuckCheck/window-plus4-identical.golden +0 -2
  757. package/engine/testdata/golden/TestParitySubagentOrient/devrites-agent-subagent-type-key.golden +0 -2
  758. package/engine/testdata/golden/TestParitySubagentOrient/devrites-agent.golden +0 -2
  759. package/engine/testdata/golden/TestParitySubagentOrient/garbage-input.golden +0 -1
  760. package/engine/testdata/golden/TestParitySubagentOrient/non-devrites-agent.golden +0 -1
  761. package/engine/testdata/golden/TestParityTestIntegrity/assertion-dropped.golden +0 -1
  762. package/engine/testdata/golden/TestParityTestIntegrity/clean.golden +0 -2
  763. package/engine/testdata/golden/TestParityTestIntegrity/deleted.golden +0 -1
  764. package/engine/testdata/golden/TestParityTestIntegrity/ghost-workspace.golden +0 -1
  765. package/engine/testdata/golden/TestParityTestIntegrity/not-git.golden +0 -1
  766. package/engine/testdata/golden/TestParityTestIntegrity/skip-added.golden +0 -1
  767. package/engine/testdata/golden/TestParityTickAfk/budget1/state.md.golden +0 -1
  768. package/engine/testdata/golden/TestParityTickAfk/budget1.golden +0 -2
  769. package/engine/testdata/golden/TestParityTickAfk/budget3/state.md.golden +0 -5
  770. package/engine/testdata/golden/TestParityTickAfk/budget3.golden +0 -2
  771. package/engine/testdata/golden/TestParityTickAfk/bulletPipe/state.md.golden +0 -1
  772. package/engine/testdata/golden/TestParityTickAfk/bulletPipe.golden +0 -2
  773. package/engine/testdata/golden/TestParityTickAfk/missingField/state.md.golden +0 -3
  774. package/engine/testdata/golden/TestParityTickAfk/missingField.golden +0 -2
  775. package/engine/testdata/golden/TestParityTickAfk/missingFile.golden +0 -1
  776. package/engine/testdata/golden/TestParityTickAfk/nonNumeric/state.md.golden +0 -1
  777. package/engine/testdata/golden/TestParityTickAfk/nonNumeric.golden +0 -1
  778. package/engine/testdata/golden/TestParityTickAfk/none/state.md.golden +0 -1
  779. package/engine/testdata/golden/TestParityTickAfk/none.golden +0 -2
  780. package/engine/testdata/golden/TestParityWrightScope/agent-required-non-wright.golden +0 -1
  781. package/engine/testdata/golden/TestParityWrightScope/devrites-edit-denied.golden +0 -2
  782. package/engine/testdata/golden/TestParityWrightScope/in-scope-allowed.golden +0 -1
  783. package/engine/testdata/golden/TestParityWrightScope/non-edit-tool.golden +0 -1
  784. package/engine/testdata/golden/TestParityWrightScope/out-of-scope-enforce-denies.golden +0 -2
  785. package/engine/testdata/golden/TestParityWrightScope/out-of-scope-observe-silent.golden +0 -1
  786. package/engine/testdata/golden/TestSpecSkeleton/missing-some.golden +0 -2
  787. package/engine/testdata/golden/TestSpecSkeleton/present-all.golden +0 -2
  788. package/engine/testdata/golden/TestStuckLogHashParity.golden +0 -1
  789. package/engine/tests/adr_0006_clock_seam_test.go +0 -36
  790. package/engine/tests/budget_test.go +0 -88
  791. package/engine/tests/canonical_ac_ids_test.go +0 -44
  792. package/engine/tests/concurrency_cli_test.go +0 -131
  793. package/engine/tests/conventions_test.go +0 -106
  794. package/engine/tests/doctor_cli_test.go +0 -223
  795. package/engine/tests/forge_cli_test.go +0 -463
  796. package/engine/tests/hook_test.go +0 -2732
  797. package/engine/tests/hooks_io_test.go +0 -309
  798. package/engine/tests/json_contract_test.go +0 -224
  799. package/engine/tests/lib_parity_test.go +0 -930
  800. package/engine/tests/migrate_cli_test.go +0 -195
  801. package/engine/tests/parity_analyze_test.go +0 -55
  802. package/engine/tests/parity_buildreadiness_test.go +0 -199
  803. package/engine/tests/parity_learnings_test.go +0 -89
  804. package/engine/tests/parity_mutationgate_test.go +0 -36
  805. package/engine/tests/parity_reconcile_test.go +0 -94
  806. package/engine/tests/parity_testintegrity_test.go +0 -65
  807. package/engine/tests/parity_tickafk_test.go +0 -83
  808. package/engine/validatepack.go +0 -192
  809. package/engine/validatepack_test.go +0 -79
  810. package/engine/validatepackskills.go +0 -181
  811. package/engine/validatepackskills_test.go +0 -171
  812. package/pack/.claude/agents/devrites-forge-judge.md +0 -118
  813. package/pack/.claude/skills/devrites-lib/reference/model-tiers.md +0 -44
  814. package/pack/.claude/skills/devrites-refresh-indexes/SKILL.md +0 -52
  815. package/pack/.claude/skills/rite-build/reference/evidence-standard.md +0 -45
  816. package/pack/.claude/skills/rite-build/reference/forge.md +0 -210
  817. package/pack/.claude/skills/rite-seal/reference/conventions-ledger.md +0 -60
  818. package/pack/.claude/skills/rite-ship/reference/adr-promotion.md +0 -9
  819. package/pack/.claude/skills/rite-ship/reference/ledger.md +0 -56
  820. package/pack/generated/claude/agents/devrites-forge-judge.md +0 -118
  821. package/pack/generated/claude/skills/devrites-lib/reference/model-tiers.md +0 -44
  822. package/pack/generated/claude/skills/devrites-refresh-indexes/SKILL.md +0 -52
  823. package/pack/generated/claude/skills/rite-build/reference/evidence-standard.md +0 -45
  824. package/pack/generated/claude/skills/rite-build/reference/forge.md +0 -210
  825. package/pack/generated/claude/skills/rite-seal/reference/conventions-ledger.md +0 -60
  826. package/pack/generated/claude/skills/rite-ship/reference/adr-promotion.md +0 -9
  827. package/pack/generated/claude/skills/rite-ship/reference/ledger.md +0 -56
  828. package/pack/generated/codex/agents/devrites-forge-judge.toml +0 -128
  829. package/pack/generated/codex/hooks.json +0 -185
  830. package/pack/generated/codex/skills/devrites-api-interface/agents/openai.yaml +0 -2
  831. package/pack/generated/codex/skills/devrites-audit/agents/openai.yaml +0 -2
  832. package/pack/generated/codex/skills/devrites-browser-proof/agents/openai.yaml +0 -2
  833. package/pack/generated/codex/skills/devrites-debug-recovery/agents/openai.yaml +0 -2
  834. package/pack/generated/codex/skills/devrites-doubt/agents/openai.yaml +0 -2
  835. package/pack/generated/codex/skills/devrites-frontend-craft/agents/openai.yaml +0 -2
  836. package/pack/generated/codex/skills/devrites-interview/agents/openai.yaml +0 -2
  837. package/pack/generated/codex/skills/devrites-lib/reference/model-tiers.md +0 -44
  838. package/pack/generated/codex/skills/devrites-prose-craft/agents/openai.yaml +0 -2
  839. package/pack/generated/codex/skills/devrites-refresh-indexes/SKILL.md +0 -71
  840. package/pack/generated/codex/skills/devrites-refresh-indexes/agents/openai.yaml +0 -2
  841. package/pack/generated/codex/skills/devrites-source-driven/agents/openai.yaml +0 -2
  842. package/pack/generated/codex/skills/devrites-ux-shape/agents/openai.yaml +0 -2
  843. package/pack/generated/codex/skills/rite-build/reference/evidence-standard.md +0 -45
  844. package/pack/generated/codex/skills/rite-build/reference/forge.md +0 -210
  845. package/pack/generated/codex/skills/rite-seal/reference/conventions-ledger.md +0 -60
  846. package/pack/generated/codex/skills/rite-ship/reference/adr-promotion.md +0 -9
  847. package/pack/generated/codex/skills/rite-ship/reference/ledger.md +0 -56
  848. package/scripts/build-binaries.sh +0 -75
  849. package/scripts/build-release-tarball.sh +0 -105
  850. package/scripts/check-authority-drift.py +0 -125
  851. package/scripts/check-cross-refs.py +0 -139
  852. package/scripts/check-generated-skill-budget.mjs +0 -42
  853. package/scripts/check-instruction-size-baseline.mjs +0 -88
  854. package/scripts/check-invocation-integrity.py +0 -115
  855. package/scripts/check-no-global-writes.sh +0 -66
  856. package/scripts/check-no-personal-paths.py +0 -50
  857. package/scripts/check-npm-audit.mjs +0 -110
  858. package/scripts/check-reference-governance.mjs +0 -83
  859. package/scripts/check-reply-contract.sh +0 -64
  860. package/scripts/check-rule-uniqueness.sh +0 -45
  861. package/scripts/devrites-detect.sh +0 -176
  862. package/scripts/grade-feature.sh +0 -204
  863. package/scripts/live-hosts/agent-result.schema.json +0 -231
  864. package/scripts/live-hosts/claude.sh +0 -87
  865. package/scripts/live-hosts/codex.sh +0 -85
  866. package/scripts/live-hosts/common.sh +0 -113
  867. package/scripts/live-hosts/fake-host.py +0 -257
  868. package/scripts/live-hosts/host-transport.py +0 -286
  869. package/scripts/npm-audit-exceptions.json +0 -26
  870. package/scripts/pin.sh +0 -249
  871. package/scripts/release-check.sh +0 -75
  872. package/scripts/run-agent-contract-evals.py +0 -1380
  873. package/scripts/run-behavioral-evals.sh +0 -196
  874. package/scripts/run-evals.sh +0 -132
  875. package/scripts/run-live-behavioral-evals.py +0 -1310
  876. package/scripts/run-outcome-evals.sh +0 -454
  877. package/scripts/run-routing-evals.py +0 -342
  878. package/scripts/run-tests.mjs +0 -232
  879. package/scripts/scan-pack-security.py +0 -209
  880. package/scripts/scan-supply-chain-iocs.py +0 -127
  881. package/scripts/skill-pruning-audit.mjs +0 -121
  882. package/scripts/skills-inventory.mjs +0 -125
  883. package/scripts/supply-chain-iocs.json +0 -11
  884. package/scripts/sync-version.sh +0 -54
  885. package/scripts/validate-agent-composition.py +0 -57
  886. package/scripts/validate-command-parity.py +0 -70
  887. package/scripts/validate-frontmatter.py +0 -218
  888. package/scripts/validate-skill-anatomy.py +0 -139
  889. package/scripts/validate-workflow-security.py +0 -184
  890. package/scripts/validate-workspace-schema.py +0 -800
  891. package/scripts/validate.sh +0 -354
  892. package/scripts/workflow_schema.py +0 -69
  893. /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/auth-tokens/decisions.md +0 -0
  894. /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/auth-tokens/plan.md +0 -0
  895. /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/auth-tokens/spec.md +0 -0
  896. /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/auth-tokens/tasks.md +0 -0
  897. /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/search-ranking/spec.md +0 -0
@@ -1,1196 +0,0 @@
1
- package main
2
-
3
- // Workspace hooks read state.md, questions.md, and other runtime files from the
4
- // active feature directory. work is canonical, while features remains readable
5
- // for compatibility. These hooks are read only and stay silent outside a
6
- // DevRites workspace.
7
-
8
- import (
9
- "bytes"
10
- "encoding/json"
11
- "fmt"
12
- "io"
13
- "os"
14
- "os/exec"
15
- "path/filepath"
16
- "regexp"
17
- "strconv"
18
- "strings"
19
- "time"
20
-
21
- "github.com/devrites/devrites/internal/forge"
22
- "github.com/devrites/devrites/internal/harness"
23
- "github.com/devrites/devrites/internal/lib"
24
- "github.com/devrites/devrites/internal/orient"
25
- drvreason "github.com/devrites/devrites/internal/reason"
26
- "github.com/devrites/devrites/internal/rootfacts"
27
- "github.com/devrites/devrites/internal/safepath"
28
- "github.com/devrites/devrites/internal/state"
29
- )
30
-
31
- var (
32
- openStatusRe = regexp.MustCompile(`(?i)^[[:space:]]*status:[[:space:]]*open`)
33
- )
34
-
35
- // resolveWorkspaceDir finds the active feature directory. New workspaces use
36
- // work, while existing features workspaces remain readable.
37
- func resolveWorkspaceDir(root, slug string) string {
38
- work := filepath.Join(root, "work", slug)
39
- if wsIsDir(work) {
40
- return work
41
- }
42
- features := filepath.Join(root, "features", slug)
43
- if wsIsDir(features) {
44
- return features
45
- }
46
- return work
47
- }
48
-
49
- // resolveWorkspace resolves the active feature's directory.
50
- // ok is false when no .devrites root or active feature exists. Callers use that
51
- // result to return silently.
52
- func resolveWorkspace() (root, slug, dir string, ok bool) {
53
- root, err := orient.ResolveRoot(os.Getenv("DEVRITES_ROOT"))
54
- if err != nil {
55
- return "", "", "", false
56
- }
57
- slug, err = orient.ActiveSlug(root)
58
- if err != nil || slug == "" {
59
- return "", "", "", false
60
- }
61
- return root, slug, resolveWorkspaceDir(root, slug), true
62
- }
63
-
64
- func recordHookGuard(h harness.Harness, hook string, reasonID drvreason.ID, strength lib.GuardStrength, outcome lib.EventOutcome, evidenceFiles ...string) {
65
- root, slug, _, ok := resolveWorkspace()
66
- if !ok {
67
- return
68
- }
69
- ev := lib.NewEventV1(lib.BoundaryHookGuard, hook, reasonID)
70
- ev.GuardStrength = strength
71
- ev.Outcome = outcome
72
- ev.Host = lib.EventHost(h)
73
- if ev.Host != lib.HostClaude && ev.Host != lib.HostCodex {
74
- ev.Host = lib.HostEngine
75
- }
76
- ev.EvidencePaths = lib.WorkspaceEvidencePaths(root, slug, evidenceFiles...)
77
- if err := lib.BindEventWorkspace(root, slug, &ev); err != nil {
78
- return
79
- }
80
- _ = lib.AppendEventV1(root, ev)
81
- }
82
-
83
- // hookAUQ records one metadata-only v1 event for each AskUserQuestion item after
84
- // the tool returns. Prompts and answers remain in questions.md and are not
85
- // copied into telemetry.
86
- func hookAUQ(stdin io.Reader, stdout, stderr io.Writer) int {
87
- data, err := io.ReadAll(io.LimitReader(stdin, 1<<20))
88
- if err != nil {
89
- return exitOK
90
- }
91
- var payload struct {
92
- ToolInput struct {
93
- Questions []struct {
94
- Question string `json:"question"`
95
- } `json:"questions"`
96
- } `json:"tool_input"`
97
- }
98
- if json.Unmarshal(data, &payload) != nil || len(payload.ToolInput.Questions) == 0 {
99
- return exitOK
100
- }
101
- root, slug, _, ok := resolveWorkspace()
102
- if !ok {
103
- return exitOK
104
- }
105
- const maxObservedQuestions = 64
106
- for i, q := range payload.ToolInput.Questions {
107
- if i == maxObservedQuestions {
108
- break
109
- }
110
- if strings.TrimSpace(q.Question) == "" {
111
- continue
112
- }
113
- event := lib.NewEventV1(
114
- lib.BoundaryHookGuard,
115
- "human-wait-resumed",
116
- drvreason.HookStopUnsurfacedHumanGate,
117
- )
118
- event.GuardStrength = lib.GuardObserved
119
- event.Outcome = lib.OutcomeObserved
120
- event.Host = lib.HostClaude
121
- if err := lib.BindEventWorkspace(root, slug, &event); err != nil {
122
- continue
123
- }
124
- _ = lib.AppendEventV1(root, event)
125
- }
126
- return exitOK
127
- }
128
-
129
- // hookEvent records host lifecycle notifications as metadata-only v1 events. It
130
- // does not change workflow state.
131
- func hookEvent(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
132
- _, _ = io.Copy(io.Discard, stdin)
133
- event := ""
134
- for _, arg := range args {
135
- if strings.HasPrefix(arg, "--") {
136
- continue
137
- }
138
- event = arg
139
- break
140
- }
141
- switch event {
142
- case "session-end":
143
- event = "session-ended"
144
- case "subagent-stop":
145
- event = "agent-finished"
146
- default:
147
- return exitOK
148
- }
149
- root, slug, _, ok := resolveWorkspace()
150
- if !ok {
151
- return exitOK
152
- }
153
- observation := lib.NewEventV1(lib.BoundaryAgentDispatch, event, drvreason.RootSelected)
154
- observation.GuardStrength = lib.GuardObserved
155
- observation.Outcome = lib.OutcomeObserved
156
- observation.Host = lib.HostClaude
157
- if err := lib.BindEventWorkspace(root, slug, &observation); err != nil {
158
- return exitOK
159
- }
160
- _ = lib.AppendEventV1(root, observation)
161
- return exitOK
162
- }
163
-
164
- // hookHandoffSnapshot appends a short resume note before context compaction. It
165
- // does not rewrite canonical workspace artifacts.
166
- func hookHandoffSnapshot(stdin io.Reader, stdout, stderr io.Writer) int {
167
- _, _ = io.Copy(io.Discard, stdin)
168
- _, slug, dir, ok := resolveWorkspace()
169
- if !ok {
170
- return exitOK
171
- }
172
- stateLines := wsReadLines(filepath.Join(dir, "state.md"))
173
- phase, _ := state.CursorField(stateLines, state.CursorPhase)
174
- status, _ := state.CursorField(stateLines, state.CursorStatus)
175
- next, _ := state.CursorField(stateLines, state.CursorNextAction)
176
- stamp := time.Now().UTC().Format(time.RFC3339)
177
- var b strings.Builder
178
- fmt.Fprintf(&b, "\n## Handoff snapshot: %s\n", stamp)
179
- fmt.Fprintf(&b, "- Feature: %s\n", slug)
180
- if phase != "" {
181
- fmt.Fprintf(&b, "- Phase: %s\n", phase)
182
- }
183
- if status != "" {
184
- fmt.Fprintf(&b, "- Status: %s\n", status)
185
- }
186
- if next != "" {
187
- fmt.Fprintf(&b, "- Next: %s\n", next)
188
- }
189
- fmt.Fprintf(&b, "- Open questions: %s\n", wsOrZero(wsGateCount(filepath.Join(dir, "questions.md"))))
190
- _ = state.AppendLog(filepath.Join(dir, "handoff.md"), strings.TrimRight(b.String(), "\n"))
191
- fmt.Fprintf(stdout, "DevRites: compaction handoff saved for %s; resume with the status command or read handoff.md.\n", slug)
192
- return exitOK
193
- }
194
-
195
- // hookCursor adds the active feature cursor to each UserPromptSubmit event so the
196
- // next action appears near the end of the current context. It keeps the plain
197
- // text output and silent nonproject behavior of devrites-cursor.sh.
198
- func hookCursor(stdin io.Reader, stdout, stderr io.Writer) int {
199
- _, _ = io.Copy(io.Discard, stdin)
200
- root, slug, dir, ok := resolveWorkspace()
201
- if !ok || !wsIsDir(dir) {
202
- return exitOK
203
- }
204
- stateLines := wsReadLines(filepath.Join(dir, "state.md"))
205
-
206
- next, _ := state.CursorField(stateLines, state.CursorNextAction)
207
- status, _ := state.CursorField(stateLines, state.CursorStatus)
208
- gates := wsGateCount(filepath.Join(dir, "questions.md"))
209
- afk := ""
210
- if wsIsFile(filepath.Join(root, "AFK")) {
211
- afk, _ = state.CursorField(stateLines, state.CursorAFKSlicesRemaining)
212
- }
213
-
214
- fmt.Fprintf(stdout, "DevRites cursor: active feature: %s\n", slug)
215
- if status != "" {
216
- fmt.Fprintf(stdout, " status: %s\n", status)
217
- }
218
- if next != "" {
219
- fmt.Fprintf(stdout, " next: %s\n", next)
220
- }
221
- fmt.Fprintf(stdout, " open questions: %s\n", wsOrZero(gates))
222
- if afk != "" {
223
- fmt.Fprintf(stdout, " AFK slices remaining: %s\n", afk)
224
- }
225
- if wsIsFile(filepath.Join(dir, ".red")) {
226
- fmt.Fprintln(stdout, " ⚠ tests/build RED: resolve before stopping")
227
- }
228
- return exitOK
229
- }
230
-
231
- // hookStatusline prints the one-line workspace status configured by settings.json.
232
- // It reads and ignores the session JSON on stdin, matching
233
- // devrites-statusline.sh, and stays silent without an active workspace.
234
- func hookStatusline(stdin io.Reader, stdout, stderr io.Writer) int {
235
- _, _ = io.Copy(io.Discard, stdin)
236
- root, slug, dir, ok := resolveWorkspace()
237
- if !ok {
238
- return exitOK
239
- }
240
- phase, _ := state.CursorField(wsReadLines(filepath.Join(dir, "state.md")), state.CursorPhase)
241
- if phase == "" {
242
- phase = "?"
243
- }
244
- gates := wsOrZero(wsGateCount(filepath.Join(dir, "questions.md")))
245
- mode := "HITL"
246
- if wsIsFile(filepath.Join(root, "AFK")) {
247
- mode = "AFK"
248
- }
249
- red := ""
250
- if wsIsFile(filepath.Join(dir, ".red")) {
251
- red = " · RED"
252
- }
253
- obs := lib.ObservabilityStatus(root, slug)
254
- if obs != "" {
255
- obs = " · " + obs
256
- }
257
- fmt.Fprintf(stdout, "DevRites: %s · %s · gates:%s · %s%s%s\n", slug, phase, gates, mode, red, obs)
258
- return exitOK
259
- }
260
-
261
- // redwatch detection patterns match devrites-redwatch.sh. They are case
262
- // insensitive, and FAIL/PASS scans are line oriented.
263
- // pack-scan-ignore: these are the hook's own red/green heuristics, not a payload.
264
- var (
265
- redTestCmdRe = regexp.MustCompile(`(?i)(npm|pnpm|yarn|bun)([[:space:]]+run)?[[:space:]]+(test|build|lint|typecheck|check)|jest|vitest|pytest|go[[:space:]]+test|cargo[[:space:]]+(test|build|clippy)|\bmvn\b|gradle|eslint|ruff|mypy|\btsc\b|\bmake[[:space:]]+(test|build|check)`)
266
- redFailRe = regexp.MustCompile(`(?im)(^|[^A-Za-z])FAIL([^A-Za-z]|$)|[1-9][0-9]*[[:space:]]+(failed|failing|failures?|errors?)|not ok|panic:|error TS[0-9]|Traceback|AssertionError|✗|✘|BUILD FAILED|exit code [1-9]`)
267
- redPassRe = regexp.MustCompile(`(?im)PASS(ED)?|0[[:space:]]+(failed|failing|errors?)|all tests passed|BUILD SUCC(ESS|EEDED)|succeeded|no (errors|problems|issues)|(^|[^a-z])ok([^a-z]|$)|✓`)
268
- reconcileRootRecoveryRe = regexp.MustCompile(`^\s*(?:rtk\s+)?(?:[A-Za-z0-9_./-]+/)?devrites-engine\s+reconcile\s+(?:restore-check|abort)(?:\s+[A-Za-z0-9._-]+)?\s*$`)
269
- reconcileRootRecoveryMentionRe = regexp.MustCompile(`\breconcile\s+(?:restore-check|abort)\b`)
270
- )
271
-
272
- const redReasonFmt = "DevRites: tests/build are RED (%s). Fix to green or record the failure + next step in state.md before stopping: the Stop gate blocks an end-of-turn while .red is set."
273
-
274
- // hookRedwatch handles PostToolUse shell events for test, build, lint, and type
275
- // checking commands. It sets or clears <featureDir>/.red so the Stop gate can
276
- // catch a known failing suite. Its command and output checks match
277
- // devrites-redwatch.sh, and uncertain input leaves state unchanged.
278
- func hookRedwatch(h harness.Harness, stdin io.Reader, stdout, stderr io.Writer) int {
279
- data, err := io.ReadAll(stdin)
280
- if err != nil {
281
- return exitOK
282
- }
283
- if !bytes.Contains(data, []byte(`"tool_name"`)) {
284
- return exitOK
285
- }
286
- _, _, dir, ok := resolveWorkspace()
287
- if !ok || !wsIsDir(dir) {
288
- return exitOK
289
- }
290
- in := h.ParseGuardInput(bytes.NewReader(data))
291
- if !isShellTool(in.ToolName) {
292
- return exitOK
293
- }
294
- // Ignore commands unrelated to tests, builds, linting, or type checking.
295
- if !redTestCmdRe.MatchString(in.Command) {
296
- return exitOK
297
- }
298
-
299
- red := filepath.Join(dir, ".red")
300
- combo := in.Command + "\n" + in.ToolResponse
301
- if redFailRe.MatchString(combo) {
302
- _ = os.WriteFile(red, []byte(in.Command+"\n"), 0o644)
303
- out, err := h.PostToolContext(fmt.Sprintf(redReasonFmt, redwatchSafe(in.Command)))
304
- if err != nil {
305
- debugf(stderr, "redwatch: %v", err)
306
- return exitOK
307
- }
308
- fmt.Fprintln(stdout, out)
309
- return exitOK
310
- }
311
- if redPassRe.MatchString(combo) {
312
- _ = os.Remove(red)
313
- }
314
- return exitOK
315
- }
316
-
317
- // redwatchSafe truncates a command to 80 bytes and drops " and \, matching
318
- // redwatch.sh's `head -c 80 | tr -d '"\\'`.
319
- func redwatchSafe(cmd string) string {
320
- if len(cmd) > 80 {
321
- cmd = cmd[:80]
322
- }
323
- return strings.NewReplacer(`"`, "", `\`, "").Replace(cmd)
324
- }
325
-
326
- // patchPathRe captures each file path an apply_patch command touches, matching the
327
- // node regex the shell guards use.
328
- var patchPathRe = regexp.MustCompile(`(?m)^\*\*\* (?:(?:Add|Update|Delete) File|Move to): (.+)$`)
329
-
330
- var shellHeredocStartRe = regexp.MustCompile(`<<(-?)[ \t]*(?:'([A-Za-z_][A-Za-z0-9_]*)'|"([A-Za-z_][A-Za-z0-9_]*)"|([A-Za-z_][A-Za-z0-9_]*))`)
331
-
332
- // isEditTool reports whether a tool can write source. toolBaseName also handles
333
- // host-qualified names such as functions.apply_patch and MCP symbol editors.
334
- func isEditTool(tool string) bool {
335
- switch toolBaseName(tool) {
336
- case "edit", "write", "multiedit", "multi_edit", "notebookedit", "notebook_edit",
337
- "apply_patch", "applypatch", "edit_file", "write_file", "create_file", "delete_file",
338
- "replace_symbol_body", "insert_before_symbol", "insert_after_symbol", "safe_delete_symbol":
339
- return true
340
- }
341
- return false
342
- }
343
-
344
- // patchPaths returns the file paths an apply_patch command touches.
345
- func patchPaths(command string) []string {
346
- var paths []string
347
- for _, m := range patchPathRe.FindAllStringSubmatch(command, -1) {
348
- paths = append(paths, strings.TrimSpace(m[1]))
349
- }
350
- return paths
351
- }
352
-
353
- func stripShellHeredocBodies(command string) (string, bool) {
354
- lines := strings.Split(strings.ReplaceAll(command, "\r\n", "\n"), "\n")
355
- out := make([]string, 0, len(lines))
356
- for i := 0; i < len(lines); i++ {
357
- line := lines[i]
358
- out = append(out, line)
359
- for _, match := range shellHeredocStartRe.FindAllStringSubmatch(line, -1) {
360
- delimiter := match[2]
361
- if delimiter == "" {
362
- delimiter = match[3]
363
- }
364
- if delimiter == "" {
365
- return "", false
366
- }
367
- found := false
368
- for i++; i < len(lines); i++ {
369
- candidate := lines[i]
370
- if match[1] == "-" {
371
- candidate = strings.TrimLeft(candidate, "\t")
372
- }
373
- if candidate == delimiter {
374
- found = true
375
- break
376
- }
377
- }
378
- if !found {
379
- return "", false
380
- }
381
- }
382
- }
383
- return strings.Join(out, "\n"), true
384
- }
385
-
386
- // shellOutputRedirections removes harmless fd redirects and returns every file
387
- // target of >, >>, or >|. Dynamic targets are opaque and therefore unsafe.
388
- func shellOutputRedirections(command string) (sanitized string, paths []string, opaque bool) {
389
- buf := []byte(command)
390
- sanitizedBytes := append([]byte(nil), buf...)
391
- var quote byte
392
- for i := 0; i < len(buf); i++ {
393
- switch {
394
- case quote != 0:
395
- if buf[i] == '\\' && quote == '"' {
396
- i++
397
- } else if buf[i] == quote {
398
- quote = 0
399
- }
400
- continue
401
- case buf[i] == '\'' || buf[i] == '"':
402
- quote = buf[i]
403
- continue
404
- case buf[i] != '>':
405
- continue
406
- }
407
-
408
- start := i
409
- for start > 0 && buf[start-1] >= '0' && buf[start-1] <= '9' {
410
- start--
411
- }
412
- i++
413
- if i < len(buf) && (buf[i] == '>' || buf[i] == '|') {
414
- i++
415
- }
416
- for i < len(buf) && (buf[i] == ' ' || buf[i] == '\t') {
417
- i++
418
- }
419
- if i < len(buf) && buf[i] == '&' {
420
- i++
421
- for i < len(buf) && buf[i] >= '0' && buf[i] <= '9' {
422
- i++
423
- }
424
- for j := start; j < i; j++ {
425
- sanitizedBytes[j] = ' '
426
- }
427
- i--
428
- continue
429
- }
430
- if i >= len(buf) || buf[i] == '(' {
431
- return "", paths, true
432
- }
433
-
434
- wordStart := i
435
- var wordQuote byte
436
- for i < len(buf) {
437
- c := buf[i]
438
- if wordQuote != 0 {
439
- if c == '\\' && wordQuote == '"' {
440
- i += 2
441
- continue
442
- }
443
- if c == wordQuote {
444
- wordQuote = 0
445
- }
446
- i++
447
- continue
448
- }
449
- if c == '\'' || c == '"' {
450
- wordQuote = c
451
- i++
452
- continue
453
- }
454
- if c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == ';' || c == '|' || c == '&' {
455
- break
456
- }
457
- i++
458
- }
459
- if wordQuote != 0 {
460
- return "", paths, true
461
- }
462
- target := strings.Trim(string(buf[wordStart:i]), `"'`)
463
- if target == "" || strings.ContainsAny(target, "$`*?[]{}()") {
464
- return "", paths, true
465
- }
466
- if target != "/dev/null" {
467
- paths = append(paths, target)
468
- }
469
- for j := start; j < i; j++ {
470
- sanitizedBytes[j] = ' '
471
- }
472
- i--
473
- }
474
- return string(sanitizedBytes), paths, false
475
- }
476
-
477
- // underDevrites reports whether an absolute path is inside the .devrites root.
478
- func underDevrites(abs, root string) bool {
479
- return pathWithin(abs, root)
480
- }
481
-
482
- func pathWithin(path, parent string) bool {
483
- path = filepath.Clean(path)
484
- parent = filepath.Clean(parent)
485
- return path == parent || strings.HasPrefix(path, parent+string(filepath.Separator))
486
- }
487
-
488
- const a1DenyReason = "DevRites A1: the orchestrator must not edit source mid-build: the slice-wright is the only writer. Re-dispatch the wright (continue it once) or stop + escalate; do not patch the code yourself. (devrites-a1-guard)"
489
-
490
- // hookA1Guard prevents /rite-build from editing source while slice-wright owns an
491
- // open build window. It matches devrites-a1-guard.sh by logging observations to
492
- // .a1-guard.log unless enforcement is enabled.
493
- func hookA1Guard(h harness.Harness, stdin io.Reader, stdout, stderr io.Writer) int {
494
- data, err := io.ReadAll(stdin)
495
- if err != nil {
496
- return exitOK
497
- }
498
- if !bytes.Contains(data, []byte(`"tool_name"`)) {
499
- return exitOK
500
- }
501
- root, _, dir, ok := resolveWorkspace()
502
- if !ok {
503
- return exitOK
504
- }
505
- // The guard is active between a reconcile snapshot and its check.
506
- if !wsIsFile(filepath.Join(dir, ".reconcile-base")) {
507
- return exitOK
508
- }
509
-
510
- in := h.ParseGuardInput(bytes.NewReader(data))
511
- // The wright may edit. Claude sends agent_id; Codex sends agent_type.
512
- if in.AgentID != "" || in.AgentType != "" {
513
- return exitOK
514
- }
515
- handled, violation, target := classifyRootBuildWindowOperation(root, data, in)
516
- if !handled || !violation {
517
- return exitOK
518
- }
519
-
520
- mode := os.Getenv("DEVRITES_A1_HOOK")
521
- if wsIsFile(filepath.Join(dir, ".a1-enforce")) {
522
- mode = "enforce"
523
- }
524
- // The sentinel file forces enforce; hookEnforce also honors the strict profile.
525
- if mode == "enforce" || hookEnforce("DEVRITES_A1_HOOK") {
526
- out, err := h.PreToolDeny(a1DenyReason)
527
- if err != nil {
528
- debugf(stderr, "a1-guard: %v", err)
529
- return exitOK
530
- }
531
- fmt.Fprintln(stdout, out)
532
- recordHookGuard(h, "a1-guard", drvreason.HookA1Denied, lib.GuardEnforced, lib.OutcomeDenied, ".reconcile-base")
533
- return exitOK
534
- }
535
- _ = state.AppendLog(filepath.Join(dir, ".a1-guard.log"), "WOULD-BLOCK\t"+in.ToolName+"\t"+target)
536
- recordHookGuard(h, "a1-guard", drvreason.HookA1Observed, lib.GuardObserved, lib.OutcomeObserved, ".reconcile-base")
537
- return exitOK
538
- }
539
-
540
- const (
541
- wrightDenyReason = "DevRites scope: this write is not in the orchestrator-provided exact wright allowlist, or it targets .devrites. Build only the slice contract; return the blocked path to the orchestrator instead of widening scope. (devrites-wright-scope)"
542
- wrightForbiddenReason = "DevRites scope: slice-wrights cannot dispatch nested agents, commit/push, install dependencies, run live migrations/deployments, or execute an uninspectable write path. (devrites-wright-scope)"
543
- forgeWrightDenyReason = "DevRites Forge scope: this candidate does not match its root-owned manifest, physical worktree, Git identity, branch, worker identity, or live process token. Return to the orchestrator; never widen or guess Forge ownership. (devrites-forge-wright-scope)"
544
- wrightAllowlistEnvName = "DEVRITES_WRIGHT_ALLOWLIST_FILE"
545
- forgeRunEnvName = "DEVRITES_FORGE_RUN_ID"
546
- forgeCandidateEnvName = "DEVRITES_FORGE_CANDIDATE"
547
- forgeWorkerEnvName = "DEVRITES_FORGE_WORKER_ID"
548
- forgePIDEnvName = "DEVRITES_FORGE_WORKER_PID"
549
- forgeProcessEnvName = "DEVRITES_FORGE_PROCESS_START"
550
- )
551
-
552
- var wrightForbiddenShellRe = regexp.MustCompile(`(?im)\bgit[[:space:]]+(add|commit|push|reset|checkout|restore|clean|rm|mv|stash|tag|apply|merge|rebase|cherry-pick|am)\b|\b(npm|pnpm|yarn)[[:space:]]+(install|i|ci|add|publish)\b|\b(pip|pip3)[[:space:]]+install\b|\b(poetry|uv)[[:space:]]+(add|install)\b|\b(bundle|composer)[[:space:]]+(install|update)\b|\bgo[[:space:]]+(get|install|generate)\b|\bcargo[[:space:]]+(add|install|publish)\b|\b(devrites-engine[[:space:]]+migrate|alembic[[:space:]]+(upgrade|downgrade)|prisma[[:space:]]+migrate|flyway[[:space:]]+migrate|liquibase[[:space:]]+update|manage\.py[[:space:]]+migrate|(rails|rake)[[:space:]]+db:(migrate|rollback|seed)|sequelize[[:space:]]+db:migrate|knex[[:space:]]+migrate:latest)\b|\b(terraform[[:space:]]+(apply|destroy)|kubectl[[:space:]]+(apply|create|delete|patch|replace)|helm[[:space:]]+(install|upgrade|uninstall)|docker[[:space:]]+push)\b|\b(curl|wget|scp|ssh|nc|socat|telnet)\b|\b(codex|claude)[[:space:]]+(exec|agent)\b`)
553
-
554
- var opaqueInterpreterRe = regexp.MustCompile(`(?im)(^|[;&|[:space:]])(python[0-9.]*[[:space:]]+(-c|-)|node[[:space:]]+(-e|--)|ruby[[:space:]]+-e|perl[[:space:]]+(-e|-p|-i)|[.][/][^[:space:]]+\.(sh|py|js|rb))([;&|[:space:]]|$)`)
555
-
556
- // hookWrightScope restricts the write-capable leaf to the orchestrator's exact
557
- // path allowlist. It always enforces active DevRites runs.
558
- func hookWrightScope(h harness.Harness, stdin io.Reader, stdout, stderr io.Writer) int {
559
- data, err := io.ReadAll(stdin)
560
- if err != nil {
561
- return exitOK
562
- }
563
- if !bytes.Contains(data, []byte(`"tool_name"`)) {
564
- return exitOK
565
- }
566
- in := h.ParseGuardInput(bytes.NewReader(data))
567
- binding := devritesAgentBindingForGuard(h, in)
568
- kind := binding.Kind
569
- genericCompat := os.Getenv("DEVRITES_CODEX_GENERIC_AGENT_COMPAT") == "1"
570
- if in.AgentType == "" && in.AgentID == "" {
571
- if code, handled := guardRootBuildWindow(h, data, in, stdout, stderr); handled {
572
- return code
573
- }
574
- }
575
- if kind == devritesAgentNone &&
576
- genericCompat &&
577
- in.AgentType == "" {
578
- return exitOK
579
- }
580
- forgeDeclared := forgeWrightDeclared()
581
- genericWright := kind == devritesAgentGenericWright
582
- active := kind != devritesAgentNone && (!genericWright || forgeDeclared)
583
- if forgeDeclared && kind != devritesAgentWright && !genericWright {
584
- return denyWright(h, stdout, stderr, forgeWrightDenyReason, drvreason.HookForgeBindingDenied)
585
- }
586
- if kind == devritesAgentNone && (in.AgentType != "" || os.Getenv("DEVRITES_WRIGHT_AGENT_REQUIRED") == "1") {
587
- return exitOK
588
- }
589
- if kind == devritesAgentReadonly {
590
- return exitOK // reviewer-readonly owns every non-wright leaf
591
- }
592
- if kind == devritesAgentInvalid {
593
- if !isEditTool(in.ToolName) && !isShellTool(in.ToolName) &&
594
- !isOpaqueExecutionTool(in.ToolName) && !isAgentDispatchTool(in.ToolName) {
595
- return exitOK
596
- }
597
- return denyWright(h, stdout, stderr, reviewerReadonlyDenyReason, drvreason.HookReviewerReadonlyDenied)
598
- }
599
-
600
- var targets []string
601
- forbidden := isAgentDispatchTool(in.ToolName) || isOpaqueExecutionTool(in.ToolName)
602
- switch {
603
- case forbidden:
604
- case isEditTool(in.ToolName):
605
- targets = mutationToolPaths(data, in)
606
- if len(targets) == 0 {
607
- forbidden = true
608
- }
609
- case isShellTool(in.ToolName):
610
- targets, forbidden = wrightShellWritePaths(in.Command)
611
- if !forbidden && len(targets) == 0 {
612
- return exitOK
613
- }
614
- default:
615
- return exitOK
616
- }
617
-
618
- root, slug, dir, ok := resolveWorkspace()
619
- if !ok {
620
- if kind == devritesAgentWright {
621
- return denyWright(h, stdout, stderr, wrightForbiddenReason, drvreason.HookWrightForbiddenDenied)
622
- }
623
- return exitOK
624
- }
625
- if genericWright && !forgeDeclared {
626
- if binding.Role != "devrites-slice-wright" {
627
- if wsIsFile(filepath.Join(dir, ".reconcile-base")) {
628
- return denyOrObserveWright(
629
- h,
630
- stdout,
631
- stderr,
632
- dir,
633
- true,
634
- wrightForbiddenReason,
635
- drvreason.HookWrightForbiddenDenied,
636
- []string{"unbound generic worker"},
637
- )
638
- }
639
- return exitOK
640
- }
641
- if !wsIsFile(filepath.Join(dir, ".reconcile-base")) {
642
- return exitOK
643
- }
644
- active = true
645
- }
646
-
647
- if wrightForbiddenShellRe.MatchString(in.Command) ||
648
- (opaqueInterpreterRe.MatchString(in.Command) && !safeReadonlyShellCommand(in.Command)) {
649
- forbidden = true
650
- }
651
- if forbidden {
652
- return denyOrObserveWright(h, stdout, stderr, dir, active, wrightForbiddenReason, drvreason.HookWrightForbiddenDenied, []string{"opaque/forbidden operation"})
653
- }
654
-
655
- projectDir := filepath.Dir(root)
656
- if !forgeDeclared && (kind == devritesAgentWright || genericWright) && insideForgeStaging(projectDir) {
657
- return denyOrObserveWright(h, stdout, stderr, dir, true, forgeWrightDenyReason, drvreason.HookForgeBindingDenied, []string{"missing Forge binding"})
658
- }
659
- if forgeDeclared {
660
- projectDir, err = forgeWrightProjectDir(projectDir, slug, in)
661
- if err != nil {
662
- debugf(stderr, "wright-scope Forge binding: %v", err)
663
- return denyOrObserveWright(h, stdout, stderr, dir, true, forgeWrightDenyReason, drvreason.HookForgeBindingDenied, []string{"invalid Forge binding"})
664
- }
665
- }
666
- allow, err := readWrightAllowlist(projectDir, root, dir)
667
- if err != nil {
668
- debugf(stderr, "wright-scope allowlist: %v", err)
669
- return denyOrObserveWright(h, stdout, stderr, dir, active, wrightDenyReason, drvreason.HookWrightScopeDenied, []string{"invalid/missing allowlist"})
670
- }
671
- var bad []string
672
- for _, target := range targets {
673
- rel, err := normalizeProjectPath(projectDir, root, target, false)
674
- if err != nil {
675
- bad = append(bad, target)
676
- continue
677
- }
678
- if _, ok := allow[rel]; !ok {
679
- bad = append(bad, target)
680
- }
681
- }
682
- if len(bad) == 0 {
683
- return exitOK
684
- }
685
- return denyOrObserveWright(h, stdout, stderr, dir, active, wrightDenyReason, drvreason.HookWrightScopeDenied, bad)
686
- }
687
-
688
- func forgeWrightDeclared() bool {
689
- for _, name := range []string{
690
- forgeRunEnvName,
691
- forgeCandidateEnvName,
692
- forgeWorkerEnvName,
693
- forgePIDEnvName,
694
- forgeProcessEnvName,
695
- } {
696
- if os.Getenv(name) != "" {
697
- return true
698
- }
699
- }
700
- return false
701
- }
702
-
703
- func insideForgeStaging(primaryProject string) bool {
704
- cwd, err := physicalWorkingDir()
705
- if err != nil {
706
- return false
707
- }
708
- primary, err := filepath.Abs(primaryProject)
709
- if err != nil {
710
- return false
711
- }
712
- primary, err = filepath.EvalSymlinks(primary)
713
- if err != nil {
714
- return false
715
- }
716
- staging := filepath.Join(filepath.Dir(primary), "."+filepath.Base(primary)+".devrites-forge")
717
- rel, err := filepath.Rel(staging, cwd)
718
- return err == nil && rel != ".." && !strings.HasPrefix(rel, ".."+string(filepath.Separator))
719
- }
720
-
721
- func forgeWrightProjectDir(primaryProject, activeSlug string, in harness.GuardInput) (string, error) {
722
- values := map[string]string{
723
- forgeRunEnvName: os.Getenv(forgeRunEnvName),
724
- forgeCandidateEnvName: os.Getenv(forgeCandidateEnvName),
725
- forgeWorkerEnvName: os.Getenv(forgeWorkerEnvName),
726
- forgePIDEnvName: os.Getenv(forgePIDEnvName),
727
- forgeProcessEnvName: os.Getenv(forgeProcessEnvName),
728
- }
729
- for name, value := range values {
730
- if value == "" || strings.TrimSpace(value) != value {
731
- return "", fmt.Errorf("%s must be present and exact", name)
732
- }
733
- }
734
-
735
- manifest, _, err := forge.Load(primaryProject, values[forgeRunEnvName])
736
- if err != nil {
737
- return "", err
738
- }
739
- if manifest.FeatureSlug != activeSlug {
740
- return "", fmt.Errorf("forge run belongs to feature %q, not active feature %q", manifest.FeatureSlug, activeSlug)
741
- }
742
- candidate, err := manifest.Candidate(forge.CandidateID(values[forgeCandidateEnvName]))
743
- if err != nil {
744
- return "", err
745
- }
746
- if candidate.State != forge.StateRunning {
747
- return "", fmt.Errorf("forge candidate %s is %s, not running", candidate.ID, candidate.State)
748
- }
749
- pid, err := strconv.Atoi(values[forgePIDEnvName])
750
- if err != nil || strconv.Itoa(pid) != values[forgePIDEnvName] {
751
- return "", fmt.Errorf("%s must be a canonical positive PID", forgePIDEnvName)
752
- }
753
- if candidate.Worker.ID != values[forgeWorkerEnvName] ||
754
- candidate.Worker.PID != pid ||
755
- candidate.Worker.ProcessStart != values[forgeProcessEnvName] {
756
- return "", fmt.Errorf("forge worker identity does not match the manifest")
757
- }
758
- if in.AgentID != "" && in.AgentID != candidate.Worker.ID {
759
- return "", fmt.Errorf("hook agent identity does not match Forge worker %q", candidate.Worker.ID)
760
- }
761
- liveToken, err := forge.ProcessStartToken(candidate.Worker.PID)
762
- if err != nil || liveToken != candidate.Worker.ProcessStart {
763
- return "", fmt.Errorf("forge worker liveness token is no longer valid")
764
- }
765
-
766
- cwd, err := physicalWorkingDir()
767
- if err != nil {
768
- return "", err
769
- }
770
- if filepath.Clean(cwd) != filepath.Clean(candidate.Worktree) {
771
- return "", fmt.Errorf("physical cwd %q is not candidate %s worktree %q", cwd, candidate.ID, candidate.Worktree)
772
- }
773
- facts, _ := rootfacts.ResolveFrom(cwd, "")
774
- if facts.Git.TopLevel != candidate.Worktree || facts.Git.CommonDir != manifest.GitCommonDir {
775
- return "", fmt.Errorf("forge candidate Git identity does not match the manifest")
776
- }
777
- cmd := exec.Command("git", "-C", cwd, "symbolic-ref", "--quiet", "--short", "HEAD")
778
- cmd.Env = append(os.Environ(), "GIT_TERMINAL_PROMPT=0", "LC_ALL=C")
779
- branch, err := cmd.Output()
780
- if err != nil || strings.TrimSpace(string(branch)) != candidate.Branch {
781
- return "", fmt.Errorf("forge candidate branch does not match the manifest")
782
- }
783
- return candidate.Worktree, nil
784
- }
785
-
786
- func physicalWorkingDir() (string, error) {
787
- cwd, err := os.Getwd()
788
- if err != nil {
789
- return "", err
790
- }
791
- cwd, err = filepath.Abs(cwd)
792
- if err != nil {
793
- return "", err
794
- }
795
- return filepath.EvalSymlinks(cwd)
796
- }
797
-
798
- func mutationToolPaths(data []byte, in harness.GuardInput) []string {
799
- var envelope struct {
800
- ToolInput any `json:"tool_input"`
801
- }
802
- if json.Unmarshal(data, &envelope) != nil {
803
- return nil
804
- }
805
- var paths []string
806
- if toolBaseName(in.ToolName) == "apply_patch" || toolBaseName(in.ToolName) == "applypatch" {
807
- collectPatchMutationPaths(envelope.ToolInput, &paths)
808
- paths = append(paths, patchPaths(in.Command)...)
809
- return uniqueStrings(paths)
810
- }
811
- collectMutationPaths(envelope.ToolInput, &paths)
812
- if len(paths) == 0 && in.FilePath != "" {
813
- paths = append(paths, in.FilePath)
814
- }
815
- return uniqueStrings(paths)
816
- }
817
-
818
- func collectPatchMutationPaths(value any, paths *[]string) {
819
- switch value := value.(type) {
820
- case string:
821
- *paths = append(*paths, patchPaths(value)...)
822
- case map[string]any:
823
- for _, child := range value {
824
- collectPatchMutationPaths(child, paths)
825
- }
826
- case []any:
827
- for _, child := range value {
828
- collectPatchMutationPaths(child, paths)
829
- }
830
- }
831
- }
832
-
833
- func collectMutationPaths(value any, paths *[]string) {
834
- switch value := value.(type) {
835
- case map[string]any:
836
- for key, child := range value {
837
- normalizedKey := strings.ReplaceAll(strings.ToLower(key), "-", "_")
838
- switch normalizedKey {
839
- case "file_path", "filepath", "path", "relative_path", "notebook_path", "new_path", "target_path":
840
- if path, ok := child.(string); ok && strings.TrimSpace(path) != "" {
841
- *paths = append(*paths, strings.TrimSpace(path))
842
- continue
843
- }
844
- }
845
- collectMutationPaths(child, paths)
846
- }
847
- case []any:
848
- for _, child := range value {
849
- collectMutationPaths(child, paths)
850
- }
851
- }
852
- }
853
-
854
- func uniqueStrings(values []string) []string {
855
- seen := make(map[string]bool, len(values))
856
- out := make([]string, 0, len(values))
857
- for _, value := range values {
858
- if !seen[value] {
859
- seen[value] = true
860
- out = append(out, value)
861
- }
862
- }
863
- return out
864
- }
865
-
866
- func wrightShellWritePaths(command string) ([]string, bool) {
867
- patches := patchPaths(command)
868
- var ok bool
869
- command, ok = stripShellHeredocBodies(command)
870
- if !ok || strings.TrimSpace(command) == "" || strings.Contains(command, "$(") ||
871
- strings.ContainsRune(command, '`') || wrightForbiddenShellRe.MatchString(command) {
872
- return nil, true
873
- }
874
- sanitized, paths, opaque := shellOutputRedirections(command)
875
- if opaque {
876
- return nil, true
877
- }
878
-
879
- if strings.Contains(command, "apply_patch") {
880
- if len(patches) == 0 {
881
- return nil, true
882
- }
883
- paths = append(paths, patches...)
884
- sanitized = strings.ReplaceAll(sanitized, "apply_patch", "true")
885
- }
886
-
887
- for _, segment := range shellSegmentRe.Split(sanitized, -1) {
888
- if safeReadonlyShellSegment(segment) {
889
- continue
890
- }
891
- fields := strings.Fields(strings.TrimSpace(segment))
892
- if len(fields) == 0 {
893
- continue
894
- }
895
- for len(fields) > 0 && filepath.Base(fields[0]) == "rtk" {
896
- fields = fields[1:]
897
- }
898
- if len(fields) == 0 {
899
- return nil, true
900
- }
901
- base := strings.ToLower(filepath.Base(strings.Trim(fields[0], `"'`)))
902
- args := fields[1:]
903
- var found []string
904
- switch base {
905
- case "gofmt", "goimports", "shfmt":
906
- if !hasAnyArg(args, "-w") {
907
- return nil, true
908
- }
909
- found = nonFlagArgs(args)
910
- case "prettier":
911
- if !hasAnyArg(args, "--write") {
912
- return nil, true
913
- }
914
- found = nonFlagArgs(args)
915
- case "ruff":
916
- if len(args) == 0 || args[0] != "format" || hasAnyArg(args, "--check") {
917
- return nil, true
918
- }
919
- found = nonFlagArgs(args[1:])
920
- case "black", "rustfmt":
921
- found = nonFlagArgs(args)
922
- case "rm", "unlink", "touch", "mkdir", "rmdir", "truncate":
923
- found = nonFlagArgs(args)
924
- case "cp":
925
- if hasAnyArg(args, "-t", "--target-directory") || hasArgPrefix(args, "--target-directory=") {
926
- return nil, true
927
- }
928
- operands := nonFlagArgs(args)
929
- if len(operands) < 2 {
930
- return nil, true
931
- }
932
- found = operands[len(operands)-1:]
933
- default:
934
- return nil, true
935
- }
936
- if len(found) == 0 {
937
- return nil, true
938
- }
939
- for _, path := range found {
940
- if strings.ContainsAny(path, "$`*?[]{}()") {
941
- return nil, true
942
- }
943
- }
944
- paths = append(paths, found...)
945
- }
946
-
947
- if len(paths) == 0 {
948
- if safeReadonlyShellCommand(command) {
949
- return nil, false
950
- }
951
- return nil, true
952
- }
953
- return uniqueStrings(paths), false
954
- }
955
-
956
- func nonFlagArgs(args []string) []string {
957
- var out []string
958
- for _, arg := range args {
959
- arg = strings.Trim(arg, `"'`)
960
- if arg == "" || strings.HasPrefix(arg, "-") {
961
- continue
962
- }
963
- out = append(out, arg)
964
- }
965
- return out
966
- }
967
-
968
- func readWrightAllowlist(projectDir, devritesRoot, workspaceDir string) (map[string]struct{}, error) {
969
- path := strings.TrimSpace(os.Getenv(wrightAllowlistEnvName))
970
- if path == "" {
971
- path = filepath.Join(workspaceDir, ".wright-allowlist")
972
- }
973
- if !filepath.IsAbs(path) {
974
- path = filepath.Join(filepath.Dir(devritesRoot), path)
975
- }
976
- path = filepath.Clean(path)
977
- if !safepath.WithinResolved(path, workspaceDir) {
978
- return nil, fmt.Errorf("allowlist file is outside the active workspace")
979
- }
980
- data, err := os.ReadFile(path)
981
- if err != nil {
982
- return nil, err
983
- }
984
- allow := make(map[string]struct{})
985
- for _, line := range strings.Split(strings.ReplaceAll(string(data), "\r\n", "\n"), "\n") {
986
- if line == "" {
987
- continue
988
- }
989
- if strings.TrimSpace(line) != line || strings.HasPrefix(line, "#") {
990
- return nil, fmt.Errorf("allowlist entry is not an exact normalized path: %q", line)
991
- }
992
- rel, err := normalizeProjectPath(projectDir, devritesRoot, line, true)
993
- if err != nil {
994
- return nil, err
995
- }
996
- if _, duplicate := allow[rel]; duplicate {
997
- return nil, fmt.Errorf("duplicate allowlist path: %q", line)
998
- }
999
- if info, statErr := os.Stat(filepath.Join(projectDir, filepath.FromSlash(rel))); statErr == nil && info.IsDir() {
1000
- return nil, fmt.Errorf("allowlist path names a directory: %q", line)
1001
- } else if statErr != nil && !os.IsNotExist(statErr) {
1002
- return nil, statErr
1003
- }
1004
- allow[rel] = struct{}{}
1005
- }
1006
- return allow, nil
1007
- }
1008
-
1009
- func normalizeProjectPath(projectDir, devritesRoot, raw string, requireNormalized bool) (string, error) {
1010
- if raw == "" || strings.ContainsRune(raw, 0) || strings.ContainsRune(raw, '\\') {
1011
- return "", fmt.Errorf("invalid project path %q", raw)
1012
- }
1013
- var abs string
1014
- if filepath.IsAbs(raw) {
1015
- if requireNormalized {
1016
- return "", fmt.Errorf("allowlist path must be project-relative: %q", raw)
1017
- }
1018
- abs = filepath.Clean(raw)
1019
- } else {
1020
- clean := filepath.Clean(filepath.FromSlash(raw))
1021
- if requireNormalized && filepath.ToSlash(clean) != raw {
1022
- return "", fmt.Errorf("allowlist path is not normalized: %q", raw)
1023
- }
1024
- abs = filepath.Join(projectDir, clean)
1025
- }
1026
- rel, err := filepath.Rel(projectDir, abs)
1027
- if err != nil || rel == "." || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
1028
- return "", fmt.Errorf("path is outside the project: %q", raw)
1029
- }
1030
- if !safepath.WithinResolved(abs, projectDir) || safepath.WithinResolved(abs, devritesRoot) {
1031
- return "", fmt.Errorf("path escapes the project or targets .devrites: %q", raw)
1032
- }
1033
- return filepath.ToSlash(rel), nil
1034
- }
1035
-
1036
- func denyOrObserveWright(h harness.Harness, stdout, stderr io.Writer, dir string, active bool, message string, reasonID drvreason.ID, bad []string) int {
1037
- if active || hookEnforce("DEVRITES_WRIGHT_SCOPE") {
1038
- return denyWright(h, stdout, stderr, message, reasonID)
1039
- }
1040
- _ = state.AppendLog(filepath.Join(dir, ".wright-scope.log"), "WOULD-BLOCK\t"+strings.Join(bad, ", "))
1041
- recordHookGuard(h, "wright-scope", drvreason.HookWrightScopeObserved, lib.GuardObserved, lib.OutcomeObserved)
1042
- return exitOK
1043
- }
1044
-
1045
- func guardRootBuildWindow(h harness.Harness, data []byte, in harness.GuardInput, stdout, stderr io.Writer) (int, bool) {
1046
- root, _, dir, ok := resolveWorkspace()
1047
- if !ok || !wsIsFile(filepath.Join(dir, ".reconcile-base")) {
1048
- return exitOK, false
1049
- }
1050
-
1051
- handled, violation, _ := classifyRootBuildWindowOperation(root, data, in)
1052
- if violation {
1053
- return denyWright(h, stdout, stderr, a1DenyReason, drvreason.HookA1Denied), true
1054
- }
1055
- return exitOK, handled
1056
- }
1057
-
1058
- func classifyRootBuildWindowOperation(root string, data []byte, in harness.GuardInput) (handled, violation bool, target string) {
1059
- if isAgentDispatchTool(in.ToolName) {
1060
- return true, false, ""
1061
- }
1062
- if isOpaqueExecutionTool(in.ToolName) {
1063
- return true, true, in.ToolName
1064
- }
1065
- if isShellTool(in.ToolName) && reconcileRootRecoveryMentionRe.MatchString(in.Command) {
1066
- return true, !reconcileRootRecoveryRe.MatchString(in.Command), in.Command
1067
- }
1068
-
1069
- projectDir := filepath.Dir(root)
1070
- targets, suspicious := rootMutationTargets(data, in)
1071
- for _, target := range targets {
1072
- absoluteTarget := filepath.IsAbs(target)
1073
- abs := target
1074
- if !absoluteTarget {
1075
- abs = filepath.Join(projectDir, abs)
1076
- }
1077
- abs = filepath.Clean(abs)
1078
- resolved, err := resolveRootMutationTarget(abs)
1079
- if err != nil {
1080
- return true, true, target
1081
- }
1082
- insideProject := pathWithin(resolved, projectDir)
1083
- if (insideProject && !underDevrites(resolved, root)) ||
1084
- (!insideProject && !absoluteTarget) {
1085
- return true, true, target
1086
- }
1087
- }
1088
- if suspicious {
1089
- return true, true, in.ToolName
1090
- }
1091
- if len(targets) > 0 {
1092
- return true, false, strings.Join(targets, ",")
1093
- }
1094
- if isShellTool(in.ToolName) && safeReadonlyShellCommand(in.Command) {
1095
- return true, false, ""
1096
- }
1097
- return false, false, ""
1098
- }
1099
-
1100
- func resolveRootMutationTarget(filename string) (string, error) {
1101
- info, err := os.Lstat(filename)
1102
- if err == nil && info.Mode()&os.ModeSymlink != 0 {
1103
- target, err := os.Readlink(filename)
1104
- if err != nil {
1105
- return "", err
1106
- }
1107
- if !filepath.IsAbs(target) {
1108
- target = filepath.Join(filepath.Dir(filename), target)
1109
- }
1110
- return safepath.ResolveExisting(target)
1111
- }
1112
- return safepath.ResolveExisting(filename)
1113
- }
1114
-
1115
- func rootMutationTargets(data []byte, in harness.GuardInput) ([]string, bool) {
1116
- switch {
1117
- case isEditTool(in.ToolName):
1118
- targets := mutationToolPaths(data, in)
1119
- return targets, len(targets) == 0
1120
- case isShellTool(in.ToolName):
1121
- targets, opaque := wrightShellWritePaths(in.Command)
1122
- if len(targets) > 0 {
1123
- return targets, false
1124
- }
1125
- _, completeHeredoc := stripShellHeredocBodies(in.Command)
1126
- return nil, opaque && (!completeHeredoc || reviewerMutateRe.MatchString(in.Command) || opaqueInterpreterRe.MatchString(in.Command))
1127
- default:
1128
- return nil, false
1129
- }
1130
- }
1131
-
1132
- func denyWright(h harness.Harness, stdout, stderr io.Writer, message string, reasonID drvreason.ID) int {
1133
- out, err := h.PreToolDeny(message)
1134
- if err != nil {
1135
- debugf(stderr, "wright-scope: %v", err)
1136
- return exitOK
1137
- }
1138
- fmt.Fprintln(stdout, out)
1139
- recordHookGuard(h, "wright-scope", reasonID, lib.GuardEnforced, lib.OutcomeDenied)
1140
- return exitOK
1141
- }
1142
-
1143
- // wsGateCount mirrors `grep -ciE '^\s*status:\s*open' questions.md || echo 0`.
1144
- // On a present file with no matches, grep prints "0" and exits 1, so the fallback
1145
- // appends another "0". The captured value is therefore "0\n0".
1146
- func wsGateCount(qPath string) string {
1147
- data, err := os.ReadFile(qPath)
1148
- if err != nil {
1149
- return "0"
1150
- }
1151
- n := 0
1152
- for _, line := range wsSplitLines(data) {
1153
- if openStatusRe.MatchString(line) {
1154
- n++
1155
- }
1156
- }
1157
- if n == 0 {
1158
- return "0\n0"
1159
- }
1160
- return strconv.Itoa(n)
1161
- }
1162
-
1163
- func wsOrZero(s string) string {
1164
- if s == "" {
1165
- return "0"
1166
- }
1167
- return s
1168
- }
1169
-
1170
- func wsReadLines(path string) []string {
1171
- data, err := os.ReadFile(path)
1172
- if err != nil {
1173
- return nil
1174
- }
1175
- return wsSplitLines(data)
1176
- }
1177
-
1178
- // wsSplitLines splits on '\n', dropping a single trailing newline's empty final
1179
- // element so a "\n"-terminated file yields N records, like grep/awk/sed.
1180
- func wsSplitLines(data []byte) []string {
1181
- s := strings.TrimSuffix(string(data), "\n")
1182
- if s == "" {
1183
- return nil
1184
- }
1185
- return strings.Split(s, "\n")
1186
- }
1187
-
1188
- func wsIsDir(p string) bool {
1189
- fi, err := os.Stat(p)
1190
- return err == nil && fi.IsDir()
1191
- }
1192
-
1193
- func wsIsFile(p string) bool {
1194
- fi, err := os.Stat(p)
1195
- return err == nil && !fi.IsDir()
1196
- }