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
@@ -1,17 +1,16 @@
1
1
  #!/usr/bin/env python3
2
- """Scan GitHub Actions workflows for supply-chain + permission risks.
2
+ """Scan GitHub Actions workflows for supply-chain and permission risks.
3
3
 
4
- DevRites' publish (release.yml) and auto-merge paths are high-value targets, so this
5
- gate fails CI when a workflow:
4
+ Release and auto-merge workflows are especially sensitive, but these rules
5
+ apply to every workflow. The check rejects workflows that:
6
6
 
7
- - uses any non-local action not pinned to a full 40-char commit SHA: a moving
8
- tag like `@v2` lets a compromised upstream inject code into the pipeline;
9
- - declares no `permissions:` scope anywhere: the default token is broad;
10
- - uses `permissions: write-all` (over-broad);
11
- - uses `pull_request_target`, except for a Dependabot-only workflow that never
12
- checks out PR code.
13
- - leaves an internal `: ` unquoted in a workflow or step name, which makes the
14
- workflow invalid YAML and prevents every job from starting.
7
+ - use a non-local action without a full 40-character commit SHA, because a
8
+ moving tag can change upstream code;
9
+ - omit an explicit `permissions:` scope or use `permissions: write-all`;
10
+ - use `pull_request_target` outside a Dependabot-only workflow that does not
11
+ check out pull-request code;
12
+ - interpolate workflow_dispatch inputs directly into a shell `run:` block;
13
+ - leave `: ` unquoted inside a workflow or step name, producing invalid YAML.
15
14
 
16
15
  Usage: validate-workflow-security.py [DIR] (default: .github/workflows)
17
16
  Exit: 0 clean; 1 on any finding.
@@ -29,6 +28,8 @@ DEPENDABOT_ONLY_RE = re.compile(
29
28
  re.MULTILINE,
30
29
  )
31
30
  UNQUOTED_NAME_COLON_RE = re.compile(r"^\s*(?:-\s*)?name:\s+[^'\"].*:\s+\S")
31
+ RUN_RE = re.compile(r"^(\s*)(?:-\s*)?run:\s*(.*)$")
32
+ DISPATCH_EXPRESSION_RE = re.compile(r"\$\{\{[^}]*\binputs\b", re.IGNORECASE)
32
33
 
33
34
 
34
35
  def safe_dependabot_target(text):
@@ -46,18 +47,18 @@ def scan_text(path, text):
46
47
  lines = text.splitlines()
47
48
  dependabot_target_is_safe = safe_dependabot_target(text)
48
49
  if not re.search(r"^\s*permissions:", text, re.MULTILINE):
49
- findings.append("%s: no permissions: scope: the default GITHUB_TOKEN is broad; "
50
- "add an explicit least-privilege permissions block" % path)
50
+ findings.append("%s: no permissions block. The default GITHUB_TOKEN is broad; "
51
+ "add an explicit least-privilege block" % path)
51
52
  for i, line in enumerate(lines, 1):
52
53
  if UNQUOTED_NAME_COLON_RE.match(line):
