devrites 3.0.6 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (472) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +67 -45
  3. package/SECURITY.md +31 -29
  4. package/docs/adr/0001-go-engine-as-control-plane.md +1 -1
  5. package/docs/adr/0002-dual-host-harness.md +1 -1
  6. package/docs/adr/0004-state-schema-phases-sections.md +1 -2
  7. package/docs/adr/0006-clock-seam-and-engine-ci-gates.md +3 -4
  8. package/docs/adr/0009-prebuild-decision-coverage-and-readiness.md +67 -0
  9. package/docs/adr/0010-agent-first-fresh-context-orchestration.md +71 -0
  10. package/docs/adr/0011-define-plan-transition-rights.md +28 -0
  11. package/docs/adr/README.md +3 -0
  12. package/docs/agents/triage-labels.md +7 -7
  13. package/docs/architecture.md +157 -140
  14. package/docs/cli.md +41 -12
  15. package/docs/command-map.md +49 -35
  16. package/docs/engine/agent-contract.md +92 -15
  17. package/docs/engine/commands.md +242 -72
  18. package/docs/engine/state-schema.md +52 -23
  19. package/docs/engine/workspace-schema.md +94 -15
  20. package/docs/extensions.md +1 -1
  21. package/docs/flow.md +80 -50
  22. package/docs/harness-compliance.md +29 -5
  23. package/docs/orchestration.md +107 -79
  24. package/docs/quick-reference.md +7 -3
  25. package/docs/release.md +4 -3
  26. package/docs/skills.md +64 -38
  27. package/docs/usage.md +57 -38
  28. package/engine/cmd/releasepack/main.go +219 -0
  29. package/engine/cmd/releasepack/main_test.go +170 -0
  30. package/engine/commands.go +170 -23
  31. package/engine/git_guard.go +187 -0
  32. package/engine/git_guard_test.go +283 -0
  33. package/engine/hookpolicy.go +53 -55
  34. package/engine/hookpolicy_test.go +91 -1
  35. package/engine/hooks.go +296 -75
  36. package/engine/hooks_events_test.go +276 -6
  37. package/engine/hooks_workspace.go +640 -159
  38. package/engine/internal/devritespaths/paths.go +65 -10
  39. package/engine/internal/devritespaths/paths_test.go +110 -0
  40. package/engine/internal/doctor/doctor.go +153 -23
  41. package/engine/internal/doctor/doctor_test.go +74 -0
  42. package/engine/internal/forge/forge.go +940 -0
  43. package/engine/internal/forge/forge_test.go +576 -0
  44. package/engine/internal/forge/git.go +245 -0
  45. package/engine/internal/forge/liveness_unix.go +48 -0
  46. package/engine/internal/forge/liveness_windows.go +67 -0
  47. package/engine/internal/forge/manifest.go +402 -0
  48. package/engine/internal/gate/gate.go +90 -71
  49. package/engine/internal/gate/gate_test.go +71 -2
  50. package/engine/internal/harness/compliance.go +18 -24
  51. package/engine/internal/harness/harness.go +37 -44
  52. package/engine/internal/harness/harness_test.go +21 -5
  53. package/engine/internal/install/install.go +575 -58
  54. package/engine/internal/install/install_test.go +490 -9
  55. package/engine/internal/iohooks/iohooks.go +350 -59
  56. package/engine/internal/iohooks/iohooks_test.go +421 -1
  57. package/engine/internal/lib/buildreadiness.go +35 -18
  58. package/engine/internal/lib/clarifyreturn.go +91 -0
  59. package/engine/internal/lib/clarifyreturn_test.go +123 -0
  60. package/engine/internal/lib/context.go +54 -15
  61. package/engine/internal/lib/cursor_compat_test.go +8 -0
  62. package/engine/internal/lib/extensions.go +2 -3
  63. package/engine/internal/lib/gitauthority.go +601 -0
  64. package/engine/internal/lib/gitauthority_test.go +346 -0
  65. package/engine/internal/lib/jsonout.go +25 -11
  66. package/engine/internal/lib/jsonout_test.go +16 -0
  67. package/engine/internal/lib/lanes.go +14 -8
  68. package/engine/internal/lib/observability_test.go +358 -0
  69. package/engine/internal/lib/packageexistence.go +151 -35
  70. package/engine/internal/lib/packageexistence_test.go +163 -0
  71. package/engine/internal/lib/progress.go +11 -9
  72. package/engine/internal/lib/provenance.go +462 -0
  73. package/engine/internal/lib/provenance_test.go +154 -0
  74. package/engine/internal/lib/readiness_contract.json +111 -0
  75. package/engine/internal/lib/readinessartifact.go +510 -0
  76. package/engine/internal/lib/readinessartifact_test.go +321 -0
  77. package/engine/internal/lib/reconcile.go +712 -88
  78. package/engine/internal/lib/reconcile_test.go +335 -16
  79. package/engine/internal/lib/recoveryattempts.go +298 -0
  80. package/engine/internal/lib/recoveryattempts_test.go +215 -0
  81. package/engine/internal/lib/resolve.go +23 -19
  82. package/engine/internal/lib/runbook_context_test.go +20 -0
  83. package/engine/internal/lib/session.go +701 -9
  84. package/engine/internal/lib/session_test.go +80 -13
  85. package/engine/internal/lib/testintegrity.go +33 -37
  86. package/engine/internal/lib/testintegrity_test.go +63 -1
  87. package/engine/internal/migrate/migrate.go +81 -1
  88. package/engine/internal/migrate/migrate_test.go +22 -1
  89. package/engine/internal/reason/reason.go +180 -0
  90. package/engine/internal/reason/reason_test.go +35 -0
  91. package/engine/internal/rootfacts/facts.go +466 -0
  92. package/engine/internal/rootfacts/facts_test.go +306 -0
  93. package/engine/internal/safepath/safepath.go +55 -0
  94. package/engine/internal/safepath/safepath_test.go +69 -0
  95. package/engine/internal/safepath/safepath_windows_test.go +26 -0
  96. package/engine/internal/state/clarify_transition.go +165 -0
  97. package/engine/internal/state/clarify_transition_test.go +130 -0
  98. package/engine/internal/state/cmd/workflowmanifest/main.go +12 -8
  99. package/engine/internal/state/cursor.go +58 -0
  100. package/engine/internal/state/cursor_test.go +42 -1
  101. package/engine/internal/state/feature.go +35 -48
  102. package/engine/internal/state/schema.go +90 -37
  103. package/engine/internal/state/snapshot.go +18 -2
  104. package/engine/internal/state/state_test.go +187 -7
  105. package/engine/internal/state/status.go +48 -11
  106. package/engine/internal/state/workflow_manifest.json +231 -50
  107. package/engine/internal/toolpolicy/classifier.go +533 -0
  108. package/engine/internal/toolpolicy/classifier_test.go +424 -0
  109. package/engine/internal/toolpolicy/git.go +616 -0
  110. package/engine/internal/toolpolicy/scanner.go +382 -0
  111. package/engine/main.go +160 -77
  112. package/engine/observability_cli_test.go +52 -0
  113. package/engine/root_routing_test.go +277 -0
  114. package/engine/testdata/golden/TestParityBuildReadiness/arg=approved.golden +1 -1
  115. package/engine/testdata/golden/TestParityBuildReadiness/arg=emptystatus.golden +1 -1
  116. package/engine/testdata/golden/TestParityBuildReadiness/arg=noclarify.golden +1 -0
  117. package/engine/testdata/golden/TestParityBuildReadiness/arg=novet.golden +1 -0
  118. package/engine/testdata/golden/TestParityBuildReadiness/arg=stalevet.golden +1 -0
  119. package/engine/testdata/golden/TestParityBuildReadiness/arg=trailhash.golden +1 -1
  120. package/engine/testdata/golden/TestParityBuildReadiness/arg=trailpipe.golden +1 -1
  121. package/engine/testdata/golden/TestParityBuildReadiness/arg=vetnotready.golden +1 -0
  122. package/engine/testdata/golden/TestParityProgress/arg=allbuilt.golden +1 -1
  123. package/engine/testdata/golden/TestParityProgress/arg=done.golden +1 -1
  124. package/engine/testdata/golden/TestParityProgress/arg=mid.golden +1 -1
  125. package/engine/testdata/golden/TestParityProgress/arg=nophase.golden +1 -1
  126. package/engine/testdata/golden/TestParityProgress/arg=noslice.golden +1 -1
  127. package/engine/testdata/golden/TestParityProgress/arg=plan.golden +1 -1
  128. package/engine/testdata/golden/TestParityProgress/arg=seal.golden +1 -1
  129. package/engine/testdata/golden/TestParityReconcile/check-clean.golden +1 -1
  130. package/engine/testdata/golden/TestParityReconcile/inline-fallback.golden +1 -0
  131. package/engine/testdata/golden/TestParityReconcile/snapshot-no-allowlist.golden +1 -0
  132. package/engine/testdata/golden/TestParityWrightScope/devrites-edit-denied.golden +2 -0
  133. package/engine/testdata/golden/TestParityWrightScope/out-of-scope-enforce-denies.golden +1 -1
  134. package/engine/tests/adr_0011_define_plan_test.go +30 -0
  135. package/engine/tests/budget_test.go +19 -8
  136. package/engine/tests/concurrency_cli_test.go +7 -1
  137. package/engine/tests/doctor_cli_test.go +148 -2
  138. package/engine/tests/forge_cli_test.go +463 -0
  139. package/engine/tests/gate_test.go +63 -8
  140. package/engine/tests/hook_test.go +260 -8
  141. package/engine/tests/hooks_io_test.go +94 -3
  142. package/engine/tests/json_contract_test.go +127 -2
  143. package/engine/tests/migrate_cli_test.go +2 -2
  144. package/engine/tests/parity_buildreadiness_test.go +141 -10
  145. package/engine/tests/parity_learnings_test.go +16 -17
  146. package/engine/tests/parity_reconcile_test.go +20 -17
  147. package/engine/tests/parity_resolve_test.go +12 -11
  148. package/engine/tests/parity_test.go +21 -17
  149. package/pack/.claude/agents/devrites-code-reviewer.md +62 -48
  150. package/pack/.claude/agents/devrites-devex-reviewer.md +69 -53
  151. package/pack/.claude/agents/devrites-doubt-reviewer.md +29 -22
  152. package/pack/.claude/agents/devrites-evidence-scout.md +69 -0
  153. package/pack/.claude/agents/devrites-forge-judge.md +74 -61
  154. package/pack/.claude/agents/devrites-frontend-reviewer.md +48 -39
  155. package/pack/.claude/agents/devrites-performance-reviewer.md +49 -40
  156. package/pack/.claude/agents/devrites-plan-drafter.md +71 -0
  157. package/pack/.claude/agents/devrites-plan-reviewer.md +80 -47
  158. package/pack/.claude/agents/devrites-proof-runner.md +74 -0
  159. package/pack/.claude/agents/devrites-retrospector.md +48 -45
  160. package/pack/.claude/agents/devrites-security-auditor.md +46 -36
  161. package/pack/.claude/agents/devrites-simplifier-reviewer.md +50 -46
  162. package/pack/.claude/agents/devrites-slice-wright.md +153 -165
  163. package/pack/.claude/agents/devrites-spec-reviewer.md +34 -32
  164. package/pack/.claude/agents/devrites-strategy-reviewer.md +63 -34
  165. package/pack/.claude/agents/devrites-test-analyst.md +40 -30
  166. package/pack/.claude/settings.json +2 -1
  167. package/pack/.claude/skills/devrites-audit/SKILL.md +40 -61
  168. package/pack/.claude/skills/devrites-debug-recovery/SKILL.md +24 -13
  169. package/pack/.claude/skills/devrites-debug-recovery/reference/build-the-loop.md +24 -21
  170. package/pack/.claude/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +34 -6
  171. package/pack/.claude/skills/devrites-debug-recovery/reference/instrumentation.md +2 -2
  172. package/pack/.claude/skills/devrites-doubt/SKILL.md +25 -17
  173. package/pack/.claude/skills/devrites-interview/SKILL.md +53 -52
  174. package/pack/.claude/skills/devrites-lib/SKILL.md +11 -9
  175. package/pack/.claude/skills/devrites-lib/reference/intent-map.md +3 -2
  176. package/pack/.claude/skills/devrites-lib/reference/parallel-dispatch.md +82 -137
  177. package/pack/.claude/skills/devrites-lib/reference/reply-contract.md +8 -1
  178. package/pack/.claude/skills/devrites-lib/reference/standards/README.md +38 -55
  179. package/pack/.claude/skills/devrites-lib/reference/standards/afk-hitl.md +53 -27
  180. package/pack/.claude/skills/devrites-lib/reference/standards/agents.md +198 -190
  181. package/pack/.claude/skills/devrites-lib/reference/standards/anti-patterns.md +5 -15
  182. package/pack/.claude/skills/devrites-lib/reference/standards/ci-cd.md +27 -58
  183. package/pack/.claude/skills/devrites-lib/reference/standards/code-review.md +16 -52
  184. package/pack/.claude/skills/devrites-lib/reference/standards/coding-style.md +2 -10
  185. package/pack/.claude/skills/devrites-lib/reference/standards/context-hygiene.md +18 -61
  186. package/pack/.claude/skills/devrites-lib/reference/standards/core.md +23 -18
  187. package/pack/.claude/skills/devrites-lib/reference/standards/deprecation.md +17 -57
  188. package/pack/.claude/skills/devrites-lib/reference/standards/development-workflow.md +4 -33
  189. package/pack/.claude/skills/devrites-lib/reference/standards/git-workflow.md +4 -20
  190. package/pack/.claude/skills/devrites-lib/reference/standards/hooks.md +3 -14
  191. package/pack/.claude/skills/devrites-lib/reference/standards/patterns.md +9 -25
  192. package/pack/.claude/skills/devrites-lib/reference/standards/performance.md +0 -9
  193. package/pack/.claude/skills/devrites-lib/reference/standards/principles.md +1 -3
  194. package/pack/.claude/skills/devrites-lib/reference/standards/security.md +17 -7
  195. package/pack/.claude/skills/devrites-lib/reference/standards/testing.md +1 -27
  196. package/pack/.claude/skills/devrites-lib/reference/standards/tooling.md +46 -50
  197. package/pack/.claude/skills/devrites-lib/reference/workspace-artifact-schema.md +23 -10
  198. package/pack/.claude/skills/devrites-source-driven/SKILL.md +23 -22
  199. package/pack/.claude/skills/rite/SKILL.md +8 -6
  200. package/pack/.claude/skills/rite/reference/menu.md +8 -6
  201. package/pack/.claude/skills/rite-adopt/SKILL.md +33 -37
  202. package/pack/.claude/skills/rite-autocomplete/SKILL.md +33 -28
  203. package/pack/.claude/skills/rite-autocomplete/reference/loop.md +21 -21
  204. package/pack/.claude/skills/rite-autocomplete/reference/stop-conditions.md +15 -6
  205. package/pack/.claude/skills/rite-build/SKILL.md +35 -30
  206. package/pack/.claude/skills/rite-build/reference/afk-discipline.md +38 -38
  207. package/pack/.claude/skills/rite-build/reference/evidence-standard.md +10 -0
  208. package/pack/.claude/skills/rite-build/reference/forge.md +186 -156
  209. package/pack/.claude/skills/rite-build/reference/one-slice-cycle.md +4 -3
  210. package/pack/.claude/skills/rite-build/reference/phase-contract.md +96 -175
  211. package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +129 -134
  212. package/pack/.claude/skills/rite-clarify/SKILL.md +89 -0
  213. package/pack/.claude/skills/rite-clarify/reference/decision-coverage.md +59 -0
  214. package/pack/.claude/skills/rite-converge/SKILL.md +35 -25
  215. package/pack/.claude/skills/rite-define/SKILL.md +57 -32
  216. package/pack/.claude/skills/rite-define/reference/gates.md +16 -15
  217. package/pack/.claude/skills/rite-define/reference/plan-template.md +16 -8
  218. package/pack/.claude/skills/rite-frame/reference/failure-modes.md +22 -24
  219. package/pack/.claude/skills/rite-plan/SKILL.md +44 -16
  220. package/pack/.claude/skills/rite-plan/reference/task-breakdown.md +4 -0
  221. package/pack/.claude/skills/rite-polish/SKILL.md +27 -23
  222. package/pack/.claude/skills/rite-prototype/SKILL.md +25 -26
  223. package/pack/.claude/skills/rite-prove/SKILL.md +27 -17
  224. package/pack/.claude/skills/rite-resolve/SKILL.md +12 -11
  225. package/pack/.claude/skills/rite-resolve/reference/answer-protocol.md +3 -0
  226. package/pack/.claude/skills/rite-review/SKILL.md +37 -28
  227. package/pack/.claude/skills/rite-seal/reference/phase-contract.md +27 -92
  228. package/pack/.claude/skills/rite-seal/reference/risk-and-rollback.md +13 -0
  229. package/pack/.claude/skills/rite-ship/reference/design-memory.md +31 -36
  230. package/pack/.claude/skills/rite-spec/SKILL.md +84 -135
  231. package/pack/.claude/skills/rite-spec/reference/investigation.md +37 -33
  232. package/pack/.claude/skills/rite-spec/reference/question-protocol.md +6 -2
  233. package/pack/.claude/skills/rite-spec/reference/spec-checklists.md +25 -24
  234. package/pack/.claude/skills/rite-spec/reference/spec-template.md +9 -2
  235. package/pack/.claude/skills/rite-spec/reference/state-workspace.md +13 -3
  236. package/pack/.claude/skills/rite-temper/SKILL.md +62 -47
  237. package/pack/.claude/skills/rite-temper/reference/review-dimensions.md +24 -22
  238. package/pack/.claude/skills/rite-vet/SKILL.md +110 -113
  239. package/pack/.claude/skills/rite-vet/reference/artifacts.md +42 -9
  240. package/pack/.claude/skills/rite-vet/reference/review-axes.md +39 -37
  241. package/pack/generated/claude/agents/devrites-code-reviewer.md +62 -48
  242. package/pack/generated/claude/agents/devrites-devex-reviewer.md +69 -53
  243. package/pack/generated/claude/agents/devrites-doubt-reviewer.md +29 -22
  244. package/pack/generated/claude/agents/devrites-evidence-scout.md +69 -0
  245. package/pack/generated/claude/agents/devrites-forge-judge.md +74 -61
  246. package/pack/generated/claude/agents/devrites-frontend-reviewer.md +48 -39
  247. package/pack/generated/claude/agents/devrites-performance-reviewer.md +49 -40
  248. package/pack/generated/claude/agents/devrites-plan-drafter.md +71 -0
  249. package/pack/generated/claude/agents/devrites-plan-reviewer.md +80 -47
  250. package/pack/generated/claude/agents/devrites-proof-runner.md +74 -0
  251. package/pack/generated/claude/agents/devrites-retrospector.md +48 -45
  252. package/pack/generated/claude/agents/devrites-security-auditor.md +46 -36
  253. package/pack/generated/claude/agents/devrites-simplifier-reviewer.md +50 -46
  254. package/pack/generated/claude/agents/devrites-slice-wright.md +153 -165
  255. package/pack/generated/claude/agents/devrites-spec-reviewer.md +34 -32
  256. package/pack/generated/claude/agents/devrites-strategy-reviewer.md +63 -34
  257. package/pack/generated/claude/agents/devrites-test-analyst.md +40 -30
  258. package/pack/generated/claude/settings.json +2 -1
  259. package/pack/generated/claude/skills/devrites-audit/SKILL.md +40 -61
  260. package/pack/generated/claude/skills/devrites-debug-recovery/SKILL.md +24 -13
  261. package/pack/generated/claude/skills/devrites-debug-recovery/reference/build-the-loop.md +24 -21
  262. package/pack/generated/claude/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +34 -6
  263. package/pack/generated/claude/skills/devrites-debug-recovery/reference/instrumentation.md +2 -2
  264. package/pack/generated/claude/skills/devrites-doubt/SKILL.md +25 -17
  265. package/pack/generated/claude/skills/devrites-interview/SKILL.md +53 -52
  266. package/pack/generated/claude/skills/devrites-lib/SKILL.md +11 -9
  267. package/pack/generated/claude/skills/devrites-lib/reference/intent-map.md +3 -2
  268. package/pack/generated/claude/skills/devrites-lib/reference/parallel-dispatch.md +82 -137
  269. package/pack/generated/claude/skills/devrites-lib/reference/reply-contract.md +8 -1
  270. package/pack/generated/claude/skills/devrites-lib/reference/standards/README.md +38 -55
  271. package/pack/generated/claude/skills/devrites-lib/reference/standards/afk-hitl.md +53 -27
  272. package/pack/generated/claude/skills/devrites-lib/reference/standards/agents.md +198 -190
  273. package/pack/generated/claude/skills/devrites-lib/reference/standards/anti-patterns.md +5 -15
  274. package/pack/generated/claude/skills/devrites-lib/reference/standards/ci-cd.md +27 -58
  275. package/pack/generated/claude/skills/devrites-lib/reference/standards/code-review.md +16 -52
  276. package/pack/generated/claude/skills/devrites-lib/reference/standards/coding-style.md +2 -10
  277. package/pack/generated/claude/skills/devrites-lib/reference/standards/context-hygiene.md +18 -61
  278. package/pack/generated/claude/skills/devrites-lib/reference/standards/core.md +23 -18
  279. package/pack/generated/claude/skills/devrites-lib/reference/standards/deprecation.md +17 -57
  280. package/pack/generated/claude/skills/devrites-lib/reference/standards/development-workflow.md +4 -33
  281. package/pack/generated/claude/skills/devrites-lib/reference/standards/git-workflow.md +4 -20
  282. package/pack/generated/claude/skills/devrites-lib/reference/standards/hooks.md +3 -14
  283. package/pack/generated/claude/skills/devrites-lib/reference/standards/patterns.md +9 -25
  284. package/pack/generated/claude/skills/devrites-lib/reference/standards/performance.md +0 -9
  285. package/pack/generated/claude/skills/devrites-lib/reference/standards/principles.md +1 -3
  286. package/pack/generated/claude/skills/devrites-lib/reference/standards/security.md +17 -7
  287. package/pack/generated/claude/skills/devrites-lib/reference/standards/testing.md +1 -27
  288. package/pack/generated/claude/skills/devrites-lib/reference/standards/tooling.md +46 -50
  289. package/pack/generated/claude/skills/devrites-lib/reference/workspace-artifact-schema.md +23 -10
  290. package/pack/generated/claude/skills/devrites-source-driven/SKILL.md +23 -22
  291. package/pack/generated/claude/skills/rite/SKILL.md +8 -6
  292. package/pack/generated/claude/skills/rite/reference/menu.md +8 -6
  293. package/pack/generated/claude/skills/rite-adopt/SKILL.md +33 -37
  294. package/pack/generated/claude/skills/rite-autocomplete/SKILL.md +33 -28
  295. package/pack/generated/claude/skills/rite-autocomplete/reference/loop.md +21 -21
  296. package/pack/generated/claude/skills/rite-autocomplete/reference/stop-conditions.md +15 -6
  297. package/pack/generated/claude/skills/rite-build/SKILL.md +35 -30
  298. package/pack/generated/claude/skills/rite-build/reference/afk-discipline.md +38 -38
  299. package/pack/generated/claude/skills/rite-build/reference/evidence-standard.md +10 -0
  300. package/pack/generated/claude/skills/rite-build/reference/forge.md +186 -156
  301. package/pack/generated/claude/skills/rite-build/reference/one-slice-cycle.md +4 -3
  302. package/pack/generated/claude/skills/rite-build/reference/phase-contract.md +96 -175
  303. package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +129 -134
  304. package/pack/generated/claude/skills/rite-clarify/SKILL.md +89 -0
  305. package/pack/generated/claude/skills/rite-clarify/reference/decision-coverage.md +59 -0
  306. package/pack/generated/claude/skills/rite-converge/SKILL.md +35 -25
  307. package/pack/generated/claude/skills/rite-define/SKILL.md +57 -32
  308. package/pack/generated/claude/skills/rite-define/reference/gates.md +16 -15
  309. package/pack/generated/claude/skills/rite-define/reference/plan-template.md +16 -8
  310. package/pack/generated/claude/skills/rite-frame/reference/failure-modes.md +22 -24
  311. package/pack/generated/claude/skills/rite-plan/SKILL.md +44 -16
  312. package/pack/generated/claude/skills/rite-plan/reference/task-breakdown.md +4 -0
  313. package/pack/generated/claude/skills/rite-polish/SKILL.md +27 -23
  314. package/pack/generated/claude/skills/rite-prototype/SKILL.md +25 -26
  315. package/pack/generated/claude/skills/rite-prove/SKILL.md +27 -17
  316. package/pack/generated/claude/skills/rite-resolve/SKILL.md +12 -11
  317. package/pack/generated/claude/skills/rite-resolve/reference/answer-protocol.md +3 -0
  318. package/pack/generated/claude/skills/rite-review/SKILL.md +37 -28
  319. package/pack/generated/claude/skills/rite-seal/reference/phase-contract.md +27 -92
  320. package/pack/generated/claude/skills/rite-seal/reference/risk-and-rollback.md +13 -0
  321. package/pack/generated/claude/skills/rite-ship/reference/design-memory.md +31 -36
  322. package/pack/generated/claude/skills/rite-spec/SKILL.md +84 -135
  323. package/pack/generated/claude/skills/rite-spec/reference/investigation.md +37 -33
  324. package/pack/generated/claude/skills/rite-spec/reference/question-protocol.md +6 -2
  325. package/pack/generated/claude/skills/rite-spec/reference/spec-checklists.md +25 -24
  326. package/pack/generated/claude/skills/rite-spec/reference/spec-template.md +9 -2
  327. package/pack/generated/claude/skills/rite-spec/reference/state-workspace.md +13 -3
  328. package/pack/generated/claude/skills/rite-temper/SKILL.md +62 -47
  329. package/pack/generated/claude/skills/rite-temper/reference/review-dimensions.md +24 -22
  330. package/pack/generated/claude/skills/rite-vet/SKILL.md +110 -113
  331. package/pack/generated/claude/skills/rite-vet/reference/artifacts.md +42 -9
  332. package/pack/generated/claude/skills/rite-vet/reference/review-axes.md +39 -37
  333. package/pack/generated/codex/AGENTS.md +5 -2
  334. package/pack/generated/codex/agents/devrites-code-reviewer.toml +69 -47
  335. package/pack/generated/codex/agents/devrites-devex-reviewer.toml +75 -51
  336. package/pack/generated/codex/agents/devrites-doubt-reviewer.toml +35 -20
  337. package/pack/generated/codex/agents/devrites-evidence-scout.toml +75 -0
  338. package/pack/generated/codex/agents/devrites-forge-judge.toml +80 -59
  339. package/pack/generated/codex/agents/devrites-frontend-reviewer.toml +54 -37
  340. package/pack/generated/codex/agents/devrites-performance-reviewer.toml +55 -38
  341. package/pack/generated/codex/agents/devrites-plan-drafter.toml +77 -0
  342. package/pack/generated/codex/agents/devrites-plan-reviewer.toml +82 -47
  343. package/pack/generated/codex/agents/devrites-proof-runner.toml +80 -0
  344. package/pack/generated/codex/agents/devrites-retrospector.toml +54 -43
  345. package/pack/generated/codex/agents/devrites-security-auditor.toml +53 -35
  346. package/pack/generated/codex/agents/devrites-simplifier-reviewer.toml +56 -44
  347. package/pack/generated/codex/agents/devrites-slice-wright.toml +159 -163
  348. package/pack/generated/codex/agents/devrites-spec-reviewer.toml +40 -30
  349. package/pack/generated/codex/agents/devrites-strategy-reviewer.toml +65 -34
  350. package/pack/generated/codex/agents/devrites-test-analyst.toml +46 -28
  351. package/pack/generated/codex/hooks.json +4 -14
  352. package/pack/generated/codex/skills/devrites-api-interface/SKILL.md +7 -3
  353. package/pack/generated/codex/skills/devrites-audit/SKILL.md +47 -64
  354. package/pack/generated/codex/skills/devrites-browser-proof/SKILL.md +7 -3
  355. package/pack/generated/codex/skills/devrites-debug-recovery/SKILL.md +31 -16
  356. package/pack/generated/codex/skills/devrites-debug-recovery/reference/build-the-loop.md +24 -21
  357. package/pack/generated/codex/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +34 -6
  358. package/pack/generated/codex/skills/devrites-debug-recovery/reference/instrumentation.md +2 -2
  359. package/pack/generated/codex/skills/devrites-doubt/SKILL.md +32 -20
  360. package/pack/generated/codex/skills/devrites-frontend-craft/SKILL.md +7 -3
  361. package/pack/generated/codex/skills/devrites-interview/SKILL.md +60 -55
  362. package/pack/generated/codex/skills/devrites-lib/SKILL.md +18 -12
  363. package/pack/generated/codex/skills/devrites-lib/reference/intent-map.md +3 -2
  364. package/pack/generated/codex/skills/devrites-lib/reference/parallel-dispatch.md +82 -137
  365. package/pack/generated/codex/skills/devrites-lib/reference/reply-contract.md +8 -1
  366. package/pack/generated/codex/skills/devrites-lib/reference/standards/README.md +38 -55
  367. package/pack/generated/codex/skills/devrites-lib/reference/standards/afk-hitl.md +53 -27
  368. package/pack/generated/codex/skills/devrites-lib/reference/standards/agents.md +198 -190
  369. package/pack/generated/codex/skills/devrites-lib/reference/standards/anti-patterns.md +5 -15
  370. package/pack/generated/codex/skills/devrites-lib/reference/standards/ci-cd.md +27 -58
  371. package/pack/generated/codex/skills/devrites-lib/reference/standards/code-review.md +16 -52
  372. package/pack/generated/codex/skills/devrites-lib/reference/standards/coding-style.md +2 -10
  373. package/pack/generated/codex/skills/devrites-lib/reference/standards/context-hygiene.md +18 -61
  374. package/pack/generated/codex/skills/devrites-lib/reference/standards/core.md +23 -18
  375. package/pack/generated/codex/skills/devrites-lib/reference/standards/deprecation.md +17 -57
  376. package/pack/generated/codex/skills/devrites-lib/reference/standards/development-workflow.md +4 -33
  377. package/pack/generated/codex/skills/devrites-lib/reference/standards/git-workflow.md +4 -20
  378. package/pack/generated/codex/skills/devrites-lib/reference/standards/hooks.md +3 -14
  379. package/pack/generated/codex/skills/devrites-lib/reference/standards/patterns.md +9 -25
  380. package/pack/generated/codex/skills/devrites-lib/reference/standards/performance.md +0 -9
  381. package/pack/generated/codex/skills/devrites-lib/reference/standards/principles.md +1 -3
  382. package/pack/generated/codex/skills/devrites-lib/reference/standards/security.md +17 -7
  383. package/pack/generated/codex/skills/devrites-lib/reference/standards/testing.md +1 -27
  384. package/pack/generated/codex/skills/devrites-lib/reference/standards/tooling.md +46 -50
  385. package/pack/generated/codex/skills/devrites-lib/reference/workspace-artifact-schema.md +23 -10
  386. package/pack/generated/codex/skills/devrites-prose-craft/SKILL.md +7 -3
  387. package/pack/generated/codex/skills/devrites-refresh-indexes/SKILL.md +7 -3
  388. package/pack/generated/codex/skills/devrites-source-driven/SKILL.md +29 -24
  389. package/pack/generated/codex/skills/devrites-ux-shape/SKILL.md +7 -3
  390. package/pack/generated/codex/skills/rite/SKILL.md +19 -13
  391. package/pack/generated/codex/skills/rite/reference/menu.md +8 -6
  392. package/pack/generated/codex/skills/rite-adopt/SKILL.md +40 -40
  393. package/pack/generated/codex/skills/rite-autocomplete/SKILL.md +40 -31
  394. package/pack/generated/codex/skills/rite-autocomplete/reference/loop.md +21 -21
  395. package/pack/generated/codex/skills/rite-autocomplete/reference/stop-conditions.md +15 -6
  396. package/pack/generated/codex/skills/rite-build/SKILL.md +42 -33
  397. package/pack/generated/codex/skills/rite-build/reference/afk-discipline.md +38 -38
  398. package/pack/generated/codex/skills/rite-build/reference/evidence-standard.md +10 -0
  399. package/pack/generated/codex/skills/rite-build/reference/forge.md +186 -156
  400. package/pack/generated/codex/skills/rite-build/reference/one-slice-cycle.md +4 -3
  401. package/pack/generated/codex/skills/rite-build/reference/phase-contract.md +96 -175
  402. package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +129 -134
  403. package/pack/generated/codex/skills/rite-clarify/SKILL.md +105 -0
  404. package/pack/generated/codex/skills/rite-clarify/reference/decision-coverage.md +59 -0
  405. package/pack/generated/codex/skills/rite-converge/SKILL.md +42 -28
  406. package/pack/generated/codex/skills/rite-customize/SKILL.md +7 -3
  407. package/pack/generated/codex/skills/rite-define/SKILL.md +64 -35
  408. package/pack/generated/codex/skills/rite-define/reference/gates.md +16 -15
  409. package/pack/generated/codex/skills/rite-define/reference/plan-template.md +16 -8
  410. package/pack/generated/codex/skills/rite-doctor/SKILL.md +7 -3
  411. package/pack/generated/codex/skills/rite-dogfood/SKILL.md +7 -3
  412. package/pack/generated/codex/skills/rite-explain/SKILL.md +7 -3
  413. package/pack/generated/codex/skills/rite-frame/SKILL.md +7 -3
  414. package/pack/generated/codex/skills/rite-frame/reference/failure-modes.md +22 -24
  415. package/pack/generated/codex/skills/rite-handoff/SKILL.md +7 -3
  416. package/pack/generated/codex/skills/rite-learn/SKILL.md +7 -3
  417. package/pack/generated/codex/skills/rite-plan/SKILL.md +51 -19
  418. package/pack/generated/codex/skills/rite-plan/reference/task-breakdown.md +5 -1
  419. package/pack/generated/codex/skills/rite-polish/SKILL.md +34 -26
  420. package/pack/generated/codex/skills/rite-pov/SKILL.md +7 -3
  421. package/pack/generated/codex/skills/rite-pr-feedback/SKILL.md +7 -3
  422. package/pack/generated/codex/skills/rite-pressure-test/SKILL.md +7 -3
  423. package/pack/generated/codex/skills/rite-prototype/SKILL.md +32 -29
  424. package/pack/generated/codex/skills/rite-prove/SKILL.md +34 -20
  425. package/pack/generated/codex/skills/rite-quick/SKILL.md +7 -3
  426. package/pack/generated/codex/skills/rite-resolve/SKILL.md +19 -14
  427. package/pack/generated/codex/skills/rite-resolve/reference/answer-protocol.md +3 -0
  428. package/pack/generated/codex/skills/rite-review/SKILL.md +44 -31
  429. package/pack/generated/codex/skills/rite-seal/SKILL.md +7 -3
  430. package/pack/generated/codex/skills/rite-seal/reference/phase-contract.md +27 -92
  431. package/pack/generated/codex/skills/rite-seal/reference/risk-and-rollback.md +13 -0
  432. package/pack/generated/codex/skills/rite-ship/SKILL.md +7 -3
  433. package/pack/generated/codex/skills/rite-ship/reference/design-memory.md +31 -36
  434. package/pack/generated/codex/skills/rite-spec/SKILL.md +91 -138
  435. package/pack/generated/codex/skills/rite-spec/reference/investigation.md +37 -33
  436. package/pack/generated/codex/skills/rite-spec/reference/question-protocol.md +6 -2
  437. package/pack/generated/codex/skills/rite-spec/reference/spec-checklists.md +25 -24
  438. package/pack/generated/codex/skills/rite-spec/reference/spec-template.md +9 -2
  439. package/pack/generated/codex/skills/rite-spec/reference/state-workspace.md +13 -3
  440. package/pack/generated/codex/skills/rite-status/SKILL.md +7 -3
  441. package/pack/generated/codex/skills/rite-temper/SKILL.md +69 -50
  442. package/pack/generated/codex/skills/rite-temper/reference/review-dimensions.md +24 -22
  443. package/pack/generated/codex/skills/rite-vet/SKILL.md +117 -116
  444. package/pack/generated/codex/skills/rite-vet/reference/artifacts.md +42 -9
  445. package/pack/generated/codex/skills/rite-vet/reference/review-axes.md +40 -38
  446. package/pack/generated/codex/skills/rite-zoom-out/SKILL.md +7 -3
  447. package/package.json +1 -1
  448. package/scripts/build-release-tarball.sh +32 -15
  449. package/scripts/check-authority-drift.py +125 -0
  450. package/scripts/check-instruction-size-baseline.mjs +19 -11
  451. package/scripts/check-invocation-integrity.py +2 -0
  452. package/scripts/codex-generate.sh +69 -33
  453. package/scripts/grade-feature.sh +121 -40
  454. package/scripts/live-hosts/agent-result.schema.json +230 -0
  455. package/scripts/live-hosts/claude.sh +87 -0
  456. package/scripts/live-hosts/codex.sh +81 -0
  457. package/scripts/live-hosts/common.sh +113 -0
  458. package/scripts/live-hosts/fake-host.py +264 -0
  459. package/scripts/live-hosts/host-transport.py +287 -0
  460. package/scripts/release-check.sh +5 -1
  461. package/scripts/run-agent-contract-evals.py +1380 -0
  462. package/scripts/run-behavioral-evals.sh +24 -30
  463. package/scripts/run-evals.sh +1 -5
  464. package/scripts/run-live-behavioral-evals.py +1274 -144
  465. package/scripts/run-outcome-evals.sh +414 -88
  466. package/scripts/run-tests.mjs +30 -2
  467. package/scripts/skills-inventory.mjs +1 -1
  468. package/scripts/validate-workflow-security.py +39 -20
  469. package/scripts/validate-workspace-schema.py +362 -10
  470. package/scripts/validate.sh +21 -15
  471. package/engine/testdata/golden/TestParityWrightScope/devrites-edit-allowed.golden +0 -1
  472. /package/engine/testdata/golden/{TestParityReconcile/check-no-claimed.golden → TestParityBuildReadiness/arg=clarifyopen.golden} +0 -0
