devrites 3.0.7 → 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 +12 -0
  2. package/README.md +67 -47
  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 +486 -38
  54. package/engine/internal/install/install_test.go +383 -10
  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,130 @@
1
+ package state
2
+
3
+ import (
4
+ "errors"
5
+ "strings"
6
+ "testing"
7
+ )
8
+
9
+ func TestNextClarifyCursorCases(t *testing.T) {
10
+ build := ClarifyCursor{Phase: PhaseBuild, Status: "running", NextAction: "/rite-build"}
11
+ clarify := ClarifyCursor{
12
+ Phase: PhaseClarify,
13
+ Status: "running",
14
+ NextAction: "/rite-clarify",
15
+ ReturnPhase: PhaseBuild,
16
+ ReturnNextAction: "/rite-build",
17
+ HasReturn: true,
18
+ }
19
+ cases := []struct {
20
+ name string
21
+ intent ClarifyIntent
22
+ current ClarifyCursor
23
+ want ClarifyCursor
24
+ wantErr bool
25
+ }{
26
+ {"enter", ClarifyEnter, build, clarify, false},
27
+ {"duplicate enter", ClarifyEnter, clarify, clarify, false},
28
+ {"too early enter", ClarifyEnter, ClarifyCursor{Phase: PhaseSpec}, ClarifyCursor{}, true},
29
+ {"missing restore target", ClarifyRestore, ClarifyCursor{Phase: PhaseClarify}, ClarifyCursor{Phase: PhaseClarify}, false},
30
+ {"invalid restore target", ClarifyRestore, ClarifyCursor{Phase: PhaseClarify, ReturnPhase: PhaseSpec, HasReturn: true}, ClarifyCursor{}, true},
31
+ {"restore", ClarifyRestore, clarify, build, false},
32
+ }
33
+ for _, tc := range cases {
34
+ t.Run(tc.name, func(t *testing.T) {
35
+ got, err := NextClarifyCursor(tc.intent, ClarifyTransitionInput{
36
+ Cursor: tc.current,
37
+ ClarifyNextAction: "/rite-clarify",
38
+ DefaultReturnNextAction: "/rite-build",
39
+ })
40
+ if tc.wantErr {
41
+ var transitionErr *ClarifyTransitionError
42
+ if !errors.As(err, &transitionErr) {
43
+ t.Fatalf("error=%v, want ClarifyTransitionError", err)
44
+ }
45
+ return
46
+ }
47
+ if err != nil || got != tc.want {
48
+ t.Fatalf("NextClarifyCursor()=(%+v, %v), want %+v", got, err, tc.want)
49
+ }
50
+ })
51
+ }
52
+ }
53
+
54
+ func TestClarifyCursorSerializationIsByteIdempotent(t *testing.T) {
55
+ original := []string{
56
+ "# State",
57
+ "",
58
+ "## Cursor",
59
+ "| Key | Value |",
60
+ "| --- | --- |",
61
+ "| phase | build |",
62
+ "| status | running |",
63
+ "| next_action | /rite-build |",
64
+ "",
65
+ "Curated note stays exactly here.",
66
+ }
67
+ current, err := ParseClarifyCursor(original)
68
+ if err != nil {
69
+ t.Fatal(err)
70
+ }
71
+ entered, err := NextClarifyCursor(ClarifyEnter, ClarifyTransitionInput{
72
+ Cursor: current,
73
+ ClarifyNextAction: "/rite-clarify",
74
+ DefaultReturnNextAction: "/rite-build",
75
+ })
76
+ if err != nil {
77
+ t.Fatal(err)
78
+ }
79
+ once := ApplyClarifyCursor(original, entered)
80
+ parsed, err := ParseClarifyCursor(once)
81
+ if err != nil {
82
+ t.Fatal(err)
83
+ }
84
+ twiceState, err := NextClarifyCursor(ClarifyEnter, ClarifyTransitionInput{
85
+ Cursor: parsed,
86
+ ClarifyNextAction: "/rite-clarify",
87
+ DefaultReturnNextAction: "/rite-build",
88
+ })
89
+ if err != nil {
90
+ t.Fatal(err)
91
+ }
92
+ twice := ApplyClarifyCursor(once, twiceState)
93
+ if got, want := strings.Join(twice, "\n"), strings.Join(once, "\n"); got != want {
94
+ t.Fatalf("second serialization changed bytes:\n--- once\n%s\n--- twice\n%s", want, got)
95
+ }
96
+ if !strings.Contains(strings.Join(once, "\n"), "Curated note stays exactly here.") {
97
+ t.Fatal("serializer removed curated Markdown")
98
+ }
99
+
100
+ restored, err := NextClarifyCursor(ClarifyRestore, ClarifyTransitionInput{
101
+ Cursor: parsed,
102
+ ClarifyNextAction: "/rite-clarify",
103
+ DefaultReturnNextAction: "/rite-build",
104
+ })
105
+ if err != nil {
106
+ t.Fatal(err)
107
+ }
108
+ restoreOnce := ApplyClarifyCursor(once, restored)
109
+ restoreParsed, err := ParseClarifyCursor(restoreOnce)
110
+ if err != nil {
111
+ t.Fatal(err)
112
+ }
113
+ restoreTwice, err := NextClarifyCursor(ClarifyRestore, ClarifyTransitionInput{Cursor: restoreParsed})
114
+ if err != nil {
115
+ t.Fatal(err)
116
+ }
117
+ if got, want := strings.Join(ApplyClarifyCursor(restoreOnce, restoreTwice), "\n"), strings.Join(restoreOnce, "\n"); got != want {
118
+ t.Fatalf("second restore serialization changed bytes:\n--- once\n%s\n--- twice\n%s", want, got)
119
+ }
120
+ }
121
+
122
+ func TestParseClarifyCursorRejectsInvalidReturnPhase(t *testing.T) {
123
+ _, err := ParseClarifyCursor([]string{
124
+ "| phase | clarify |",
125
+ "| return_phase | not-a-phase |",
126
+ })
127
+ if err == nil || !strings.Contains(err.Error(), "invalid return phase") {
128
+ t.Fatalf("error=%v, want invalid return phase", err)
129
+ }
130
+ }
@@ -18,15 +18,19 @@ func main() {
18
18
  flag.Parse()
19
19
 
20
20
  document := struct {
21
- GeneratedBy string `json:"generatedBy"`
22
- SchemaVersion int `json:"schemaVersion"`
23
- Phases []state.WorkflowPhase `json:"phases"`
24
- CursorKeyAliases []state.CursorKeyAlias `json:"cursorKeyAliases"`
21
+ GeneratedBy string `json:"generatedBy"`
22
+ SchemaVersion int `json:"schemaVersion"`
23
+ AuthorityPolicy state.AuthorityPolicy `json:"authorityPolicy"`
24
+ Phases []state.WorkflowPhase `json:"phases"`
25
+ ClarifyReturnFields []state.ClarifyFieldPolicy `json:"clarifyReturnFields"`
26
+ CursorKeyAliases []state.CursorKeyAlias `json:"cursorKeyAliases"`
25
27
  }{
26
- GeneratedBy: "go generate ./internal/state; DO NOT EDIT",
27
- SchemaVersion: state.SchemaVersion,
28
- Phases: state.WorkflowPhases(),
29
- CursorKeyAliases: state.CursorKeyAliases(),
28
+ GeneratedBy: "go generate ./internal/state; DO NOT EDIT",
29
+ SchemaVersion: state.SchemaVersion,
30
+ AuthorityPolicy: state.WorkflowAuthorityPolicy(),
31
+ Phases: state.WorkflowPhases(),
32
+ ClarifyReturnFields: state.ClarifyFieldPolicies(),
33
+ CursorKeyAliases: state.CursorKeyAliases(),
30
34
  }
31
35
  data, err := json.MarshalIndent(document, "", " ")
32
36
  if err != nil {
@@ -16,6 +16,8 @@ const (
16
16
  CursorQuestionID = "question_id"
17
17
  CursorActiveSlice = "active_slice"
18
18
  CursorAFKSlicesRemaining = "afk_slices_remaining"
19
+ CursorReturnPhase = "return_phase"
20
+ CursorReturnNextAction = "return_next_action"
19
21
  )
20
22
 
21
23
  var cursorKeyAliases = map[string]string{
@@ -87,6 +89,62 @@ func SetCursorField(lines []string, key, value string) ([]string, bool) {
87
89
  return out, false
88
90
  }
89
91
 
92
+ // UpsertCursorField replaces key when present or inserts it into the canonical
93
+ // cursor table (falling back to a legacy bullet when no table exists).
94
+ func UpsertCursorField(lines []string, key, value string) []string {
95
+ if out, ok := SetCursorField(lines, key, value); ok {
96
+ return out
97
+ }
98
+ out := append([]string(nil), lines...)
99
+ lastTable := -1
100
+ inCursor := false
101
+ for i, line := range out {
102
+ trimmed := strings.TrimSpace(line)
103
+ if strings.HasPrefix(trimmed, "## ") {
104
+ if inCursor {
105
+ break
106
+ }
107
+ inCursor = strings.EqualFold(strings.TrimSpace(strings.TrimPrefix(trimmed, "## ")), "Cursor")
108
+ continue
109
+ }
110
+ if len(trimmed) >= 2 && trimmed[0] == '|' && trimmed[len(trimmed)-1] == '|' {
111
+ gotKey, _, kind, ok := parseCursorLine(line)
112
+ normalized := normalizeCursorKey(gotKey)
113
+ if !inCursor && ok && (normalized == normalizeCursorKey(CursorPhase) ||
114
+ normalized == normalizeCursorKey(CursorStatus) ||
115
+ normalized == normalizeCursorKey(CursorNextAction)) {
116
+ inCursor = true
117
+ }
118
+ if inCursor && ok && kind == cursorLineTable {
119
+ lastTable = i
120
+ }
121
+ }
122
+ }
123
+ if lastTable >= 0 {
124
+ insert := "| " + key + " | " + value + " |"
125
+ out = append(out, "")
126
+ copy(out[lastTable+2:], out[lastTable+1:])
127
+ out[lastTable+1] = insert
128
+ return out
129
+ }
130
+ return append(out, "- "+key+": "+value)
131
+ }
132
+
133
+ // DeleteCursorField removes every presentation of key from canonical or legacy
134
+ // state without disturbing unrelated prose.
135
+ func DeleteCursorField(lines []string, key string) []string {
136
+ want := normalizeCursorKey(key)
137
+ out := make([]string, 0, len(lines))
138
+ for _, line := range lines {
139
+ gotKey, _, _, ok := parseCursorLine(line)
140
+ if ok && normalizeCursorKey(gotKey) == want {
141
+ continue
142
+ }
143
+ out = append(out, line)
144
+ }
145
+ return out
146
+ }
147
+
90
148
  func parseCursorLine(line string) (key, value string, kind cursorLineKind, ok bool) {
91
149
  trimmed := strings.TrimSpace(line)
92
150
  if len(trimmed) >= 2 && trimmed[0] == '|' && trimmed[len(trimmed)-1] == '|' {
@@ -1,6 +1,9 @@
1
1
  package state
2
2
 
3
- import "testing"
3
+ import (
4
+ "strings"
5
+ "testing"
6
+ )
4
7
 
5
8
  func TestCursorFieldReadsCanonicalTableAndLegacyLines(t *testing.T) {
6
9
  table := []string{
@@ -56,3 +59,41 @@ func TestSetCursorFieldPreservesCanonicalAndLegacyFormats(t *testing.T) {
56
59
  })
57
60
  }
58
61
  }
62
+
63
+ func TestUpsertAndDeleteCursorFieldStayInsideCursorTable(t *testing.T) {
64
+ lines := []string{
65
+ "# State",
66
+ "",
67
+ "## Cursor",
68
+ "| Key | Value |",
69
+ "| --- | --- |",
70
+ "| phase | build |",
71
+ "| status | running |",
72
+ "",
73
+ "## Awaiting human",
74
+ "| Key | Value |",
75
+ "| --- | --- |",
76
+ "| question_id | q-1 |",
77
+ }
78
+ lines = UpsertCursorField(lines, CursorReturnPhase, "build")
79
+ value, ok := CursorField(lines, CursorReturnPhase)
80
+ if !ok || value != "build" {
81
+ t.Fatalf("return phase=(%q,%v), want build", value, ok)
82
+ }
83
+ returnIndex, questionIndex := -1, -1
84
+ for i, line := range lines {
85
+ if strings.Contains(line, CursorReturnPhase) {
86
+ returnIndex = i
87
+ }
88
+ if strings.Contains(line, CursorQuestionID) {
89
+ questionIndex = i
90
+ }
91
+ }
92
+ if returnIndex < 0 || questionIndex < 0 || returnIndex > questionIndex {
93
+ t.Fatalf("upsert inserted outside cursor table:\n%s", strings.Join(lines, "\n"))
94
+ }
95
+ lines = DeleteCursorField(lines, CursorReturnPhase)
96
+ if _, ok := CursorField(lines, CursorReturnPhase); ok {
97
+ t.Fatalf("DeleteCursorField left return phase:\n%s", strings.Join(lines, "\n"))
98
+ }
99
+ }
@@ -10,52 +10,28 @@ import (
10
10
  "strings"
11
11
 
12
12
  "github.com/devrites/devrites/internal/devritespaths"
13
+ "github.com/devrites/devrites/internal/rootfacts"
13
14
  )
14
15
 
15
16
  // ResolveRoot returns the .devrites directory to operate on. A non-empty
16
17
  // override (e.g. from DEVRITES_ROOT) may name either the project root or its
17
18
  // .devrites directory; otherwise the nearest ancestor .devrites is used.
18
19
  func ResolveRoot(override string) (string, error) {
19
- if override != "" {
20
- info, err := os.Stat(override)
21
- if err != nil {
22
- return "", fmt.Errorf("DEVRITES_ROOT %q: %w", override, err)
23
- }
24
- if !info.IsDir() {
25
- return "", fmt.Errorf("DEVRITES_ROOT %q is not a directory", override)
26
- }
27
- if filepath.Base(filepath.Clean(override)) == ".devrites" {
28
- return override, nil
29
- }
30
- child := filepath.Join(override, ".devrites")
31
- if info, err := os.Stat(child); err == nil && info.IsDir() {
32
- return child, nil
33
- }
34
- return "", fmt.Errorf("DEVRITES_ROOT %q is not a project root or .devrites directory", override)
35
- }
36
- cwd, err := os.Getwd()
20
+ facts, err := rootfacts.Resolve(override)
37
21
  if err != nil {
38
- return "", fmt.Errorf("resolve working directory: %w", err)
39
- }
40
- for dir := cwd; ; {
41
- cand := filepath.Join(dir, ".devrites")
42
- if info, err := os.Stat(cand); err == nil && info.IsDir() {
43
- return cand, nil
44
- }
45
- parent := filepath.Dir(dir)
46
- if parent == dir {
47
- return "", errors.New("no .devrites directory found (set DEVRITES_ROOT or run inside a DevRites workspace)")
48
- }
49
- dir = parent
22
+ return "", err
50
23
  }
24
+ return facts.PhysicalRoot, nil
51
25
  }
52
26
 
53
27
  // Feature is a loaded per-feature workspace: its declared phase plus which
54
28
  // completeness sections currently have real content.
55
29
  type Feature struct {
56
- Slug string
57
- Phase Phase
58
- Present map[Section]bool
30
+ Slug string
31
+ Phase Phase
32
+ Present map[Section]bool
33
+ PresentFiles map[string]bool
34
+ LegacyLayout bool
59
35
  }
60
36
 
61
37
  // featureDir is where per-feature state lives under the root. work/ is the
@@ -174,7 +150,17 @@ func LoadFeature(root, slug string) (*Feature, error) {
174
150
  for _, s := range Sections {
175
151
  present[s] = sectionPresentAny(dir, s)
176
152
  }
177
- return &Feature{Slug: slug, Phase: phase, Present: present}, nil
153
+ presentFiles := make(map[string]bool)
154
+ for _, name := range WorkspaceFiles() {
155
+ presentFiles[name] = sectionPresent(filepath.Join(dir, name))
156
+ }
157
+ return &Feature{
158
+ Slug: slug,
159
+ Phase: phase,
160
+ Present: present,
161
+ PresentFiles: presentFiles,
162
+ LegacyLayout: filepath.Clean(dir) == filepath.Clean(filepath.Join(root, "features", slug)),
163
+ }, nil
178
164
  }
179
165
 
180
166
  // sectionPresentAny reports whether any file that can satisfy section s has real
@@ -204,22 +190,23 @@ func declaredPhaseFromLedger(path string) (Phase, bool) {
204
190
  return Phase(word), true
205
191
  }
206
192
 
207
- // ReadDeclaredSchemaVersion returns the schemaVersion declared in a feature's
208
- // feature.md frontmatter, or 0 if the file, frontmatter, or field is
209
- // absent/unparseable. Unlike LoadFeature it does NOT refuse a version newer than
210
- // the engine supports: doctor needs to observe a newer version to report skew,
211
- // not fail on it.
193
+ // ReadDeclaredSchemaVersion returns the schemaVersion from feature.md
194
+ // frontmatter, or 0 if the file, frontmatter, or field is missing or invalid.
195
+ // Unlike LoadFeature, it returns newer versions so doctor can report the skew.
212
196
  func ReadDeclaredSchemaVersion(root, slug string) int {
213
- raw, err := os.ReadFile(filepath.Join(featureDir(root, slug), "feature.md"))
214
- if err != nil {
215
- return 0
216
- }
217
- fm, _ := splitFrontmatter(raw)
218
- n, err := strconv.Atoi(strings.TrimSpace(fm["schemaVersion"]))
219
- if err != nil {
220
- return 0
197
+ dir := featureDir(root, slug)
198
+ for _, name := range workspaceMapFiles {
199
+ raw, err := os.ReadFile(filepath.Join(dir, name))
200
+ if err != nil {
201
+ continue
202
+ }
203
+ fm, _ := splitFrontmatter(raw)
204
+ n, err := strconv.Atoi(strings.TrimSpace(fm["schemaVersion"]))
205
+ if err == nil {
206
+ return n
207
+ }
221
208
  }
222
- return n
209
+ return 0
223
210
  }
224
211
 
225
212
  // MaxDeclaredSchemaVersion returns the highest schemaVersion declared by any
@@ -6,7 +6,7 @@ package state
6
6
  // A workspace map may declare its own schemaVersion in frontmatter; the engine
7
7
  // refuses a version newer than this (see LoadFeature) and otherwise reads the
8
8
  // files, which evolve additively.
9
- const SchemaVersion = 1
9
+ const SchemaVersion = 2
10
10
 
11
11
  const (
12
12
  WorkspaceMapFile = "README.md"
@@ -20,9 +20,8 @@ func WorkspaceMapFiles() []string {
20
20
  return append([]string(nil), workspaceMapFiles...)
21
21
  }
22
22
 
23
- // Section is one single-concern completeness file within a feature directory.
24
- // Splitting a feature into small files (rather than one long document) keeps
25
- // each file context-cheap and makes completeness self-evident.
23
+ // Section is one single-purpose completeness file in a feature directory. Small
24
+ // files use less context and make missing content easier to spot.
26
25
  type Section string
27
26
 
28
27
  const (
@@ -47,7 +46,7 @@ var Sections = []Section{
47
46
  // sectionFiles lists the filenames that can satisfy each section, canonical name
48
47
  // first, then supported aliases: the same mapping `devrites-engine migrate`
49
48
  // normalizes (proof→evidence, status→state). A section
50
- // counts as present if ANY of its files has real content, so the engine reads a
49
+ // counts as present if any of its files has real content, so the engine reads a
51
50
  // live workspace before the pack sweep converges the filenames. The workspace
52
51
  // map is not a section; it is handled separately in LoadFeature.
53
52
  var sectionFiles = map[Section][]string{
@@ -70,6 +69,7 @@ type Phase string
70
69
  const (
71
70
  PhaseFrame Phase = "frame" // problem framing
72
71
  PhaseSpec Phase = "spec" // specification
72
+ PhaseClarify Phase = "clarify" // decision-coverage closure
73
73
  PhaseTemper Phase = "temper" // strategic specification review
74
74
  PhaseDefine Phase = "define" // plan definition
75
75
  PhasePlan Phase = "plan" // approved plan
@@ -84,13 +84,13 @@ const (
84
84
  PhaseDone Phase = "done" // archived completion
85
85
  )
86
86
 
87
- // phaseDefinition is the single source of truth for lifecycle order and
88
- // phase-specific behavior. Workflow topology is versioned application logic,
89
- // rather than deploy-time configuration, so keeping it typed and ordered makes
90
- // additions compile-visible and lets every consumer derive the same arc.
87
+ // phaseDefinition owns lifecycle order and phase behavior. Workflow topology is
88
+ // versioned application logic rather than deployment configuration, so the
89
+ // compiler sees changes and every consumer derives the same ordering.
91
90
  type phaseDefinition struct {
92
91
  phase Phase
93
92
  resumeVerb string
93
+ transitionRight string
94
94
  required []Section
95
95
  aliases []string
96
96
  workspaceRequired []string
@@ -106,43 +106,64 @@ var (
106
106
  sectionsProof = []Section{SectionSpec, SectionPlan, SectionDecisions, SectionTasks, SectionProof}
107
107
  sectionsComplete = []Section{SectionSpec, SectionPlan, SectionDecisions, SectionTasks, SectionProof, SectionStatus}
108
108
 
109
- workspaceFrame = []string{"state.md"}
110
- workspaceSpec = []string{"brief.md", "spec.md", "state.md", "decisions.md", "assumptions.md", "questions.md"}
111
- workspacePlan = []string{"brief.md", "spec.md", "architecture.md", "plan.md", "tasks.md", "traceability.md", "state.md", "decisions.md", "assumptions.md", "questions.md"}
112
- workspaceProof = append(append([]string(nil), workspacePlan...), "evidence.md", "touched-files.md")
109
+ workspaceFrame = []string{"state.md"}
110
+ workspaceSpec = []string{"brief.md", "spec.md", "state.md", "decisions.md", "assumptions.md", "questions.md"}
111
+ workspaceClarify = append(append([]string(nil), workspaceSpec...), "decision-coverage.md")
112
+ workspacePlan = append(append([]string(nil), workspaceClarify...), "architecture.md", "plan.md", "tasks.md", "traceability.md")
113
+ workspaceVetted = append(append([]string(nil), workspacePlan...), "eng-review.md", "test-plan.md")
114
+ workspaceProof = append(append([]string(nil), workspaceVetted...), "evidence.md", "touched-files.md")
113
115
  )
114
116
 
115
117
  // Completeness is phase-relative: a section not yet required (e.g. proof during
116
- // spec) never blocks. Requirements are additive down the arc. Plan resumes at
117
- // define because plan is the artifact state produced by rite-define; done has no
118
- // resume command.
118
+ // spec) never blocks. Requirements are additive down the arc. Define is active
119
+ // authoring; Plan is the approved/repaired checkpoint that resumes at Vet.
119
120
  var phaseDefinitions = []phaseDefinition{
120
- {phase: PhaseFrame, resumeVerb: "frame", workspaceRequired: workspaceFrame},
121
- {phase: PhaseSpec, resumeVerb: "spec", required: sectionsSpec, aliases: []string{"specced", "specifying"}, workspaceRequired: workspaceSpec},
122
- {phase: PhaseTemper, resumeVerb: "temper", required: sectionsSpec, aliases: []string{"tempered", "tempering"}, workspaceRequired: workspaceSpec},
123
- {phase: PhaseDefine, resumeVerb: "define", required: sectionsPlan, aliases: []string{"defined", "defining"}, workspaceRequired: workspacePlan, blocksOpenQuestions: true},
124
- {phase: PhasePlan, resumeVerb: "define", required: sectionsPlan, aliases: []string{"planned", "planning"}, workspaceRequired: workspacePlan, blocksOpenQuestions: true},
125
- {phase: PhaseVet, resumeVerb: "vet", required: sectionsBuild, aliases: []string{"vetted", "vetting"}, workspaceRequired: workspacePlan, blocksOpenQuestions: true},
126
- {phase: PhaseBuild, resumeVerb: "build", required: sectionsBuild, aliases: []string{"building", "wip", "in", "in-progress"}, workspaceRequired: workspacePlan, blocksOpenQuestions: true},
127
- {phase: PhaseConverge, resumeVerb: "converge", required: sectionsBuild, aliases: []string{"converged", "converging"}, workspaceRequired: workspacePlan, blocksOpenQuestions: true},
128
- {phase: PhaseProve, resumeVerb: "prove", required: sectionsProof, aliases: []string{"proving", "proven", "testing"}, workspaceRequired: workspaceProof, proofRequired: true, blocksOpenQuestions: true},
129
- {phase: PhasePolish, resumeVerb: "polish", required: sectionsProof, aliases: []string{"polished", "polishing"}, workspaceRequired: workspaceProof, proofRequired: true, blocksOpenQuestions: true},
130
- {phase: PhaseReview, resumeVerb: "review", required: sectionsProof, aliases: []string{"reviewed", "reviewing"}, workspaceRequired: workspaceProof, proofRequired: true, blocksOpenQuestions: true},
131
- {phase: PhaseSeal, resumeVerb: "seal", required: sectionsComplete, aliases: []string{"sealed", "sealing"}, workspaceRequired: workspaceProof, proofRequired: true, blocksOpenQuestions: true, shippable: true},
132
- {phase: PhaseShip, resumeVerb: "ship", required: sectionsComplete, aliases: []string{"shipped", "shipping"}, workspaceRequired: workspaceProof, proofRequired: true, blocksOpenQuestions: true, shippable: true},
133
- {phase: PhaseDone, required: sectionsComplete, aliases: []string{"closed", "complete", "completed"}, workspaceRequired: workspaceProof, proofRequired: true, blocksOpenQuestions: true, shippable: true},
121
+ {phase: PhaseFrame, resumeVerb: "frame", transitionRight: "Frame an unstructured request before lifecycle work.", workspaceRequired: workspaceFrame},
122
+ {phase: PhaseSpec, resumeVerb: "spec", transitionRight: "Author the product specification.", required: sectionsSpec, aliases: []string{"specced", "specifying"}, workspaceRequired: workspaceSpec},
123
+ {phase: PhaseClarify, resumeVerb: "clarify", transitionRight: "Close decision coverage in the written specification.", required: sectionsSpec, aliases: []string{"clarified", "clarifying"}, workspaceRequired: workspaceClarify, blocksOpenQuestions: true},
124
+ {phase: PhaseTemper, resumeVerb: "temper", transitionRight: "Optionally challenge the clarified specification strategy.", required: sectionsSpec, aliases: []string{"tempered", "tempering"}, workspaceRequired: workspaceClarify, blocksOpenQuestions: true},
125
+ {phase: PhaseDefine, resumeVerb: "define", transitionRight: "Author and approve the initial implementation plan.", required: sectionsPlan, aliases: []string{"defined", "defining"}, workspaceRequired: workspacePlan, blocksOpenQuestions: true},
126
+ {phase: PhasePlan, resumeVerb: "vet", transitionRight: "Hold the approved or repaired plan checkpoint for engineering review.", required: sectionsPlan, aliases: []string{"planned", "planning"}, workspaceRequired: workspacePlan, blocksOpenQuestions: true},
127
+ {phase: PhaseVet, resumeVerb: "vet", transitionRight: "Review implementation readiness before build.", required: sectionsBuild, aliases: []string{"vetted", "vetting"}, workspaceRequired: workspaceVetted, blocksOpenQuestions: true},
128
+ {phase: PhaseBuild, resumeVerb: "build", transitionRight: "Implement the next approved vertical slice.", required: sectionsBuild, aliases: []string{"building", "wip", "in", "in-progress"}, workspaceRequired: workspaceVetted, blocksOpenQuestions: true},
129
+ {phase: PhaseConverge, resumeVerb: "converge", transitionRight: "Recover unmet clarified intent into new slices.", required: sectionsBuild, aliases: []string{"converged", "converging"}, workspaceRequired: workspaceVetted, blocksOpenQuestions: true},
130
+ {phase: PhaseProve, resumeVerb: "prove", transitionRight: "Produce acceptance evidence for the implementation.", required: sectionsProof, aliases: []string{"proving", "proven", "testing"}, workspaceRequired: workspaceProof, proofRequired: true, blocksOpenQuestions: true},
131
+ {phase: PhasePolish, resumeVerb: "polish", transitionRight: "Apply the bounded quality pass.", required: sectionsProof, aliases: []string{"polished", "polishing"}, workspaceRequired: workspaceProof, proofRequired: true, blocksOpenQuestions: true},
132
+ {phase: PhaseReview, resumeVerb: "review", transitionRight: "Review the proven implementation.", required: sectionsProof, aliases: []string{"reviewed", "reviewing"}, workspaceRequired: workspaceProof, proofRequired: true, blocksOpenQuestions: true},
133
+ {phase: PhaseSeal, resumeVerb: "seal", transitionRight: "Decide the final GO or NO-GO.", required: sectionsComplete, aliases: []string{"sealed", "sealing"}, workspaceRequired: workspaceProof, proofRequired: true, blocksOpenQuestions: true, shippable: true},
134
+ {phase: PhaseShip, resumeVerb: "ship", transitionRight: "Perform authorized release and close-out mutations.", required: sectionsComplete, aliases: []string{"shipped", "shipping"}, workspaceRequired: workspaceProof, proofRequired: true, blocksOpenQuestions: true, shippable: true},
135
+ {phase: PhaseDone, transitionRight: "Represent archived completion with no resume command.", required: sectionsComplete, aliases: []string{"closed", "complete", "completed"}, workspaceRequired: workspaceProof, proofRequired: true, blocksOpenQuestions: true, shippable: true},
134
136
  }
135
137
 
136
138
  // WorkflowPhase is the read-only cross-format view used to generate the compact
137
139
  // manifest consumed by non-Go release tooling.
138
140
  type WorkflowPhase struct {
139
- ID Phase `json:"id"`
140
- ResumeVerb string `json:"resumeVerb,omitempty"`
141
- Aliases []string `json:"aliases,omitempty"`
142
- WorkspaceRequired []string `json:"workspaceRequired"`
143
- ProofRequired bool `json:"proofRequired,omitempty"`
144
- BlocksOpenQuestions bool `json:"blocksOpenQuestions,omitempty"`
145
- Shippable bool `json:"shippable,omitempty"`
141
+ ID Phase `json:"id"`
142
+ ResumeVerb string `json:"resumeVerb,omitempty"`
143
+ TransitionRight string `json:"transitionRight"`
144
+ Aliases []string `json:"aliases,omitempty"`
145
+ RequiredSections []Section `json:"requiredSections,omitempty"`
146
+ WorkspaceRequired []string `json:"workspaceRequired"`
147
+ ProofRequired bool `json:"proofRequired,omitempty"`
148
+ BlocksOpenQuestions bool `json:"blocksOpenQuestions,omitempty"`
149
+ Shippable bool `json:"shippable,omitempty"`
150
+ }
151
+
152
+ // AuthorityPolicy owns the small cross-format trust and tracking assertions
153
+ // that otherwise drift between current docs.
154
+ type AuthorityPolicy struct {
155
+ PrinciplesTrust string `json:"principlesTrust"`
156
+ TrackedState []string `json:"trackedState"`
157
+ LocalState []string `json:"localState"`
158
+ }
159
+
160
+ // WorkflowAuthorityPolicy returns copied policy metadata for the manifest.
161
+ func WorkflowAuthorityPolicy() AuthorityPolicy {
162
+ return AuthorityPolicy{
163
+ PrinciplesTrust: "Project principles may become project policy only after explicit provenance and validation; arbitrary project-local Markdown is never inherently trusted executable instruction.",
164
+ TrackedState: []string{".devrites/specs/"},
165
+ LocalState: []string{".devrites/work/", ".devrites/archive/", ".devrites/ACTIVE"},
166
+ }
146
167
  }
147
168
 
148
169
  // WorkflowPhases returns copied metadata suitable for deterministic generation.
@@ -152,7 +173,9 @@ func WorkflowPhases() []WorkflowPhase {
152
173
  out = append(out, WorkflowPhase{
153
174
  ID: definition.phase,
154
175
  ResumeVerb: definition.resumeVerb,
176
+ TransitionRight: definition.transitionRight,
155
177
  Aliases: append([]string(nil), definition.aliases...),
178
+ RequiredSections: append([]Section(nil), definition.required...),
156
179
  WorkspaceRequired: append([]string(nil), definition.workspaceRequired...),
157
180
  ProofRequired: definition.proofRequired,
158
181
  BlocksOpenQuestions: definition.blocksOpenQuestions,
@@ -239,3 +262,33 @@ func RequiredSections(p Phase) []Section {
239
262
  }
240
263
  return out
241
264
  }
265
+
266
+ // RequiredWorkspaceFiles returns the canonical per-file completeness contract
267
+ // for phase p. Unlike the legacy Section view, this includes every durable
268
+ // workflow artifact (for example decision-coverage.md and test-plan.md), so
269
+ // runtime gates cannot silently ignore files added to the lifecycle registry.
270
+ func RequiredWorkspaceFiles(p Phase) []string {
271
+ definition, ok := definitionFor(p)
272
+ if !ok {
273
+ return nil
274
+ }
275
+ return append([]string(nil), definition.workspaceRequired...)
276
+ }
277
+
278
+ // WorkspaceFiles returns every lifecycle-owned workspace filename once, in
279
+ // first-required order. It is derived from phaseDefinitions so the runtime
280
+ // presence model and the generated workflow manifest share one authority.
281
+ func WorkspaceFiles() []string {
282
+ seen := make(map[string]bool)
283
+ var out []string
284
+ for _, definition := range phaseDefinitions {
285
+ for _, name := range definition.workspaceRequired {
286
+ if seen[name] {
287
+ continue
288
+ }
289
+ seen[name] = true
290
+ out = append(out, name)
291
+ }
292
+ }
293
+ return out
294
+ }
@@ -38,6 +38,8 @@ type WorkspaceSnapshot struct {
38
38
  Complete bool `json:"complete"`
39
39
  Sections []SectionSnapshot `json:"sections"`
40
40
  MissingSections []string `json:"missingSections,omitempty"`
41
+ Files []FileSnapshot `json:"files"`
42
+ MissingFiles []string `json:"missingFiles,omitempty"`
41
43
  CurrentSlice *SliceSnapshot `json:"currentSlice,omitempty"`
42
44
  Evidence EvidenceSnapshot `json:"evidence"`
43
45
  Drift DriftSnapshot `json:"drift"`
@@ -63,6 +65,12 @@ type SectionSnapshot struct {
63
65
  Required bool `json:"required"`
64
66
  }
65
67
 
68
+ type FileSnapshot struct {
69
+ Name string `json:"name"`
70
+ Present bool `json:"present"`
71
+ Required bool `json:"required"`
72
+ }
73
+
66
74
  type SliceSnapshot struct {
67
75
  Index int `json:"index"`
68
76
  Total int `json:"total"`
@@ -150,8 +158,16 @@ func Snapshot(root, slug string) (*WorkspaceSnapshot, error) {
150
158
  for _, section := range report.Missing {
151
159
  snap.MissingSections = append(snap.MissingSections, string(section))
152
160
  }
153
- if snap.RunMode == "AFK" && len(snap.MissingSections) > 0 {
154
- snap.Warnings = append(snap.Warnings, "AFK workspace has missing required sections")
161
+ for _, name := range WorkspaceFiles() {
162
+ snap.Files = append(snap.Files, FileSnapshot{
163
+ Name: name,
164
+ Present: report.PresentFiles[name],
165
+ Required: report.RequiredFiles[name],
166
+ })
167
+ }
168
+ snap.MissingFiles = append(snap.MissingFiles, report.MissingFiles...)
169
+ if snap.RunMode == "AFK" && len(snap.MissingFiles) > 0 {
170
+ snap.Warnings = append(snap.Warnings, "AFK workspace has missing required files")
155
171
  }
156
172
  if report.Required[SectionProof] && snap.Evidence.Status != "fresh" {
157
173
  snap.Warnings = append(snap.Warnings, "proof phase requires fresh evidence")