53
- findings.append("%s:%d: name contains an unquoted colon: quote the complete "
54
+ findings.append("%s:%d: name has an unquoted colon. Quote the complete "
54
55
  "name so GitHub can parse the workflow" % (path, i))
55
56
  if "write-all" in line:
56
- findings.append("%s:%d: permissions: write-all is over-broad: scope to the "
57
- "minimum needed" % (path, i))
57
+ findings.append("%s:%d: permissions: write-all grants too much access. "
58
+ "Limit it to the permissions this workflow needs" % (path, i))
58
59
  if "pull_request_target" in line and not dependabot_target_is_safe:
59
- findings.append("%s:%d: pull_request_target runs with secrets on untrusted PR "
60
- "code: only a Dependabot-only workflow without checkout is allowed"
60
+ findings.append("%s:%d: pull_request_target exposes secrets to untrusted PR "
61
+ "code. Only a Dependabot-only workflow without checkout is allowed"
61
62
  % (path, i))
62
63
  m = USES_RE.match(line)
63
64
  if not m:
@@ -68,9 +69,27 @@ def scan_text(path, text):
68
69
  at = ref.rsplit("@", 1)
69
70
  pin = at[1] if len(at) == 2 else ""
70
71
  if not SHA_RE.match(pin):
71
- findings.append("%s:%d: action '%s' not pinned to a full commit "
72
- "SHA: pin it (a moving tag is a supply-chain risk)"
72
+ findings.append("%s:%d: action '%s' is not pinned to a full commit "
73
+ "SHA. Pin it because a moving tag is a supply-chain risk"
73
74
  % (path, i, ref))
75
+ for i, line in enumerate(lines):
76
+ match = RUN_RE.match(line)
77
+ if not match:
78
+ continue
79
+ run_indent = len(match.group(1))
80
+ run_lines = [(i + 1, match.group(2))]
81
+ for j in range(i + 1, len(lines)):
82
+ candidate = lines[j]
83
+ if candidate.strip() and len(candidate) - len(candidate.lstrip()) <= run_indent:
84
+ break
85
+ run_lines.append((j + 1, candidate))
86
+ for line_number, command in run_lines:
87
+ if DISPATCH_EXPRESSION_RE.search(command):
88
+ findings.append(
89
+ "%s:%d: workflow_dispatch input appears directly in run. "
90
+ "Pass it through env and quote the shell variable"
91
+ % (path, line_number)
92
+ )
74
93
  return findings
75
94
 
76
95
 
@@ -1,13 +1,15 @@
1
1
  #!/usr/bin/env python3
2
- """Validate generated DevRites workspace artifacts.
2
+ """Validate generated DevRites workspace artifacts against the Markdown contract.
3
3
 
4
- The validator is intentionally dependency-free: it checks the Markdown contract
5
- well enough to catch drift without introducing a runtime parser dependency.
4
+ The validator uses only the standard library, so it does not add a runtime
5
+ parser dependency.
6
6
  """
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
10
  import argparse
11
+ import hashlib
12
+ import json
11
13
  import re
12
14
  import sys
13
15
  from pathlib import Path
@@ -15,6 +17,21 @@ from pathlib import Path
15
17
  from workflow_schema import PHASES, cursor_field
16
18
 
17
19
 
20
+ READINESS_CONTRACT_PATH = (
21
+ Path(__file__).resolve().parents[1]
22
+ / "engine"
23
+ / "internal"
24
+ / "lib"
25
+ / "readiness_contract.json"
26
+ )
27
+ READINESS_CONTRACT = json.loads(READINESS_CONTRACT_PATH.read_text(encoding="utf-8"))
28
+ READINESS_PLACEHOLDER_RE = re.compile(
29
+ r"<[^>\n]+>|\b(?:TODO|TBD|FIXME|UNKNOWN)\b", re.IGNORECASE
30
+ )
31
+ READINESS_DIGEST_RE = re.compile(r"^[0-9a-f]{64}$")
32
+ READINESS_AC_RE = re.compile(r"\bAC-?\d+\b", re.IGNORECASE)
33
+
34
+
18
35
  ID_PATTERNS = {
19
36
  "AC": re.compile(r"\bAC-\d{3}\b"),
20
37
  "REQ": re.compile(r"\bREQ-\d{3}\b"),
@@ -49,10 +66,13 @@ HIGH_TRAFFIC_BUDGETS = {
49
66
  "feature.md": 120,
50
67
  "brief.md": 80,
51
68
  "spec.md": 260,
69
+ "decision-coverage.md": 200,
52
70
  "ai-spec.md": 160,
53
71
  "plan.md": 220,
54
72
  "tasks.md": 280,
55
73
  "traceability.md": 220,
74
+ "eng-review.md": 240,
75
+ "test-plan.md": 260,
56
76
  "state.md": 120,
57
77
  "handoff.md": 120,
58
78
  "evidence.md": 280,
@@ -77,6 +97,13 @@ REQUIRED_HEADINGS = {
77
97
  "Measurable success",
78
98
  "Scope boundaries",
79
99
  ],
100
+ "decision-coverage.md": [
101
+ "Topology",
102
+ "Coverage matrix",
103
+ "Assumption audit",
104
+ "Residual uncertainty",
105
+ "Readiness verdict",
106
+ ],
80
107
  "architecture.md": [
81
108
  "Owning module / layer",
82
109
  "Integration points",
@@ -106,6 +133,9 @@ WORKSPACE_MAP_FIELDS = ("phase", "status", "next_action", "last_updated")
106
133
  SLICE_REQUIRED_FIELDS = (
107
134
  "Goal",
108
135
  "Satisfies",
136
+ "Forge",
137
+ "Forge strategies",
138
+ "Forge scorecard",
109
139
  "Files likely touched",
110
140
  "Tests/proof",
111
141
  "Mode",
@@ -169,10 +199,9 @@ def existing_or_alias(workspace: Path, name: str) -> bool:
169
199
 
170
200
 
171
201
  def find_local_workspaces(root: Path) -> list[Path]:
172
- # A devrites workspace is identified by its devrites-specific artifacts, NOT by
173
- # README.md: every repo root has a README.md, and matching on it here both
174
- # misclassifies the repo root as a workspace and short-circuits the .devrites/work
175
- # scan below (return [root] before the loop ever runs).
202
+ # Detect workspaces by DevRites artifacts, not README.md. Most repository
203
+ # roots have a README, which would misclassify the root and stop the
204
+ # .devrites/work scan before it starts.
176
205
  if root.is_dir() and any((root / name).exists() for name in ("spec.md", "state.md", "feature.md")):
177
206
  return [root]
178
207
  out: list[Path] = []
@@ -257,6 +286,56 @@ def validate_tasks(path: Path, text: str, errors: list[str]) -> None:
257
286
  if not re.search(rf"(?im)^{re.escape(field)}\s*:\s*\S+", body):
258
287
  errors.append(f"{path}: {slice_id} missing field '{field}:'")
259
288
 
289
+ def field(name: str) -> str:
290
+ match = re.search(rf"(?im)^{re.escape(name)}\s*:\s*(.+?)\s*$", body)
291
+ return match.group(1).strip() if match else ""
292
+
293
+ forge = field("Forge")
294
+ strategies = field("Forge strategies")
295
+ scorecard = field("Forge scorecard")
296
+ if not forge:
297
+ continue
298
+ if re.fullmatch(r"(?i)no", forge):
299
+ if strategies.lower() != "none" or scorecard.lower() != "none":
300
+ errors.append(
301
+ f"{path}: {slice_id} Forge:no requires strategies and scorecard 'none'"
302
+ )
303
+ continue
304
+ if not re.fullmatch(r"(?i)yes\s+[—-]\s+\S.*", forge):
305
+ errors.append(f"{path}: {slice_id} Forge must be 'no' or 'yes — <reason>'")
306
+ continue
307
+
308
+ parsed: list[tuple[str, str]] = []
309
+ for part in strategies.split("|"):
310
+ match = re.fullmatch(r"\s*([ABC])\s*=\s*(\S.*?)\s*", part)
311
+ if not match:
312
+ parsed = []
313
+ break
314
+ parsed.append((match.group(1), " ".join(match.group(2).split())))
315
+ ids = [item[0] for item in parsed]
316
+ descriptions = [item[1].casefold() for item in parsed]
317
+ if ids not in (["A", "B"], ["A", "B", "C"]) or len(set(descriptions)) != len(
318
+ descriptions
319
+ ):
320
+ errors.append(
321
+ f"{path}: {slice_id} Forge strategies must be 2-3 distinct contiguous A-C entries"
322
+ )
323
+
324
+ acceptance = re.search(r"(?i)\bacceptance\s*=\s*[^;\n]*", scorecard)
325
+ satisfies_ids = set(ID_PATTERNS["AC"].findall(field("Satisfies")))
326
+ scorecard_ids = set(ID_PATTERNS["AC"].findall(acceptance.group(0))) if acceptance else set()
327
+ test_plan = re.search(
328
+ r"(?i)\btest-plan\s*=\s*(?=\S)(?!none\b).*test-plan\.md", scorecard
329
+ )
330
+ if (
331
+ not satisfies_ids
332
+ or not satisfies_ids.issubset(scorecard_ids)
333
+ or not test_plan
334
+ ):
335
+ errors.append(
336
+ f"{path}: {slice_id} Forge scorecard must bind every Satisfies AC ID and exact test-plan.md rows or commands"
337
+ )
338
+
260
339
 
261
340
  def validate_id_register(path: Path, text: str, kind: str, errors: list[str]) -> None:
262
341
  if text and not ID_PATTERNS[kind].search(text):
@@ -299,9 +378,273 @@ def blocking_questions(questions: str) -> list[str]:
299
378
  qid = (ID_PATTERNS["Q"].search(chunk) or re.search(r"\bq-\d{4}-\d{2}-\d{2}-\d{3}\b", chunk))
300
379
  status = re.search(r"(?im)^status:\s*(\w+)", chunk)
301
380
  gate = re.search(r"(?im)^gate:\s*(\w+)", chunk)
302
- if status and status.group(1).lower() == "open" and gate and gate.group(1).lower() in {"blocking", "escalating"}:
381
+ if status and status.group(1).lower() == "open" and gate and gate.group(1).lower() in {"blocking", "validating", "escalating"}:
303
382
  out.append(qid.group(0) if qid else "<unknown>")
304
- return out
383
+
384
+ status_index = gate_index = qid_index = -1
385
+ for raw in questions.splitlines():
386
+ line = raw.strip()
387
+ if not (line.startswith("|") and line.endswith("|")):
388
+ status_index = gate_index = qid_index = -1
389
+ continue
390
+ cells = [cell.strip() for cell in line[1:-1].split("|")]
391
+ lowered = [cell.lower() for cell in cells]
392
+ if status_index < 0:
393
+ status_index = lowered.index("status") if "status" in lowered else -1
394
+ gate_index = lowered.index("gate") if "gate" in lowered else -1
395
+ for label in ("question id", "question", "id"):
396
+ if label in lowered:
397
+ qid_index = lowered.index(label)
398
+ break
399
+ if status_index < 0 or gate_index < 0:
400
+ status_index = gate_index = qid_index = -1
401
+ continue
402
+ if status_index >= len(cells) or gate_index >= len(cells):
403
+ continue
404
+ if cells[status_index].lower() != "open" or cells[gate_index].lower() not in {
405
+ "blocking",
406
+ "validating",
407
+ "escalating",
408
+ }:
409
+ continue
410
+ qid = cells[qid_index] if 0 <= qid_index < len(cells) else "<unknown>"
411
+ out.append(qid or "<unknown>")
412
+ return list(dict.fromkeys(out))
413
+
414
+
415
+ def readiness_digest(workspace: Path, names: list[str]) -> str:
416
+ digest = hashlib.sha256()
417
+ for name in names:
418
+ path = workspace / name
419
+ data = path.read_bytes()
420
+ if not data.strip():
421
+ raise ValueError(f"{name} is empty")
422
+ digest.update(name.encode())
423
+ digest.update(b"\0")
424
+ digest.update(str(len(data)).encode())
425
+ digest.update(b"\0")
426
+ digest.update(data)
427
+ digest.update(b"\0")
428
+ return digest.hexdigest()
429
+
430
+
431
+ def cursor_values(text: str, key: str) -> list[str]:
432
+ want = key.strip().lower().replace("_", " ")
433
+ values: list[str] = []
434
+ for raw in text.splitlines():
435
+ line = raw.strip()
436
+ if line.startswith("|") and line.endswith("|"):
437
+ cells = [cell.strip() for cell in line[1:-1].split("|")]
438
+ if len(cells) >= 2 and cells[0].lower().replace("_", " ") == want:
439
+ values.append(cells[1])
440
+ continue
441
+ line = line.lstrip("-*+ \t")
442
+ if ":" not in line:
443
+ continue
444
+ found, value = line.split(":", 1)
445
+ if found.strip().lower().replace("_", " ") == want:
446
+ values.append(value.strip())
447
+ return values
448
+
449
+
450
+ def markdown_section(text: str, wanted: str) -> str | None:
451
+ lines = text.splitlines()
452
+ start = -1
453
+ level = 0
454
+ for index, line in enumerate(lines):
455
+ match = re.match(r"^(#{1,6})\s+(.+?)\s*#*\s*$", line.strip())
456
+ if not match:
457
+ continue
458
+ if start < 0:
459
+ if match.group(2).strip().lower() == wanted.lower():
460
+ start = index + 1
461
+ level = len(match.group(1))
462
+ continue
463
+ if len(match.group(1)) <= level:
464
+ return "\n".join(lines[start:index])
465
+ return "\n".join(lines[start:]) if start >= 0 else None
466
+
467
+
468
+ def markdown_table(text: str, heading: str) -> list[list[str]]:
469
+ section = markdown_section(text, heading)
470
+ if section is None:
471
+ return []
472
+ rows: list[list[str]] = []
473
+ header_seen = False
474
+ for raw in section.splitlines():
475
+ line = raw.strip()
476
+ if not (line.startswith("|") and line.endswith("|")):
477
+ continue
478
+ cells = [cell.strip() for cell in line[1:-1].split("|")]
479
+ if all(not cell.strip(" :-") for cell in cells):
480
+ continue
481
+ if not header_seen:
482
+ header_seen = True
483
+ continue
484
+ rows.append(cells)
485
+ return rows
486
+
487
+
488
+ def empty_or_na(value: str) -> bool:
489
+ return value.strip().lower() in {"", "-", "n/a", "none"}
490
+
491
+
492
+ def substantive_cells(row: list[str], *indexes: int) -> bool:
493
+ return all(index < len(row) and not empty_or_na(row[index]) for index in indexes)
494
+
495
+
496
+ def none_row(value: str) -> bool:
497
+ return value.strip().lower() in {
498
+ "none",
499
+ "no material assumptions",
500
+ "no residual uncertainty",
501
+ }
502
+
503
+
504
+ def validate_readiness_base(
505
+ workspace: Path, contract: dict[str, object], errors: list[str]
506
+ ) -> str | None:
507
+ path = workspace / str(contract["artifact"])
508
+ if not path.exists():
509
+ errors.append(f"{path}: missing readiness artifact")
510
+ return None
511
+ text = read(path)
512
+ if not text.strip():
513
+ errors.append(f"{path}: empty readiness artifact")
514
+ return None
515
+ if READINESS_PLACEHOLDER_RE.search(text):
516
+ errors.append(f"{path}: contains an unresolved placeholder")
517
+ for heading in contract.get("requiredHeadings", []):
518
+ if markdown_section(text, str(heading)) is None:
519
+ errors.append(f"{path}: missing heading '{heading}'")
520
+ for heading in contract.get("requiredTables", []):
521
+ if not markdown_table(text, str(heading)):
522
+ errors.append(f"{path}: heading '{heading}' has no table data rows")
523
+
524
+ verdicts = cursor_values(text, str(contract["verdictField"]))
525
+ if len(verdicts) != 1 or verdicts[0].upper() != str(contract["readyValue"]).upper():
526
+ errors.append(
527
+ f"{path}: must contain exactly one "
528
+ f"{contract['verdictField']}: {contract['readyValue']}"
529
+ )
530
+ digests = cursor_values(text, str(contract["digestField"]))
531
+ if len(digests) != 1 or not READINESS_DIGEST_RE.fullmatch(digests[0].lower()):
532
+ errors.append(f"{path}: must contain exactly one valid {contract['digestField']}")
533
+ else:
534
+ try:
535
+ expected = readiness_digest(workspace, list(contract["inputs"]))
536
+ except (OSError, ValueError) as exc:
537
+ errors.append(f"{path}: cannot compute readiness digest: {exc}")
538
+ else:
539
+ if digests[0].lower() != expected:
540
+ errors.append(f"{path}: input digest is stale")
541
+ return text
542
+
543
+
544
+ def validate_decision_coverage(workspace: Path, errors: list[str]) -> None:
545
+ contract = READINESS_CONTRACT["coverage"]
546
+ text = validate_readiness_base(workspace, contract, errors)
547
+ if text is None:
548
+ return
549
+ path = workspace / "decision-coverage.md"
550
+ for index, row in enumerate(markdown_table(text, "Topology"), 1):
551
+ if len(row) < 4 or not substantive_cells(row, 0, 1, 3):
552
+ errors.append(f"{path}: topology row {index} is not evidence-backed")
553
+ rows = markdown_table(text, "Coverage matrix")
554
+ allowed = {"closed", "agent-owned", "not-applicable", "deferred-nonblocking"}
555
+ for index, row in enumerate(rows, 1):
556
+ if len(row) < 6:
557
+ errors.append(f"{path}: coverage row {index} has fewer than 6 cells")
558
+ continue
559
+ status = row[2].strip().lower()
560
+ if not substantive_cells(row, 0, 1, 5):
561
+ errors.append(f"{path}: coverage row {index} is incomplete")
562
+ if status not in allowed:
563
+ errors.append(
564
+ f"{path}: coverage row {index} has unresolved status {row[2]!r}"
565
+ )
566
+ if status == "closed" and not substantive_cells(row, 3):
567
+ errors.append(f"{path}: coverage row {index} is closed without a canonical reference")
568
+ if status in {"agent-owned", "deferred-nonblocking"} and empty_or_na(row[4]):
569
+ errors.append(
570
+ f"{path}: coverage row {index} has no owner/validation gate"
571
+ )
572
+ for index, row in enumerate(markdown_table(text, "Assumption audit"), 1):
573
+ if len(row) < 6:
574
+ errors.append(f"{path}: assumption row {index} has fewer than 6 cells")
575
+ elif not none_row(row[0]) and not substantive_cells(row, 0, 1, 2, 3, 4, 5):
576
+ errors.append(f"{path}: assumption row {index} is unowned or unverifiable")
577
+ for index, row in enumerate(markdown_table(text, "Residual uncertainty"), 1):
578
+ if len(row) < 4:
579
+ errors.append(f"{path}: residual row {index} has fewer than 4 cells")
580
+ elif not none_row(row[0]) and not substantive_cells(row, 0, 1, 2, 3):
581
+ errors.append(f"{path}: residual row {index} is unowned or unverifiable")
582
+ questions_path = workspace / "questions.md"
583
+ if questions_path.exists():
584
+ open_gates = blocking_questions(read(questions_path))
585
+ if open_gates:
586
+ errors.append(
587
+ f"{questions_path}: open blocking/validating/escalating questions: "
588
+ + ", ".join(open_gates)
589
+ )
590
+
591
+
592
+ def validate_engineering_readiness(workspace: Path, errors: list[str]) -> None:
593
+ contract = READINESS_CONTRACT["engineering"]
594
+ text = validate_readiness_base(workspace, contract, errors)
595
+ if text is None:
596
+ return
597
+ review_path = workspace / "eng-review.md"
598
+ for index, row in enumerate(markdown_table(text, "2a. Build-entry preflight"), 1):
599
+ if len(row) < 7:
600
+ errors.append(f"{review_path}: preflight row {index} has fewer than 7 cells")
601
+ continue
602
+ verdict = row[6].strip().lower()
603
+ if verdict not in {"pass", "n/a"}:
604
+ errors.append(f"{review_path}: preflight row {index} is not passing")
605
+ if not substantive_cells(row, 0) or (
606
+ verdict == "pass" and not substantive_cells(row, 1, 2, 4, 5)
607
+ ):
608
+ errors.append(f"{review_path}: preflight row {index} lacks executable provenance")
609
+ for index, row in enumerate(markdown_table(text, "2b. Implementation readiness"), 1):
610
+ if (
611
+ len(row) < 6
612
+ or row[5].strip().lower() != "ready"
613
+ or not substantive_cells(row, 0, 1, 2, 3, 4)
614
+ ):
615
+ errors.append(f"{review_path}: readiness row {index} is not ready")
616
+
617
+ test_contract = READINESS_CONTRACT["testPlan"]
618
+ test_path = workspace / str(test_contract["artifact"])
619
+ if not test_path.exists():
620
+ errors.append(f"{test_path}: missing test plan")
621
+ return
622
+ test_text = read(test_path)
623
+ if not test_text.strip() or READINESS_PLACEHOLDER_RE.search(test_text):
624
+ errors.append(f"{test_path}: empty or contains an unresolved placeholder")
625
+ return
626
+ for heading in test_contract.get("requiredHeadings", []):
627
+ if markdown_section(test_text, str(heading)) is None:
628
+ errors.append(f"{test_path}: missing heading '{heading}'")
629
+ for heading in test_contract.get("requiredTables", []):
630
+ rows = markdown_table(test_text, str(heading))
631
+ if not rows:
632
+ errors.append(f"{test_path}: heading '{heading}' has no table data rows")
633
+ continue
634
+ required_cells = (0, 1, 2, 3, 5)
635
+ if heading == "Per-gap test requirements":
636
+ required_cells = (0, 1, 2, 3, 4, 5, 6)
637
+ for index, row in enumerate(rows, 1):
638
+ if not substantive_cells(row, *required_cells):
639
+ errors.append(f"{test_path}: heading '{heading}' row {index} is incomplete")
640
+ mapping = markdown_section(test_text, "Acceptance → test map") or ""
641
+ if "→" not in mapping and "->" not in mapping:
642
+ errors.append(f"{test_path}: acceptance map has no mappings")
643
+ spec_path = workspace / "spec.md"
644
+ if spec_path.exists():
645
+ for acceptance_id in sorted(set(READINESS_AC_RE.findall(read(spec_path)))):
646
+ if acceptance_id.upper() not in mapping.upper():
647
+ errors.append(f"{test_path}: acceptance map does not map {acceptance_id}")
305
648
 
306
649
 
307
650
  def validate_workspace(workspace: Path) -> list[str]:
@@ -316,6 +659,10 @@ def validate_workspace(workspace: Path) -> list[str]:
316
659
  for name in required:
317
660
  if not existing_or_alias(workspace, name):
318
661
  errors.append(f"{workspace}: phase {phase} requires {name}")
662
+ if "decision-coverage.md" in required:
663
+ validate_decision_coverage(workspace, errors)
664
+ if "eng-review.md" in required:
665
+ validate_engineering_readiness(workspace, errors)
319
666
 
320
667
  for path in sorted(workspace.glob("*.md")):
321
668
  text = read(path)
@@ -419,7 +766,12 @@ def validate_workspace(workspace: Path) -> list[str]:
419
766
 
420
767
  def main(argv: list[str]) -> int:
421
768
  parser = argparse.ArgumentParser(description=__doc__)
422
- parser.add_argument("paths", nargs="*", default=["."], help="workspace dirs or roots containing .devrites/work|features|archive")
769
+ parser.add_argument(
770
+ "paths",
771
+ nargs="*",
772
+ default=["."],
773
+ help="workspace directories or roots containing .devrites/work|features|archive",
774
+ )
423
775
  args = parser.parse_args(argv)
424
776
 
425
777
  workspaces: list[Path] = []
@@ -230,6 +230,13 @@ else
230
230
  echo "skip: go not found; workflow manifest freshness not checked"
231
231
  fi
232
232
  if command -v python3 >/dev/null 2>&1; then
233
+ if python3 "$ROOT/scripts/check-authority-drift.py" >/tmp/dr_authority_drift 2>&1; then
234
+ cat /tmp/dr_authority_drift
235
+ good "authority-derived docs are current"
236
+ else
237
+ cat /tmp/dr_authority_drift
238
+ bad "authority-derived docs drifted"
239
+ fi
233
240
  if python3 "$ROOT/scripts/validate-workspace-schema.py" "$ROOT/tests/fixtures/workspace-schema" >/tmp/dr_workspace_schema 2>&1; then
234
241
  cat /tmp/dr_workspace_schema
235
242
  good "workspace artifact schema fixtures valid"
@@ -252,13 +259,13 @@ else
252
259
  fi
253
260
 
254
261
  # ---- 12. no runtime-broken pack/.claude/ path in installed prose ---------
255
- # After install the leading pack/ is stripped, so any literal pack/.claude/skills/devrites-lib/reference/standards/
256
- # or pack/.claude/skills/ in shipped SKILL.md / reference prose is a dead path
257
- # at runtime. (Repo README/docs links are out of scope: they're GitHub links.)
262
+ # Installed paths omit the leading pack/. A literal pack/.claude/skills/ path
263
+ # in shipped skill or reference prose will not resolve. Repository README and
264
+ # documentation links are GitHub links, so this check ignores them.
258
265
  section "no literal pack/.claude/ paths in shipped skill prose"
259
- # Exclude the intentional resolution-snippet fallback (`... || P=pack/.claude/...`): the
260
- # preamble snippet tries the installed `.claude/` path first, then `${CLAUDE_SKILL_DIR}`
261
- # (plugin, best-effort), then the repo `pack/.claude/...` for DevRites self-development.
266
+ # Keep the resolution-snippet fallback (`... || P=pack/.claude/...`). It checks
267
+ # the installed `.claude/` path first, then `${CLAUDE_SKILL_DIR}` as a
268
+ # best-effort plugin path, and finally the repository path during development.
262
269
  PACKPATH_HITS="$(grep -rnI -e 'pack/\.claude/skills/devrites-lib/reference/standards/' -e 'pack/\.claude/skills/' "$SKILLS" 2>/dev/null | grep -vE '\|\| [A-Z]+=pack/\.claude/skills/' || true)"
263
270
  if [ -n "$PACKPATH_HITS" ]; then
264
271
  bad "literal pack/.claude/ path in shipped skill prose (strips to .claude/ on install):"
@@ -268,8 +275,9 @@ else
268
275
  fi
269
276
 
270
277
  # ---- 14. no false session-start autoload claim ---------------------------
271
- # DevRites ships no autoload wiring; skills Read .claude/skills/devrites-lib/reference/standards/core.md at step 0.
272
- # Fail if any shipped skill or doc asserts native/session-start autoload.
278
+ # DevRites has no autoload wiring. Skills read
279
+ # .claude/skills/devrites-lib/reference/standards/core.md at step 0. Reject any
280
+ # shipped skill or document that claims native session-start autoloading.
273
281
  section "no false session-start autoload claim"
274
282
  AUTOLOAD_HITS="$(grep -rl 'autoloaded by Claude Code' "$ROOT/pack" "$ROOT/docs" "$ROOT/README.md" 2>/dev/null || true)"
275
283
  if [ -n "$AUTOLOAD_HITS" ]; then
@@ -280,9 +288,8 @@ else
280
288
  fi
281
289
 
282
290
  # ---- 14b. no deleted shell-helper guidance -------------------------------
283
- # The workflow control plane moved from devrites-lib/*.sh helpers to the
284
- # installed `devrites-engine` binary. Public docs and generated installer
285
- # guidance must not direct users or agents back to deleted helper files.
291
+ # Public docs and generated installer guidance must use the installed
292
+ # `devrites-engine` binary, not the retired devrites-lib/*.sh helpers.
286
293
  section "no deleted shell-helper guidance"
287
294
  DELETED_HELPER_HITS="$(grep -rnI \
288
295
  -e 'analyze\.sh' \
@@ -325,15 +332,14 @@ else
325
332
  fi
326
333
 
327
334
  # ---- 15. shellcheck (error = blocking, warning = advisory) ---------------
328
- # CI runners ship shellcheck, so the error-level gate is enforced on every PR.
329
- # Locally it self-skips when shellcheck is absent (the gate is non-blocking only
330
- # where the tool isn't installed: never silently downgraded where it is).
335
+ # CI runners include shellcheck and enforce the error-level gate on every PR.
336
+ # Local validation skips this gate only when shellcheck is not installed.
331
337
  section "shellcheck (-S error blocking · -S warning advisory)"
332
338
  if command -v shellcheck >/dev/null 2>&1; then
333
339
  for f in $SH_LIST; do
334
340
  if shellcheck -S error "$f"; then good "shellcheck ${f#"$ROOT"/}"; else bad "shellcheck (error) ${f#"$ROOT"/}"; fi
335
341
  done
336
- # warning-level is informational: surfaced per file, never fails the build.
342
+ # Warnings are advisory. Print them per file without failing the build.
337
343
  for f in $SH_LIST; do
338
344
  shellcheck -S warning "$f" >/dev/null 2>&1 || echo " advisory (warning-level): ${f#"$ROOT"/}"
339
345
  done