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,277 @@
1
+ package main
2
+
3
+ import (
4
+ "bytes"
5
+ "os"
6
+ "os/exec"
7
+ "path/filepath"
8
+ "reflect"
9
+ "sort"
10
+ "strings"
11
+ "testing"
12
+ )
13
+
14
+ func TestRootModeForCoversReadAndWriteSurfaces(t *testing.T) {
15
+ tests := []struct {
16
+ name string
17
+ command string
18
+ args []string
19
+ want rootMode
20
+ }{
21
+ {name: "unrelated command", command: "doctor", want: rootUnused},
22
+ {name: "first task", command: "first-task", want: rootLenient},
23
+ {name: "spec dedupe", command: "spec-dedupe", want: rootLenient},
24
+ {name: "footprint render", command: "footprint", args: []string{"render"}, want: rootLenient},
25
+ {name: "footprint roster", command: "footprint", args: []string{"roster"}, want: rootLenient},
26
+ {name: "footprint log", command: "footprint", args: []string{"log"}, want: rootStrict},
27
+ {name: "evidence fresh", command: "evidence-fresh", want: rootLenient},
28
+ {name: "coverage", command: "coverage", want: rootLenient},
29
+ {name: "doubt coverage", command: "doubt-coverage", want: rootLenient},
30
+ {name: "budget", command: "budget", want: rootLenient},
31
+ {name: "preamble", command: "preamble", want: rootLenient},
32
+ {name: "progress", command: "progress", want: rootLenient},
33
+ {name: "stuck check", command: "stuck", args: []string{"check"}, want: rootLenient},
34
+ {name: "stuck log", command: "stuck", args: []string{"log"}, want: rootStrict},
35
+ {name: "recovery route", command: "recovery", args: []string{"route"}, want: rootLenient},
36
+ {name: "recovery check", command: "recovery", args: []string{"check"}, want: rootLenient},
37
+ {name: "recovery fingerprint", command: "recovery", args: []string{"fingerprint"}, want: rootLenient},
38
+ {name: "recovery record", command: "recovery", args: []string{"record"}, want: rootStrict},
39
+ {name: "recovery clear", command: "recovery", args: []string{"clear"}, want: rootStrict},
40
+ {name: "build readiness", command: "build-readiness", want: rootLenient},
41
+ {name: "readiness digest", command: "readiness-digest", want: rootLenient},
42
+ {name: "clarify enter", command: "clarify-return", args: []string{"enter"}, want: rootStrict},
43
+ {name: "clarify restore", command: "clarify-return", args: []string{"restore"}, want: rootStrict},
44
+ {name: "analyze", command: "analyze", want: rootLenient},
45
+ {name: "mutation gate", command: "mutation-gate", want: rootLenient},
46
+ {name: "test integrity", command: "test-integrity", want: rootLenient},
47
+ {name: "review integrity", command: "review-integrity", want: rootLenient},
48
+ {name: "package existence", command: "package-existence", want: rootLenient},
49
+ {name: "reconcile snapshot", command: "reconcile", args: []string{"snapshot"}, want: rootStrict},
50
+ {name: "reconcile check", command: "reconcile", args: []string{"check"}, want: rootStrict},
51
+ {name: "reconcile close", command: "reconcile", args: []string{"close"}, want: rootStrict},
52
+ {name: "resolve", command: "resolve", want: rootStrict},
53
+ {name: "resolve next qid", command: "resolve", args: []string{"next-qid"}, want: rootLenient},
54
+ {name: "close out", command: "close-out", want: rootStrict},
55
+ {name: "archive search", command: "archive-search", want: rootLenient},
56
+ {name: "decisions search", command: "decisions", args: []string{"search"}, want: rootLenient},
57
+ {name: "decisions index", command: "decisions", args: []string{"index"}, want: rootStrict},
58
+ {name: "ledger diff", command: "ledger", args: []string{"diff"}, want: rootLenient},
59
+ {name: "ledger validate", command: "ledger", args: []string{"validate"}, want: rootLenient},
60
+ {name: "ledger list", command: "ledger", args: []string{"list"}, want: rootLenient},
61
+ {name: "ledger show", command: "ledger", args: []string{"show"}, want: rootLenient},
62
+ {name: "ledger sync", command: "ledger", args: []string{"sync"}, want: rootStrict},
63
+ {name: "ledger json before sync", command: "ledger", args: []string{"--json", "sync"}, want: rootStrict},
64
+ {name: "learnings list", command: "learnings", args: []string{"list"}, want: rootLenient},
65
+ {name: "learnings top", command: "learnings", args: []string{"top"}, want: rootLenient},
66
+ {name: "learnings mine", command: "learnings", args: []string{"mine"}, want: rootLenient},
67
+ {name: "learnings nudge", command: "learnings", args: []string{"nudge"}, want: rootLenient},
68
+ {name: "learnings add", command: "learnings", args: []string{"add"}, want: rootStrict},
69
+ {name: "timeline list", command: "timeline", args: []string{"list"}, want: rootLenient},
70
+ {name: "timeline report", command: "timeline", args: []string{"report"}, want: rootLenient},
71
+ {name: "timeline log", command: "timeline", args: []string{"log"}, want: rootStrict},
72
+ {name: "timeline purge", command: "timeline", args: []string{"purge"}, want: rootStrict},
73
+ {name: "health list", command: "health", args: []string{"list"}, want: rootLenient},
74
+ {name: "health default", command: "health", want: rootStrict},
75
+ {name: "health run", command: "health", args: []string{"run"}, want: rootStrict},
76
+ {name: "health check", command: "health", args: []string{"check"}, want: rootStrict},
77
+ {name: "health record", command: "health", args: []string{"record"}, want: rootStrict},
78
+ {name: "config", command: "config", args: []string{"get"}, want: rootLenient},
79
+ {name: "reviewers", command: "reviewers", args: []string{"list"}, want: rootLenient},
80
+ {name: "outside voice", command: "outside-voice", want: rootLenient},
81
+ {name: "docs stale", command: "docs-stale", want: rootLenient},
82
+ {name: "secret scan", command: "secret-scan", want: rootLenient},
83
+ {name: "review fingerprints read", command: "review-fingerprints", want: rootLenient},
84
+ {name: "review fingerprints write", command: "review-fingerprints", args: []string{"demo", "--write"}, want: rootStrict},
85
+ {name: "reviewer stats report", command: "reviewer-stats", args: []string{"report"}, want: rootLenient},
86
+ {name: "reviewer stats record", command: "reviewer-stats", args: []string{"record"}, want: rootStrict},
87
+ {name: "lanes", command: "lanes", args: []string{"plan"}, want: rootLenient},
88
+ {name: "forge missing verb", command: "forge", want: rootStrict},
89
+ {name: "forge plan", command: "forge", args: []string{"plan"}, want: rootStrict},
90
+ {name: "forge record", command: "forge", args: []string{"record"}, want: rootStrict},
91
+ {name: "forge extract", command: "forge", args: []string{"extract"}, want: rootStrict},
92
+ {name: "forge merge", command: "forge", args: []string{"merge"}, want: rootStrict},
93
+ {name: "forge cleanup", command: "forge", args: []string{"cleanup"}, want: rootStrict},
94
+ {name: "forge reap", command: "forge", args: []string{"reap"}, want: rootStrict},
95
+ {name: "extensions list", command: "extensions", args: []string{"list"}, want: rootLenient},
96
+ {name: "extensions validate", command: "extensions", args: []string{"validate"}, want: rootLenient},
97
+ {name: "extensions sync", command: "extensions", args: []string{"sync"}, want: rootStrict},
98
+ {name: "overrides", command: "overrides", args: []string{"validate"}, want: rootLenient},
99
+ {name: "context show", command: "context", args: []string{"show"}, want: rootLenient},
100
+ {name: "context sync", command: "context", args: []string{"sync"}, want: rootStrict},
101
+ {name: "runbook list", command: "runbook", args: []string{"list"}, want: rootLenient},
102
+ {name: "runbook validate", command: "runbook", args: []string{"validate"}, want: rootLenient},
103
+ {name: "runbook run", command: "runbook", args: []string{"run"}, want: rootStrict},
104
+ {name: "runbook resume", command: "runbook", args: []string{"resume"}, want: rootStrict},
105
+ }
106
+
107
+ for _, test := range tests {
108
+ t.Run(test.name, func(t *testing.T) {
109
+ if got := rootModeFor(test.command, test.args); got != test.want {
110
+ t.Fatalf("rootModeFor(%q, %q) = %d, want %d", test.command, test.args, got, test.want)
111
+ }
112
+ })
113
+ }
114
+ }
115
+
116
+ func TestStrictForgeRefusesParentWorkspaceAcrossNestedRepository(t *testing.T) {
117
+ parent := t.TempDir()
118
+ if err := os.Mkdir(filepath.Join(parent, ".devrites"), 0o755); err != nil {
119
+ t.Fatal(err)
120
+ }
121
+ child := filepath.Join(parent, "nested")
122
+ if err := os.Mkdir(child, 0o755); err != nil {
123
+ t.Fatal(err)
124
+ }
125
+ initRootRoutingGitRepo(t, child)
126
+ withRootRoutingCWD(t, child)
127
+ t.Setenv("DEVRITES_ROOT", "")
128
+ t.Setenv("DEVRITES_WORKSPACE", "")
129
+
130
+ var stdout, stderr bytes.Buffer
131
+ code := run([]string{"forge", "plan"}, strings.NewReader(""), &stdout, &stderr)
132
+ if code != exitBlocked {
133
+ t.Fatalf("forge plan exit = %d, want %d; stdout=%q stderr=%q", code, exitBlocked, stdout.String(), stderr.String())
134
+ }
135
+ if !strings.Contains(stderr.String(), "unsafe DevRites root") {
136
+ t.Fatalf("stderr = %q, want unsafe-root refusal", stderr.String())
137
+ }
138
+ if _, err := os.Stat(filepath.Join(child, ".devrites")); !os.IsNotExist(err) {
139
+ t.Fatalf("strict refusal must not create nested workspace; stat err=%v", err)
140
+ }
141
+
142
+ stdout.Reset()
143
+ stderr.Reset()
144
+ code = run([]string{"context", "show"}, strings.NewReader(""), &stdout, &stderr)
145
+ if code != exitOK {
146
+ t.Fatalf("context show exit = %d, want %d; stderr=%q", code, exitOK, stderr.String())
147
+ }
148
+ if !strings.Contains(stdout.String(), "DRV-ROOT-OUTSIDE-GIT") {
149
+ t.Fatalf("context show should diagnose the unsafe parent root; stdout=%q", stdout.String())
150
+ }
151
+ }
152
+
153
+ func TestStrictWriterRefusesExternalWorkspaceBeforeMutation(t *testing.T) {
154
+ project := t.TempDir()
155
+ root := filepath.Join(project, ".devrites")
156
+ if err := os.MkdirAll(filepath.Join(root, "work", "demo"), 0o755); err != nil {
157
+ t.Fatal(err)
158
+ }
159
+ initRootRoutingGitRepo(t, project)
160
+ withRootRoutingCWD(t, project)
161
+ t.Setenv("DEVRITES_ROOT", "")
162
+ t.Setenv("DEVRITES_WORKSPACE", t.TempDir())
163
+ before := rootRoutingTree(t, root)
164
+
165
+ var stdout, stderr bytes.Buffer
166
+ code := run([]string{"timeline", "log", "rite-build"}, strings.NewReader(""), &stdout, &stderr)
167
+ if code != exitBlocked {
168
+ t.Fatalf("timeline log exit = %d, want %d; stdout=%q stderr=%q", code, exitBlocked, stdout.String(), stderr.String())
169
+ }
170
+ if !strings.Contains(stderr.String(), "unsafe DevRites root") {
171
+ t.Fatalf("stderr = %q, want unsafe-root refusal", stderr.String())
172
+ }
173
+ if after := rootRoutingTree(t, root); !reflect.DeepEqual(after, before) {
174
+ t.Fatalf("strict refusal mutated workspace tree: before=%q after=%q", before, after)
175
+ }
176
+
177
+ stdout.Reset()
178
+ stderr.Reset()
179
+ code = run(
180
+ []string{"timeline", "purge", "--run", "drv-run-v1:0123456789abcdef0123456789abcdef"},
181
+ strings.NewReader(""),
182
+ &stdout,
183
+ &stderr,
184
+ )
185
+ if code != exitBlocked {
186
+ t.Fatalf("timeline purge exit = %d, want %d; stdout=%q stderr=%q", code, exitBlocked, stdout.String(), stderr.String())
187
+ }
188
+ if after := rootRoutingTree(t, root); !reflect.DeepEqual(after, before) {
189
+ t.Fatalf("unsafe timeline purge mutated workspace tree: before=%q after=%q", before, after)
190
+ }
191
+ }
192
+
193
+ func TestStrictWriterRefusesWorkspaceOverrideWithoutRoot(t *testing.T) {
194
+ project := t.TempDir()
195
+ initRootRoutingGitRepo(t, project)
196
+ withRootRoutingCWD(t, project)
197
+ t.Setenv("DEVRITES_ROOT", "")
198
+ t.Setenv("DEVRITES_WORKSPACE", t.TempDir())
199
+
200
+ var stdout, stderr bytes.Buffer
201
+ code := run([]string{"learnings", "add", "demo", "must not escape"}, strings.NewReader(""), &stdout, &stderr)
202
+ if code != exitBlocked {
203
+ t.Fatalf("learnings add exit = %d, want %d; stdout=%q stderr=%q", code, exitBlocked, stdout.String(), stderr.String())
204
+ }
205
+ if !strings.Contains(stderr.String(), "DEVRITES_WORKSPACE requires a selected DevRites root") {
206
+ t.Fatalf("stderr = %q, want workspace-without-root refusal", stderr.String())
207
+ }
208
+ if _, err := os.Stat(filepath.Join(project, ".devrites")); !os.IsNotExist(err) {
209
+ t.Fatalf("strict refusal must not create a root; stat err=%v", err)
210
+ }
211
+ }
212
+
213
+ func TestStrictWriterRefusesMissingExplicitRoot(t *testing.T) {
214
+ project := t.TempDir()
215
+ initRootRoutingGitRepo(t, project)
216
+ withRootRoutingCWD(t, project)
217
+ root := filepath.Join(project, ".devrites")
218
+ t.Setenv("DEVRITES_ROOT", root)
219
+ t.Setenv("DEVRITES_WORKSPACE", "")
220
+
221
+ var stdout, stderr bytes.Buffer
222
+ code := run([]string{"learnings", "add", "demo", "must not bootstrap"}, strings.NewReader(""), &stdout, &stderr)
223
+ if code != exitBlocked {
224
+ t.Fatalf("learnings add exit = %d, want %d; stdout=%q stderr=%q", code, exitBlocked, stdout.String(), stderr.String())
225
+ }
226
+ if !strings.Contains(stderr.String(), "unsafe DevRites root") {
227
+ t.Fatalf("stderr = %q, want invalid explicit-root refusal", stderr.String())
228
+ }
229
+ if _, err := os.Stat(root); !os.IsNotExist(err) {
230
+ t.Fatalf("strict refusal must not create explicit missing root; stat err=%v", err)
231
+ }
232
+ }
233
+
234
+ func initRootRoutingGitRepo(t *testing.T, dir string) {
235
+ t.Helper()
236
+ cmd := exec.Command("git", "init", "-q", dir)
237
+ if output, err := cmd.CombinedOutput(); err != nil {
238
+ t.Fatalf("git init %s: %v\n%s", dir, err, output)
239
+ }
240
+ }
241
+
242
+ func withRootRoutingCWD(t *testing.T, dir string) {
243
+ t.Helper()
244
+ before, err := os.Getwd()
245
+ if err != nil {
246
+ t.Fatal(err)
247
+ }
248
+ if err := os.Chdir(dir); err != nil {
249
+ t.Fatal(err)
250
+ }
251
+ t.Cleanup(func() {
252
+ if err := os.Chdir(before); err != nil {
253
+ t.Errorf("restore cwd: %v", err)
254
+ }
255
+ })
256
+ }
257
+
258
+ func rootRoutingTree(t *testing.T, root string) []string {
259
+ t.Helper()
260
+ var paths []string
261
+ err := filepath.WalkDir(root, func(path string, entry os.DirEntry, err error) error {
262
+ if err != nil {
263
+ return err
264
+ }
265
+ rel, err := filepath.Rel(root, path)
266
+ if err != nil {
267
+ return err
268
+ }
269
+ paths = append(paths, rel)
270
+ return nil
271
+ })
272
+ if err != nil {
273
+ t.Fatal(err)
274
+ }
275
+ sort.Strings(paths)
276
+ return paths
277
+ }
@@ -1,2 +1,2 @@
1
1
  exit 0