@@ -0,0 +1,346 @@
1
+ package lib
2
+
3
+ import (
4
+ "bytes"
5
+ "encoding/json"
6
+ "os"
7
+ "path/filepath"
8
+ "runtime"
9
+ "strings"
10
+ "sync"
11
+ "testing"
12
+
13
+ "github.com/devrites/devrites/internal/reason"
14
+ "github.com/devrites/devrites/internal/toolpolicy"
15
+ )
16
+
17
+ func TestGitAuthorityOpensAndReusesOneQuestion(t *testing.T) {
18
+ root, slug, work := gitAuthorityWorkspace(t)
19
+ t.Setenv("DEVRITES_NOW", "2026-07-23T10:00:00Z")
20
+ classified := toolpolicy.ClassifyGitCommand("git reset --hard HEAD")
21
+
22
+ first := AuthorizeGitOperation(root, slug, classified.Digest, destructiveGitReasons(classified))
23
+ second := AuthorizeGitOperation(root, slug, classified.Digest, destructiveGitReasons(classified))
24
+ if first.Allowed || !first.Opened || first.ReasonID != reason.GitAuthorityPending {
25
+ t.Fatalf("first decision = %#v", first)
26
+ }
27
+ if second.Allowed || second.Opened || second.QuestionID != first.QuestionID ||
28
+ second.ReasonID != reason.GitAuthorityPending {
29
+ t.Fatalf("second decision = %#v", second)
30
+ }
31
+
32
+ questions := gitAuthorityRead(t, filepath.Join(work, "questions.md"))
33
+ if strings.Count(questions, "schema: "+GitAuthoritySchemaV1) != 1 {
34
+ t.Fatalf("question was not idempotent:\n%s", questions)
35
+ }
36
+ for _, forbidden := range []string{"git reset", "--hard", "HEAD"} {
37
+ if strings.Contains(questions, forbidden) {
38
+ t.Fatalf("questions.md leaked %q:\n%s", forbidden, questions)
39
+ }
40
+ }
41
+ stateText := gitAuthorityRead(t, filepath.Join(work, "state.md"))
42
+ for _, want := range []string{"awaiting_human", "## Awaiting human", "- qid: " + first.QuestionID} {
43
+ if !strings.Contains(stateText, want) {
44
+ t.Fatalf("state missing %q:\n%s", want, stateText)
45
+ }
46
+ }
47
+ }
48
+
49
+ func TestGitAuthorityResolvedExactGrantIsOneShot(t *testing.T) {
50
+ root, slug, work := gitAuthorityWorkspace(t)
51
+ t.Setenv("DEVRITES_NOW", "2026-07-23T10:00:00Z")
52
+ classified := toolpolicy.ClassifyGitCommand("git clean -fdx")
53
+ reasons := destructiveGitReasons(classified)
54
+ pending := AuthorizeGitOperation(root, slug, classified.Digest, reasons)
55
+
56
+ t.Setenv("DEVRITES_NOW", "2026-07-23T10:01:00Z")
57
+ var stdout, stderr bytes.Buffer
58
+ if code := Resolve(root, []string{pending.QuestionID, GitAuthorityAnswer}, &stdout, &stderr); code != 0 {
59
+ t.Fatalf("resolve code=%d stderr=%q", code, stderr.String())
60
+ }
61
+ granted := AuthorizeGitOperation(root, slug, classified.Digest, reasons)
62
+ if !granted.Allowed || granted.ReasonID != reason.GitAuthorityGranted ||
63
+ granted.QuestionID != pending.QuestionID {
64
+ t.Fatalf("grant = %#v", granted)
65
+ }
66
+
67
+ ledgerPath := filepath.Join(work, GitAuthorityLedgerFile)
68
+ info, err := os.Stat(ledgerPath)
69
+ if err != nil {
70
+ t.Fatal(err)
71
+ }
72
+ if runtime.GOOS != "windows" && info.Mode().Perm() != 0o600 {
73
+ t.Fatalf("ledger mode = %o, want 600", info.Mode().Perm())
74
+ }
75
+ ledgerText := gitAuthorityRead(t, ledgerPath)
76
+ if strings.Count(ledgerText, GitAuthorityConsumptionSchemaV1) != 1 {
77
+ t.Fatalf("ledger = %q", ledgerText)
78
+ }
79
+ for _, forbidden := range []string{"git clean", "-fdx"} {
80
+ if strings.Contains(ledgerText, forbidden) {
81
+ t.Fatalf("ledger leaked %q: %s", forbidden, ledgerText)
82
+ }
83
+ }
84
+
85
+ replay := AuthorizeGitOperation(root, slug, classified.Digest, reasons)
86
+ if replay.Allowed || !replay.Opened || replay.ReasonID != reason.GitAuthorityReplayed ||
87
+ replay.QuestionID == pending.QuestionID {
88
+ t.Fatalf("replay = %#v", replay)
89
+ }
90
+ if got := strings.Count(gitAuthorityRead(t, ledgerPath), GitAuthorityConsumptionSchemaV1); got != 1 {
91
+ t.Fatalf("replay changed ledger entries: %d", got)
92
+ }
93
+ }
94
+
95
+ func TestGitAuthorityWrongDigestNeverGrants(t *testing.T) {
96
+ root, slug, _ := gitAuthorityWorkspace(t)
97
+ t.Setenv("DEVRITES_NOW", "2026-07-23T10:00:00Z")
98
+ first := toolpolicy.ClassifyGitCommand("git reset --hard HEAD")
99
+ second := toolpolicy.ClassifyGitCommand("git clean -fd")
100
+ pending := AuthorizeGitOperation(root, slug, first.Digest, destructiveGitReasons(first))
101
+
102
+ t.Setenv("DEVRITES_NOW", "2026-07-23T10:01:00Z")
103
+ var stdout, stderr bytes.Buffer
104
+ if code := Resolve(root, []string{pending.QuestionID, GitAuthorityAnswer}, &stdout, &stderr); code != 0 {
105
+ t.Fatalf("resolve code=%d stderr=%q", code, stderr.String())
106
+ }
107
+ mismatch := AuthorizeGitOperation(root, slug, second.Digest, destructiveGitReasons(second))
108
+ if mismatch.Allowed || !mismatch.Opened || mismatch.QuestionID == pending.QuestionID {
109
+ t.Fatalf("wrong digest decision = %#v", mismatch)
110
+ }
111
+ }
112
+
113
+ func TestGitAuthorityResolveEnforcesExactAnswerContract(t *testing.T) {
114
+ root, slug, work := gitAuthorityWorkspace(t)
115
+ t.Setenv("DEVRITES_NOW", "2026-07-23T10:00:00Z")
116
+ classified := toolpolicy.ClassifyGitCommand("git reset --hard HEAD")
117
+ pending := AuthorizeGitOperation(root, slug, classified.Digest, destructiveGitReasons(classified))
118
+ before := gitAuthorityRead(t, filepath.Join(work, "questions.md"))
119
+
120
+ var stdout, stderr bytes.Buffer
121
+ if code := Resolve(root, []string{pending.QuestionID, "Approve everything"}, &stdout, &stderr); code != 5 {
122
+ t.Fatalf("wrong answer code=%d stderr=%q", code, stderr.String())
123
+ }
124
+ if after := gitAuthorityRead(t, filepath.Join(work, "questions.md")); after != before {
125
+ t.Fatalf("wrong answer mutated authority question:\n%s", after)
126
+ }
127
+
128
+ stderr.Reset()
129
+ if code := Resolve(root, []string{pending.QuestionID, " " + GitAuthorityAnswer + " "}, &stdout, &stderr); code != 0 {
130
+ t.Fatalf("trimmed exact answer code=%d stderr=%q", code, stderr.String())
131
+ }
132
+ questions := gitAuthorityRead(t, filepath.Join(work, "questions.md"))
133
+ if !strings.Contains(questions, "answer: "+GitAuthorityAnswer) ||
134
+ strings.Contains(questions, "answer: "+GitAuthorityAnswer) {
135
+ t.Fatalf("answer was not canonicalized:\n%s", questions)
136
+ }
137
+ stderr.Reset()
138
+ if code := Resolve(root, []string{pending.QuestionID, GitAuthorityAnswer}, &stdout, &stderr); code != 4 {
139
+ t.Fatalf("re-answer code=%d, want immutable-question code 4 (stderr=%q)", code, stderr.String())
140
+ }
141
+ }
142
+
143
+ func TestGitAuthorityExpiryAndRefusal(t *testing.T) {
144
+ t.Run("expired answer opens fresh question", func(t *testing.T) {
145
+ root, slug, _ := gitAuthorityWorkspace(t)
146
+ t.Setenv("DEVRITES_NOW", "2026-07-23T10:00:00Z")
147
+ classified := toolpolicy.ClassifyGitCommand("git branch -D old")
148
+ pending := AuthorizeGitOperation(root, slug, classified.Digest, destructiveGitReasons(classified))
149
+
150
+ t.Setenv("DEVRITES_NOW", "2026-07-23T10:01:00Z")
151
+ var stdout, stderr bytes.Buffer
152
+ if code := Resolve(root, []string{pending.QuestionID, GitAuthorityAnswer}, &stdout, &stderr); code != 0 {
153
+ t.Fatalf("resolve code=%d stderr=%q", code, stderr.String())
154
+ }
155
+ t.Setenv("DEVRITES_NOW", "2026-07-23T10:16:00Z")
156
+ expired := AuthorizeGitOperation(root, slug, classified.Digest, destructiveGitReasons(classified))
157
+ if expired.Allowed || !expired.Opened || expired.ReasonID != reason.GitAuthorityExpired ||
158
+ expired.QuestionID == pending.QuestionID {
159
+ t.Fatalf("expired = %#v", expired)
160
+ }
161
+ })
162
+
163
+ t.Run("drop remains refused without question spam", func(t *testing.T) {
164
+ root, slug, work := gitAuthorityWorkspace(t)
165
+ t.Setenv("DEVRITES_NOW", "2026-07-23T10:00:00Z")
166
+ classified := toolpolicy.ClassifyGitCommand("git tag -d v1")
167
+ pending := AuthorizeGitOperation(root, slug, classified.Digest, destructiveGitReasons(classified))
168
+ var stdout, stderr bytes.Buffer
169
+ if code := Resolve(root, []string{"--drop", pending.QuestionID, "No"}, &stdout, &stderr); code != 0 {
170
+ t.Fatalf("drop code=%d stderr=%q", code, stderr.String())
171
+ }
172
+ refused := AuthorizeGitOperation(root, slug, classified.Digest, destructiveGitReasons(classified))
173
+ if refused.Allowed || refused.Opened || refused.ReasonID != reason.GitAuthorityRefused {
174
+ t.Fatalf("refused = %#v", refused)
175
+ }
176
+ if got := strings.Count(gitAuthorityRead(t, filepath.Join(work, "questions.md")), GitAuthoritySchemaV1); got != 1 {
177
+ t.Fatalf("refusal created %d questions", got)
178
+ }
179
+ questions := gitAuthorityRead(t, filepath.Join(work, "questions.md"))
180
+ if !strings.Contains(questions, "answer: refused") || strings.Contains(questions, "answer: No") {
181
+ t.Fatalf("drop reason was not privacy-normalized:\n%s", questions)
182
+ }
183
+ })
184
+ }
185
+
186
+ func TestGitAuthorityCorruptionAndUnavailableStateDeny(t *testing.T) {
187
+ t.Run("corrupt ledger", func(t *testing.T) {
188
+ root, slug, work := gitAuthorityWorkspace(t)
189
+ t.Setenv("DEVRITES_NOW", "2026-07-23T10:00:00Z")
190
+ if err := os.WriteFile(filepath.Join(work, GitAuthorityLedgerFile), []byte("{bad\n"), 0o600); err != nil {
191
+ t.Fatal(err)
192
+ }
193
+ classified := toolpolicy.ClassifyGitCommand("git reset --hard HEAD")
194
+ got := AuthorizeGitOperation(root, slug, classified.Digest, destructiveGitReasons(classified))
195
+ if got.Allowed || got.Opened || got.ReasonID != reason.GitAuthorityCorrupt {
196
+ t.Fatalf("corrupt ledger decision = %#v", got)
197
+ }
198
+ })
199
+
200
+ t.Run("corrupt typed question", func(t *testing.T) {
201
+ root, slug, work := gitAuthorityWorkspace(t)
202
+ t.Setenv("DEVRITES_NOW", "2026-07-23T10:00:00Z")
203
+ classified := toolpolicy.ClassifyGitCommand("git reset --hard HEAD")
204
+ _ = AuthorizeGitOperation(root, slug, classified.Digest, destructiveGitReasons(classified))
205
+ qpath := filepath.Join(work, "questions.md")
206
+ data := gitAuthorityRead(t, qpath) + "unexpected: forged\n"
207
+ if err := os.WriteFile(qpath, []byte(data), 0o644); err != nil {
208
+ t.Fatal(err)
209
+ }
210
+ got := AuthorizeGitOperation(root, slug, classified.Digest, destructiveGitReasons(classified))
211
+ if got.Allowed || got.Opened || got.ReasonID != reason.GitAuthorityCorrupt {
212
+ t.Fatalf("corrupt question decision = %#v", got)
213
+ }
214
+ })
215
+
216
+ t.Run("no workspace", func(t *testing.T) {
217
+ root := filepath.Join(t.TempDir(), ".devrites")
218
+ if err := os.MkdirAll(root, 0o755); err != nil {
219
+ t.Fatal(err)
220
+ }
221
+ classified := toolpolicy.ClassifyGitCommand("git reset --hard HEAD")
222
+ got := AuthorizeGitOperation(root, "missing", classified.Digest, destructiveGitReasons(classified))
223
+ if got.Allowed || got.ReasonID != reason.GitWorkspaceUnavailable {
224
+ t.Fatalf("missing workspace decision = %#v", got)
225
+ }
226
+ })
227
+
228
+ t.Run("lock failure leaves question unchanged", func(t *testing.T) {
229
+ root, slug, work := gitAuthorityWorkspace(t)
230
+ before := gitAuthorityRead(t, filepath.Join(work, "questions.md"))
231
+ if err := os.Mkdir(filepath.Join(work, ".lock"), 0o755); err != nil {
232
+ t.Fatal(err)
233
+ }
234
+ classified := toolpolicy.ClassifyGitCommand("git reset --hard HEAD")
235
+ got := AuthorizeGitOperation(root, slug, classified.Digest, destructiveGitReasons(classified))
236
+ if got.Allowed || got.ReasonID != reason.GitAuthorityUnavailable {
237
+ t.Fatalf("lock failure decision = %#v", got)
238
+ }
239
+ if after := gitAuthorityRead(t, filepath.Join(work, "questions.md")); after != before {
240
+ t.Fatalf("lock failure mutated questions:\n%s", after)
241
+ }
242
+ })
243
+ }
244
+
245
+ func TestGitAuthorityConcurrentGrantAllowsExactlyOnce(t *testing.T) {
246
+ root, slug, work := gitAuthorityWorkspace(t)
247
+ t.Setenv("DEVRITES_NOW", "2026-07-23T10:00:00Z")
248
+ classified := toolpolicy.ClassifyGitCommand("git update-ref -d refs/heads/old")
249
+ reasons := destructiveGitReasons(classified)
250
+ pending := AuthorizeGitOperation(root, slug, classified.Digest, reasons)
251
+ var stdout, stderr bytes.Buffer
252
+ if code := Resolve(root, []string{pending.QuestionID, GitAuthorityAnswer}, &stdout, &stderr); code != 0 {
253
+ t.Fatalf("resolve code=%d stderr=%q", code, stderr.String())
254
+ }
255
+
256
+ const workers = 12
257
+ start := make(chan struct{})
258
+ results := make(chan GitAuthorityDecision, workers)
259
+ var wg sync.WaitGroup
260
+ for i := 0; i < workers; i++ {
261
+ wg.Add(1)
262
+ go func() {
263
+ defer wg.Done()
264
+ <-start
265
+ results <- AuthorizeGitOperation(root, slug, classified.Digest, reasons)
266
+ }()
267
+ }
268
+ close(start)
269
+ wg.Wait()
270
+ close(results)
271
+
272
+ allowed := 0
273
+ for result := range results {
274
+ if result.Allowed {
275
+ allowed++
276
+ }
277
+ }
278
+ if allowed != 1 {
279
+ t.Fatalf("allowed = %d, want exactly 1", allowed)
280
+ }
281
+ ledgerData := gitAuthorityRead(t, filepath.Join(work, GitAuthorityLedgerFile))
282
+ lines := strings.Fields(ledgerData)
283
+ if len(lines) != 1 {
284
+ t.Fatalf("ledger entries = %d, want 1: %q", len(lines), ledgerData)
285
+ }
286
+ var record gitAuthorityConsumption
287
+ if err := json.Unmarshal([]byte(lines[0]), &record); err != nil {
288
+ t.Fatal(err)
289
+ }
290
+ if record.QuestionID != pending.QuestionID {
291
+ t.Fatalf("consumed qid = %q, want %q", record.QuestionID, pending.QuestionID)
292
+ }
293
+ }
294
+
295
+ func gitAuthorityWorkspace(t *testing.T) (root, slug, work string) {
296
+ t.Helper()
297
+ t.Setenv("DEVRITES_WORKSPACE", "")
298
+ project := t.TempDir()
299
+ root = filepath.Join(project, ".devrites")
300
+ slug = "feature"
301
+ work = filepath.Join(root, "work", slug)
302
+ if err := os.MkdirAll(work, 0o755); err != nil {
303
+ t.Fatal(err)
304
+ }
305
+ gitAuthorityWrite(t, filepath.Join(root, "ACTIVE"), slug+"\n", 0o644)
306
+ gitAuthorityWrite(t, filepath.Join(work, "questions.md"), "# Questions\n\nNone.\n", 0o644)
307
+ gitAuthorityWrite(t, filepath.Join(work, "state.md"), `# State
308
+
309
+ - Phase: build
310
+ - Status: running
311
+ - Next step: /rite-build
312
+
313
+ ## Log
314
+ - 2026-07-23T09:00:00Z build: running
315
+ `, 0o644)
316
+ return root, slug, work
317
+ }
318
+
319
+ func destructiveGitReasons(result toolpolicy.Result) []toolpolicy.ReasonID {
320
+ var out []toolpolicy.ReasonID
321
+ for _, finding := range result.Findings {
322
+ if finding.Verdict == toolpolicy.VerdictDestructive {
323
+ out = append(out, finding.ReasonID)
324
+ }
325
+ }
326
+ return out
327
+ }
328
+
329
+ func gitAuthorityWrite(t *testing.T, path, content string, mode os.FileMode) {
330
+ t.Helper()
331
+ if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
332
+ t.Fatal(err)
333
+ }
334
+ if err := os.WriteFile(path, []byte(content), mode); err != nil {
335
+ t.Fatal(err)
336
+ }
337
+ }
338
+
339
+ func gitAuthorityRead(t *testing.T, path string) string {
340
+ t.Helper()
341
+ data, err := os.ReadFile(path)
342
+ if err != nil {
343
+ t.Fatal(err)
344
+ }
345
+ return string(data)
346
+ }
@@ -6,25 +6,30 @@ import (
6
6
  "regexp"
7
7
  "strconv"
8
8
  "strings"
9
+
10
+ "github.com/devrites/devrites/internal/reason"
9
11
  )
