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
@@ -127,6 +127,169 @@ func TestPackageExistenceJavaScriptWorkspaces(t *testing.T) {
127
127
  }
128
128
  }
129
129
 
130
+ func TestPackageExistenceCSSImports(t *testing.T) {
131
+ tests := []struct {
132
+ name string
133
+ imports string
134
+ manifest string
135
+ wantCode int
136
+ wantFinding string
137
+ }{
138
+ {
139
+ name: "declared multi import ignores comment prefix",
140
+ imports: "/* @import \"comment-only\"; */ @import \"tailwindcss\"; @import \"./theme.css\";",
141
+ manifest: `{"dependencies":{"tailwindcss":"^4.1.0"}}`,
142
+ },
143
+ {
144
+ name: "undeclared url import",
145
+ imports: `@import url("missing-theme");`,
146
+ manifest: `{}`,
147
+ wantCode: 3,
148
+ wantFinding: "missing-theme",
149
+ },
150
+ {
151
+ name: "remote imports ignored",
152
+ imports: `@import url("https://cdn.example.invalid/theme.css"); @import "//cdn.example.invalid/other.css";`,
153
+ manifest: `{}`,
154
+ },
155
+ }
156
+
157
+ for _, test := range tests {
158
+ t.Run(test.name, func(t *testing.T) {
159
+ repo := t.TempDir()
160
+ writePackageExistenceFile(t, repo, "package.json", test.manifest+"\n")
161
+ writePackageExistenceFile(t, repo, "styles/app.css", "/* baseline */\n")
162
+ gitPackageExistence(t, repo, "init", "-q")
163
+ gitPackageExistence(t, repo, "config", "user.email", "test@example.com")
164
+ gitPackageExistence(t, repo, "config", "user.name", "Test")
165
+ gitPackageExistence(t, repo, "add", ".")
166
+ gitPackageExistence(t, repo, "commit", "-qm", "baseline")
167
+ if err := os.MkdirAll(featureDir(repo, "feat"), 0o755); err != nil {
168
+ t.Fatal(err)
169
+ }
170
+ writePackageExistenceFile(t, repo, "styles/app.css", "/* baseline */\n"+test.imports+"\n")
171
+ t.Chdir(repo)
172
+
173
+ var stdout, stderr bytes.Buffer
174
+ gotCode := PackageExistence(repo, []string{"feat"}, &stdout, &stderr)
175
+ if gotCode != test.wantCode {
176
+ t.Fatalf("code = %d, want %d\nstdout: %s\nstderr: %s", gotCode, test.wantCode, stdout.String(), stderr.String())
177
+ }
178
+ if test.wantFinding != "" && !strings.Contains(stderr.String(), test.wantFinding) {
179
+ t.Fatalf("stderr does not name %q: %s", test.wantFinding, stderr.String())
180
+ }
181
+ })
182
+ }
183
+ }
184
+
185
+ func TestPackageExistenceCSSResolvesBareSiblingPath(t *testing.T) {
186
+ repo := t.TempDir()
187
+ writePackageExistenceFile(t, repo, "package.json", "{}\n")
188
+ writePackageExistenceFile(t, repo, "styles/app.css", "/* baseline */\n")
189
+ writePackageExistenceFile(t, repo, "styles/theme.css", ":root { color-scheme: dark; }\n")
190
+ gitPackageExistence(t, repo, "init", "-q")
191
+ gitPackageExistence(t, repo, "config", "user.email", "test@example.com")
192
+ gitPackageExistence(t, repo, "config", "user.name", "Test")
193
+ gitPackageExistence(t, repo, "add", ".")
194
+ gitPackageExistence(t, repo, "commit", "-qm", "baseline")
195
+ if err := os.MkdirAll(featureDir(repo, "feat"), 0o755); err != nil {
196
+ t.Fatal(err)
197
+ }
198
+ writePackageExistenceFile(t, repo, "styles/app.css", "/* baseline */\n@import \"theme.css\";\n")
199
+ t.Chdir(repo)
200
+
201
+ var stdout, stderr bytes.Buffer
202
+ if code := PackageExistence(repo, []string{"feat"}, &stdout, &stderr); code != 0 {
203
+ t.Fatalf("code = %d, want 0\nstdout: %s\nstderr: %s", code, stdout.String(), stderr.String())
204
+ }
205
+ }
206
+
207
+ func TestPackageExistenceCSSHonorsBaselineCommentContext(t *testing.T) {
208
+ repo := t.TempDir()
209
+ baseline := "/* comment opened on an unchanged line\nunchanged content\n*/\n"
210
+ current := "/* comment opened on an unchanged line\n@import \"comment-only-package\";\nunchanged content\n*/\n"
211
+ writePackageExistenceFile(t, repo, "package.json", "{}\n")
212
+ writePackageExistenceFile(t, repo, "styles/app.css", baseline)
213
+ gitPackageExistence(t, repo, "init", "-q")
214
+ gitPackageExistence(t, repo, "config", "user.email", "test@example.com")
215
+ gitPackageExistence(t, repo, "config", "user.name", "Test")
216
+ gitPackageExistence(t, repo, "add", ".")
217
+ gitPackageExistence(t, repo, "commit", "-qm", "baseline")
218
+ if err := os.MkdirAll(featureDir(repo, "feat"), 0o755); err != nil {
219
+ t.Fatal(err)
220
+ }
221
+ writePackageExistenceFile(t, repo, "styles/app.css", current)
222
+ t.Chdir(repo)
223
+
224
+ var stdout, stderr bytes.Buffer
225
+ if code := PackageExistence(repo, []string{"feat"}, &stdout, &stderr); code != 0 {
226
+ t.Fatalf("code = %d, want 0\nstdout: %s\nstderr: %s", code, stdout.String(), stderr.String())
227
+ }
228
+ }
229
+
230
+ func TestPackageExistenceScansUntrackedSourceFiles(t *testing.T) {
231
+ repo := t.TempDir()
232
+ writePackageExistenceFile(t, repo, "package.json", "{}\n")
233
+ gitPackageExistence(t, repo, "init", "-q")
234
+ gitPackageExistence(t, repo, "config", "user.email", "test@example.com")
235
+ gitPackageExistence(t, repo, "config", "user.name", "Test")
236
+ gitPackageExistence(t, repo, "add", ".")
237
+ gitPackageExistence(t, repo, "commit", "-qm", "baseline")
238
+ if err := os.MkdirAll(featureDir(repo, "feat"), 0o755); err != nil {
239
+ t.Fatal(err)
240
+ }
241
+ writePackageExistenceFile(t, repo, "src/new.ts", `import missing from "untracked-missing-package";`+"\n")
242
+ t.Chdir(repo)
243
+
244
+ var stdout, stderr bytes.Buffer
245
+ gotCode := PackageExistence(repo, []string{"feat"}, &stdout, &stderr)
246
+ if gotCode != 3 {
247
+ t.Fatalf("code = %d, want 3\nstdout: %s\nstderr: %s", gotCode, stdout.String(), stderr.String())
248
+ }
249
+ if !strings.Contains(stderr.String(), "untracked-missing-package") {
250
+ t.Fatalf("stderr does not name untracked dependency: %s", stderr.String())
251
+ }
252
+ }
253
+
254
+ func TestPackageExistenceScansUntrackedFilesAddedAfterSliceSnapshot(t *testing.T) {
255
+ repo := newGitRepo(t)
256
+ root := workspace(t, "feat")
257
+ writePackageExistenceFile(t, repo, "package.json", "{}\n")
258
+ commitAll(t, repo, "manifest")
259
+ writeWrightAllowlist(t, root, "feat", "src/new.ts")
260
+ if code, out := runReconcile(t, root, "snapshot", "feat"); code != 0 {
261
+ t.Fatalf("snapshot = %d, want 0\n%s", code, out)
262
+ }
263
+ writePackageExistenceFile(t, repo, "src/new.ts", `import missing from "slice-untracked-package";`+"\n")
264
+ if code, out := runReconcile(t, root, "check", "feat"); code != 0 {
265
+ t.Fatalf("reconcile check = %d, want 0\n%s", code, out)
266
+ }
267
+
268
+ var stdout, stderr bytes.Buffer
269
+ code := PackageExistence(root, []string{"feat"}, &stdout, &stderr)
270
+ if code != 3 {
271
+ t.Fatalf("code = %d, want 3\nstdout: %s\nstderr: %s", code, stdout.String(), stderr.String())
272
+ }
273
+ if !strings.Contains(stderr.String(), "slice-untracked-package") {
274
+ t.Fatalf("stderr does not name slice dependency: %s", stderr.String())
275
+ }
276
+ }
277
+
278
+ func TestPackageExistenceFailsClosedOnPartialReconcileBaseline(t *testing.T) {
279
+ newGitRepo(t)
280
+ root := workspace(t, "feat")
281
+ writeFile(t, filepath.Join(featureDir(root, "feat"), reconcileBaseName), "deadbeef\n")
282
+
283
+ var stdout, stderr bytes.Buffer
284
+ code := PackageExistence(root, []string{"feat"}, &stdout, &stderr)
285
+ if code != 2 {
286
+ t.Fatalf("code = %d, want 2\nstdout: %s\nstderr: %s", code, stdout.String(), stderr.String())
287
+ }
288
+ if !strings.Contains(stderr.String(), "partial lifecycle") {
289
+ t.Fatalf("missing fail-closed lifecycle diagnostic:\n%s", stderr.String())
290
+ }
291
+ }
292
+
130
293
  func TestPackageExistenceNonJavaScriptWorkspaces(t *testing.T) {
131
294
  tests := []struct {
132
295
  name string
@@ -11,9 +11,9 @@ import (
11
11
  "github.com/devrites/devrites/internal/state"
12
12
  )
13
13
 
14
- // State-parsing patterns ported verbatim from progress.sh's awk. Each name-strip
15
- // sub is anchored (^ or $), so it matches at most once per line: ReplaceAllString
16
- // is therefore equivalent to awk's single-match sub.
14
+ // These state parsing patterns match progress.sh's awk expressions. Each
15
+ // name-removal pattern is anchored, so ReplaceAllString behaves like awk's
16
+ // single-match sub.
17
17
  var (
18
18
  sliceCheckRe = regexp.MustCompile(`^- \[[^\]]*\][[:space:]]*`) // drop "- [x] "
19
19
  sliceNameRe = regexp.MustCompile(`^Slice[[:space:]]*[0-9]+:[[:space:]]*`) // drop "Slice N: "
@@ -23,10 +23,9 @@ var (
23
23
  sliceHeadingRe = regexp.MustCompile(`^##[[:space:]]+(SLICE-[0-9]{3})(?:[[:space:]]+(.*))?$`)
24
24
  )
25
25
 
26
- // Progress renders the active feature's position: the deterministic footer every
27
- // workspace-operating rite-* skill prints last. Read-only. Prints nothing and
28
- // exits 0 when there is no active workspace, so pre-spec callers stay silent. root
29
- // is the .devrites directory; slug defaults to the active feature at <root>/ACTIVE.
26
+ // Progress renders the footer printed by workspace-aware rite-* skills. It reads
27
+ // from the .devrites root and returns 0 without output when no workspace is
28
+ // active. An empty slug uses <root>/ACTIVE.
30
29
  func Progress(root string, args []string, stdout, stderr io.Writer) int {
31
30
  slug := ""
32
31
  if len(args) > 0 {
@@ -82,8 +81,8 @@ func Progress(root string, args []string, stdout, stderr io.Writer) int {
82
81
  }
83
82
  }
84
83
 
85
- // Flow ribbon: the lifecycle spine; optional phases appear once their artifact
86
- // exists or while they are the active phase, so the cursor is never omitted.
84
+ // The flow ribbon includes optional phases when their artifact exists or while
85
+ // they are active. It always includes the current phase.
87
86
  order := make([]string, 0, len(state.LifecyclePhases()))
88
87
  for _, lifecyclePhase := range state.LifecyclePhases() {
89
88
  name := string(lifecyclePhase)
@@ -128,6 +127,9 @@ func Progress(root string, args []string, stdout, stderr io.Writer) int {
128
127
  ribbon += " " + name + " " + g
129
128
  }
130
129
  fmt.Fprintf(stdout, "%s\n", ribbon)
130
+ if obs := ObservabilityStatus(root, slug); obs != "" {
131
+ fmt.Fprintf(stdout, "Obs %s\n", obs)
132
+ }
131
133
  return 0
132
134
  }
133
135
 
@@ -0,0 +1,462 @@
1
+ package lib
2
+
3
+ import (
4
+ "crypto/rand"
5
+ "crypto/sha256"
6
+ "encoding/hex"
7
+ "encoding/json"
8
+ "fmt"
9
+ "os"
10
+ "path"
11
+ "path/filepath"
12
+ "regexp"
13
+ "strings"
14
+ "time"
15
+
16
+ "github.com/devrites/devrites/internal/reason"
17
+ "github.com/devrites/devrites/internal/rootfacts"
18
+ "github.com/devrites/devrites/internal/safepath"
19
+ "github.com/devrites/devrites/internal/state"
20
+ )
21
+
22
+ const EventSchemaV1 = "devrites-event/v1"
23
+
24
+ type EventBoundary string
25
+ type RootSource string
26
+ type ExecutionMode string
27
+ type GuardStrength string
28
+ type EventOutcome string
29
+ type EventHost string
30
+
31
+ const (
32
+ BoundaryRootSelection EventBoundary = "root-selection"
33
+ BoundaryLifecycleGate EventBoundary = "lifecycle-gate"
34
+ BoundaryHookGuard EventBoundary = "hook-guard"
35
+ BoundaryAgentDispatch EventBoundary = "agent-dispatch"
36
+ BoundaryResultReconcile EventBoundary = "result-reconciliation"
37
+ BoundaryGitPolicy EventBoundary = "git-policy"
38
+
39
+ RootSourceExplicit RootSource = "DEVRITES_ROOT"
40
+ RootSourceGit RootSource = "git-ancestor"
41
+ RootSourceGitMarker RootSource = "git-marker-ancestor"
42
+ RootSourceFilesystem RootSource = "filesystem-ancestor"
43
+ RootSourceNone RootSource = "none"
44
+
45
+ ExecutionNamed ExecutionMode = "named"
46
+ ExecutionGeneric ExecutionMode = "generic"
47
+ ExecutionInline ExecutionMode = "inline"
48
+ ExecutionNone ExecutionMode = "none"
49
+
50
+ GuardEnforced GuardStrength = "enforced"
51
+ GuardObserved GuardStrength = "observed"
52
+ GuardUnavailable GuardStrength = "unavailable"
53
+ GuardBypassed GuardStrength = "bypassed"
54
+ GuardNA GuardStrength = "n/a"
55
+
56
+ OutcomePassed EventOutcome = "passed"
57
+ OutcomeBlocked EventOutcome = "blocked"
58
+ OutcomeDenied EventOutcome = "denied"
59
+ OutcomeAllowed EventOutcome = "allowed"
60
+ OutcomeObserved EventOutcome = "observed"
61
+ OutcomeBypassed EventOutcome = "bypassed"
62
+ OutcomeUnavailable EventOutcome = "unavailable"
63
+ OutcomeWarning EventOutcome = "warning"
64
+ OutcomeRecorded EventOutcome = "recorded"
65
+
66
+ HostEngine EventHost = "engine"
67
+ HostClaude EventHost = "claude"
68
+ HostCodex EventHost = "codex"
69
+ )
70
+
71
+ // EventV1 is the privacy-bounded execution/provenance record shared by the
72
+ // existing root timeline and per-workspace event logs.
73
+ type EventV1 struct {
74
+ Schema string `json:"schema"`
75
+ TS string `json:"ts"`
76
+ RunID string `json:"run_id"`
77
+ Boundary EventBoundary `json:"boundary"`
78
+ RootSource RootSource `json:"root_source"`
79
+ Workspace string `json:"workspace,omitempty"`
80
+ PhaseBefore state.Phase `json:"phase_before,omitempty"`
81
+ Event string `json:"event"`
82
+ RuleIDs []reason.ID `json:"rule_ids"`
83
+ EvidencePaths []string `json:"evidence_paths"`
84
+ PhaseAfter state.Phase `json:"phase_after,omitempty"`
85
+ ExecutionMode ExecutionMode `json:"execution_mode"`
86
+ GuardStrength GuardStrength `json:"guard_strength"`
87
+ ReasonID reason.ID `json:"reason_id"`
88
+ Outcome EventOutcome `json:"outcome"`
89
+ Host EventHost `json:"host"`
90
+ }
91
+
92
+ var (
93
+ eventToken = regexp.MustCompile(`^[a-z][a-z0-9.-]{0,63}$`)
94
+ slugToken = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$`)
95
+ runIDToken = regexp.MustCompile(`^drv-run-v1:[0-9a-f]{32}$`)
96
+ processRun = makeProcessRunID()
97
+ )
98
+
99
+ func makeProcessRunID() string {
100
+ var raw [16]byte
101
+ if _, err := rand.Read(raw[:]); err == nil {
102
+ return "drv-run-v1:" + hex.EncodeToString(raw[:])
103
+ }
104
+ sum := sha256.Sum256([]byte(fmt.Sprintf("%d:%s", os.Getpid(), time.Now().UTC().Format(time.RFC3339Nano))))
105
+ return "drv-run-v1:" + hex.EncodeToString(sum[:16])
106
+ }
107
+
108
+ // CurrentRunID returns one opaque ID per engine process. Tests and hermetic
109
+ // runners may inject an already-opaque canonical ID; arbitrary environment
110
+ // values are never persisted.
111
+ func CurrentRunID() string {
112
+ if value := os.Getenv("DEVRITES_RUN_ID"); runIDToken.MatchString(value) {
113
+ return value
114
+ }
115
+ return processRun
116
+ }
117
+
118
+ // NewEventV1 fills the version, timestamp, and opaque process identity.
119
+ func NewEventV1(boundary EventBoundary, event string, reasonID reason.ID) EventV1 {
120
+ return EventV1{
121
+ Schema: EventSchemaV1,
122
+ TS: nowUTC(),
123
+ RunID: CurrentRunID(),
124
+ Boundary: boundary,
125
+ Event: event,
126
+ RuleIDs: []reason.ID{reasonID},
127
+ EvidencePaths: []string{},
128
+ ExecutionMode: ExecutionNone,
129
+ GuardStrength: GuardNA,
130
+ ReasonID: reasonID,
131
+ Host: HostEngine,
132
+ }
133
+ }
134
+
135
+ // NewRootSelectionEvent records the canonical root chosen for one command
136
+ // invocation without persisting that root's absolute path.
137
+ func NewRootSelectionEvent(root, command string) (EventV1, error) {
138
+ ev := NewEventV1(BoundaryRootSelection, "invoke."+command, reason.RootSelected)
139
+ ev.Outcome = OutcomePassed
140
+ if err := BindEventWorkspace(root, activeSlug(root), &ev); err != nil {
141
+ return EventV1{}, err
142
+ }
143
+ return ev, nil
144
+ }
145
+
146
+ // NewLifecycleGateEvent turns a typed gate result into a v1 provenance record.
147
+ func NewLifecycleGateEvent(root, slug, gateName string, phase state.Phase, blocked bool, reasonID reason.ID, evidenceFiles []string) (EventV1, error) {
148
+ ev := NewEventV1(BoundaryLifecycleGate, gateName, reasonID)
149
+ ev.PhaseBefore = phase
150
+ ev.PhaseAfter = phase
151
+ ev.EvidencePaths = WorkspaceEvidencePaths(root, slug, evidenceFiles...)
152
+ ev.Outcome = OutcomePassed
153
+ if blocked {
154
+ ev.Outcome = OutcomeBlocked
155
+ }
156
+ if err := BindEventWorkspace(root, slug, &ev); err != nil {
157
+ return EventV1{}, err
158
+ }
159
+ return ev, nil
160
+ }
161
+
162
+ // BindEventWorkspace adds only canonical, project-relative root/workspace
163
+ // identity and the current phase. It never copies an absolute path into ev.
164
+ func BindEventWorkspace(root, slug string, ev *EventV1) error {
165
+ if ev == nil {
166
+ return fmt.Errorf("nil event")
167
+ }
168
+ ev.RootSource = eventRootSource(root)
169
+ if slug == "" {
170
+ return nil
171
+ }
172
+ dir := featureDir(root, slug)
173
+ rel, err := filepath.Rel(filepath.Dir(root), dir)
174
+ if err != nil {
175
+ return fmt.Errorf("derive workspace identity: %w", err)
176
+ }
177
+ ev.Workspace = filepath.ToSlash(rel)
178
+ if feature, err := state.LoadFeature(root, slug); err == nil {
179
+ if ev.PhaseBefore == "" {
180
+ ev.PhaseBefore = feature.Phase
181
+ }
182
+ if ev.PhaseAfter == "" {
183
+ ev.PhaseAfter = feature.Phase
184
+ }
185
+ }
186
+ return nil
187
+ }
188
+
189
+ func eventRootSource(root string) RootSource {
190
+ override := os.Getenv("DEVRITES_ROOT")
191
+ facts, err := rootfacts.Resolve(override)
192
+ if err == nil && sameEventPath(facts.PhysicalRoot, root) {
193
+ switch facts.SelectionReason {
194
+ case string(RootSourceExplicit):
195
+ return RootSourceExplicit
196
+ case string(RootSourceGit):
197
+ return RootSourceGit
198
+ case string(RootSourceGitMarker):
199
+ return RootSourceGitMarker
200
+ case string(RootSourceFilesystem):
201
+ return RootSourceFilesystem
202
+ }
203
+ }
204
+ if strings.TrimSpace(override) != "" {
205
+ return RootSourceExplicit
206
+ }
207
+ return RootSourceNone
208
+ }
209
+
210
+ func sameEventPath(a, b string) bool {
211
+ if a == "" || b == "" {
212
+ return false
213
+ }
214
+ ai, aerr := os.Stat(a)
215
+ bi, berr := os.Stat(b)
216
+ if aerr == nil && berr == nil {
217
+ return os.SameFile(ai, bi)
218
+ }
219
+ return filepath.Clean(a) == filepath.Clean(b)
220
+ }
221
+
222
+ // WorkspaceEvidencePaths converts workspace-local names to project-relative
223
+ // paths. Invalid or absolute names are omitted; final validation remains the
224
+ // append boundary.
225
+ func WorkspaceEvidencePaths(root, slug string, names ...string) []string {
226
+ if slug == "" {
227
+ return []string{}
228
+ }
229
+ dir := featureDir(root, slug)
230
+ project := filepath.Dir(root)
231
+ out := make([]string, 0, len(names))
232
+ for _, name := range names {
233
+ name = filepath.ToSlash(name)
234
+ if !validRelativePath(name) {
235
+ continue
236
+ }
237
+ rel, err := filepath.Rel(project, filepath.Join(dir, filepath.FromSlash(name)))
238
+ if err == nil {
239
+ out = append(out, filepath.ToSlash(rel))
240
+ }
241
+ }
242
+ return out
243
+ }
244
+
245
+ // ValidateEventV1 enforces the privacy and compatibility boundary before disk.
246
+ func ValidateEventV1(ev EventV1) error {
247
+ if ev.Schema != EventSchemaV1 {
248
+ return fmt.Errorf("schema must be %q", EventSchemaV1)
249
+ }
250
+ if len(ev.TS) > 64 {
251
+ return fmt.Errorf("timestamp is too long")
252
+ }
253
+ if _, err := time.Parse(time.RFC3339, ev.TS); err != nil {
254
+ return fmt.Errorf("timestamp must be RFC3339: %w", err)
255
+ }
256
+ if !runIDToken.MatchString(ev.RunID) {
257
+ return fmt.Errorf("run_id must be an opaque drv-run-v1 token")
258
+ }
259
+ if !knownBoundary(ev.Boundary) {
260
+ return fmt.Errorf("unknown boundary %q", ev.Boundary)
261
+ }
262
+ if !knownRootSource(ev.RootSource) {
263
+ return fmt.Errorf("unknown root_source %q", ev.RootSource)
264
+ }
265
+ if ev.Workspace != "" && !validWorkspaceIdentity(ev.Workspace) {
266
+ return fmt.Errorf("workspace must be a canonical project-relative DevRites workspace")
267
+ }
268
+ if !validPhase(ev.PhaseBefore) || !validPhase(ev.PhaseAfter) {
269
+ return fmt.Errorf("phase_before and phase_after must be canonical phases")
270
+ }
271
+ if !eventToken.MatchString(ev.Event) {
272
+ return fmt.Errorf("event must be a bounded lowercase identifier")
273
+ }
274
+ if len(ev.RuleIDs) == 0 || len(ev.RuleIDs) > 16 {
275
+ return fmt.Errorf("rule_ids must contain 1..16 entries")
276
+ }
277
+ if err := validateReasonIDs(ev.RuleIDs); err != nil {
278
+ return err
279
+ }
280
+ if len(ev.EvidencePaths) > 32 {
281
+ return fmt.Errorf("evidence_paths exceeds 32 entries")
282
+ }
283
+ if err := validateRelativePaths(ev.EvidencePaths); err != nil {
284
+ return err
285
+ }
286
+ if !knownExecutionMode(ev.ExecutionMode) {
287
+ return fmt.Errorf("unknown execution_mode %q", ev.ExecutionMode)
288
+ }
289
+ if !knownGuardStrength(ev.GuardStrength) {
290
+ return fmt.Errorf("unknown guard_strength %q", ev.GuardStrength)
291
+ }
292
+ if !reason.Known(ev.ReasonID) {
293
+ return fmt.Errorf("unknown reason_id %q", ev.ReasonID)
294
+ }
295
+ if !knownOutcome(ev.Outcome) {
296
+ return fmt.Errorf("unknown outcome %q", ev.Outcome)
297
+ }
298
+ if ev.Host != HostEngine && ev.Host != HostClaude && ev.Host != HostCodex {
299
+ return fmt.Errorf("unknown host %q", ev.Host)
300
+ }
301
+ return nil
302
+ }
303
+
304
+ func validateReasonIDs(ids []reason.ID) error {
305
+ seen := make(map[reason.ID]bool, len(ids))
306
+ for _, id := range ids {
307
+ if !reason.Known(id) {
308
+ return fmt.Errorf("unknown rule id %q", id)
309
+ }
310
+ if seen[id] {
311
+ return fmt.Errorf("duplicate rule id %q", id)
312
+ }
313
+ seen[id] = true
314
+ }
315
+ return nil
316
+ }
317
+
318
+ func validateRelativePaths(paths []string) error {
319
+ seen := make(map[string]bool, len(paths))
320
+ for _, value := range paths {
321
+ if !validRelativePath(value) {
322
+ return fmt.Errorf("evidence path %q is not canonical project-relative", value)
323
+ }
324
+ if seen[value] {
325
+ return fmt.Errorf("duplicate evidence path %q", value)
326
+ }
327
+ seen[value] = true
328
+ }
329
+ return nil
330
+ }
331
+
332
+ func validRelativePath(value string) bool {
333
+ if value == "" || len(value) > 512 || strings.ContainsAny(value, "\\\x00") ||
334
+ path.IsAbs(value) || filepath.IsAbs(value) {
335
+ return false
336
+ }
337
+ clean := path.Clean(value)
338
+ return clean == value && clean != "." && clean != ".." && !strings.HasPrefix(clean, "../")
339
+ }
340
+
341
+ func validWorkspaceIdentity(value string) bool {
342
+ if !validRelativePath(value) {
343
+ return false
344
+ }
345
+ parts := strings.Split(value, "/")
346
+ return len(parts) == 3 && parts[0] == ".devrites" &&
347
+ (parts[1] == "work" || parts[1] == "features") &&
348
+ slugToken.MatchString(parts[2])
349
+ }
350
+
351
+ func validPhase(phase state.Phase) bool {
352
+ return phase == "" || state.KnownPhase(phase)
353
+ }
354
+
355
+ func knownBoundary(value EventBoundary) bool {
356
+ switch value {
357
+ case BoundaryRootSelection, BoundaryLifecycleGate, BoundaryHookGuard,
358
+ BoundaryAgentDispatch, BoundaryResultReconcile, BoundaryGitPolicy:
359
+ return true
360
+ }
361
+ return false
362
+ }
363
+
364
+ func knownRootSource(value RootSource) bool {
365
+ switch value {
366
+ case RootSourceExplicit, RootSourceGit, RootSourceGitMarker,
367
+ RootSourceFilesystem, RootSourceNone:
368
+ return true
369
+ }
370
+ return false
371
+ }
372
+
373
+ func knownExecutionMode(value ExecutionMode) bool {
374
+ switch value {
375
+ case ExecutionNamed, ExecutionGeneric, ExecutionInline, ExecutionNone:
376
+ return true
377
+ }
378
+ return false
379
+ }
380
+
381
+ func knownGuardStrength(value GuardStrength) bool {
382
+ switch value {
383
+ case GuardEnforced, GuardObserved, GuardUnavailable, GuardBypassed, GuardNA:
384
+ return true
385
+ }
386
+ return false
387
+ }
388
+
389
+ func knownOutcome(value EventOutcome) bool {
390
+ switch value {
391
+ case OutcomePassed, OutcomeBlocked, OutcomeDenied, OutcomeAllowed,
392
+ OutcomeObserved, OutcomeBypassed, OutcomeUnavailable, OutcomeWarning,
393
+ OutcomeRecorded:
394
+ return true
395
+ }
396
+ return false
397
+ }
398
+
399
+ // AppendEventV1 appends one validated record to the existing root timeline and,
400
+ // when present, the existing per-workspace event log. Callers intentionally
401
+ // treat errors as telemetry degradation rather than a policy decision.
402
+ func AppendEventV1(root string, ev EventV1) error {
403
+ if err := ValidateEventV1(ev); err != nil {
404
+ return err
405
+ }
406
+ info, err := os.Stat(root)
407
+ if err != nil || !info.IsDir() {
408
+ if err == nil {
409
+ err = fmt.Errorf("not a directory")
410
+ }
411
+ return fmt.Errorf("event root unavailable: %w", err)
412
+ }
413
+ rootLog := filepath.Join(root, "timeline.jsonl")
414
+ if err := validateEventLineTarget(rootLog, root); err != nil {
415
+ return err
416
+ }
417
+ workspaceLog := ""
418
+ if ev.Workspace != "" {
419
+ workspace := filepath.Join(filepath.Dir(root), filepath.FromSlash(ev.Workspace))
420
+ workspaceInfo, err := os.Stat(workspace)
421
+ if err != nil || !workspaceInfo.IsDir() || !safepath.WithinResolved(workspace, root) {
422
+ if err == nil {
423
+ err = fmt.Errorf("not a workspace directory inside root")
424
+ }
425
+ return fmt.Errorf("event workspace unavailable: %w", err)
426
+ }
427
+ workspaceLog = filepath.Join(workspace, "events.jsonl")
428
+ if err := validateEventLineTarget(workspaceLog, root); err != nil {
429
+ return err
430
+ }
431
+ }
432
+ line, err := json.Marshal(ev)
433
+ if err != nil {
434
+ return fmt.Errorf("marshal event: %w", err)
435
+ }
436
+ if err := appendEventLine(rootLog, root, line); err != nil {
437
+ return err
438
+ }
439
+ if workspaceLog == "" {
440
+ return nil
441
+ }
442
+ return appendEventLine(workspaceLog, root, line)
443
+ }
444
+
445
+ func appendEventLine(file, root string, line []byte) error {
446
+ if err := validateEventLineTarget(file, root); err != nil {
447
+ return err
448
+ }
449
+ return appendJSONLine(file, json.RawMessage(line))
450
+ }
451
+
452
+ func validateEventLineTarget(file, root string) error {
453
+ if info, err := os.Lstat(file); err == nil && info.Mode()&os.ModeSymlink != 0 {
454
+ return fmt.Errorf("event log is a symlink: %s", filepath.Base(file))
455
+ } else if err != nil && !os.IsNotExist(err) {
456
+ return fmt.Errorf("inspect event log: %w", err)
457
+ }
458
+ if !safepath.WithinResolved(filepath.Dir(file), root) {
459
+ return fmt.Errorf("event log escapes root")
460
+ }
461
+ return nil
462
+ }