devrites 3.0.7 → 3.2.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 (484) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +82 -50
  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/0012-semantic-workspace-upgrades.md +77 -0
  12. package/docs/adr/README.md +4 -0
  13. package/docs/agents/triage-labels.md +7 -7
  14. package/docs/architecture.md +179 -141
  15. package/docs/cli.md +47 -12
  16. package/docs/command-map.md +61 -38
  17. package/docs/engine/agent-contract.md +92 -15
  18. package/docs/engine/commands.md +248 -72
  19. package/docs/engine/state-schema.md +52 -23
  20. package/docs/engine/workspace-schema.md +103 -15
  21. package/docs/extensions.md +1 -1
  22. package/docs/flow.md +86 -51
  23. package/docs/harness-compliance.md +29 -5
  24. package/docs/orchestration.md +107 -79
  25. package/docs/quick-reference.md +11 -3
  26. package/docs/release.md +4 -3
  27. package/docs/skills.md +67 -38
  28. package/docs/usage.md +84 -39
  29. package/engine/cmd/releasepack/main.go +219 -0
  30. package/engine/cmd/releasepack/main_test.go +170 -0
  31. package/engine/commands.go +170 -23
  32. package/engine/git_guard.go +187 -0
  33. package/engine/git_guard_test.go +283 -0
  34. package/engine/hookpolicy.go +53 -55
  35. package/engine/hookpolicy_test.go +91 -1
  36. package/engine/hooks.go +296 -75
  37. package/engine/hooks_events_test.go +276 -6
  38. package/engine/hooks_workspace.go +640 -159
  39. package/engine/internal/devritespaths/paths.go +65 -10
  40. package/engine/internal/devritespaths/paths_test.go +110 -0
  41. package/engine/internal/doctor/doctor.go +153 -23
  42. package/engine/internal/doctor/doctor_test.go +74 -0
  43. package/engine/internal/forge/forge.go +940 -0
  44. package/engine/internal/forge/forge_test.go +576 -0
  45. package/engine/internal/forge/git.go +245 -0
  46. package/engine/internal/forge/liveness_unix.go +48 -0
  47. package/engine/internal/forge/liveness_windows.go +67 -0
  48. package/engine/internal/forge/manifest.go +402 -0
  49. package/engine/internal/gate/gate.go +90 -71
  50. package/engine/internal/gate/gate_test.go +71 -2
  51. package/engine/internal/harness/compliance.go +18 -24
  52. package/engine/internal/harness/harness.go +37 -44
  53. package/engine/internal/harness/harness_test.go +21 -5
  54. package/engine/internal/install/install.go +486 -38
  55. package/engine/internal/install/install_test.go +383 -10
  56. package/engine/internal/iohooks/iohooks.go +350 -59
  57. package/engine/internal/iohooks/iohooks_test.go +421 -1
  58. package/engine/internal/lib/buildreadiness.go +43 -18
  59. package/engine/internal/lib/clarifyreturn.go +91 -0
  60. package/engine/internal/lib/clarifyreturn_test.go +123 -0
  61. package/engine/internal/lib/context.go +54 -15
  62. package/engine/internal/lib/cursor_compat_test.go +8 -0
  63. package/engine/internal/lib/extensions.go +2 -3
  64. package/engine/internal/lib/gitauthority.go +601 -0
  65. package/engine/internal/lib/gitauthority_test.go +346 -0
  66. package/engine/internal/lib/jsonout.go +25 -11
  67. package/engine/internal/lib/jsonout_test.go +16 -0
  68. package/engine/internal/lib/lanes.go +14 -8
  69. package/engine/internal/lib/observability_test.go +358 -0
  70. package/engine/internal/lib/packageexistence.go +151 -35
  71. package/engine/internal/lib/packageexistence_test.go +163 -0
  72. package/engine/internal/lib/progress.go +11 -9
  73. package/engine/internal/lib/provenance.go +462 -0
  74. package/engine/internal/lib/provenance_test.go +154 -0
  75. package/engine/internal/lib/readiness_contract.json +118 -0
  76. package/engine/internal/lib/readinessartifact.go +533 -0
  77. package/engine/internal/lib/readinessartifact_test.go +422 -0
  78. package/engine/internal/lib/reconcile.go +712 -88
  79. package/engine/internal/lib/reconcile_test.go +335 -16
  80. package/engine/internal/lib/recoveryattempts.go +298 -0
  81. package/engine/internal/lib/recoveryattempts_test.go +215 -0
  82. package/engine/internal/lib/resolve.go +23 -19
  83. package/engine/internal/lib/runbook_context_test.go +20 -0
  84. package/engine/internal/lib/session.go +701 -9
  85. package/engine/internal/lib/session_test.go +80 -13
  86. package/engine/internal/lib/testintegrity.go +33 -37
  87. package/engine/internal/lib/testintegrity_test.go +63 -1
  88. package/engine/internal/migrate/migrate.go +81 -1
  89. package/engine/internal/migrate/migrate_test.go +22 -1
  90. package/engine/internal/reason/reason.go +180 -0
  91. package/engine/internal/reason/reason_test.go +35 -0
  92. package/engine/internal/rootfacts/facts.go +466 -0
  93. package/engine/internal/rootfacts/facts_test.go +306 -0
  94. package/engine/internal/safepath/safepath.go +55 -0
  95. package/engine/internal/safepath/safepath_test.go +69 -0
  96. package/engine/internal/safepath/safepath_windows_test.go +26 -0
  97. package/engine/internal/state/clarify_transition.go +165 -0
  98. package/engine/internal/state/clarify_transition_test.go +130 -0
  99. package/engine/internal/state/cmd/workflowmanifest/main.go +12 -8
  100. package/engine/internal/state/cursor.go +58 -0
  101. package/engine/internal/state/cursor_test.go +42 -1
  102. package/engine/internal/state/feature.go +35 -48
  103. package/engine/internal/state/schema.go +90 -37
  104. package/engine/internal/state/snapshot.go +18 -2
  105. package/engine/internal/state/state_test.go +187 -7
  106. package/engine/internal/state/status.go +48 -11
  107. package/engine/internal/state/workflow_manifest.json +231 -50
  108. package/engine/internal/toolpolicy/classifier.go +533 -0
  109. package/engine/internal/toolpolicy/classifier_test.go +424 -0
  110. package/engine/internal/toolpolicy/git.go +616 -0
  111. package/engine/internal/toolpolicy/scanner.go +382 -0
  112. package/engine/main.go +160 -77
  113. package/engine/observability_cli_test.go +52 -0
  114. package/engine/root_routing_test.go +277 -0
  115. package/engine/testdata/golden/TestParityBuildReadiness/arg=approved.golden +1 -1
  116. package/engine/testdata/golden/TestParityBuildReadiness/arg=emptystatus.golden +1 -1
  117. package/engine/testdata/golden/TestParityBuildReadiness/arg=legacycontract.golden +1 -0
  118. package/engine/testdata/golden/TestParityBuildReadiness/arg=noclarify.golden +1 -0
  119. package/engine/testdata/golden/TestParityBuildReadiness/arg=novet.golden +1 -0
  120. package/engine/testdata/golden/TestParityBuildReadiness/arg=stalevet.golden +1 -0
  121. package/engine/testdata/golden/TestParityBuildReadiness/arg=trailhash.golden +1 -1
  122. package/engine/testdata/golden/TestParityBuildReadiness/arg=trailpipe.golden +1 -1
  123. package/engine/testdata/golden/TestParityBuildReadiness/arg=vetnotready.golden +1 -0
  124. package/engine/testdata/golden/TestParityProgress/arg=allbuilt.golden +1 -1
  125. package/engine/testdata/golden/TestParityProgress/arg=done.golden +1 -1
  126. package/engine/testdata/golden/TestParityProgress/arg=mid.golden +1 -1
  127. package/engine/testdata/golden/TestParityProgress/arg=nophase.golden +1 -1
  128. package/engine/testdata/golden/TestParityProgress/arg=noslice.golden +1 -1
  129. package/engine/testdata/golden/TestParityProgress/arg=plan.golden +1 -1
  130. package/engine/testdata/golden/TestParityProgress/arg=seal.golden +1 -1
  131. package/engine/testdata/golden/TestParityReconcile/check-clean.golden +1 -1
  132. package/engine/testdata/golden/TestParityReconcile/inline-fallback.golden +1 -0
  133. package/engine/testdata/golden/TestParityReconcile/snapshot-no-allowlist.golden +1 -0
  134. package/engine/testdata/golden/TestParityWrightScope/devrites-edit-denied.golden +2 -0
  135. package/engine/testdata/golden/TestParityWrightScope/out-of-scope-enforce-denies.golden +1 -1
  136. package/engine/tests/adr_0011_define_plan_test.go +30 -0
  137. package/engine/tests/budget_test.go +19 -8
  138. package/engine/tests/concurrency_cli_test.go +7 -1
  139. package/engine/tests/doctor_cli_test.go +148 -2
  140. package/engine/tests/forge_cli_test.go +463 -0
  141. package/engine/tests/gate_test.go +63 -8
  142. package/engine/tests/hook_test.go +260 -8
  143. package/engine/tests/hooks_io_test.go +94 -3
  144. package/engine/tests/json_contract_test.go +127 -2
  145. package/engine/tests/migrate_cli_test.go +2 -2
  146. package/engine/tests/parity_buildreadiness_test.go +167 -10
  147. package/engine/tests/parity_learnings_test.go +16 -17
  148. package/engine/tests/parity_reconcile_test.go +20 -17
  149. package/engine/tests/parity_resolve_test.go +12 -11
  150. package/engine/tests/parity_test.go +21 -17
  151. package/pack/.claude/agents/devrites-code-reviewer.md +62 -48
  152. package/pack/.claude/agents/devrites-devex-reviewer.md +69 -53
  153. package/pack/.claude/agents/devrites-doubt-reviewer.md +29 -22
  154. package/pack/.claude/agents/devrites-evidence-scout.md +69 -0
  155. package/pack/.claude/agents/devrites-forge-judge.md +74 -61
  156. package/pack/.claude/agents/devrites-frontend-reviewer.md +48 -39
  157. package/pack/.claude/agents/devrites-performance-reviewer.md +49 -40
  158. package/pack/.claude/agents/devrites-plan-drafter.md +73 -0
  159. package/pack/.claude/agents/devrites-plan-reviewer.md +80 -47
  160. package/pack/.claude/agents/devrites-proof-runner.md +74 -0
  161. package/pack/.claude/agents/devrites-retrospector.md +48 -45
  162. package/pack/.claude/agents/devrites-security-auditor.md +46 -36
  163. package/pack/.claude/agents/devrites-simplifier-reviewer.md +50 -46
  164. package/pack/.claude/agents/devrites-slice-wright.md +153 -165
  165. package/pack/.claude/agents/devrites-spec-reviewer.md +34 -32
  166. package/pack/.claude/agents/devrites-strategy-reviewer.md +63 -34
  167. package/pack/.claude/agents/devrites-test-analyst.md +40 -30
  168. package/pack/.claude/agents/devrites-upgrade-planner.md +100 -0
  169. package/pack/.claude/settings.json +2 -1
  170. package/pack/.claude/skills/devrites-audit/SKILL.md +40 -61
  171. package/pack/.claude/skills/devrites-debug-recovery/SKILL.md +24 -13
  172. package/pack/.claude/skills/devrites-debug-recovery/reference/build-the-loop.md +24 -21
  173. package/pack/.claude/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +34 -6
  174. package/pack/.claude/skills/devrites-debug-recovery/reference/instrumentation.md +2 -2
  175. package/pack/.claude/skills/devrites-doubt/SKILL.md +25 -17
  176. package/pack/.claude/skills/devrites-interview/SKILL.md +53 -52
  177. package/pack/.claude/skills/devrites-lib/SKILL.md +12 -9
  178. package/pack/.claude/skills/devrites-lib/reference/intent-map.md +4 -2
  179. package/pack/.claude/skills/devrites-lib/reference/parallel-dispatch.md +82 -137
  180. package/pack/.claude/skills/devrites-lib/reference/reply-contract.md +8 -1
  181. package/pack/.claude/skills/devrites-lib/reference/standards/README.md +38 -55
  182. package/pack/.claude/skills/devrites-lib/reference/standards/afk-hitl.md +53 -27
  183. package/pack/.claude/skills/devrites-lib/reference/standards/agents.md +200 -190
  184. package/pack/.claude/skills/devrites-lib/reference/standards/anti-patterns.md +5 -15
  185. package/pack/.claude/skills/devrites-lib/reference/standards/ci-cd.md +27 -58
  186. package/pack/.claude/skills/devrites-lib/reference/standards/code-review.md +16 -52
  187. package/pack/.claude/skills/devrites-lib/reference/standards/coding-style.md +2 -10
  188. package/pack/.claude/skills/devrites-lib/reference/standards/context-hygiene.md +18 -61
  189. package/pack/.claude/skills/devrites-lib/reference/standards/core.md +23 -18
  190. package/pack/.claude/skills/devrites-lib/reference/standards/deprecation.md +17 -57
  191. package/pack/.claude/skills/devrites-lib/reference/standards/development-workflow.md +4 -33
  192. package/pack/.claude/skills/devrites-lib/reference/standards/git-workflow.md +4 -20
  193. package/pack/.claude/skills/devrites-lib/reference/standards/hooks.md +3 -14
  194. package/pack/.claude/skills/devrites-lib/reference/standards/patterns.md +9 -25
  195. package/pack/.claude/skills/devrites-lib/reference/standards/performance.md +0 -9
  196. package/pack/.claude/skills/devrites-lib/reference/standards/principles.md +1 -3
  197. package/pack/.claude/skills/devrites-lib/reference/standards/security.md +17 -7
  198. package/pack/.claude/skills/devrites-lib/reference/standards/testing.md +1 -27
  199. package/pack/.claude/skills/devrites-lib/reference/standards/tooling.md +46 -50
  200. package/pack/.claude/skills/devrites-lib/reference/workspace-artifact-schema.md +40 -10
  201. package/pack/.claude/skills/devrites-source-driven/SKILL.md +23 -22
  202. package/pack/.claude/skills/rite/SKILL.md +10 -6
  203. package/pack/.claude/skills/rite/reference/menu.md +13 -7
  204. package/pack/.claude/skills/rite-adopt/SKILL.md +33 -37
  205. package/pack/.claude/skills/rite-autocomplete/SKILL.md +33 -28
  206. package/pack/.claude/skills/rite-autocomplete/reference/loop.md +21 -21
  207. package/pack/.claude/skills/rite-autocomplete/reference/stop-conditions.md +15 -6
  208. package/pack/.claude/skills/rite-build/SKILL.md +35 -30
  209. package/pack/.claude/skills/rite-build/reference/afk-discipline.md +41 -39
  210. package/pack/.claude/skills/rite-build/reference/evidence-standard.md +10 -0
  211. package/pack/.claude/skills/rite-build/reference/forge.md +186 -156
  212. package/pack/.claude/skills/rite-build/reference/one-slice-cycle.md +4 -3
  213. package/pack/.claude/skills/rite-build/reference/phase-contract.md +96 -175
  214. package/pack/.claude/skills/rite-build/reference/spec-drift-guard.md +10 -4
  215. package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +129 -134
  216. package/pack/.claude/skills/rite-clarify/SKILL.md +90 -0
  217. package/pack/.claude/skills/rite-clarify/reference/decision-coverage.md +61 -0
  218. package/pack/.claude/skills/rite-converge/SKILL.md +35 -25
  219. package/pack/.claude/skills/rite-define/SKILL.md +58 -32
  220. package/pack/.claude/skills/rite-define/reference/gates.md +16 -15
  221. package/pack/.claude/skills/rite-define/reference/plan-template.md +18 -8
  222. package/pack/.claude/skills/rite-frame/reference/failure-modes.md +22 -24
  223. package/pack/.claude/skills/rite-plan/SKILL.md +47 -16
  224. package/pack/.claude/skills/rite-plan/reference/task-breakdown.md +4 -0
  225. package/pack/.claude/skills/rite-polish/SKILL.md +27 -23
  226. package/pack/.claude/skills/rite-prototype/SKILL.md +25 -26
  227. package/pack/.claude/skills/rite-prove/SKILL.md +27 -17
  228. package/pack/.claude/skills/rite-resolve/SKILL.md +12 -11
  229. package/pack/.claude/skills/rite-resolve/reference/answer-protocol.md +3 -0
  230. package/pack/.claude/skills/rite-review/SKILL.md +37 -28
  231. package/pack/.claude/skills/rite-seal/reference/phase-contract.md +27 -92
  232. package/pack/.claude/skills/rite-seal/reference/risk-and-rollback.md +13 -0
  233. package/pack/.claude/skills/rite-ship/reference/design-memory.md +31 -36
  234. package/pack/.claude/skills/rite-spec/SKILL.md +84 -135
  235. package/pack/.claude/skills/rite-spec/reference/investigation.md +37 -33
  236. package/pack/.claude/skills/rite-spec/reference/question-protocol.md +6 -2
  237. package/pack/.claude/skills/rite-spec/reference/spec-checklists.md +25 -24
  238. package/pack/.claude/skills/rite-spec/reference/spec-template.md +9 -2
  239. package/pack/.claude/skills/rite-spec/reference/state-workspace.md +13 -3
  240. package/pack/.claude/skills/rite-temper/SKILL.md +62 -47
  241. package/pack/.claude/skills/rite-temper/reference/review-dimensions.md +24 -22
  242. package/pack/.claude/skills/rite-upgrade/SKILL.md +125 -0
  243. package/pack/.claude/skills/rite-vet/SKILL.md +114 -113
  244. package/pack/.claude/skills/rite-vet/reference/artifacts.md +50 -9
  245. package/pack/.claude/skills/rite-vet/reference/review-axes.md +39 -37
  246. package/pack/generated/claude/agents/devrites-code-reviewer.md +62 -48
  247. package/pack/generated/claude/agents/devrites-devex-reviewer.md +69 -53
  248. package/pack/generated/claude/agents/devrites-doubt-reviewer.md +29 -22
  249. package/pack/generated/claude/agents/devrites-evidence-scout.md +69 -0
  250. package/pack/generated/claude/agents/devrites-forge-judge.md +74 -61
  251. package/pack/generated/claude/agents/devrites-frontend-reviewer.md +48 -39
  252. package/pack/generated/claude/agents/devrites-performance-reviewer.md +49 -40
  253. package/pack/generated/claude/agents/devrites-plan-drafter.md +73 -0
  254. package/pack/generated/claude/agents/devrites-plan-reviewer.md +80 -47
  255. package/pack/generated/claude/agents/devrites-proof-runner.md +74 -0
  256. package/pack/generated/claude/agents/devrites-retrospector.md +48 -45
  257. package/pack/generated/claude/agents/devrites-security-auditor.md +46 -36
  258. package/pack/generated/claude/agents/devrites-simplifier-reviewer.md +50 -46
  259. package/pack/generated/claude/agents/devrites-slice-wright.md +153 -165
  260. package/pack/generated/claude/agents/devrites-spec-reviewer.md +34 -32
  261. package/pack/generated/claude/agents/devrites-strategy-reviewer.md +63 -34
  262. package/pack/generated/claude/agents/devrites-test-analyst.md +40 -30
  263. package/pack/generated/claude/agents/devrites-upgrade-planner.md +100 -0
  264. package/pack/generated/claude/settings.json +2 -1
  265. package/pack/generated/claude/skills/devrites-audit/SKILL.md +40 -61
  266. package/pack/generated/claude/skills/devrites-debug-recovery/SKILL.md +24 -13
  267. package/pack/generated/claude/skills/devrites-debug-recovery/reference/build-the-loop.md +24 -21
  268. package/pack/generated/claude/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +34 -6
  269. package/pack/generated/claude/skills/devrites-debug-recovery/reference/instrumentation.md +2 -2
  270. package/pack/generated/claude/skills/devrites-doubt/SKILL.md +25 -17
  271. package/pack/generated/claude/skills/devrites-interview/SKILL.md +53 -52
  272. package/pack/generated/claude/skills/devrites-lib/SKILL.md +12 -9
  273. package/pack/generated/claude/skills/devrites-lib/reference/intent-map.md +4 -2
  274. package/pack/generated/claude/skills/devrites-lib/reference/parallel-dispatch.md +82 -137
  275. package/pack/generated/claude/skills/devrites-lib/reference/reply-contract.md +8 -1
  276. package/pack/generated/claude/skills/devrites-lib/reference/standards/README.md +38 -55
  277. package/pack/generated/claude/skills/devrites-lib/reference/standards/afk-hitl.md +53 -27
  278. package/pack/generated/claude/skills/devrites-lib/reference/standards/agents.md +200 -190
  279. package/pack/generated/claude/skills/devrites-lib/reference/standards/anti-patterns.md +5 -15
  280. package/pack/generated/claude/skills/devrites-lib/reference/standards/ci-cd.md +27 -58
  281. package/pack/generated/claude/skills/devrites-lib/reference/standards/code-review.md +16 -52
  282. package/pack/generated/claude/skills/devrites-lib/reference/standards/coding-style.md +2 -10
  283. package/pack/generated/claude/skills/devrites-lib/reference/standards/context-hygiene.md +18 -61
  284. package/pack/generated/claude/skills/devrites-lib/reference/standards/core.md +23 -18
  285. package/pack/generated/claude/skills/devrites-lib/reference/standards/deprecation.md +17 -57
  286. package/pack/generated/claude/skills/devrites-lib/reference/standards/development-workflow.md +4 -33
  287. package/pack/generated/claude/skills/devrites-lib/reference/standards/git-workflow.md +4 -20
  288. package/pack/generated/claude/skills/devrites-lib/reference/standards/hooks.md +3 -14
  289. package/pack/generated/claude/skills/devrites-lib/reference/standards/patterns.md +9 -25
  290. package/pack/generated/claude/skills/devrites-lib/reference/standards/performance.md +0 -9
  291. package/pack/generated/claude/skills/devrites-lib/reference/standards/principles.md +1 -3
  292. package/pack/generated/claude/skills/devrites-lib/reference/standards/security.md +17 -7
  293. package/pack/generated/claude/skills/devrites-lib/reference/standards/testing.md +1 -27
  294. package/pack/generated/claude/skills/devrites-lib/reference/standards/tooling.md +46 -50
  295. package/pack/generated/claude/skills/devrites-lib/reference/workspace-artifact-schema.md +40 -10
  296. package/pack/generated/claude/skills/devrites-source-driven/SKILL.md +23 -22
  297. package/pack/generated/claude/skills/rite/SKILL.md +10 -6
  298. package/pack/generated/claude/skills/rite/reference/menu.md +13 -7
  299. package/pack/generated/claude/skills/rite-adopt/SKILL.md +33 -37
  300. package/pack/generated/claude/skills/rite-autocomplete/SKILL.md +33 -28
  301. package/pack/generated/claude/skills/rite-autocomplete/reference/loop.md +21 -21
  302. package/pack/generated/claude/skills/rite-autocomplete/reference/stop-conditions.md +15 -6
  303. package/pack/generated/claude/skills/rite-build/SKILL.md +35 -30
  304. package/pack/generated/claude/skills/rite-build/reference/afk-discipline.md +41 -39
  305. package/pack/generated/claude/skills/rite-build/reference/evidence-standard.md +10 -0
  306. package/pack/generated/claude/skills/rite-build/reference/forge.md +186 -156
  307. package/pack/generated/claude/skills/rite-build/reference/one-slice-cycle.md +4 -3
  308. package/pack/generated/claude/skills/rite-build/reference/phase-contract.md +96 -175
  309. package/pack/generated/claude/skills/rite-build/reference/spec-drift-guard.md +10 -4
  310. package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +129 -134
  311. package/pack/generated/claude/skills/rite-clarify/SKILL.md +90 -0
  312. package/pack/generated/claude/skills/rite-clarify/reference/decision-coverage.md +61 -0
  313. package/pack/generated/claude/skills/rite-converge/SKILL.md +35 -25
  314. package/pack/generated/claude/skills/rite-define/SKILL.md +58 -32
  315. package/pack/generated/claude/skills/rite-define/reference/gates.md +16 -15
  316. package/pack/generated/claude/skills/rite-define/reference/plan-template.md +18 -8
  317. package/pack/generated/claude/skills/rite-frame/reference/failure-modes.md +22 -24
  318. package/pack/generated/claude/skills/rite-plan/SKILL.md +47 -16
  319. package/pack/generated/claude/skills/rite-plan/reference/task-breakdown.md +4 -0
  320. package/pack/generated/claude/skills/rite-polish/SKILL.md +27 -23
  321. package/pack/generated/claude/skills/rite-prototype/SKILL.md +25 -26
  322. package/pack/generated/claude/skills/rite-prove/SKILL.md +27 -17
  323. package/pack/generated/claude/skills/rite-resolve/SKILL.md +12 -11
  324. package/pack/generated/claude/skills/rite-resolve/reference/answer-protocol.md +3 -0
  325. package/pack/generated/claude/skills/rite-review/SKILL.md +37 -28
  326. package/pack/generated/claude/skills/rite-seal/reference/phase-contract.md +27 -92
  327. package/pack/generated/claude/skills/rite-seal/reference/risk-and-rollback.md +13 -0
  328. package/pack/generated/claude/skills/rite-ship/reference/design-memory.md +31 -36
  329. package/pack/generated/claude/skills/rite-spec/SKILL.md +84 -135
  330. package/pack/generated/claude/skills/rite-spec/reference/investigation.md +37 -33
  331. package/pack/generated/claude/skills/rite-spec/reference/question-protocol.md +6 -2
  332. package/pack/generated/claude/skills/rite-spec/reference/spec-checklists.md +25 -24
  333. package/pack/generated/claude/skills/rite-spec/reference/spec-template.md +9 -2
  334. package/pack/generated/claude/skills/rite-spec/reference/state-workspace.md +13 -3
  335. package/pack/generated/claude/skills/rite-temper/SKILL.md +62 -47
  336. package/pack/generated/claude/skills/rite-temper/reference/review-dimensions.md +24 -22
  337. package/pack/generated/claude/skills/rite-upgrade/SKILL.md +125 -0
  338. package/pack/generated/claude/skills/rite-vet/SKILL.md +114 -113
  339. package/pack/generated/claude/skills/rite-vet/reference/artifacts.md +50 -9
  340. package/pack/generated/claude/skills/rite-vet/reference/review-axes.md +39 -37
  341. package/pack/generated/codex/AGENTS.md +5 -2
  342. package/pack/generated/codex/agents/devrites-code-reviewer.toml +69 -47
  343. package/pack/generated/codex/agents/devrites-devex-reviewer.toml +75 -51
  344. package/pack/generated/codex/agents/devrites-doubt-reviewer.toml +35 -20
  345. package/pack/generated/codex/agents/devrites-evidence-scout.toml +75 -0
  346. package/pack/generated/codex/agents/devrites-forge-judge.toml +80 -59
  347. package/pack/generated/codex/agents/devrites-frontend-reviewer.toml +54 -37
  348. package/pack/generated/codex/agents/devrites-performance-reviewer.toml +55 -38
  349. package/pack/generated/codex/agents/devrites-plan-drafter.toml +79 -0
  350. package/pack/generated/codex/agents/devrites-plan-reviewer.toml +82 -47
  351. package/pack/generated/codex/agents/devrites-proof-runner.toml +80 -0
  352. package/pack/generated/codex/agents/devrites-retrospector.toml +54 -43
  353. package/pack/generated/codex/agents/devrites-security-auditor.toml +53 -35
  354. package/pack/generated/codex/agents/devrites-simplifier-reviewer.toml +56 -44
  355. package/pack/generated/codex/agents/devrites-slice-wright.toml +159 -163
  356. package/pack/generated/codex/agents/devrites-spec-reviewer.toml +40 -30
  357. package/pack/generated/codex/agents/devrites-strategy-reviewer.toml +65 -34
  358. package/pack/generated/codex/agents/devrites-test-analyst.toml +46 -28
  359. package/pack/generated/codex/agents/devrites-upgrade-planner.toml +106 -0
  360. package/pack/generated/codex/hooks.json +4 -14
  361. package/pack/generated/codex/skills/devrites-api-interface/SKILL.md +7 -3
  362. package/pack/generated/codex/skills/devrites-audit/SKILL.md +47 -64
  363. package/pack/generated/codex/skills/devrites-browser-proof/SKILL.md +7 -3
  364. package/pack/generated/codex/skills/devrites-debug-recovery/SKILL.md +31 -16
  365. package/pack/generated/codex/skills/devrites-debug-recovery/reference/build-the-loop.md +24 -21
  366. package/pack/generated/codex/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +34 -6
  367. package/pack/generated/codex/skills/devrites-debug-recovery/reference/instrumentation.md +2 -2
  368. package/pack/generated/codex/skills/devrites-doubt/SKILL.md +32 -20
  369. package/pack/generated/codex/skills/devrites-frontend-craft/SKILL.md +7 -3
  370. package/pack/generated/codex/skills/devrites-interview/SKILL.md +60 -55
  371. package/pack/generated/codex/skills/devrites-lib/SKILL.md +19 -12
  372. package/pack/generated/codex/skills/devrites-lib/reference/intent-map.md +4 -2
  373. package/pack/generated/codex/skills/devrites-lib/reference/parallel-dispatch.md +82 -137
  374. package/pack/generated/codex/skills/devrites-lib/reference/reply-contract.md +8 -1
  375. package/pack/generated/codex/skills/devrites-lib/reference/standards/README.md +38 -55
  376. package/pack/generated/codex/skills/devrites-lib/reference/standards/afk-hitl.md +53 -27
  377. package/pack/generated/codex/skills/devrites-lib/reference/standards/agents.md +200 -190
  378. package/pack/generated/codex/skills/devrites-lib/reference/standards/anti-patterns.md +5 -15
  379. package/pack/generated/codex/skills/devrites-lib/reference/standards/ci-cd.md +27 -58
  380. package/pack/generated/codex/skills/devrites-lib/reference/standards/code-review.md +16 -52
  381. package/pack/generated/codex/skills/devrites-lib/reference/standards/coding-style.md +2 -10
  382. package/pack/generated/codex/skills/devrites-lib/reference/standards/context-hygiene.md +18 -61
  383. package/pack/generated/codex/skills/devrites-lib/reference/standards/core.md +23 -18
  384. package/pack/generated/codex/skills/devrites-lib/reference/standards/deprecation.md +17 -57
  385. package/pack/generated/codex/skills/devrites-lib/reference/standards/development-workflow.md +4 -33
  386. package/pack/generated/codex/skills/devrites-lib/reference/standards/git-workflow.md +4 -20
  387. package/pack/generated/codex/skills/devrites-lib/reference/standards/hooks.md +3 -14
  388. package/pack/generated/codex/skills/devrites-lib/reference/standards/patterns.md +9 -25
  389. package/pack/generated/codex/skills/devrites-lib/reference/standards/performance.md +0 -9
  390. package/pack/generated/codex/skills/devrites-lib/reference/standards/principles.md +1 -3
  391. package/pack/generated/codex/skills/devrites-lib/reference/standards/security.md +17 -7
  392. package/pack/generated/codex/skills/devrites-lib/reference/standards/testing.md +1 -27
  393. package/pack/generated/codex/skills/devrites-lib/reference/standards/tooling.md +46 -50
  394. package/pack/generated/codex/skills/devrites-lib/reference/workspace-artifact-schema.md +40 -10
  395. package/pack/generated/codex/skills/devrites-prose-craft/SKILL.md +7 -3
  396. package/pack/generated/codex/skills/devrites-refresh-indexes/SKILL.md +7 -3
  397. package/pack/generated/codex/skills/devrites-source-driven/SKILL.md +29 -24
  398. package/pack/generated/codex/skills/devrites-ux-shape/SKILL.md +7 -3
  399. package/pack/generated/codex/skills/rite/SKILL.md +21 -13
  400. package/pack/generated/codex/skills/rite/reference/menu.md +13 -7
  401. package/pack/generated/codex/skills/rite-adopt/SKILL.md +40 -40
  402. package/pack/generated/codex/skills/rite-autocomplete/SKILL.md +40 -31
  403. package/pack/generated/codex/skills/rite-autocomplete/reference/loop.md +21 -21
  404. package/pack/generated/codex/skills/rite-autocomplete/reference/stop-conditions.md +15 -6
  405. package/pack/generated/codex/skills/rite-build/SKILL.md +42 -33
  406. package/pack/generated/codex/skills/rite-build/reference/afk-discipline.md +41 -39
  407. package/pack/generated/codex/skills/rite-build/reference/evidence-standard.md +10 -0
  408. package/pack/generated/codex/skills/rite-build/reference/forge.md +186 -156
  409. package/pack/generated/codex/skills/rite-build/reference/one-slice-cycle.md +4 -3
  410. package/pack/generated/codex/skills/rite-build/reference/phase-contract.md +96 -175
  411. package/pack/generated/codex/skills/rite-build/reference/spec-drift-guard.md +10 -4
  412. package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +129 -134
  413. package/pack/generated/codex/skills/rite-clarify/SKILL.md +106 -0
  414. package/pack/generated/codex/skills/rite-clarify/reference/decision-coverage.md +61 -0
  415. package/pack/generated/codex/skills/rite-converge/SKILL.md +42 -28
  416. package/pack/generated/codex/skills/rite-customize/SKILL.md +7 -3
  417. package/pack/generated/codex/skills/rite-define/SKILL.md +65 -35
  418. package/pack/generated/codex/skills/rite-define/reference/gates.md +16 -15
  419. package/pack/generated/codex/skills/rite-define/reference/plan-template.md +18 -8
  420. package/pack/generated/codex/skills/rite-doctor/SKILL.md +7 -3
  421. package/pack/generated/codex/skills/rite-dogfood/SKILL.md +7 -3
  422. package/pack/generated/codex/skills/rite-explain/SKILL.md +7 -3
  423. package/pack/generated/codex/skills/rite-frame/SKILL.md +7 -3
  424. package/pack/generated/codex/skills/rite-frame/reference/failure-modes.md +22 -24
  425. package/pack/generated/codex/skills/rite-handoff/SKILL.md +7 -3
  426. package/pack/generated/codex/skills/rite-learn/SKILL.md +7 -3
  427. package/pack/generated/codex/skills/rite-plan/SKILL.md +54 -19
  428. package/pack/generated/codex/skills/rite-plan/reference/task-breakdown.md +5 -1
  429. package/pack/generated/codex/skills/rite-polish/SKILL.md +34 -26
  430. package/pack/generated/codex/skills/rite-pov/SKILL.md +7 -3
  431. package/pack/generated/codex/skills/rite-pr-feedback/SKILL.md +7 -3
  432. package/pack/generated/codex/skills/rite-pressure-test/SKILL.md +7 -3
  433. package/pack/generated/codex/skills/rite-prototype/SKILL.md +32 -29
  434. package/pack/generated/codex/skills/rite-prove/SKILL.md +34 -20
  435. package/pack/generated/codex/skills/rite-quick/SKILL.md +7 -3
  436. package/pack/generated/codex/skills/rite-resolve/SKILL.md +19 -14
  437. package/pack/generated/codex/skills/rite-resolve/reference/answer-protocol.md +3 -0
  438. package/pack/generated/codex/skills/rite-review/SKILL.md +44 -31
  439. package/pack/generated/codex/skills/rite-seal/SKILL.md +7 -3
  440. package/pack/generated/codex/skills/rite-seal/reference/phase-contract.md +27 -92
  441. package/pack/generated/codex/skills/rite-seal/reference/risk-and-rollback.md +13 -0
  442. package/pack/generated/codex/skills/rite-ship/SKILL.md +7 -3
  443. package/pack/generated/codex/skills/rite-ship/reference/design-memory.md +31 -36
  444. package/pack/generated/codex/skills/rite-spec/SKILL.md +91 -138
  445. package/pack/generated/codex/skills/rite-spec/reference/investigation.md +37 -33
  446. package/pack/generated/codex/skills/rite-spec/reference/question-protocol.md +6 -2
  447. package/pack/generated/codex/skills/rite-spec/reference/spec-checklists.md +25 -24
  448. package/pack/generated/codex/skills/rite-spec/reference/spec-template.md +9 -2
  449. package/pack/generated/codex/skills/rite-spec/reference/state-workspace.md +13 -3
  450. package/pack/generated/codex/skills/rite-status/SKILL.md +7 -3
  451. package/pack/generated/codex/skills/rite-temper/SKILL.md +69 -50
  452. package/pack/generated/codex/skills/rite-temper/reference/review-dimensions.md +24 -22
  453. package/pack/generated/codex/skills/rite-upgrade/SKILL.md +141 -0
  454. package/pack/generated/codex/skills/rite-upgrade/agents/openai.yaml +2 -0
  455. package/pack/generated/codex/skills/rite-vet/SKILL.md +121 -116
  456. package/pack/generated/codex/skills/rite-vet/reference/artifacts.md +50 -9
  457. package/pack/generated/codex/skills/rite-vet/reference/review-axes.md +40 -38
  458. package/pack/generated/codex/skills/rite-zoom-out/SKILL.md +7 -3
  459. package/package.json +1 -1
  460. package/scripts/build-release-tarball.sh +32 -15
  461. package/scripts/check-authority-drift.py +125 -0
  462. package/scripts/check-instruction-size-baseline.mjs +19 -11
  463. package/scripts/check-invocation-integrity.py +2 -0
  464. package/scripts/codex-generate.sh +69 -33
  465. package/scripts/grade-feature.sh +121 -40
  466. package/scripts/live-hosts/agent-result.schema.json +230 -0
  467. package/scripts/live-hosts/claude.sh +87 -0
  468. package/scripts/live-hosts/codex.sh +81 -0
  469. package/scripts/live-hosts/common.sh +113 -0
  470. package/scripts/live-hosts/fake-host.py +264 -0
  471. package/scripts/live-hosts/host-transport.py +287 -0
  472. package/scripts/release-check.sh +5 -1
  473. package/scripts/run-agent-contract-evals.py +1380 -0
  474. package/scripts/run-behavioral-evals.sh +24 -30
  475. package/scripts/run-evals.sh +1 -5
  476. package/scripts/run-live-behavioral-evals.py +1274 -144
  477. package/scripts/run-outcome-evals.sh +445 -88
  478. package/scripts/run-tests.mjs +30 -2
  479. package/scripts/skills-inventory.mjs +1 -1
  480. package/scripts/validate-workflow-security.py +39 -20
  481. package/scripts/validate-workspace-schema.py +362 -10
  482. package/scripts/validate.sh +21 -15
  483. package/engine/testdata/golden/TestParityWrightScope/devrites-edit-allowed.golden +0 -1
  484. /package/engine/testdata/golden/{TestParityReconcile/check-no-claimed.golden → TestParityBuildReadiness/arg=clarifyopen.golden} +0 -0
