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,402 @@
1
+ package forge
2
+
3
+ import (
4
+ "crypto/rand"
5
+ "encoding/hex"
6
+ "encoding/json"
7
+ "errors"
8
+ "fmt"
9
+ "io/fs"
10
+ "os"
11
+ "path/filepath"
12
+ "regexp"
13
+ "sort"
14
+ "strings"
15
+ "time"
16
+ )
17
+
18
+ const Schema = "devrites-forge/v1"
19
+
20
+ var (
21
+ safeSlug = regexp.MustCompile(`^[a-z0-9][a-z0-9._-]{0,63}$`)
22
+ safeRunID = regexp.MustCompile(`^forge-[a-f0-9]{24}$`)
23
+ )
24
+
25
+ type CandidateID string
26
+
27
+ const (
28
+ CandidateA CandidateID = "A"
29
+ CandidateB CandidateID = "B"
30
+ CandidateC CandidateID = "C"
31
+ )
32
+
33
+ type CandidateState string
34
+
35
+ const (
36
+ StatePlanned CandidateState = "planned"
37
+ StateCreated CandidateState = "created"
38
+ StateRunning CandidateState = "running"
39
+ StateFinished CandidateState = "finished"
40
+ StateFailed CandidateState = "failed"
41
+ StateExtracted CandidateState = "extracted"
42
+ StateMerged CandidateState = "merged"
43
+ StateCleaned CandidateState = "cleaned"
44
+ )
45
+
46
+ type Primary struct {
47
+ Branch string `json:"branch"`
48
+ BaseCommit string `json:"base_commit"`
49
+ BaselineTree string `json:"baseline_tree"`
50
+ BaselineSHA256 string `json:"baseline_sha256"`
51
+ }
52
+
53
+ type Worker struct {
54
+ ID string `json:"id,omitempty"`
55
+ PID int `json:"pid,omitempty"`
56
+ ProcessStart string `json:"process_start,omitempty"`
57
+ StartedAt string `json:"started_at,omitempty"`
58
+ FinishedAt string `json:"finished_at,omitempty"`
59
+ Result string `json:"result,omitempty"`
60
+ }
61
+
62
+ type Candidate struct {
63
+ ID CandidateID `json:"id"`
64
+ Strategy string `json:"strategy"`
65
+ Worktree string `json:"worktree"`
66
+ Branch string `json:"branch"`
67
+ InitialBase string `json:"initial_base"`
68
+ State CandidateState `json:"state"`
69
+ Worker Worker `json:"worker"`
70
+ Commit string `json:"commit,omitempty"`
71
+ Tree string `json:"tree,omitempty"`
72
+ DeltaSHA256 string `json:"delta_sha256,omitempty"`
73
+ Preservation string `json:"preservation,omitempty"`
74
+ LastTransition string `json:"last_transition"`
75
+ }
76
+
77
+ type Winner struct {
78
+ Candidate CandidateID `json:"candidate,omitempty"`
79
+ RecordedBy string `json:"recorded_by,omitempty"`
80
+ RecordedAt string `json:"recorded_at,omitempty"`
81
+ }
82
+
83
+ type MergeState struct {
84
+ State string `json:"state,omitempty"`
85
+ Commit string `json:"commit,omitempty"`
86
+ Tree string `json:"tree,omitempty"`
87
+ RecordedAt string `json:"recorded_at,omitempty"`
88
+ }
89
+
90
+ type VerificationState struct {
91
+ State string `json:"state,omitempty"`
92
+ RecordedBy string `json:"recorded_by,omitempty"`
93
+ RecordedAt string `json:"recorded_at,omitempty"`
94
+ }
95
+
96
+ type CleanupState struct {
97
+ State string `json:"state,omitempty"`
98
+ Preserved map[string]string `json:"preserved,omitempty"`
99
+ RecordedAt string `json:"recorded_at,omitempty"`
100
+ }
101
+
102
+ type Manifest struct {
103
+ Schema string `json:"schema"`
104
+ RunID string `json:"run_id"`
105
+ CreationNonce string `json:"creation_nonce"`
106
+ CreatedAt string `json:"created_at"`
107
+ FeatureSlug string `json:"feature_slug"`
108
+ SliceID string `json:"slice_id"`
109
+ AcceptanceHash string `json:"acceptance_hash"`
110
+ TestPlanHash string `json:"test_plan_hash"`
111
+ PrimaryRoot string `json:"primary_root"`
112
+ GitCommonDir string `json:"git_common_dir"`
113
+ ForgeRoot string `json:"forge_root"`
114
+ Primary Primary `json:"primary"`
115
+ Candidates []Candidate `json:"candidates"`
116
+ Winner Winner `json:"winner"`
117
+ Merge MergeState `json:"merge"`
118
+ Verification VerificationState `json:"verification"`
119
+ Cleanup CleanupState `json:"cleanup"`
120
+ }
121
+
122
+ func (m *Manifest) Candidate(id CandidateID) (*Candidate, error) {
123
+ for i := range m.Candidates {
124
+ if m.Candidates[i].ID == id {
125
+ return &m.Candidates[i], nil
126
+ }
127
+ }
128
+ return nil, fmt.Errorf("forge: candidate %q is not in manifest", id)
129
+ }
130
+
131
+ func ManifestPath(root, slug, runID string) (string, error) {
132
+ if !safeSlug.MatchString(slug) {
133
+ return "", fmt.Errorf("forge: invalid feature slug %q", slug)
134
+ }
135
+ if !safeRunID.MatchString(runID) {
136
+ return "", fmt.Errorf("forge: invalid run ID %q", runID)
137
+ }
138
+ root, err := physicalExisting(root)
139
+ if err != nil {
140
+ return "", err
141
+ }
142
+ return filepath.Join(root, ".devrites", "work", slug, ".forge", runID, "manifest.json"), nil
143
+ }
144
+
145
+ // Load returns the unique manifest for runID below root. Duplicate manifests
146
+ // fail closed instead of letting a caller choose which ownership record wins.
147
+ func Load(root, runID string) (*Manifest, string, error) {
148
+ if !safeRunID.MatchString(runID) {
149
+ return nil, "", fmt.Errorf("forge: invalid run ID %q", runID)
150
+ }
151
+ root, err := physicalExisting(root)
152
+ if err != nil {
153
+ return nil, "", err
154
+ }
155
+ paths, err := manifestPaths(root, "")
156
+ if err != nil {
157
+ return nil, "", err
158
+ }
159
+ var matches []string
160
+ for _, path := range paths {
161
+ if filepath.Base(filepath.Dir(path)) == runID {
162
+ matches = append(matches, path)
163
+ }
164
+ }
165
+ if len(matches) != 1 {
166
+ return nil, "", fmt.Errorf("forge: run %s has %d manifests; want exactly one", runID, len(matches))
167
+ }
168
+ m, err := loadAt(root, matches[0])
169
+ if err != nil {
170
+ return nil, "", err
171
+ }
172
+ return m, matches[0], nil
173
+ }
174
+
175
+ func loadAt(root, path string) (*Manifest, error) {
176
+ if err := validateNoSymlink(root, path); err != nil {
177
+ return nil, err
178
+ }
179
+ info, err := os.Lstat(path)
180
+ if err != nil {
181
+ return nil, fmt.Errorf("forge: stat manifest: %w", err)
182
+ }
183
+ if !info.Mode().IsRegular() {
184
+ return nil, fmt.Errorf("forge: manifest is not a regular file: %s", path)
185
+ }
186
+ raw, err := os.ReadFile(path)
187
+ if err != nil {
188
+ return nil, fmt.Errorf("forge: read manifest: %w", err)
189
+ }
190
+ var m Manifest
191
+ if err := json.Unmarshal(raw, &m); err != nil {
192
+ return nil, fmt.Errorf("forge: decode manifest: %w", err)
193
+ }
194
+ if err := validateManifest(root, path, &m); err != nil {
195
+ return nil, err
196
+ }
197
+ return &m, nil
198
+ }
199
+
200
+ func validateManifest(root, path string, m *Manifest) error {
201
+ if m.Schema != Schema || !safeRunID.MatchString(m.RunID) || !safeSlug.MatchString(m.FeatureSlug) {
202
+ return errors.New("forge: invalid manifest identity")
203
+ }
204
+ wantPath, _ := ManifestPath(root, m.FeatureSlug, m.RunID)
205
+ if filepath.Clean(path) != wantPath {
206
+ return fmt.Errorf("forge: manifest path mismatch: got %s want %s", path, wantPath)
207
+ }
208
+ if m.PrimaryRoot != root {
209
+ return fmt.Errorf("forge: primary root mismatch: got %s want %s", m.PrimaryRoot, root)
210
+ }
211
+ wantForge := forgeRoot(root, m.RunID)
212
+ if m.ForgeRoot != wantForge {
213
+ return fmt.Errorf("forge: staging root mismatch: got %s want %s", m.ForgeRoot, wantForge)
214
+ }
215
+ if len(m.Candidates) < 2 || len(m.Candidates) > 3 {
216
+ return fmt.Errorf("forge: manifest has %d candidates; want 2 or 3", len(m.Candidates))
217
+ }
218
+ seen := map[CandidateID]bool{}
219
+ for _, c := range m.Candidates {
220
+ if seen[c.ID] || !validCandidateID(c.ID) {
221
+ return fmt.Errorf("forge: invalid or duplicate candidate %q", c.ID)
222
+ }
223
+ seen[c.ID] = true
224
+ wantWorktree := filepath.Join(wantForge, "candidate-"+strings.ToLower(string(c.ID)))
225
+ wantBranch := candidateBranch(m.RunID, c.ID)
226
+ if c.Worktree != wantWorktree || c.Branch != wantBranch || c.InitialBase != m.Primary.BaseCommit {
227
+ return fmt.Errorf("forge: candidate %s ownership fields do not match derived values", c.ID)
228
+ }
229
+ }
230
+ return nil
231
+ }
232
+
233
+ func save(path string, m *Manifest) error {
234
+ root := m.PrimaryRoot
235
+ if err := validateManifest(root, path, m); err != nil {
236
+ return err
237
+ }
238
+ if err := validateNoSymlink(root, filepath.Dir(path)); err != nil && !errors.Is(err, fs.ErrNotExist) {
239
+ return err
240
+ }
241
+ if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
242
+ return fmt.Errorf("forge: create manifest directory: %w", err)
243
+ }
244
+ if err := validateNoSymlink(root, filepath.Dir(path)); err != nil {
245
+ return err
246
+ }
247
+ if info, err := os.Lstat(path); err == nil && !info.Mode().IsRegular() {
248
+ return fmt.Errorf("forge: refusing to replace non-regular manifest %s", path)
249
+ } else if err != nil && !errors.Is(err, fs.ErrNotExist) {
250
+ return fmt.Errorf("forge: stat manifest: %w", err)
251
+ }
252
+ raw, err := json.MarshalIndent(m, "", " ")
253
+ if err != nil {
254
+ return fmt.Errorf("forge: encode manifest: %w", err)
255
+ }
256
+ raw = append(raw, '\n')
257
+ tmp, err := os.CreateTemp(filepath.Dir(path), ".manifest-*.tmp")
258
+ if err != nil {
259
+ return fmt.Errorf("forge: create manifest temp file: %w", err)
260
+ }
261
+ name := tmp.Name()
262
+ defer os.Remove(name)
263
+ if err := tmp.Chmod(0o600); err != nil {
264
+ _ = tmp.Close()
265
+ return fmt.Errorf("forge: chmod manifest temp file: %w", err)
266
+ }
267
+ if _, err := tmp.Write(raw); err != nil {
268
+ _ = tmp.Close()
269
+ return fmt.Errorf("forge: write manifest: %w", err)
270
+ }
271
+ if err := tmp.Sync(); err != nil {
272
+ _ = tmp.Close()
273
+ return fmt.Errorf("forge: sync manifest: %w", err)
274
+ }
275
+ if err := tmp.Close(); err != nil {
276
+ return fmt.Errorf("forge: close manifest: %w", err)
277
+ }
278
+ if err := os.Rename(name, path); err != nil {
279
+ return fmt.Errorf("forge: replace manifest: %w", err)
280
+ }
281
+ return nil
282
+ }
283
+
284
+ func manifestPaths(root, slug string) ([]string, error) {
285
+ base := filepath.Join(root, ".devrites", "work")
286
+ if err := validateNoSymlink(root, base); err != nil {
287
+ if errors.Is(err, fs.ErrNotExist) {
288
+ return nil, nil
289
+ }
290
+ return nil, err
291
+ }
292
+ var paths []string
293
+ err := filepath.WalkDir(base, func(path string, d fs.DirEntry, err error) error {
294
+ if errors.Is(err, fs.ErrNotExist) {
295
+ return nil
296
+ }
297
+ if err != nil {
298
+ return err
299
+ }
300
+ if d.Type()&os.ModeSymlink != 0 {
301
+ if d.IsDir() {
302
+ return filepath.SkipDir
303
+ }
304
+ return nil
305
+ }
306
+ if d.IsDir() {
307
+ return nil
308
+ }
309
+ rel, err := filepath.Rel(base, path)
310
+ if err != nil {
311
+ return err
312
+ }
313
+ parts := strings.Split(filepath.ToSlash(rel), "/")
314
+ if len(parts) == 4 && parts[1] == ".forge" && parts[3] == "manifest.json" && safeSlug.MatchString(parts[0]) && safeRunID.MatchString(parts[2]) {
315
+ if slug == "" || parts[0] == slug {
316
+ paths = append(paths, path)
317
+ }
318
+ }
319
+ return nil
320
+ })
321
+ if errors.Is(err, fs.ErrNotExist) {
322
+ return nil, nil
323
+ }
324
+ sort.Strings(paths)
325
+ return paths, err
326
+ }
327
+
328
+ func validatedManifestPaths(root string) ([]string, error) {
329
+ paths, err := manifestPaths(root, "")
330
+ if err != nil {
331
+ return nil, err
332
+ }
333
+ for _, path := range paths {
334
+ if _, err := loadAt(root, path); err != nil {
335
+ return nil, err
336
+ }
337
+ }
338
+ return paths, nil
339
+ }
340
+
341
+ func newIdentity() (runID, nonce string, err error) {
342
+ raw := make([]byte, 28)
343
+ if _, err := rand.Read(raw); err != nil {
344
+ return "", "", fmt.Errorf("forge: generate identity: %w", err)
345
+ }
346
+ return "forge-" + hex.EncodeToString(raw[:12]), hex.EncodeToString(raw[12:]), nil
347
+ }
348
+
349
+ func forgeRoot(root, runID string) string {
350
+ return filepath.Join(filepath.Dir(root), "."+filepath.Base(root)+".devrites-forge", runID)
351
+ }
352
+
353
+ func candidateBranch(runID string, id CandidateID) string {
354
+ return "devrites/forge/" + runID + "/" + strings.ToLower(string(id))
355
+ }
356
+
357
+ func validCandidateID(id CandidateID) bool {
358
+ return id == CandidateA || id == CandidateB || id == CandidateC
359
+ }
360
+
361
+ func now() string {
362
+ return time.Now().UTC().Format(time.RFC3339Nano)
363
+ }
364
+
365
+ func physicalExisting(path string) (string, error) {
366
+ abs, err := filepath.Abs(path)
367
+ if err != nil {
368
+ return "", fmt.Errorf("forge: resolve path: %w", err)
369
+ }
370
+ real, err := filepath.EvalSymlinks(abs)
371
+ if err != nil {
372
+ return "", fmt.Errorf("forge: resolve physical path %s: %w", abs, err)
373
+ }
374
+ return filepath.Clean(real), nil
375
+ }
376
+
377
+ func validateNoSymlink(root, path string) error {
378
+ root = filepath.Clean(root)
379
+ path = filepath.Clean(path)
380
+ rel, err := filepath.Rel(root, path)
381
+ if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
382
+ return fmt.Errorf("forge: path escapes primary root: %s", path)
383
+ }
384
+ current := root
385
+ if rel == "." {
386
+ return nil
387
+ }
388
+ for _, part := range strings.Split(rel, string(filepath.Separator)) {
389
+ current = filepath.Join(current, part)
390
+ info, err := os.Lstat(current)
391
+ if errors.Is(err, fs.ErrNotExist) {
392
+ return fs.ErrNotExist
393
+ }
394
+ if err != nil {
395
+ return fmt.Errorf("forge: inspect path %s: %w", current, err)
396
+ }
397
+ if info.Mode()&os.ModeSymlink != 0 {
398
+ return fmt.Errorf("forge: symlink is not allowed in manifest path: %s", current)
399
+ }
400
+ }
401
+ return nil
402
+ }
@@ -1,10 +1,7 @@
1
- // Package gate implements DevRites' deterministic completeness gates.
2
- //
3
- // Enforcement is phase-relative, gate-scoped, and transition-fired: a gate
4
- // checks only the sections required to make its transition, and only when it is
5
- // run: never on every tool call. A block is a HITL pause (a structured
6
- // "missing X" the human resolves and retries), never a crash. Judgment gates
7
- // stay advisory; these deterministic gates are the ones allowed to block.
1
+ // Package gate implements deterministic completeness checks. Each gate checks
2
+ // only the files required for its transition when the command runs. Missing
3
+ // content returns a human-resolvable block instead of an error. Judgment gates
4
+ // remain advisory; only deterministic checks can block.
8
5
  package gate