10
12
 
11
- // The machine-readable agent contract. A `--json` run of an AFK-parsed command
12
- // wraps its normal text output in a stable envelope so an unattended driver reads
13
- // a structured result instead of scraping prose. The command's own logic is
14
- // unchanged: the envelope captures its stdout (as data.text), its stderr (parsed
15
- // into diagnostics), and the exit code. Full contract: docs/engine/agent-contract.md.
13
+ // A `--json` run wraps a command's normal output in the stable machine-readable
14
+ // agent contract without changing command behavior. The envelope stores stdout
15
+ // in data.text, parses stderr into diagnostics, and includes the exit code. See
16
+ // docs/engine/agent-contract.md.
16
17
 
17
18
  // Envelope is the top-level JSON result of a `--json` command run.
18
19
  type Envelope struct {
20
+ Schema string `json:"schema"`
19
21
  Command string `json:"command"`
20
22
  OK bool `json:"ok"`
21
23
  ExitCode int `json:"exitCode"`
24
+ ReasonID reason.ID `json:"reason_id,omitempty"`
22
25
  Data *EnvelopeData `json:"data,omitempty"`
23
26
  Diagnostics []Diagnostic `json:"diagnostics,omitempty"`
24
27
  }
25
28
 
26
- // EnvelopeData carries the command's human-readable stdout verbatim, so nothing is
27
- // lost versus the text form while structured consumers key on ok/exitCode/diagnostics.
29
+ const CommandEnvelopeSchemaV1 = "devrites-command/v1"
30
+
31
+ // EnvelopeData keeps stdout unchanged while structured consumers read ok,
32
+ // exitCode, and diagnostics from the envelope.
28
33
  type EnvelopeData struct {
29
34
  Text string `json:"text"`
30
35
  }