2
- readiness: OK: plan approved 2024-01-01, status running. Ready to build.
2
+ readiness: OK: plan approved 2024-01-01, decision coverage CLEAR, implementation READY, status running. Ready to build.
@@ -1,2 +1,2 @@
1
1
  exit 0
2
- readiness: OK: plan approved 2024-01-01, status running. Ready to build.
2
+ readiness: OK: plan approved 2024-01-01, decision coverage CLEAR, implementation READY, status running. Ready to build.
@@ -1,2 +1,2 @@
1
1
  exit 0
2
- readiness: OK: plan approved 2024-01-01, status running. Ready to build.
2
+ readiness: OK: plan approved 2024-01-01, decision coverage CLEAR, implementation READY, status running. Ready to build.
@@ -1,2 +1,2 @@
1
1
  exit 0
2
- readiness: OK: plan approved 2024-01-01, status running. Ready to build.
2
+ readiness: OK: plan approved 2024-01-01, decision coverage CLEAR, implementation READY, status running. Ready to build.
@@ -1,4 +1,4 @@
1
1
  exit 0
2
2
  ── rite-build ─────────
3
3
  Slices 2/2 ██████████ ✅ ALL BUILT
4
- Flow spec ✓ define ✓ plan ✓ build ◉ prove ○ polish ○ review ○ seal ○ ship ○
4
+ Flow spec ✓ clarify ✓ define ✓ plan ✓ build ◉ prove ○ polish ○ review ○ seal ○ ship ○
@@ -1,3 +1,3 @@
1
1
  exit 0