9
6
 
10
7
  import (
@@ -15,6 +12,7 @@ import (
15
12
 
16
13
  "github.com/devrites/devrites/internal/devritespaths"
17
14
  "github.com/devrites/devrites/internal/orient"
15
+ "github.com/devrites/devrites/internal/reason"
18
16
  "github.com/devrites/devrites/internal/state"
19
17
  )
20
18
 
@@ -33,19 +31,20 @@ const (
33
31
  // Result is a gate outcome. Blocked is true iff a required section is missing;
34
32
  // Missing lists them in canonical order for an actionable message.
35
33
  type Result struct {
36
- Kind Kind
37
- Slug string
38
- Phase state.Phase
39
- Target state.Phase // the phase whose requirements were checked
40
- Missing []state.Section
41
- Blocked bool
34
+ Kind Kind
35
+ Slug string
36
+ Phase state.Phase
37
+ Target state.Phase // the phase whose requirements were checked
38
+ Missing []state.Section // compact legacy view
39
+ MissingFiles []string // authoritative per-file view
40
+ LegacyLayout bool
41
+ Blocked bool
42
+ ReasonID reason.ID
42
43
  }
43
44
 
44
- // Check runs a gate against feature <slug> under root, reading the files
45
- // directly (the files are always the source of truth, so a gate never trusts a
46
- // possibly-stale cache). It returns an error only for a genuinely broken request
47
- // (unknown slug, unreadable state): a legitimately incomplete feature is a
48
- // Result with Blocked set, not an error.
45
+ // Check runs a gate against feature <slug> under root. It reads workspace files
46
+ // directly instead of trusting a cache. Invalid requests and unreadable state
47
+ // return errors; missing required content returns a blocked Result.
49
48
  func Check(kind Kind, root, slug string) (*Result, error) {
50
49
  f, err := state.LoadFeature(root, slug)
51
50
  if err != nil {
@@ -56,19 +55,46 @@ func Check(kind Kind, root, slug string) (*Result, error) {
56
55
  target = state.PhaseSeal
57
56
  }
58
57
  missing := state.MissingFor(f, target)
59
- return &Result{
60
- Kind: kind,
61
- Slug: slug,
62
- Phase: f.Phase,
63
- Target: target,
64
- Missing: missing,
65
- Blocked: len(missing) > 0,
66
- }, nil
58
+ var missingFiles []string
59
+ blocked := len(missing) > 0
60
+ if !f.LegacyLayout {
61
+ missingFiles = state.MissingWorkspaceFiles(f, target)
62
+ blocked = len(missingFiles) > 0
63
+ }
64
+ result := &Result{
65
+ Kind: kind,
66
+ Slug: slug,
67
+ Phase: f.Phase,
68
+ Target: target,
69
+ Missing: missing,
70
+ MissingFiles: missingFiles,
71
+ LegacyLayout: f.LegacyLayout,
72
+ Blocked: blocked,
73
+ }
74
+ result.ReasonID = ResultReasonID(kind, blocked)
75
+ return result, nil
76
+ }
77
+
78
+ // ResultReasonID returns the typed outcome owned by the lifecycle gate.
79
+ func ResultReasonID(kind Kind, blocked bool) reason.ID {
80
+ switch kind {
81
+ case Readiness:
82
+ if blocked {
83
+ return reason.GateReadinessMissing
84
+ }
85
+ return reason.GateReadinessPassed
86
+ case Seal:
87
+ if blocked {
88
+ return reason.GateSealMissing
89
+ }
90
+ return reason.GateSealPassed
91
+ default:
92
+ return ""
93
+ }
67
94
  }
68
95
 
69
- // Render produces the deterministic, greppable gate output (with a trailing
70
- // newline). A block names exactly the missing sections and the resolve step, so
71
- // a human (or an AFK agent) knows precisely what to fix.
96
+ // Render returns stable, greppable output with a trailing newline. A blocked
97
+ // result names the missing files and the command to rerun.
72
98
  func (r *Result) Render() string {
73
99
  var b strings.Builder
74
100
  fmt.Fprintf(&b, "gate: %s\n", r.Kind)
@@ -78,26 +104,28 @@ func (r *Result) Render() string {
78
104
  b.WriteString("result: pass\n")
79
105
  return b.String()
80
106
  }
81
- names := sectionNames(r.Missing)
107
+ missing := r.MissingFiles
108
+ if r.LegacyLayout {
109
+ missing = make([]string, len(r.Missing))
110
+ for i, section := range r.Missing {
111
+ missing[i] = string(section)
112
+ }
113
+ }
82
114
  if r.Kind == Seal {
83
- fmt.Fprintf(&b, "result: blocked (missing to seal: %s)\n", strings.Join(names, ", "))
115
+ fmt.Fprintf(&b, "result: blocked (missing to seal: %s)\n", strings.Join(missing, ", "))
84
116
  } else {
85
- fmt.Fprintf(&b, "result: blocked (missing to leave %q: %s)\n", r.Phase, strings.Join(names, ", "))
117
+ fmt.Fprintf(&b, "result: blocked (missing to leave %q: %s)\n", r.Phase, strings.Join(missing, ", "))
86
118
  }
87
119
  fmt.Fprintf(&b, "next: add real content to %s, then re-run: devrites-engine %s %s\n",
88
- strings.Join(fileNames(r.Missing), ", "), r.Kind, r.Slug)
120
+ strings.Join(missing, ", "), r.Kind, r.Slug)
89
121
  return b.String()
90
122
  }
91
123
 
92
- // StopGate evaluates the stop-hook rest-point invariant for the active feature:
93
- // a feature that CLAIMS completion (it has advanced to seal, ship, or done)
94
- // must not have an empty proof section. This is a rest-point check, NOT
95
- // whole-feature completeness: normal in-progress incompleteness never trips it,
96
- // so a mid-build turn is never blocked.
124
+ // StopGate checks whether the active feature can end a turn in its current state.
125
+ // A feature in seal, ship, or done must have proof. Normal incompleteness during
126
+ // earlier phases does not block.
97
127
  //
98
- // It returns a zero StopResult (Blocked false) (silent, no block) when there
99
- // is no active feature or the workspace can't be read, keeping the stop hook
100
- // fail-open.
128
+ // Missing or unreadable workspace state returns an unblocked zero result.
101
129
  func StopGate(root string) (StopResult, error) {
102
130
  slug, err := orient.ActiveSlug(root)
103
131
  if err != nil || slug == "" {
@@ -105,16 +133,17 @@ func StopGate(root string) (StopResult, error) {
105
133
  }
106
134
  f, err := state.LoadFeature(root, slug)
107
135
  if err != nil {
108
- return StopResult{}, nil // fail-open: a broken workspace never wedges Stop
136
+ return StopResult{}, nil // Unreadable state does not block Stop.
109
137
  }
110
- // Fail-on-red: the redwatch hook marks a known-red suite by writing .red. A turn
111
- // must not rest while it is set: evidence over confidence. This is a rest-point
112
- // invariant (a concrete, provable inconsistency), not whole-feature completeness.
138
+ // redwatch writes .red for a known failing suite. Stop blocks while that file
139
+ // exists, independently of whole-feature completeness.
113
140
  featureDir := devritespaths.FeatureDir(root, slug)
114
141
  if _, statErr := os.Stat(filepath.Join(featureDir, ".red")); statErr == nil {
115
142
  return StopResult{
116
- Slug: slug,
117
- Blocked: true,
143
+ Slug: slug,
144
+ Blocked: true,
145
+ ReasonID: reason.HookStopRed,
146
+ EvidenceFiles: []string{".red"},
118
147
  Reason: fmt.Sprintf(
119
148
  "feature %q has tests/build RED (.red is set): fix to green, or record the failure and next step, before stopping",
120
149
  slug),
@@ -122,8 +151,10 @@ func StopGate(root string) (StopResult, error) {
122
151
  }
123
152
  if gates, blocked := unsurfacedHumanGates(featureDir); blocked {
124
153
  return StopResult{
125
- Slug: slug,
126
- Blocked: true,
154
+ Slug: slug,
155
+ Blocked: true,
156
+ ReasonID: reason.HookStopUnsurfacedHumanGate,
157
+ EvidenceFiles: []string{"questions.md", "state.md"},
127
158
  Reason: fmt.Sprintf(
128
159
  "feature %q has open %s human question(s) in questions.md but state.md is not awaiting_human: surface the gate before stopping",
129
160
  slug, strings.Join(gates, "/")),
@@ -132,14 +163,16 @@ func StopGate(root string) (StopResult, error) {
132
163
  claimsDone := state.ShippablePhase(f.Phase)
133
164
  if claimsDone && !f.Present[state.SectionProof] {
134
165
  return StopResult{
135
- Slug: slug,
136
- Blocked: true,
166
+ Slug: slug,
167
+ Blocked: true,
168
+ ReasonID: reason.HookStopMissingProof,
169
+ EvidenceFiles: []string{"evidence.md", "proof.md", "state.md"},
137
170
  Reason: fmt.Sprintf(
138
171
  "feature %q is at phase %q but proof.md is empty: record acceptance evidence, or move the phase back, before stopping",
139
172
  slug, f.Phase),
140
173
  }, nil
141
174
  }
142
- return StopResult{Slug: slug}, nil
175
+ return StopResult{Slug: slug, ReasonID: reason.HookStopClear}, nil
143
176
  }
144
177
 
145
178
  const gateSpaceChars = " \t\n\v\f\r"
@@ -216,23 +249,9 @@ func splitLinesNoTrailing(data []byte) []string {
216
249
  // none); Blocked is true iff the rest-point invariant is violated; Reason is the
217
250
  // actionable explanation when Blocked.
218
251
  type StopResult struct {
219
- Slug string
220
- Reason string
221
- Blocked bool
222
- }
223
-
224
- func sectionNames(ss []state.Section) []string {
225
- out := make([]string, len(ss))
226
- for i, s := range ss {
227
- out[i] = string(s)
228
- }
229
- return out
230
- }
231
-
232
- func fileNames(ss []state.Section) []string {
233
- out := make([]string, len(ss))
234
- for i, s := range ss {
235
- out[i] = string(s) + ".md"
236
- }
237
- return out
252
+ Slug string
253
+ Reason string
254
+ ReasonID reason.ID
255
+ EvidenceFiles []string
256
+ Blocked bool
238
257
  }