@@ -48,7 +53,7 @@ var (
48
53
 
49
54
  // NewEnvelope builds the result envelope from a captured command run.
50
55
  func NewEnvelope(command string, code int, stdout, stderr string) Envelope {
51
- env := Envelope{Command: command, OK: code == 0, ExitCode: code}
56
+ env := Envelope{Schema: CommandEnvelopeSchemaV1, Command: command, OK: code == 0, ExitCode: code}
52
57
  if s := strings.TrimRight(stdout, "\n"); s != "" {
53
58
  env.Data = &EnvelopeData{Text: s}
54
59
  }
@@ -62,6 +67,15 @@ func NewEnvelope(command string, code int, stdout, stderr string) Envelope {
62
67
  return env
63
68
  }
64
69
 
70
+ // WithReason adds a rule-owned reason without changing the legacy envelope
71
+ // fields. Unknown IDs are ignored rather than written as false provenance.
72
+ func (env Envelope) WithReason(id reason.ID) Envelope {
73
+ if reason.Known(id) {
74
+ env.ReasonID = id
75
+ }
76
+ return env
77
+ }
78
+
65
79
  // parseDiagnostic classifies one stderr line. It recognizes the ERROR:/WARN:
66
80
  // prefixes and the `<path>: <message> (line N)` shape the spec/ledger validators
67
81
  // emit; anything else becomes an info-level diagnostic with the raw text.
@@ -91,9 +105,9 @@ func parseDiagnostic(command, line string) Diagnostic {
91
105
  return d
92
106
  }
93
107
 
94
- // diagnosticCode assigns a stable, greppable code. It is intentionally coarse in
95
- // v1 (a `<command>_<severity>` slug) so the catalog in agent-contract.md can grow
96
- // specific codes over time without breaking consumers that match on the prefix.
108
+ // diagnosticCode returns a stable `<command>_<severity>` code. Version 1 keeps
109
+ // codes broad so the catalog can add more specific entries without breaking
110
+ // consumers that match the prefix.
97
111
  func diagnosticCode(command, severity, message string) string {
98
112
  base := diagCodeWord.ReplaceAllString(strings.ToLower(command), "_")
99
113
  switch {
@@ -4,6 +4,8 @@ import (
4
4
  "bytes"
5
5
  "encoding/json"
6
6
  "testing"
7
+
8
+ "github.com/devrites/devrites/internal/reason"
7
9
  )
8
10
 
9
11
  func TestNewEnvelopeOK(t *testing.T) {
@@ -11,6 +13,9 @@ func TestNewEnvelopeOK(t *testing.T) {
11
13
  if !env.OK || env.ExitCode != 0 {
12
14
  t.Fatalf("want ok/0, got ok=%v code=%d", env.OK, env.ExitCode)
13
15
  }
16
+ if env.Schema != CommandEnvelopeSchemaV1 {
17
+ t.Fatalf("schema=%q", env.Schema)
18
+ }
14
19
  if env.Data == nil || env.Data.Text != "phase: build\nstatus: running" {
15
20
  t.Fatalf("data.text not captured verbatim: %+v", env.Data)
16
21
  }
@@ -19,6 +24,17 @@ func TestNewEnvelopeOK(t *testing.T) {
19
24
  }
20
25
  }
21
26
 
27
+ func TestEnvelopeReasonIsExplicitAndCatalogBound(t *testing.T) {
28
+ env := NewEnvelope("seal", 3, "", "").WithReason(reason.GateSealMissing)
29
+ if env.ReasonID != reason.GateSealMissing {
30
+ t.Fatalf("reason_id=%q", env.ReasonID)
31
+ }
32
+ env = env.WithReason("DRV-UNREGISTERED")
33
+ if env.ReasonID != reason.GateSealMissing {
34
+ t.Fatalf("unknown reason replaced typed reason: %q", env.ReasonID)
35
+ }
36
+ }
37
+
22
38
  func TestNewEnvelopeParsesDiagnostics(t *testing.T) {
23
39
  stderr := `ERROR:.devrites/work/feat/spec.md: Requirement "X" (line 2) is marked ADDED but already exists in ledger capability "theming"
24
40
  ERROR:.devrites/work/feat/spec.md: Requirement "Y" (line 9) has no THEN line`
@@ -9,12 +9,15 @@ import (
9
9
  "strings"
10
10
  )
11
11
 
12
- var lanePathRe = regexp.MustCompile("`([^`]+)`")
12
+ var (
13
+ lanePathRe = regexp.MustCompile("`([^`]+)`")
14
+ laneForgeYes = regexp.MustCompile(`(?i)^Forge:[[:space:]]+yes(?:[[:space:]]+(?:—|-)[[:space:]]+.+)?$`)
15
+ )
13
16
 
14
- // Lanes plans conservative parallelism for a feature without changing state. It
15
- // never authorizes parallel production writes; it only labels read-only lanes,
16
- // Forge candidates, and obviously independent slices so the human/agent can keep
17
- // DevRites' one-verified-slice default intact.
17
+ // Lanes suggests conservative parallel work without changing state. It labels
18
+ // read-only lanes, Forge candidates, and independent slices but does not
19
+ // authorize parallel production writes, preserving the one-verified-slice
20
+ // default.
18
21
  func Lanes(root string, args []string, stdout, stderr io.Writer) int {
19
22
  if argAt(args, 0) != "plan" {
20
23
  fmt.Fprintln(stderr, "usage: devrites-engine lanes plan [slug]")
@@ -62,7 +65,11 @@ func Lanes(root string, args []string, stdout, stderr io.Writer) int {
62
65
  if len(s.paths) > 0 {
63
66
  conflict = strings.Join(s.paths, ", ")
64
67
  }
65
- fmt.Fprintf(stdout, "- %s: %s: %s\n", s.name, kind, conflict)
68
+ fmt.Fprintf(stdout, "- %s: %s: %s", s.name, kind, conflict)
69
+ if s.forge {
70
+ fmt.Fprint(stdout, "; advisory only. `devrites-engine forge plan` must authorize 2 or 3 strategies")
71
+ }
72
+ fmt.Fprintln(stdout)
66
73
  }
67
74
  return 0
68
75
  }
@@ -91,8 +98,7 @@ func parseLaneSlices(md string) []laneSlice {
91
98
  if cur == nil {
92
99
  continue
93
100
  }
94
- lower := strings.ToLower(trim)
95
- if strings.Contains(lower, "forge: yes") || strings.Contains(lower, "forge yes") {
101
+ if laneForgeYes.MatchString(trim) {
96
102
  cur.forge = true
97
103
  }
98
104
  for _, m := range lanePathRe.FindAllStringSubmatch(trim, -1) {