2
2
  ── rite-done ─────────
3
- Flow spec ✓ define ✓ plan ✓ build ✓ prove ✓ polish ✓ review ✓ seal ✓ ship ✓
3
+ Flow spec ✓ clarify ✓ define ✓ plan ✓ build ✓ prove ✓ polish ✓ review ✓ seal ✓ ship ✓
@@ -1,4 +1,4 @@
1
1
  exit 0
2
2
  ── rite-build ─────────
3
3
  Slice 2/3 ███████░░░ detail ✓
4
- Flow spec ✓ temper ✓ define ✓ plan ✓ vet ✓ build ◉ prove ○ polish ○ review ○ seal ○ ship ○
4
+ Flow spec ✓ clarify ✓ temper ✓ define ✓ plan ✓ vet ✓ build ◉ prove ○ polish ○ review ○ seal ○ ship ○
@@ -1,3 +1,3 @@
1
1
  exit 0
2
2
  ── rite-spec ─────────
3
- Flow spec ◉ define ○ plan ○ build ○ prove ○ polish ○ review ○ seal ○ ship ○
3
+ Flow spec ◉ clarify ○ define ○ plan ○ build ○ prove ○ polish ○ review ○ seal ○ ship ○
@@ -1,3 +1,3 @@
1
1
  exit 0