@@ -0,0 +1,533 @@
1
+ // Package toolpolicy contains pure, host-independent tool-call policy helpers.
2
+ package toolpolicy
3
+
4
+ import (
5
+ "crypto/sha256"
6
+ "encoding/hex"
7
+ "strings"
8
+ )
9
+
10
+ const (
11
+ // MaxCommandBytes bounds policy work on one structured shell command.
12
+ MaxCommandBytes = 64 << 10
13
+ // NormalizationVersion identifies the exact token grammar hashed by Digest.
14
+ NormalizationVersion = "drv-git-op-v1"
15
+ )
16
+
17
+ // Verdict is the classifier's policy-neutral description of a command.
18
+ type Verdict string
19
+
20
+ const (
21
+ VerdictSafe Verdict = "safe"
22
+ VerdictDestructive Verdict = "destructive"
23
+ VerdictAmbiguous Verdict = "ambiguous"
24
+ )
25
+
26
+ // ReasonID is a stable semantic diagnostic identifier.
27
+ type ReasonID string
28
+
29
+ const (
30
+ ReasonInputTooLarge ReasonID = "DRV-GIT-INPUT-TOO-LARGE"
31
+ ReasonAmbiguousSyntax ReasonID = "DRV-GIT-AMBIGUOUS-SYNTAX"
32
+ ReasonAmbiguousDynamic ReasonID = "DRV-GIT-AMBIGUOUS-DYNAMIC"
33
+ ReasonAmbiguousAlias ReasonID = "DRV-GIT-AMBIGUOUS-ALIAS"
34
+ ReasonAmbiguousGlobalOption ReasonID = "DRV-GIT-AMBIGUOUS-GLOBAL-OPTION"
35
+ ReasonAmbiguousStdin ReasonID = "DRV-GIT-AMBIGUOUS-STDIN"
36
+ ReasonDestructiveReset ReasonID = "DRV-GIT-DESTRUCTIVE-RESET"
37
+ ReasonDestructiveClean ReasonID = "DRV-GIT-DESTRUCTIVE-CLEAN"
38
+ ReasonDestructiveCheckout ReasonID = "DRV-GIT-DESTRUCTIVE-CHECKOUT"
39
+ ReasonDestructiveRestore ReasonID = "DRV-GIT-DESTRUCTIVE-RESTORE"
40
+ ReasonDestructiveSwitch ReasonID = "DRV-GIT-DESTRUCTIVE-SWITCH"
41
+ ReasonDestructiveRemove ReasonID = "DRV-GIT-DESTRUCTIVE-RM"
42
+ ReasonDestructiveBranch ReasonID = "DRV-GIT-DESTRUCTIVE-BRANCH"
43
+ ReasonDestructiveTag ReasonID = "DRV-GIT-DESTRUCTIVE-TAG"
44
+ ReasonDestructiveUpdateRef ReasonID = "DRV-GIT-DESTRUCTIVE-UPDATE-REF"
45
+ ReasonDestructiveStash ReasonID = "DRV-GIT-DESTRUCTIVE-STASH"
46
+ ReasonDestructiveReflog ReasonID = "DRV-GIT-DESTRUCTIVE-REFLOG"
47
+ ReasonDestructivePrune ReasonID = "DRV-GIT-DESTRUCTIVE-PRUNE"
48
+ ReasonDestructiveHistory ReasonID = "DRV-GIT-DESTRUCTIVE-HISTORY"
49
+ ReasonDestructivePushForce ReasonID = "DRV-GIT-DESTRUCTIVE-PUSH-FORCE"
50
+ ReasonDestructivePushDelete ReasonID = "DRV-GIT-DESTRUCTIVE-PUSH-DELETE"
51
+ ReasonDestructiveWorktree ReasonID = "DRV-GIT-DESTRUCTIVE-WORKTREE"
52
+ ReasonDestructiveRefDeletion ReasonID = "DRV-GIT-DESTRUCTIVE-REF-DELETE"
53
+ )
54
+
55
+ const directCommandRemediation = "Use a direct, literal git command without shell expansion, eval, an interpreter payload, or an undeclared alias."
56
+
57
+ // Finding describes one destructive or ambiguous Git operation.
58
+ type Finding struct {
59
+ Verdict Verdict
60
+ ReasonID ReasonID
61
+ Segment int
62
+ Remediation string
63
+ }
64
+
65
+ // Result is deterministic and contains no repository or environment facts.
66
+ // Digest is SHA-256 over a versioned canonical token stream; normalized command
67
+ // text is deliberately not exported because tool arguments may be sensitive.
68
+ // A future authority check may compare it only when Verdict is Destructive;
69
+ // Ambiguous commands must be rewritten as direct literal commands instead.
70
+ type Result struct {
71
+ Verdict Verdict
72
+ ReasonID ReasonID
73
+ Digest string
74
+ Findings []Finding
75
+ Remediation string
76
+ }
77
+
78
+ // ClassifyGitCommand scans one structured shell-command string for direct,
79
+ // syntactically provable destructive Git operations. It is not a shell
80
+ // firewall: unsupported dynamic forms are ambiguous only when they visibly
81
+ // may synthesize a high-impact Git command.
82
+ func ClassifyGitCommand(command string) Result {
83
+ if len(command) > MaxCommandBytes {
84
+ return primaryResult(nil, Finding{
85
+ Verdict: VerdictAmbiguous,
86
+ ReasonID: ReasonInputTooLarge,
87
+ Segment: -1,
88
+ Remediation: directCommandRemediation,
89
+ })
90
+ }
91
+
92
+ shell, err := scanShell(command)
93
+ if err != nil {
94
+ if !potentialHighImpactGit(command) {
95
+ return Result{Verdict: VerdictSafe}
96
+ }
97
+ reason := ReasonAmbiguousSyntax
98
+ if failure, ok := err.(*scanFailure); ok && failure.dynamic {
99
+ reason = ReasonAmbiguousDynamic
100
+ }
101
+ return primaryResult(nil, Finding{
102
+ Verdict: VerdictAmbiguous,
103
+ ReasonID: reason,
104
+ Segment: -1,
105
+ Remediation: directCommandRemediation,
106
+ })
107
+ }
108
+
109
+ normalized := normalizeTokens(shell.tokens)
110
+ result := Result{
111
+ Verdict: VerdictSafe,
112
+ Digest: digestNormalized(normalized),
113
+ }
114
+ for _, segment := range shell.segments {
115
+ result.Findings = append(result.Findings, classifySegment(segment)...)
116
+ }
117
+ return primaryResult(&result)
118
+ }
119
+
120
+ func primaryResult(base *Result, extra ...Finding) Result {
121
+ result := Result{Verdict: VerdictSafe}
122
+ if base != nil {
123
+ result = *base
124
+ }
125
+ result.Findings = append(result.Findings, extra...)
126
+
127
+ for _, wanted := range []Verdict{VerdictAmbiguous, VerdictDestructive} {
128
+ for _, finding := range result.Findings {
129
+ if finding.Verdict != wanted {
130
+ continue
131
+ }
132
+ result.Verdict = finding.Verdict
133
+ result.ReasonID = finding.ReasonID
134
+ result.Remediation = finding.Remediation
135
+ return result
136
+ }
137
+ }
138
+ return result
139
+ }
140
+
141
+ func digestNormalized(normalized string) string {
142
+ sum := sha256.Sum256([]byte(normalized))
143
+ return NormalizationVersion + ":sha256:" + hex.EncodeToString(sum[:])
144
+ }
145
+
146
+ func classifySegment(segment shellSegment) []Finding {
147
+ words, err := commandWords(segment.tokens)
148
+ if err != nil || len(words) == 0 {
149
+ return nil
150
+ }
151
+ original := words
152
+
153
+ peeled, terminal, peelReason := peelTransparent(words)
154
+ if terminal || len(peeled) == 0 {
155
+ return nil
156
+ }
157
+ if peelReason != "" {
158
+ if wordsPotentialHighImpactGit(original) {
159
+ return []Finding{ambiguousFinding(segment.index, peelReason)}
160
+ }
161
+ return nil
162
+ }
163
+
164
+ executable := basename(peeled[0].text)
165
+ if payload, ok := interpreterPayload(executable, peeled); ok {
166
+ nested := ClassifyGitCommand(payload)
167
+ if nested.Verdict != VerdictSafe || opaqueInterpreter(executable) && potentialHighImpactGit(payload) {
168
+ return []Finding{ambiguousFinding(segment.index, ReasonAmbiguousDynamic)}
169
+ }
170
+ return nil
171
+ }
172
+
173
+ if peeled[0].dynamic {
174
+ if wordsHaveHighImpactHint(peeled[1:]) {
175
+ return []Finding{ambiguousFinding(segment.index, ReasonAmbiguousDynamic)}
176
+ }
177
+ return nil
178
+ }
179
+ if isCompoundReserved(executable) && wordsPotentialHighImpactGit(peeled) {
180
+ return []Finding{ambiguousFinding(segment.index, ReasonAmbiguousSyntax)}
181
+ }
182
+ if executable != "git" {
183
+ return nil
184
+ }
185
+
186
+ invocation, finding := parseGitInvocation(peeled, segment.index)
187
+ if finding != nil {
188
+ return []Finding{*finding}
189
+ }
190
+ if invocation.verb == "update-ref" && hasArg(invocation.args, "--stdin") {
191
+ return []Finding{ambiguousFinding(segment.index, ReasonAmbiguousStdin)}
192
+ }
193
+ reasons := destructiveReasons(invocation)
194
+ if invocation.semanticConfig && dynamicSensitiveGitCommand(invocation.verb) &&
195
+ !gitOperationIsDryRun(invocation) &&
196
+ !hasArg(invocation.args, "--help") && !hasArg(invocation.args, "-h") {
197
+ return []Finding{ambiguousFinding(segment.index, ReasonAmbiguousGlobalOption)}
198
+ }
199
+ if len(reasons) > 0 && segmentHasDynamic(segment.tokens) ||
200
+ invocation.dynamic && dynamicSensitiveGitCommand(invocation.verb) {
201
+ return []Finding{ambiguousFinding(segment.index, ReasonAmbiguousDynamic)}
202
+ }
203
+ if len(reasons) == 0 && invocation.verb == "checkout" &&
204
+ !hasArg(invocation.args, "--help") && !hasArg(invocation.args, "-h") &&
205
+ checkoutOperandIsAmbiguous(invocation.args) {
206
+ return []Finding{ambiguousFinding(segment.index, ReasonAmbiguousSyntax)}
207
+ }
208
+
209
+ findings := make([]Finding, 0, len(reasons))
210
+ for _, reason := range reasons {
211
+ findings = append(findings, Finding{
212
+ Verdict: VerdictDestructive,
213
+ ReasonID: reason,
214
+ Segment: segment.index,
215
+ Remediation: "Obtain fresh one-shot authority for this exact normalized operation.",
216
+ })
217
+ }
218
+ return findings
219
+ }
220
+
221
+ func segmentHasDynamic(tokens []shellToken) bool {
222
+ for _, tok := range tokens {
223
+ if tok.dynamic {
224
+ return true
225
+ }
226
+ }
227
+ return false
228
+ }
229
+
230
+ func ambiguousFinding(segment int, reason ReasonID) Finding {
231
+ return Finding{
232
+ Verdict: VerdictAmbiguous,
233
+ ReasonID: reason,
234
+ Segment: segment,
235
+ Remediation: directCommandRemediation,
236
+ }
237
+ }
238
+
239
+ func peelTransparent(words []shellToken) ([]shellToken, bool, ReasonID) {
240
+ for {
241
+ for len(words) > 0 && isAssignment(words[0].text) {
242
+ words = words[1:]
243
+ }
244
+ for len(words) > 0 && words[0].text == "!" {
245
+ words = words[1:]
246
+ }
247
+ if len(words) == 0 {
248
+ return nil, true, ""
249
+ }
250
+
251
+ switch basename(words[0].text) {
252
+ case "command":
253
+ var terminal bool
254
+ words, terminal = peelCommand(words[1:])
255
+ if terminal {
256
+ return nil, true, ""
257
+ }
258
+ case "env":
259
+ var reason ReasonID
260
+ words, reason = peelEnv(words[1:])
261
+ if reason != "" {
262
+ return words, false, reason
263
+ }
264
+ case "rtk":
265
+ words = words[1:]
266
+ if len(words) > 0 && words[0].text == "--" {
267
+ words = words[1:]
268
+ }
269
+ if len(words) > 0 && words[0].text == "proxy" {
270
+ words = words[1:]
271
+ if len(words) > 0 && words[0].text == "--" {
272
+ words = words[1:]
273
+ }
274
+ }
275
+ case "timeout":
276
+ var reason ReasonID
277
+ words, reason = peelTimeout(words[1:])
278
+ if reason != "" {
279
+ return words, false, reason
280
+ }
281
+ case "nice":
282
+ var reason ReasonID
283
+ words, reason = peelNice(words[1:])
284
+ if reason != "" {
285
+ return words, false, reason
286
+ }
287
+ case "nohup":
288
+ var terminal bool
289
+ var reason ReasonID
290
+ words, terminal, reason = peelNohup(words[1:])
291
+ if terminal {
292
+ return nil, true, ""
293
+ }
294
+ if reason != "" {
295
+ return words, false, reason
296
+ }
297
+ case "exec":
298
+ var reason ReasonID
299
+ words, reason = peelExec(words[1:])
300
+ if reason != "" {
301
+ return words, false, reason
302
+ }
303
+ default:
304
+ return words, false, ""
305
+ }
306
+ }
307
+ }
308
+
309
+ func peelCommand(words []shellToken) ([]shellToken, bool) {
310
+ for len(words) > 0 {
311
+ switch words[0].text {
312
+ case "--":
313
+ return words[1:], false
314
+ case "-p":
315
+ words = words[1:]
316
+ case "-v", "-V":
317
+ return nil, true
318
+ default:
319
+ return words, false
320
+ }
321
+ }
322
+ return nil, true
323
+ }
324
+
325
+ func peelEnv(words []shellToken) ([]shellToken, ReasonID) {
326
+ for len(words) > 0 {
327
+ word := words[0].text
328
+ switch {
329
+ case word == "--":
330
+ return words[1:], ""
331
+ case isAssignment(word):
332
+ words = words[1:]
333
+ case word == "-i" || word == "--ignore-environment" || word == "-0" || word == "--null":
334
+ words = words[1:]
335
+ case word == "-u" || word == "--unset" || word == "-C" || word == "--chdir":
336
+ if len(words) < 2 {
337
+ return words, ReasonAmbiguousSyntax
338
+ }
339
+ words = words[2:]
340
+ case strings.HasPrefix(word, "--unset=") || strings.HasPrefix(word, "--chdir="):
341
+ words = words[1:]
342
+ case word == "-S" || word == "--split-string" || strings.HasPrefix(word, "--split-string="):
343
+ return words, ReasonAmbiguousDynamic
344
+ case strings.HasPrefix(word, "-"):
345
+ return words, ReasonAmbiguousSyntax
346
+ default:
347
+ return words, ""
348
+ }
349
+ }
350
+ return nil, ""
351
+ }
352
+
353
+ func peelTimeout(words []shellToken) ([]shellToken, ReasonID) {
354
+ for len(words) > 0 {
355
+ word := words[0].text
356
+ switch {
357
+ case word == "--":
358
+ words = words[1:]
359
+ goto duration
360
+ case word == "-s" || word == "--signal" || word == "-k" || word == "--kill-after":
361
+ if len(words) < 2 {
362
+ return words, ReasonAmbiguousSyntax
363
+ }
364
+ words = words[2:]
365
+ case strings.HasPrefix(word, "--signal=") || strings.HasPrefix(word, "--kill-after="):
366
+ words = words[1:]
367
+ case word == "--foreground" || word == "--preserve-status" || word == "-v" || word == "--verbose":
368
+ words = words[1:]
369
+ case strings.HasPrefix(word, "-s") && len(word) > 2:
370
+ words = words[1:]
371
+ case strings.HasPrefix(word, "-k") && len(word) > 2:
372
+ words = words[1:]
373
+ case strings.HasPrefix(word, "-"):
374
+ return words, ReasonAmbiguousSyntax
375
+ default:
376
+ goto duration
377
+ }
378
+ }
379
+ return nil, ""
380
+
381
+ duration:
382
+ if len(words) == 0 {
383
+ return nil, ReasonAmbiguousSyntax
384
+ }
385
+ return words[1:], ""
386
+ }
387
+
388
+ func peelNice(words []shellToken) ([]shellToken, ReasonID) {
389
+ for len(words) > 0 {
390
+ word := words[0].text
391
+ switch {
392
+ case word == "--":
393
+ return words[1:], ""
394
+ case word == "-n" || word == "--adjustment":
395
+ if len(words) < 2 {
396
+ return words, ReasonAmbiguousSyntax
397
+ }
398
+ words = words[2:]
399
+ case strings.HasPrefix(word, "--adjustment="):
400
+ words = words[1:]
401
+ case isSignedDecimal(word):
402
+ words = words[1:]
403
+ case strings.HasPrefix(word, "-"):
404
+ return words, ReasonAmbiguousSyntax
405
+ default:
406
+ return words, ""
407
+ }
408
+ }
409
+ return nil, ""
410
+ }
411
+
412
+ func peelNohup(words []shellToken) ([]shellToken, bool, ReasonID) {
413
+ if len(words) == 0 {
414
+ return nil, true, ""
415
+ }
416
+ switch words[0].text {
417
+ case "--":
418
+ return words[1:], false, ""
419
+ case "--help", "--version":
420
+ return nil, true, ""
421
+ default:
422
+ if strings.HasPrefix(words[0].text, "-") {
423
+ return words, false, ReasonAmbiguousSyntax
424
+ }
425
+ return words, false, ""
426
+ }
427
+ }
428
+
429
+ func peelExec(words []shellToken) ([]shellToken, ReasonID) {
430
+ for len(words) > 0 {
431
+ word := words[0].text
432
+ switch {
433
+ case word == "--":
434
+ return words[1:], ""
435
+ case word == "-a":
436
+ if len(words) < 2 {
437
+ return words, ReasonAmbiguousSyntax
438
+ }
439
+ words = words[2:]
440
+ case strings.HasPrefix(word, "-a") && len(word) > 2:
441
+ words = words[1:]
442
+ case word == "-c" || word == "-l" || word == "-cl" || word == "-lc":
443
+ words = words[1:]
444
+ case strings.HasPrefix(word, "-"):
445
+ return words, ReasonAmbiguousSyntax
446
+ default:
447
+ return words, ""
448
+ }
449
+ }
450
+ return nil, ""
451
+ }
452
+
453
+ func isSignedDecimal(s string) bool {
454
+ if len(s) < 2 || (s[0] != '-' && s[0] != '+') {
455
+ return false
456
+ }
457
+ return isDigits(s[1:])
458
+ }
459
+
460
+ func interpreterPayload(executable string, words []shellToken) (string, bool) {
461
+ if versionedExecutable(executable, "python") {
462
+ return optionPayload(words, "-c", "")
463
+ }
464
+ if versionedExecutable(executable, "node") {
465
+ return optionPayload(words, "-e", "--eval")
466
+ }
467
+ if versionedExecutable(executable, "ruby") || versionedExecutable(executable, "perl") {
468
+ return optionPayload(words, "-e", "")
469
+ }
470
+
471
+ switch executable {
472
+ case "eval":
473
+ if len(words) < 2 {
474
+ return "", false
475
+ }
476
+ parts := make([]string, 0, len(words)-1)
477
+ for _, word := range words[1:] {
478
+ parts = append(parts, word.text)
479
+ }
480
+ return strings.Join(parts, " "), true
481
+ case "sh", "bash", "dash", "zsh", "ksh":
482
+ for i := 1; i < len(words); i++ {
483
+ if words[i].text == "-c" || (strings.HasPrefix(words[i].text, "-") && strings.Contains(words[i].text[1:], "c")) {
484
+ if i+1 < len(words) {
485
+ return words[i+1].text, true
486
+ }
487
+ return "", false
488
+ }
489
+ }
490
+ }
491
+ return "", false
492
+ }
493
+
494
+ func versionedExecutable(executable, base string) bool {
495
+ if executable == base {
496
+ return true
497
+ }
498
+ version := strings.TrimPrefix(executable, base)
499
+ if version == executable || version == "" || version[0] == '.' || version[len(version)-1] == '.' {
500
+ return false
501
+ }
502
+ for i, r := range version {
503
+ switch {
504
+ case r >= '0' && r <= '9':
505
+ case r == '.' && i > 0 && version[i-1] != '.':
506
+ default:
507
+ return false
508
+ }
509
+ }
510
+ return true
511
+ }
512
+
513
+ func opaqueInterpreter(executable string) bool {
514
+ return versionedExecutable(executable, "python") ||
515
+ versionedExecutable(executable, "node") ||
516
+ versionedExecutable(executable, "ruby") ||
517
+ versionedExecutable(executable, "perl")
518
+ }
519
+
520
+ func optionPayload(words []shellToken, short, long string) (string, bool) {
521
+ for i := 1; i < len(words); i++ {
522
+ if words[i].text == short || (long != "" && words[i].text == long) {
523
+ if i+1 < len(words) {
524
+ return words[i+1].text, true
525
+ }
526
+ return "", false
527
+ }
528
+ if long != "" && strings.HasPrefix(words[i].text, long+"=") {
529
+ return strings.TrimPrefix(words[i].text, long+"="), true
530
+ }
531
+ }
532
+ return "", false
533
+ }