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,424 @@
1
+ package toolpolicy
2
+
3
+ import (
4
+ "strings"
5
+ "testing"
6
+ )
7
+
8
+ func TestClassifyGitCommandDestructiveForms(t *testing.T) {
9
+ t.Parallel()
10
+ tests := []struct {
11
+ name string
12
+ command string
13
+ reason ReasonID
14
+ }{
15
+ {"reset hard", "git reset --hard HEAD", ReasonDestructiveReset},
16
+ {"reset default", "git reset HEAD", ReasonDestructiveReset},
17
+ {"reset soft rewrites ref", "git reset --soft HEAD^", ReasonDestructiveHistory},
18
+ {"forced clean", "git clean -fdx", ReasonDestructiveClean},
19
+ {"forced checkout", "git checkout --force main", ReasonDestructiveCheckout},
20
+ {"path checkout", "git checkout HEAD -- file.txt", ReasonDestructiveCheckout},
21
+ {"patch checkout", "git checkout -p file.txt", ReasonDestructiveCheckout},
22
+ {"restore worktree", "git restore --worktree -- file.txt", ReasonDestructiveRestore},
23
+ {"forced switch", "git switch --discard-changes main", ReasonDestructiveSwitch},
24
+ {"forced create switch", "git switch -C main HEAD^", ReasonDestructiveSwitch},
25
+ {"forced tracked remove", "git rm -f file.txt", ReasonDestructiveRemove},
26
+ {"forced index remove", "git update-index --force-remove file.txt", ReasonDestructiveRemove},
27
+ {"branch delete", "git branch -D topic", ReasonDestructiveBranch},
28
+ {"forced branch move", "git branch -f topic HEAD^", ReasonDestructiveBranch},
29
+ {"tag delete", "git tag --delete v1", ReasonDestructiveTag},
30
+ {"update ref delete", "git update-ref -d refs/heads/topic", ReasonDestructiveUpdateRef},
31
+ {"stash drop", "git stash drop stash@{0}", ReasonDestructiveStash},
32
+ {"stash clear", "git stash clear", ReasonDestructiveStash},
33
+ {"reflog expiry", "git reflog expire --expire=now --all", ReasonDestructiveReflog},
34
+ {"prune now gc", "git gc --prune=now", ReasonDestructivePrune},
35
+ {"direct prune", "git prune", ReasonDestructivePrune},
36
+ {"forced worktree remove", "git worktree remove --force ../old", ReasonDestructiveWorktree},
37
+ {"history rebase", "git rebase main", ReasonDestructiveHistory},
38
+ {"history amend", "git commit --amend --no-edit", ReasonDestructiveHistory},
39
+ {"history filter", "git filter-branch -- --all", ReasonDestructiveHistory},
40
+ {"force push", "git push --force-with-lease origin main", ReasonDestructivePushForce},
41
+ {"clustered force push", "git push -fu origin main", ReasonDestructivePushForce},
42
+ {"plus refspec push", "git push origin +main:main", ReasonDestructivePushForce},
43
+ {"delete push flag", "git push --delete origin topic", ReasonDestructivePushDelete},
44
+ {"delete push refspec", "git push origin :topic", ReasonDestructivePushDelete},
45
+ {"pruning fetch", "git fetch --prune origin", ReasonDestructiveRefDeletion},
46
+ {"remote prune", "git remote prune origin", ReasonDestructiveRefDeletion},
47
+ }
48
+ for _, tt := range tests {
49
+ tt := tt
50
+ t.Run(tt.name, func(t *testing.T) {
51
+ t.Parallel()
52
+ got := ClassifyGitCommand(tt.command)
53
+ if got.Verdict != VerdictDestructive || got.ReasonID != tt.reason {
54
+ t.Fatalf("ClassifyGitCommand(%q) = (%q, %q), want (%q, %q): %#v",
55
+ tt.command, got.Verdict, got.ReasonID, VerdictDestructive, tt.reason, got)
56
+ }
57
+ if got.Digest == "" {
58
+ t.Fatalf("destructive result lacks normalized digest: %#v", got)
59
+ }
60
+ })
61
+ }
62
+ }
63
+
64
+ func TestClassifyGitCommandSafeControls(t *testing.T) {
65
+ t.Parallel()
66
+ commands := []string{
67
+ "",
68
+ "git status",
69
+ "git diff -- reset",
70
+ "git show HEAD",
71
+ "git switch main",
72
+ "git add file.txt",
73
+ "git commit -m message",
74
+ "git push origin main",
75
+ "git clean -ndx",
76
+ "git clean -fn",
77
+ "git clean -- -f",
78
+ "git branch --list",
79
+ "git tag --list",
80
+ "git stash list",
81
+ "git reflog show",
82
+ "git gc",
83
+ "git prune --dry-run",
84
+ "git rm --dry-run file.txt",
85
+ "git rm file.txt",
86
+ "git push --dry-run --force origin main",
87
+ "git reflog expire --dry-run --expire=now --all",
88
+ "git worktree remove --dry-run ../old",
89
+ "git worktree remove ../old",
90
+ "git fetch --dry-run --prune origin",
91
+ "git remote prune --dry-run origin",
92
+ "git replace",
93
+ "git reset --help",
94
+ "git push -- --force",
95
+ `grep 'git reset --hard' notes.txt`,
96
+ `printf '%s\n' 'git clean -fdx'`,
97
+ `sh -c "printf '%s\n' 'git reset --hard'"`,
98
+ `eval "printf '%s\n' 'git clean -fdx'"`,
99
+ `echo reset clean git`,
100
+ `/tmp/git-helper reset --hard`,
101
+ `printf git reset --hard`,
102
+ `git status "$path"`,
103
+ `command -v git`,
104
+ `git --version`,
105
+ }
106
+ for _, command := range commands {
107
+ command := command
108
+ t.Run(command, func(t *testing.T) {
109
+ t.Parallel()
110
+ got := ClassifyGitCommand(command)
111
+ if got.Verdict != VerdictSafe {
112
+ t.Fatalf("ClassifyGitCommand(%q) = %#v, want safe", command, got)
113
+ }
114
+ })
115
+ }
116
+ }
117
+
118
+ func TestClassifyGitCommandGlobalOptionsAndAliases(t *testing.T) {
119
+ t.Parallel()
120
+ tests := []struct {
121
+ name string
122
+ command string
123
+ verdict Verdict
124
+ reason ReasonID
125
+ }{
126
+ {"dash C", "git -C repo reset --hard", VerdictDestructive, ReasonDestructiveReset},
127
+ {"attached dash C", "git -Crepo clean -fd", VerdictDestructive, ReasonDestructiveClean},
128
+ {"work trees", "git --git-dir=.git --work-tree=. clean -fd", VerdictDestructive, ReasonDestructiveClean},
129
+ {"namespace value", "git --namespace=ns --no-pager reset --hard", VerdictDestructive, ReasonDestructiveReset},
130
+ {"repeated config", "git -c user.name=Dev -c core.abbrev=9 status", VerdictSafe, ""},
131
+ {"destructive alias", "git -c alias.nuke='reset --hard' nuke", VerdictDestructive, ReasonDestructiveReset},
132
+ {"benign alias", "git -c alias.st=status st", VerdictSafe, ""},
133
+ {"alias keeps args", "git -c alias.wipe=clean wipe -fdx", VerdictDestructive, ReasonDestructiveClean},
134
+ {"payload alias with external-shaped name", "git -c alias.gui='reset --hard' gui", VerdictDestructive, ReasonDestructiveReset},
135
+ {"nested alias", "git -c alias.a=b -c alias.b='reset --hard' a", VerdictDestructive, ReasonDestructiveReset},
136
+ {"alias cycle", "git -c alias.a=b -c alias.b=a a", VerdictAmbiguous, ReasonAmbiguousAlias},
137
+ {"alias cannot shadow builtin", "git -c alias.reset=status reset --hard", VerdictDestructive, ReasonDestructiveReset},
138
+ {"shell alias", "git -c alias.nuke='!git reset --hard' nuke", VerdictAmbiguous, ReasonAmbiguousAlias},
139
+ {"unknown ambient alias", "git nuke", VerdictAmbiguous, ReasonAmbiguousAlias},
140
+ {"malformed global value", "git -C", VerdictAmbiguous, ReasonAmbiguousGlobalOption},
141
+ {"unsupported global", "git --mystery reset --hard", VerdictAmbiguous, ReasonAmbiguousGlobalOption},
142
+ {"stdin ref updates", "git update-ref --stdin", VerdictAmbiguous, ReasonAmbiguousStdin},
143
+ }
144
+ for _, tt := range tests {
145
+ tt := tt
146
+ t.Run(tt.name, func(t *testing.T) {
147
+ t.Parallel()
148
+ got := ClassifyGitCommand(tt.command)
149
+ if got.Verdict != tt.verdict || got.ReasonID != tt.reason {
150
+ t.Fatalf("ClassifyGitCommand(%q) = (%q, %q), want (%q, %q): %#v",
151
+ tt.command, got.Verdict, got.ReasonID, tt.verdict, tt.reason, got)
152
+ }
153
+ })
154
+ }
155
+ }
156
+
157
+ func TestClassifyGitCommandCriticBypasses(t *testing.T) {
158
+ t.Parallel()
159
+ tests := []struct {
160
+ name string
161
+ command string
162
+ verdict Verdict
163
+ reason ReasonID
164
+ }{
165
+ {
166
+ "semantic clean config",
167
+ "git -c clean.requireForce=false clean -d",
168
+ VerdictAmbiguous,
169
+ ReasonAmbiguousGlobalOption,
170
+ },
171
+ {
172
+ "checkout paths without separator",
173
+ "git checkout HEAD .",
174
+ VerdictDestructive,
175
+ ReasonDestructiveCheckout,
176
+ },
177
+ {
178
+ "nohup wrapper",
179
+ "nohup git clean -fd </dev/null",
180
+ VerdictDestructive,
181
+ ReasonDestructiveClean,
182
+ },
183
+ {
184
+ "exec wrapper",
185
+ "exec git reset --hard HEAD",
186
+ VerdictDestructive,
187
+ ReasonDestructiveReset,
188
+ },
189
+ {
190
+ "versioned interpreter",
191
+ `python3.12 -c "import os; os.system('git reset --hard HEAD')"`,
192
+ VerdictAmbiguous,
193
+ ReasonAmbiguousDynamic,
194
+ },
195
+ }
196
+ for _, tt := range tests {
197
+ tt := tt
198
+ t.Run(tt.name, func(t *testing.T) {
199
+ t.Parallel()
200
+ got := ClassifyGitCommand(tt.command)
201
+ if got.Verdict != tt.verdict || got.ReasonID != tt.reason {
202
+ t.Fatalf("ClassifyGitCommand(%q) = (%q, %q), want (%q, %q): %#v",
203
+ tt.command, got.Verdict, got.ReasonID, tt.verdict, tt.reason, got)
204
+ }
205
+ })
206
+ }
207
+ }
208
+
209
+ func TestClassifyGitCommandCriticBypassSafeNeighbors(t *testing.T) {
210
+ t.Parallel()
211
+ commands := []string{
212
+ "git -c color.ui=false clean -nd",
213
+ "git -c color.ui=false checkout -b topic HEAD",
214
+ "git -c color.ui=false reset --hard HEAD",
215
+ "git -c clean.requireForce=false clean -nd",
216
+ "git -c clean.requireForce=false clean --help",
217
+ "git -c user.name=Dev status",
218
+ "git checkout -b topic HEAD",
219
+ "git checkout --detach HEAD",
220
+ "nohup git status",
221
+ "nohup -- git status",
222
+ "nohup --help",
223
+ "exec git status",
224
+ `python3.12 -c "print(1)"`,
225
+ "python3.12-config --help",
226
+ }
227
+ for _, command := range commands {
228
+ command := command
229
+ t.Run(command, func(t *testing.T) {
230
+ t.Parallel()
231
+ got := ClassifyGitCommand(command)
232
+ if command == "git -c color.ui=false reset --hard HEAD" {
233
+ if got.Verdict != VerdictDestructive || got.ReasonID != ReasonDestructiveReset {
234
+ t.Fatalf("ClassifyGitCommand(%q) = %#v, want destructive reset", command, got)
235
+ }
236
+ return
237
+ }
238
+ if got.Verdict != VerdictSafe {
239
+ t.Fatalf("ClassifyGitCommand(%q) = %#v, want safe", command, got)
240
+ }
241
+ })
242
+ }
243
+ }
244
+
245
+ func TestClassifyGitCommandListsWrappersAndQuoting(t *testing.T) {
246
+ t.Parallel()
247
+ tests := []struct {
248
+ name string
249
+ command string
250
+ count int
251
+ }{
252
+ {"semicolon", "echo ok; git reset --hard; echo done", 1},
253
+ {"newline", "echo ok\ngit clean -fd\ntrue", 1},
254
+ {"and", "true && git restore -- file", 1},
255
+ {"or", "false || git branch -D topic", 1},
256
+ {"pipeline", "printf x | git reset --hard | cat", 1},
257
+ {"two operations", "git reset --hard && git clean -fdx", 2},
258
+ {"quoted executable", `"/usr/bin/git" "reset" "--hard"`, 1},
259
+ {"transparent wrappers", "env X=1 command rtk git reset --hard", 1},
260
+ {"rtk raw proxy", "rtk proxy git reset --hard", 1},
261
+ {"timeout nice", "timeout --signal TERM 5 nice -n 2 git clean -fd", 1},
262
+ {"escaped executable", `g\it re\set --ha\rd`, 1},
263
+ {"quoted operator path", `git restore -- 'dir/a;b && c|d'`, 1},
264
+ {"redirection prefix", `>/tmp/a git reset --hard`, 1},
265
+ {"redirection suffix", `git clean -fd 2>/tmp/error`, 1},
266
+ {"comment", "echo ok # git reset --hard\ntrue", 0},
267
+ }
268
+ for _, tt := range tests {
269
+ tt := tt
270
+ t.Run(tt.name, func(t *testing.T) {
271
+ t.Parallel()
272
+ got := ClassifyGitCommand(tt.command)
273
+ if tt.count == 0 {
274
+ if got.Verdict != VerdictSafe {
275
+ t.Fatalf("ClassifyGitCommand(%q) = %#v, want safe", tt.command, got)
276
+ }
277
+ return
278
+ }
279
+ if got.Verdict != VerdictDestructive {
280
+ t.Fatalf("ClassifyGitCommand(%q) = %#v, want destructive", tt.command, got)
281
+ }
282
+ destructive := 0
283
+ for _, finding := range got.Findings {
284
+ if finding.Verdict == VerdictDestructive {
285
+ destructive++
286
+ }
287
+ }
288
+ if destructive != tt.count {
289
+ t.Fatalf("ClassifyGitCommand(%q) has %d destructive findings, want %d: %#v",
290
+ tt.command, destructive, tt.count, got)
291
+ }
292
+ })
293
+ }
294
+ }
295
+
296
+ func TestClassifyGitCommandAmbiguousForms(t *testing.T) {
297
+ t.Parallel()
298
+ tests := []struct {
299
+ name string
300
+ command string
301
+ reason ReasonID
302
+ }{
303
+ {"unmatched quote", `git reset --hard "HEAD`, ReasonAmbiguousSyntax},
304
+ {"command substitution", `git reset --hard "$(git rev-parse HEAD)"`, ReasonAmbiguousDynamic},
305
+ {"backticks", "git clean -fd `printf path`", ReasonAmbiguousDynamic},
306
+ {"dynamic executable", `"$cmd" reset --hard`, ReasonAmbiguousDynamic},
307
+ {"dynamic git verb", `git "$verb" --hard`, ReasonAmbiguousDynamic},
308
+ {"dynamic push refspec", `git push "$refspec"`, ReasonAmbiguousDynamic},
309
+ {"dynamic wrapper value", `env TARGET="$target" git reset --hard`, ReasonAmbiguousDynamic},
310
+ {"dynamic redirect", `git clean -fd >"$log"`, ReasonAmbiguousDynamic},
311
+ {"checkout operand can be branch or path", `git checkout file.txt`, ReasonAmbiguousSyntax},
312
+ {"eval", `eval 'git reset --hard'`, ReasonAmbiguousDynamic},
313
+ {"shell interpreter", `sh -c 'git clean -fdx'`, ReasonAmbiguousDynamic},
314
+ {"node interpreter", `node -e "require('child_process').execSync('git reset --hard')"`, ReasonAmbiguousDynamic},
315
+ {"env split", `env -S 'git reset --hard'`, ReasonAmbiguousDynamic},
316
+ {"heredoc", "sh <<'EOF'\ngit reset --hard\nEOF", ReasonAmbiguousDynamic},
317
+ {"compound", `(git reset --hard)`, ReasonAmbiguousSyntax},
318
+ }
319
+ for _, tt := range tests {
320
+ tt := tt
321
+ t.Run(tt.name, func(t *testing.T) {
322
+ t.Parallel()
323
+ got := ClassifyGitCommand(tt.command)
324
+ if got.Verdict != VerdictAmbiguous || got.ReasonID != tt.reason {
325
+ t.Fatalf("ClassifyGitCommand(%q) = (%q, %q), want (%q, %q): %#v",
326
+ tt.command, got.Verdict, got.ReasonID, VerdictAmbiguous, tt.reason, got)
327
+ }
328
+ if got.Remediation != directCommandRemediation {
329
+ t.Fatalf("ambiguous remediation = %q, want %q", got.Remediation, directCommandRemediation)
330
+ }
331
+ })
332
+ }
333
+ }
334
+
335
+ func TestClassifyGitCommandAmbiguityOutranksDestructiveFinding(t *testing.T) {
336
+ t.Parallel()
337
+ got := ClassifyGitCommand(`git reset --hard; eval 'git clean -fd'`)
338
+ if got.Verdict != VerdictAmbiguous || got.ReasonID != ReasonAmbiguousDynamic {
339
+ t.Fatalf("result = %#v, want dynamic ambiguity as primary", got)
340
+ }
341
+ if len(got.Findings) != 2 {
342
+ t.Fatalf("findings = %#v, want both direct and ambiguous operations", got.Findings)
343
+ }
344
+ }
345
+
346
+ func TestClassifyGitCommandDigestIsCanonicalAndExact(t *testing.T) {
347
+ t.Parallel()
348
+ equivalent := []string{
349
+ `git reset --hard HEAD`,
350
+ `"git" 'reset' --hard HEAD`,
351
+ `g\it reset --hard HEAD;`,
352
+ }
353
+ want := ClassifyGitCommand(equivalent[0])
354
+ for _, command := range equivalent[1:] {
355
+ got := ClassifyGitCommand(command)
356
+ if got.Digest != want.Digest {
357
+ t.Fatalf("equivalent command %q normalized differently:\n%#v\n%#v", command, want, got)
358
+ }
359
+ }
360
+
361
+ for _, command := range []string{
362
+ `git reset --hard HEAD^`,
363
+ `echo ok; git reset --hard HEAD`,
364
+ `timeout 5 git reset --hard HEAD`,
365
+ `git reset --hard HEAD &`,
366
+ } {
367
+ got := ClassifyGitCommand(command)
368
+ if got.Digest == want.Digest {
369
+ t.Fatalf("different exact operation %q reused digest %q", command, got.Digest)
370
+ }
371
+ }
372
+
373
+ literal := ClassifyGitCommand(`git reset --hard '$ref'`)
374
+ dynamic := ClassifyGitCommand(`git reset --hard "$ref"`)
375
+ if literal.Digest == dynamic.Digest {
376
+ t.Fatalf("literal and dynamic tokens reused digest %q", literal.Digest)
377
+ }
378
+ }
379
+
380
+ func TestClassifyGitCommandBound(t *testing.T) {
381
+ t.Parallel()
382
+ got := ClassifyGitCommand(strings.Repeat("x", MaxCommandBytes+1))
383
+ if got.Verdict != VerdictAmbiguous || got.ReasonID != ReasonInputTooLarge {
384
+ t.Fatalf("oversized result = %#v", got)
385
+ }
386
+ if got.Digest != "" {
387
+ t.Fatalf("oversized input must not receive an authorizable digest: %#v", got)
388
+ }
389
+ }
390
+
391
+ func FuzzClassifyGitCommand(f *testing.F) {
392
+ for _, seed := range []string{
393
+ "",
394
+ "git status",
395
+ "git reset --hard HEAD",
396
+ "git -C repo clean -fdx",
397
+ "git -c alias.nuke='reset --hard' nuke",
398
+ "env X=1 command rtk git push --force origin main",
399
+ "grep 'git reset --hard' notes",
400
+ "git reset --hard \"unterminated",
401
+ "sh -c 'git clean -fd'",
402
+ "\x00\xffgit reset --hard",
403
+ } {
404
+ f.Add(seed)
405
+ }
406
+
407
+ f.Fuzz(func(t *testing.T, command string) {
408
+ got := ClassifyGitCommand(command)
409
+ switch got.Verdict {
410
+ case VerdictSafe, VerdictDestructive, VerdictAmbiguous:
411
+ default:
412
+ t.Fatalf("invalid verdict %q for %q", got.Verdict, command)
413
+ }
414
+ if got.Verdict != VerdictSafe && got.ReasonID == "" {
415
+ t.Fatalf("non-safe result lacks reason: %#v", got)
416
+ }
417
+ if got.Digest != "" {
418
+ prefix := NormalizationVersion + ":sha256:"
419
+ if len(got.Digest) != len(prefix)+64 || !strings.HasPrefix(got.Digest, prefix) {
420
+ t.Fatalf("invalid digest contract: %#v", got)
421
+ }
422
+ }
423
+ })
424
+ }