2
2
  ── rite-spec ─────────
3
- Flow spec ◉ define ○ plan ○ build ○ prove ○ polish ○ review ○ seal ○ ship ○
3
+ Flow spec ◉ clarify ○ define ○ plan ○ build ○ prove ○ polish ○ review ○ seal ○ ship ○
@@ -1,4 +1,4 @@
1
1
  exit 0
2
2
  ── rite-plan ─────────
3
3
  Slice 2/3 ███████░░░ detail ✓
4
- Flow spec ✓ define ✓ plan ◉ build ○ prove ○ polish ○ review ○ seal ○ ship ○
4
+ Flow spec ✓ clarify ✓ define ✓ plan ◉ build ○ prove ○ polish ○ review ○ seal ○ ship ○
@@ -1,3 +1,3 @@
1
1
  exit 0
2
2
  ── rite-seal ─────────
3
- Flow spec ✓ define ✓ plan ✓ build ✓ prove ✓ polish ✓ review ✓ seal ◉ ship ○
3
+ Flow spec ✓ clarify ✓ define ✓ plan ✓ build ✓ prove ✓ polish ✓ review ✓ seal ◉ ship ○
@@ -1,2 +1,2 @@
1
1
  exit 0
2
- reconcile: OK: every source change is the wright's; orchestrator touched only bookkeeping.
2
+ reconcile: OK: every slice change matches the orchestrator allowlist; baseline retained for post-slice gates (run "reconcile close" afterward).
@@ -1 +1,2 @@
1
1
  exit 0
2
+ reconcile: closed slice window for feat.
@@ -0,0 +1,2 @@
1
+ exit 0
2
+ {"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"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)"}}
@@ -1,2 +1,2 @@
1
1
  exit 0
2
- {"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"DevRites scope: this path is not in touched-files.md. Build only the slice contract; if the slice genuinely needs this file, return an Escalation so the orchestrator routes it through the Spec Drift Guard: do not widen scope yourself. (devrites-wright-scope)"}}
2
+ {"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"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)"}}
@@ -0,0 +1,30 @@
1
+ package main_test
2
+
3
+ import (
4
+ "testing"
5
+
6
+ "github.com/devrites/devrites/internal/state"
7
+ )
8
+
9
+ func TestADR0011DefineAndPlanHaveDistinctTransitionRights(t *testing.T) {
10
+ if got := state.ResumeVerb(state.PhaseDefine); got != "define" {
11
+ t.Fatalf("define resumes %q, want define", got)
12
+ }
13
+ if got := state.ResumeVerb(state.PhasePlan); got != "vet" {
14
+ t.Fatalf("plan resumes %q, want vet", got)
15
+ }
16
+ if got, ok := state.PhaseForName("planning"); !ok || got != state.PhasePlan {
17
+ t.Fatalf("planning alias=(%q,%v), want plan compatibility", got, ok)
18
+ }
19
+
20
+ rights := map[string]state.Phase{}
21
+ for _, phase := range state.WorkflowPhases() {
22
+ if phase.TransitionRight == "" {
23
+ t.Fatalf("phase %q has no transition right", phase.ID)
24
+ }
25
+ if prior := rights[phase.TransitionRight]; prior != "" {
26
+ t.Fatalf("phases %q and %q share transition right %q", prior, phase.ID, phase.TransitionRight)
27
+ }
28
+ rights[phase.TransitionRight] = phase.ID
29
+ }
30
+ }
@@ -5,18 +5,29 @@ import (
5
5
  "testing"
6
6
  )
7
7
 
8
- // TestStatusLiveWorkspace is the P2 acceptance check: `devrites-engine status <slug>`
9
- // must report a canonical work/<slug> feature the live pack created without a
10
- // workspace-map frontmatter: phase from the canonical state.md ledger and proof/status
11
- // completeness from canonical evidence.md/state.md files. Before schema unification this returned
8
+ // TestStatusLiveWorkspace checks a canonical work/<slug> feature without
9
+ // workspace-map frontmatter. Status must read the phase from state.md and
10
+ // completeness from evidence.md and state.md instead of reporting
12
11
  // "feature not found".
13
12
  func TestStatusLiveWorkspace(t *testing.T) {
14
13
  work := t.TempDir()
15
14
  writeFile(t, work, ".devrites/work/live/state.md", "- Phase: build\n- Status: running\n")
16
- writeFile(t, work, ".devrites/work/live/spec.md", "# Spec\n\nDo the thing.\n")
17
- writeFile(t, work, ".devrites/work/live/plan.md", "# Plan\n\nApproach.\n")
18
- writeFile(t, work, ".devrites/work/live/decisions.md", "# Decisions\n\nChose X.\n")
19
- writeFile(t, work, ".devrites/work/live/tasks.md", "# Tasks\n\n- [x] slice 1\n")
15
+ for name, body := range map[string]string{
16
+ "brief.md": "# Brief\n\nDo the thing.\n",
17
+ "spec.md": "# Spec\n\nDo the thing.\n",
18
+ "decisions.md": "# Decisions\n\nChose X.\n",
19
+ "assumptions.md": "# Assumptions\n\nNone.\n",
20
+ "questions.md": "# Questions\n\nNone.\n",
21
+ "decision-coverage.md": "# Decision coverage\n\nCLEAR.\n",
22
+ "architecture.md": "# Architecture\n\nExisting layer.\n",
23
+ "plan.md": "# Plan\n\nApproach.\n",
24
+ "tasks.md": "# Tasks\n\n- [x] slice 1\n",
25
+ "traceability.md": "# Traceability\n\nMapped.\n",
26
+ "eng-review.md": "# Engineering review\n\nREADY.\n",
27
+ "test-plan.md": "# Test plan\n\nRun tests.\n",
28
+ } {
29
+ writeFile(t, work, ".devrites/work/live/"+name, body)
30
+ }
20
31
  (parityCase{
21
32
  workdir: work,
22
33
  env: libRootEnv(work),
@@ -19,8 +19,13 @@ import (
19
19
  // from many goroutines. It returns stdout and exit code (and a fatal error only
20
20
  // for a non-exit failure like the binary being unrunnable).
21
21
  func runProc(root string, args ...string) (stdout string, code int, fatal error) {
22
+ return runProcInput(root, "", args...)
23
+ }
24
+
25
+ func runProcInput(root, stdin string, args ...string) (stdout string, code int, fatal error) {
22
26
  cmd := exec.Command(binPath, args...)
23
27
  cmd.Env = append(os.Environ(), "DEVRITES_ROOT="+root)
28
+ cmd.Stdin = strings.NewReader(stdin)
24
29
  var out bytes.Buffer
25
30
  cmd.Stdout = &out
26
31
  cmd.Stderr = &bytes.Buffer{}
@@ -94,7 +99,8 @@ func TestConcurrentStopGateAppendsNeverTear(t *testing.T) {
94
99
  wg.Add(1)
95
100
  go func(i int) {
96
101
  defer wg.Done()
97
- _, code, fatal := runProc(root, "hook", "stop-gate", "--harness=claude")
102
+ _, code, fatal := runProcInput(root, `{"stop_hook_active":false}`,
103
+ "hook", "stop-gate", "--harness=claude")
98
104
  if fatal != nil {
99
105
  code = -1
100
106
  }
@@ -1,9 +1,12 @@
1
1
  package main_test
2
2
 
3
- // Issue 05: `doctor` version triangle, at the CLI seam.
3
+ // CLI coverage for Issue 05: the `doctor` version triangle.
4
4
 
5
5
  import (
6
+ "bytes"
7
+ "errors"
6
8
  "os"
9
+ "os/exec"
7
10
  "path/filepath"
8
11
  "strings"
9
12
  "testing"
@@ -15,13 +18,101 @@ func TestDoctorPrintsTriangle(t *testing.T) {
15
18
  if code != 0 {
16
19
  t.Fatalf("exit = %d, want 0 (stderr: %s)", code, errOut)
17
20
  }
18
- for _, leg := range []string{"binary:", "pack:", "state-schema:", "verdict:"} {
21
+ for _, leg := range []string{"project:", "root:", "root-selection:", "binary:", "pack:", "state-schema:", "verdict:"} {
19
22
  if !strings.Contains(out, leg) {
20
23
  t.Errorf("doctor output missing %q leg\n%s", leg, out)
21
24
  }
22
25
  }
23
26
  }
24
27
 
28
+ func TestDoctorReportsStaleActiveWithPasteableRepair(t *testing.T) {
29
+ root := newWorkspace(t)
30
+ if err := os.WriteFile(filepath.Join(root, "ACTIVE"), []byte("ghost\n"), 0o644); err != nil {
31
+ t.Fatal(err)
32
+ }
33
+ out, errOut, code := runDevrites(t, root, "doctor")
34
+ if code != 0 {
35
+ t.Fatalf("exit = %d, want warning exit 0 (stderr: %s)\n%s", code, errOut, out)
36
+ }
37
+ if !strings.Contains(out, "[DRV-ACTIVE-STALE]") || !strings.Contains(out, "fix: rm -f") {
38
+ t.Fatalf("doctor did not diagnose stale ACTIVE with remediation:\n%s", out)
39
+ }
40
+ }
41
+
42
+ func TestDoctorRefusesParentStateAcrossNestedRepositoryBoundary(t *testing.T) {
43
+ outer := doctorRepo(t, filepath.Join(t.TempDir(), "outer"))
44
+ if err := os.MkdirAll(filepath.Join(outer, ".devrites"), 0o755); err != nil {
45
+ t.Fatal(err)
46
+ }
47
+ child := doctorRepo(t, filepath.Join(outer, "services", "child"))
48
+ out, errOut, code := runDoctorAt(t, child)
49
+ if code != 3 {
50
+ t.Fatalf("exit = %d, want unsafe-root refusal (stderr: %s)\n%s", code, errOut, out)
51
+ }
52
+ if !strings.Contains(out, "[DRV-ROOT-OUTSIDE-GIT]") || !strings.Contains(out, "fix: cd ") {
53
+ t.Fatalf("doctor did not explain nested-repository boundary:\n%s", out)
54
+ }
55
+ }
56
+
57
+ func TestDoctorKeepsOrdinaryRootAbsenceDiagnosable(t *testing.T) {
58
+ repo := doctorRepo(t, filepath.Join(t.TempDir(), "repo"))
59
+ out, errOut, code := runDoctorAt(t, repo)
60
+ if code != 0 {
61
+ t.Fatalf("exit = %d, want diagnostic exit 0 (stderr: %s)\n%s", code, errOut, out)
62
+ }
63
+ if !strings.Contains(out, "root: none") || !strings.Contains(out, "[DRV-ROOT-NOT-FOUND]") {
64
+ t.Fatalf("doctor hid ordinary root absence:\n%s", out)
65
+ }
66
+ }
67
+
68
+ func TestDoctorRefusesWorkspaceOverrideWithoutRoot(t *testing.T) {
69
+ repo := doctorRepo(t, filepath.Join(t.TempDir(), "repo"))
70
+ out, errOut, code := runDoctorAtEnv(t, repo, "DEVRITES_WORKSPACE="+t.TempDir())
71
+ if code != 3 {
72
+ t.Fatalf("exit = %d, want unsafe-root refusal (stderr: %s)\n%s", code, errOut, out)
73
+ }
74
+ if !strings.Contains(out, "[DRV-WORKSPACE-WITHOUT-ROOT]") || !strings.Contains(out, "fix: unset DEVRITES_WORKSPACE") {
75
+ t.Fatalf("doctor hid workspace-without-root remediation:\n%s", out)
76
+ }
77
+ }
78
+
79
+ func TestDoctorReportsLinkedWorktreeAndSubmoduleTopology(t *testing.T) {
80
+ t.Run("linked worktree", func(t *testing.T) {
81
+ base := t.TempDir()
82
+ main := doctorRepo(t, filepath.Join(base, "main"))
83
+ worktree := filepath.Join(base, "linked")
84
+ doctorGit(t, main, "worktree", "add", "-q", "-b", "doctor-linked", worktree)
85
+ if err := os.MkdirAll(filepath.Join(worktree, ".devrites"), 0o755); err != nil {
86
+ t.Fatal(err)
87
+ }
88
+ out, errOut, code := runDoctorAt(t, worktree)
89
+ if code != 0 {
90
+ t.Fatalf("exit = %d stderr=%s\n%s", code, errOut, out)
91
+ }
92
+ if !strings.Contains(out, "linked-worktree=true") {
93
+ t.Fatalf("doctor omitted linked-worktree topology:\n%s", out)
94
+ }
95
+ })
96
+
97
+ t.Run("submodule", func(t *testing.T) {
98
+ base := t.TempDir()
99
+ origin := doctorRepo(t, filepath.Join(base, "origin"))
100
+ super := doctorRepo(t, filepath.Join(base, "super"))
101
+ doctorGit(t, super, "-c", "protocol.file.allow=always", "submodule", "add", "-q", origin, "deps/sub")
102
+ sub := filepath.Join(super, "deps", "sub")
103
+ if err := os.MkdirAll(filepath.Join(sub, ".devrites"), 0o755); err != nil {
104
+ t.Fatal(err)
105
+ }
106
+ out, errOut, code := runDoctorAt(t, sub)
107
+ if code != 0 {
108
+ t.Fatalf("exit = %d stderr=%s\n%s", code, errOut, out)
109
+ }
110
+ if !strings.Contains(out, "submodule=true") || !strings.Contains(out, "git-superproject:") {
111
+ t.Fatalf("doctor omitted submodule topology:\n%s", out)
112
+ }
113
+ })
114
+ }
115
+
25
116
  func TestDoctorReportsMergeInProgress(t *testing.T) {
26
117
  root := newWorkspace(t)
27
118
  git := filepath.Join(filepath.Dir(root), ".git")
@@ -60,3 +151,58 @@ func TestDoctorRefusesNewerStateSchema(t *testing.T) {
60
151
  t.Errorf("verdict not a REFUSE\n%s", out)
61
152
  }
62
153
  }
154
+
155
+ func runDoctorAt(t *testing.T, dir string) (stdout, stderr string, code int) {
156
+ t.Helper()
157
+ return runDoctorAtEnv(t, dir)
158
+ }
159
+
160
+ func runDoctorAtEnv(t *testing.T, dir string, extraEnv ...string) (stdout, stderr string, code int) {
161
+ t.Helper()
162
+ cmd := exec.Command(binPath, "doctor")
163
+ cmd.Dir = dir
164
+ for _, item := range os.Environ() {
165
+ if strings.HasPrefix(item, "DEVRITES_ROOT=") || strings.HasPrefix(item, "DEVRITES_WORKSPACE=") {
166
+ continue
167
+ }
168
+ cmd.Env = append(cmd.Env, item)
169
+ }
170
+ cmd.Env = append(cmd.Env, extraEnv...)
171
+ var outBuf, errBuf bytes.Buffer
172
+ cmd.Stdout = &outBuf
173
+ cmd.Stderr = &errBuf
174
+ err := cmd.Run()
175
+ if err != nil {
176
+ var exitErr *exec.ExitError
177
+ if !errors.As(err, &exitErr) {
178
+ t.Fatalf("doctor: %v", err)
179
+ }
180
+ code = exitErr.ExitCode()
181
+ }
182
+ return outBuf.String(), errBuf.String(), code
183
+ }
184
+
185
+ func doctorRepo(t *testing.T, path string) string {
186
+ t.Helper()
187
+ if err := os.MkdirAll(path, 0o755); err != nil {
188
+ t.Fatal(err)
189
+ }
190
+ doctorGit(t, path, "init", "-q")
191
+ doctorGit(t, path, "config", "user.email", "devrites@example.invalid")
192
+ doctorGit(t, path, "config", "user.name", "DevRites Test")
193
+ if err := os.WriteFile(filepath.Join(path, ".fixture"), []byte("fixture\n"), 0o644); err != nil {
194
+ t.Fatal(err)
195
+ }
196
+ doctorGit(t, path, "add", ".fixture")
197
+ doctorGit(t, path, "commit", "-q", "-m", "fixture")
198
+ return path
199
+ }
200
+
201
+ func doctorGit(t *testing.T, dir string, args ...string) {
202
+ t.Helper()
203
+ cmd := exec.Command("git", append([]string{"-C", dir}, args...)...)
204
+ cmd.Env = append(os.Environ(), "GIT_TERMINAL_PROMPT=0")
205
+ if out, err := cmd.CombinedOutput(); err != nil {
206
+ t.Fatalf("git %s: %v\n%s", strings.Join(args, " "), err, out)
207
+ }
208
+ }