devrites 5.14.4 → 6.0.1

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 (637) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/NOTICE.md +6 -4
  3. package/README.md +77 -16
  4. package/SECURITY.md +25 -11
  5. package/bin/devrites.mjs +49 -15
  6. package/docs/adr/0001-go-engine-as-control-plane.md +6 -3
  7. package/docs/adr/0029-v5-workspace-schema-and-native-migration.md +9 -0
  8. package/docs/adr/README.md +3 -2
  9. package/docs/agents/issue-tracker.md +1 -1
  10. package/docs/architecture.md +6 -3
  11. package/docs/cli.md +32 -9
  12. package/docs/command-map.md +5 -3
  13. package/docs/engine/state-schema.md +1 -1
  14. package/docs/flow.md +1 -1
  15. package/docs/harness-compliance.md +2 -2
  16. package/docs/release.md +15 -11
  17. package/docs/usage.md +1 -1
  18. package/engine/commands.go +1 -1
  19. package/engine/go.mod +1 -1
  20. package/engine/help.go +41 -15
  21. package/engine/help_test.go +134 -0
  22. package/engine/internal/acceptance/acceptance_test.go +298 -7
  23. package/engine/internal/acceptance/approve.go +7 -2
  24. package/engine/internal/acceptance/cli.go +3 -0
  25. package/engine/internal/acceptance/evidence.go +69 -5
  26. package/engine/internal/acceptance/lint.go +1 -1
  27. package/engine/internal/acceptance/run.go +5 -3
  28. package/engine/internal/acceptance/scaffold.go +5 -16
  29. package/engine/internal/devritespaths/paths.go +7 -6
  30. package/engine/internal/devritespaths/paths_test.go +18 -0
  31. package/engine/internal/fsutil/copy.go +83 -49
  32. package/engine/internal/fsutil/copy_test.go +70 -109
  33. package/engine/internal/gate/gate.go +89 -16
  34. package/engine/internal/gate/gate_test.go +101 -0
  35. package/engine/internal/hostpack/hostpack.go +52 -2
  36. package/engine/internal/hostpack/hostpack_test.go +83 -2
  37. package/engine/internal/install/apply.go +59 -16
  38. package/engine/internal/install/binary.go +10 -1
  39. package/engine/internal/install/install_test.go +428 -0
  40. package/engine/internal/install/merge.go +69 -38
  41. package/engine/internal/install/preflight.go +7 -0
  42. package/engine/internal/install/uninstall.go +43 -4
  43. package/engine/internal/lib/claims.go +25 -4
  44. package/engine/internal/lib/claims_test.go +57 -0
  45. package/engine/internal/lib/closeout.go +41 -3
  46. package/engine/internal/lib/closeout_test.go +144 -0
  47. package/engine/internal/lib/context.go +8 -4
  48. package/engine/internal/lib/context_next_test.go +100 -0
  49. package/engine/internal/lib/detect.go +1 -1
  50. package/engine/internal/lib/detect_test.go +13 -0
  51. package/engine/internal/lib/diffscope.go +19 -9
  52. package/engine/internal/lib/dispatch.go +7 -0
  53. package/engine/internal/lib/dup.go +40 -37
  54. package/engine/internal/lib/dup_test.go +178 -0
  55. package/engine/internal/lib/manifest_union.go +4 -1
  56. package/engine/internal/lib/manifest_union_test.go +37 -0
  57. package/engine/internal/lib/metrics.go +4 -0
  58. package/engine/internal/lib/metrics_test.go +28 -0
  59. package/engine/internal/lib/migrate_test.go +59 -0
  60. package/engine/internal/lib/observe_budgets.go +25 -9
  61. package/engine/internal/lib/observe_budgets_test.go +254 -0
  62. package/engine/internal/lib/observe_summary.go +23 -2
  63. package/engine/internal/lib/open_visual.go +4 -2
  64. package/engine/internal/lib/open_visual_test.go +11 -0
  65. package/engine/internal/lib/regression.go +62 -23
  66. package/engine/internal/lib/regression_test.go +103 -0
  67. package/engine/internal/lib/resolve.go +36 -17
  68. package/engine/internal/lib/resolve_remediation_test.go +132 -0
  69. package/engine/internal/lib/skilltrust.go +0 -11
  70. package/engine/internal/lib/slicecheck.go +7 -5
  71. package/engine/internal/lib/slicecheck_test.go +18 -0
  72. package/engine/internal/lib/triggers.go +9 -3
  73. package/engine/internal/lib/triggers_test.go +23 -0
  74. package/engine/internal/lib/util.go +17 -3
  75. package/engine/internal/lib/util_test.go +104 -0
  76. package/engine/internal/lib/visual_outline.go +7 -3
  77. package/engine/internal/lib/visual_outline_test.go +28 -0
  78. package/engine/internal/lib/windows.go +13 -9
  79. package/engine/internal/lib/windows_test.go +98 -0
  80. package/engine/internal/lib/workspace_read.go +2 -11
  81. package/engine/internal/lib/workspace_read_test.go +102 -0
  82. package/engine/internal/markdowntext/markdowntext.go +194 -11
  83. package/engine/internal/markdowntext/testdata/structural.json +250 -0
  84. package/engine/internal/notes/cli.go +30 -12
  85. package/engine/internal/notes/notes.go +242 -39
  86. package/engine/internal/notes/notes_test.go +372 -0
  87. package/engine/internal/overhaul/admit/packet.go +89 -0
  88. package/engine/internal/overhaul/admit/packet_test.go +223 -0
  89. package/engine/internal/overhaul/admit/run.go +4 -0
  90. package/engine/internal/overhaul/bench/run.go +6 -0
  91. package/engine/internal/overhaul/bench/run_test.go +46 -0
  92. package/engine/internal/overhaul/overhaul.go +6 -1
  93. package/engine/internal/overhaul/ovio/ovio.go +10 -3
  94. package/engine/internal/overhaul/ovio/ovio_test.go +75 -0
  95. package/engine/internal/overhaul/packet/packet.go +208 -0
  96. package/engine/internal/overhaul/packet/packet_test.go +263 -0
  97. package/engine/internal/overhaul/records/check.go +6 -0
  98. package/engine/internal/overhaul/records/init.go +16 -5
  99. package/engine/internal/overhaul/records/records_test.go +4 -0
  100. package/engine/internal/overhaul/records/run.go +66 -0
  101. package/engine/internal/overhaul/records/run_test.go +77 -0
  102. package/engine/internal/overhaul/render/run.go +15 -11
  103. package/engine/internal/overhaul/render/run_test.go +463 -1
  104. package/engine/internal/overhaul/render/visual.go +95 -33
  105. package/engine/internal/overhaul/score/run.go +20 -0
  106. package/engine/internal/overhaul/score/run_test.go +156 -1
  107. package/engine/internal/overhaul/snapshot/run.go +78 -21
  108. package/engine/internal/overhaul/snapshot/run_test.go +344 -8
  109. package/engine/internal/parallel/cli.go +33 -20
  110. package/engine/internal/parallel/cli_test.go +59 -0
  111. package/engine/internal/parallel/git.go +39 -1
  112. package/engine/internal/parallel/ops.go +101 -11
  113. package/engine/internal/parallel/ops_test.go +552 -69
  114. package/engine/internal/parallel/parallel_test.go +31 -1
  115. package/engine/internal/parallel/pathdisjoint.go +19 -1
  116. package/engine/internal/parallel/pathdisjoint_test.go +72 -0
  117. package/engine/internal/parallel/select.go +6 -2
  118. package/engine/internal/release/release.go +13 -3
  119. package/engine/internal/release/release_test.go +208 -3
  120. package/engine/internal/rootfacts/facts.go +9 -0
  121. package/engine/internal/rootfacts/facts_test.go +38 -0
  122. package/engine/internal/state/acceptance.go +7 -1
  123. package/engine/internal/state/budgets.go +11 -10
  124. package/engine/internal/state/budgets_test.go +93 -0
  125. package/engine/internal/state/cursor.go +19 -8
  126. package/engine/internal/state/cursor_test.go +120 -0
  127. package/engine/internal/state/lock_unix.go +7 -1
  128. package/engine/internal/state/lock_windows.go +7 -1
  129. package/engine/internal/state/state_test.go +49 -18
  130. package/engine/internal/state/taskgraph.go +18 -4
  131. package/engine/internal/state/taskgraph_test.go +43 -0
  132. package/engine/internal/state/workspaceschema.go +16 -2
  133. package/engine/internal/state/workspaceschema_test.go +34 -0
  134. package/engine/internal/state/write.go +67 -6
  135. package/engine/internal/state/write_test.go +136 -0
  136. package/engine/internal/version/version_test.go +2 -0
  137. package/engine/main.go +8 -1
  138. package/engine/observation_contract_test.go +1 -1
  139. package/engine/root_routing_test.go +23 -1
  140. package/install.sh +132 -12
  141. package/pack/.claude/agents/devrites-spec-reviewer.md +2 -2
  142. package/pack/.claude/skills/devrites-browser-proof/SKILL.md +2 -2
  143. package/pack/.claude/skills/devrites-frontend-craft/reference/design-references.md +18 -18
  144. package/pack/.claude/skills/devrites-frontend-craft/reference/quality-standards.md +6 -4
  145. package/pack/.claude/skills/devrites-interview/SKILL.md +4 -4
  146. package/pack/.claude/skills/devrites-lib/reference/parallel-dispatch.md +1 -1
  147. package/pack/.claude/skills/devrites-lib/reference/standards/afk-hitl.md +9 -1
  148. package/pack/.claude/skills/devrites-lib/reference/standards/agents.md +5 -2
  149. package/pack/.claude/skills/devrites-lib/reference/standards/gates.md +6 -5
  150. package/pack/.claude/skills/devrites-lib/reference/standards/spec-grammar.md +1 -1
  151. package/pack/.claude/skills/devrites-lib/reference/standards/verification-methods.md +3 -3
  152. package/pack/.claude/skills/devrites-lib/reference/standards/windows.md +3 -3
  153. package/pack/.claude/skills/devrites-lib/reference/standards/workflow-artifacts.md +2 -2
  154. package/pack/.claude/skills/devrites-lib/reference/visual-playbooks/outline-template.md +1 -1
  155. package/pack/.claude/skills/devrites-lib/reference/workspace-artifact-schema.md +4 -4
  156. package/pack/.claude/skills/devrites-ux-shape/SKILL.md +11 -12
  157. package/pack/.claude/skills/devrites-ux-shape/reference/visual-direction-probe.md +3 -1
  158. package/pack/.claude/skills/overhaul/SKILL.md +1 -1
  159. package/pack/.claude/skills/overhaul/references/orchestration.md +5 -3
  160. package/pack/.claude/skills/overhaul/references/records.md +39 -0
  161. package/pack/.claude/skills/overhaul/references/scoring.md +14 -4
  162. package/pack/.claude/skills/rite/reference/menu.md +1 -1
  163. package/pack/.claude/skills/rite-adopt/SKILL.md +8 -2
  164. package/pack/.claude/skills/rite-autocomplete/SKILL.md +11 -6
  165. package/pack/.claude/skills/rite-autocomplete/reference/decision-policy.md +3 -2
  166. package/pack/.claude/skills/rite-autocomplete/reference/loop.md +1 -1
  167. package/pack/.claude/skills/rite-autocomplete/reference/stop-conditions.md +6 -3
  168. package/pack/.claude/skills/rite-build/SKILL.md +2 -1
  169. package/pack/.claude/skills/rite-build/reference/afk-discipline.md +1 -1
  170. package/pack/.claude/skills/rite-build/reference/checkpoint-protocol.md +1 -1
  171. package/pack/.claude/skills/rite-build/reference/checkpoint.md +5 -0
  172. package/pack/.claude/skills/rite-build/reference/parallel-batch.md +2 -2
  173. package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +6 -3
  174. package/pack/.claude/skills/rite-clarify/SKILL.md +5 -4
  175. package/pack/.claude/skills/rite-clarify/reference/anti-patterns.md +2 -2
  176. package/pack/.claude/skills/rite-clarify/reference/decision-coverage.md +3 -2
  177. package/pack/.claude/skills/rite-converge/SKILL.md +2 -0
  178. package/pack/.claude/skills/rite-customize/SKILL.md +6 -4
  179. package/pack/.claude/skills/rite-define/SKILL.md +2 -0
  180. package/pack/.claude/skills/rite-define/reference/gates.md +8 -7
  181. package/pack/.claude/skills/rite-explain/SKILL.md +1 -1
  182. package/pack/.claude/skills/rite-explain/reference/intake.md +1 -1
  183. package/pack/.claude/skills/rite-fast/SKILL.md +1 -1
  184. package/pack/.claude/skills/rite-plan/SKILL.md +4 -3
  185. package/pack/.claude/skills/rite-plan/reference/replan-and-repair.md +4 -4
  186. package/pack/.claude/skills/rite-polish/reference/anti-ai-slop.md +1 -1
  187. package/pack/.claude/skills/rite-prove/reference/browser-proof.md +5 -5
  188. package/pack/.claude/skills/rite-resolve/SKILL.md +3 -3
  189. package/pack/.claude/skills/rite-seal/SKILL.md +4 -3
  190. package/pack/.claude/skills/rite-seal/reference/phase-contract.md +3 -2
  191. package/pack/.claude/skills/rite-seal/reference/seal-template.md +2 -0
  192. package/pack/.claude/skills/rite-ship/SKILL.md +4 -2
  193. package/pack/.claude/skills/rite-ship/reference/git-ship.md +8 -2
  194. package/pack/.claude/skills/rite-ship/reference/ship-template.md +2 -1
  195. package/pack/.claude/skills/rite-spec/SKILL.md +2 -2
  196. package/pack/.claude/skills/rite-spec/reference/spec-checklists.md +1 -1
  197. package/pack/.claude/skills/rite-spec/reference/spec-template.md +7 -3
  198. package/pack/.claude/skills/rite-spec/reference/state-workspace.md +2 -0
  199. package/pack/.claude/skills/rite-temper/SKILL.md +12 -9
  200. package/pack/.claude/skills/rite-temper/reference/scope-modes.md +1 -1
  201. package/pack/.claude/skills/rite-temper/reference/significance.md +10 -3
  202. package/pack/.claude/skills/rite-temper/reference/strategy-template.md +13 -2
  203. package/pack/.claude/skills/rite-vet/SKILL.md +4 -3
  204. package/pack/.claude/skills/rite-vet/reference/artifacts.md +1 -1
  205. package/pack/.claude/workflows/devrites-readonly-review.js +5 -4
  206. package/pack/generated/claude/agents/devrites-spec-reviewer.md +2 -2
  207. package/pack/generated/claude/skills/devrites-browser-proof/SKILL.md +2 -2
  208. package/pack/generated/claude/skills/devrites-frontend-craft/reference/design-references.md +18 -18
  209. package/pack/generated/claude/skills/devrites-frontend-craft/reference/quality-standards.md +6 -4
  210. package/pack/generated/claude/skills/devrites-interview/SKILL.md +4 -4
  211. package/pack/generated/claude/skills/devrites-lib/reference/parallel-dispatch.md +1 -1
  212. package/pack/generated/claude/skills/devrites-lib/reference/standards/afk-hitl.md +9 -1
  213. package/pack/generated/claude/skills/devrites-lib/reference/standards/agents.md +5 -2
  214. package/pack/generated/claude/skills/devrites-lib/reference/standards/gates.md +6 -5
  215. package/pack/generated/claude/skills/devrites-lib/reference/standards/spec-grammar.md +1 -1
  216. package/pack/generated/claude/skills/devrites-lib/reference/standards/verification-methods.md +3 -3
  217. package/pack/generated/claude/skills/devrites-lib/reference/standards/windows.md +3 -3
  218. package/pack/generated/claude/skills/devrites-lib/reference/standards/workflow-artifacts.md +2 -2
  219. package/pack/generated/claude/skills/devrites-lib/reference/visual-playbooks/outline-template.md +1 -1
  220. package/pack/generated/claude/skills/devrites-lib/reference/workspace-artifact-schema.md +4 -4
  221. package/pack/generated/claude/skills/devrites-ux-shape/SKILL.md +11 -12
  222. package/pack/generated/claude/skills/devrites-ux-shape/reference/visual-direction-probe.md +3 -1
  223. package/pack/generated/claude/skills/overhaul/SKILL.md +1 -1
  224. package/pack/generated/claude/skills/overhaul/references/orchestration.md +5 -3
  225. package/pack/generated/claude/skills/overhaul/references/records.md +39 -0
  226. package/pack/generated/claude/skills/overhaul/references/scoring.md +14 -4
  227. package/pack/generated/claude/skills/rite/reference/menu.md +1 -1
  228. package/pack/generated/claude/skills/rite-adopt/SKILL.md +8 -2
  229. package/pack/generated/claude/skills/rite-autocomplete/SKILL.md +11 -6
  230. package/pack/generated/claude/skills/rite-autocomplete/reference/decision-policy.md +3 -2
  231. package/pack/generated/claude/skills/rite-autocomplete/reference/loop.md +1 -1
  232. package/pack/generated/claude/skills/rite-autocomplete/reference/stop-conditions.md +6 -3
  233. package/pack/generated/claude/skills/rite-build/SKILL.md +2 -1
  234. package/pack/generated/claude/skills/rite-build/reference/afk-discipline.md +1 -1
  235. package/pack/generated/claude/skills/rite-build/reference/checkpoint-protocol.md +1 -1
  236. package/pack/generated/claude/skills/rite-build/reference/checkpoint.md +5 -0
  237. package/pack/generated/claude/skills/rite-build/reference/parallel-batch.md +2 -2
  238. package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +6 -3
  239. package/pack/generated/claude/skills/rite-clarify/SKILL.md +5 -4
  240. package/pack/generated/claude/skills/rite-clarify/reference/anti-patterns.md +2 -2
  241. package/pack/generated/claude/skills/rite-clarify/reference/decision-coverage.md +3 -2
  242. package/pack/generated/claude/skills/rite-converge/SKILL.md +2 -0
  243. package/pack/generated/claude/skills/rite-customize/SKILL.md +6 -4
  244. package/pack/generated/claude/skills/rite-define/SKILL.md +2 -0
  245. package/pack/generated/claude/skills/rite-define/reference/gates.md +8 -7
  246. package/pack/generated/claude/skills/rite-explain/SKILL.md +1 -1
  247. package/pack/generated/claude/skills/rite-explain/reference/intake.md +1 -1
  248. package/pack/generated/claude/skills/rite-fast/SKILL.md +1 -1
  249. package/pack/generated/claude/skills/rite-plan/SKILL.md +4 -3
  250. package/pack/generated/claude/skills/rite-plan/reference/replan-and-repair.md +4 -4
  251. package/pack/generated/claude/skills/rite-polish/reference/anti-ai-slop.md +1 -1
  252. package/pack/generated/claude/skills/rite-prove/reference/browser-proof.md +5 -5
  253. package/pack/generated/claude/skills/rite-resolve/SKILL.md +3 -3
  254. package/pack/generated/claude/skills/rite-seal/SKILL.md +4 -3
  255. package/pack/generated/claude/skills/rite-seal/reference/phase-contract.md +3 -2
  256. package/pack/generated/claude/skills/rite-seal/reference/seal-template.md +2 -0
  257. package/pack/generated/claude/skills/rite-ship/SKILL.md +4 -2
  258. package/pack/generated/claude/skills/rite-ship/reference/git-ship.md +8 -2
  259. package/pack/generated/claude/skills/rite-ship/reference/ship-template.md +2 -1
  260. package/pack/generated/claude/skills/rite-spec/SKILL.md +2 -2
  261. package/pack/generated/claude/skills/rite-spec/reference/spec-checklists.md +1 -1
  262. package/pack/generated/claude/skills/rite-spec/reference/spec-template.md +7 -3
  263. package/pack/generated/claude/skills/rite-spec/reference/state-workspace.md +2 -0
  264. package/pack/generated/claude/skills/rite-temper/SKILL.md +12 -9
  265. package/pack/generated/claude/skills/rite-temper/reference/scope-modes.md +1 -1
  266. package/pack/generated/claude/skills/rite-temper/reference/significance.md +10 -3
  267. package/pack/generated/claude/skills/rite-temper/reference/strategy-template.md +13 -2
  268. package/pack/generated/claude/skills/rite-vet/SKILL.md +4 -3
  269. package/pack/generated/claude/skills/rite-vet/reference/artifacts.md +1 -1
  270. package/pack/generated/claude/workflows/devrites-readonly-review.js +5 -4
  271. package/pack/generated/codex/AGENTS.md +1 -1
  272. package/pack/generated/codex/agents/devrites-spec-reviewer.toml +2 -2
  273. package/pack/generated/codex/skills/devrites-browser-proof/SKILL.md +2 -2
  274. package/pack/generated/codex/skills/devrites-frontend-craft/reference/design-references.md +18 -18
  275. package/pack/generated/codex/skills/devrites-frontend-craft/reference/quality-standards.md +6 -4
  276. package/pack/generated/codex/skills/devrites-interview/SKILL.md +4 -4
  277. package/pack/generated/codex/skills/devrites-lib/reference/parallel-dispatch.md +1 -1
  278. package/pack/generated/codex/skills/devrites-lib/reference/standards/afk-hitl.md +9 -1
  279. package/pack/generated/codex/skills/devrites-lib/reference/standards/agents.md +5 -2
  280. package/pack/generated/codex/skills/devrites-lib/reference/standards/gates.md +6 -5
  281. package/pack/generated/codex/skills/devrites-lib/reference/standards/spec-grammar.md +1 -1
  282. package/pack/generated/codex/skills/devrites-lib/reference/standards/verification-methods.md +3 -3
  283. package/pack/generated/codex/skills/devrites-lib/reference/standards/windows.md +3 -3
  284. package/pack/generated/codex/skills/devrites-lib/reference/standards/workflow-artifacts.md +2 -2
  285. package/pack/generated/codex/skills/devrites-lib/reference/visual-playbooks/outline-template.md +1 -1
  286. package/pack/generated/codex/skills/devrites-lib/reference/workspace-artifact-schema.md +4 -4
  287. package/pack/generated/codex/skills/devrites-ux-shape/SKILL.md +11 -12
  288. package/pack/generated/codex/skills/devrites-ux-shape/reference/visual-direction-probe.md +3 -1
  289. package/pack/generated/codex/skills/overhaul/SKILL.md +1 -1
  290. package/pack/generated/codex/skills/overhaul/references/orchestration.md +5 -3
  291. package/pack/generated/codex/skills/overhaul/references/records.md +39 -0
  292. package/pack/generated/codex/skills/overhaul/references/scoring.md +14 -4
  293. package/pack/generated/codex/skills/rite/reference/menu.md +1 -1
  294. package/pack/generated/codex/skills/rite-adopt/SKILL.md +8 -2
  295. package/pack/generated/codex/skills/rite-autocomplete/SKILL.md +11 -6
  296. package/pack/generated/codex/skills/rite-autocomplete/reference/decision-policy.md +3 -2
  297. package/pack/generated/codex/skills/rite-autocomplete/reference/loop.md +1 -1
  298. package/pack/generated/codex/skills/rite-autocomplete/reference/stop-conditions.md +6 -3
  299. package/pack/generated/codex/skills/rite-build/SKILL.md +2 -1
  300. package/pack/generated/codex/skills/rite-build/reference/afk-discipline.md +1 -1
  301. package/pack/generated/codex/skills/rite-build/reference/checkpoint-protocol.md +1 -1
  302. package/pack/generated/codex/skills/rite-build/reference/checkpoint.md +5 -0
  303. package/pack/generated/codex/skills/rite-build/reference/parallel-batch.md +2 -2
  304. package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +6 -3
  305. package/pack/generated/codex/skills/rite-clarify/SKILL.md +5 -4
  306. package/pack/generated/codex/skills/rite-clarify/reference/anti-patterns.md +2 -2
  307. package/pack/generated/codex/skills/rite-clarify/reference/decision-coverage.md +3 -2
  308. package/pack/generated/codex/skills/rite-converge/SKILL.md +2 -0
  309. package/pack/generated/codex/skills/rite-customize/SKILL.md +6 -4
  310. package/pack/generated/codex/skills/rite-define/SKILL.md +2 -0
  311. package/pack/generated/codex/skills/rite-define/reference/gates.md +8 -7
  312. package/pack/generated/codex/skills/rite-explain/SKILL.md +1 -1
  313. package/pack/generated/codex/skills/rite-explain/reference/intake.md +1 -1
  314. package/pack/generated/codex/skills/rite-fast/SKILL.md +1 -1
  315. package/pack/generated/codex/skills/rite-plan/SKILL.md +4 -3
  316. package/pack/generated/codex/skills/rite-plan/reference/replan-and-repair.md +4 -4
  317. package/pack/generated/codex/skills/rite-polish/reference/anti-ai-slop.md +1 -1
  318. package/pack/generated/codex/skills/rite-prove/reference/browser-proof.md +5 -5
  319. package/pack/generated/codex/skills/rite-resolve/SKILL.md +3 -3
  320. package/pack/generated/codex/skills/rite-seal/SKILL.md +4 -3
  321. package/pack/generated/codex/skills/rite-seal/reference/phase-contract.md +3 -2
  322. package/pack/generated/codex/skills/rite-seal/reference/seal-template.md +2 -0
  323. package/pack/generated/codex/skills/rite-ship/SKILL.md +4 -2
  324. package/pack/generated/codex/skills/rite-ship/reference/git-ship.md +8 -2
  325. package/pack/generated/codex/skills/rite-ship/reference/ship-template.md +2 -1
  326. package/pack/generated/codex/skills/rite-spec/SKILL.md +2 -2
  327. package/pack/generated/codex/skills/rite-spec/reference/spec-checklists.md +1 -1
  328. package/pack/generated/codex/skills/rite-spec/reference/spec-template.md +7 -3
  329. package/pack/generated/codex/skills/rite-spec/reference/state-workspace.md +2 -0
  330. package/pack/generated/codex/skills/rite-temper/SKILL.md +12 -9
  331. package/pack/generated/codex/skills/rite-temper/reference/scope-modes.md +1 -1
  332. package/pack/generated/codex/skills/rite-temper/reference/significance.md +10 -3
  333. package/pack/generated/codex/skills/rite-temper/reference/strategy-template.md +13 -2
  334. package/pack/generated/codex/skills/rite-vet/SKILL.md +4 -3
  335. package/pack/generated/codex/skills/rite-vet/reference/artifacts.md +1 -1
  336. package/pack/generated/devin/AGENTS.md +1 -1
  337. package/pack/generated/devin/agents/devrites-spec-reviewer.md +2 -2
  338. package/pack/generated/devin/skills/devrites-browser-proof/SKILL.md +2 -2
  339. package/pack/generated/devin/skills/devrites-frontend-craft/reference/design-references.md +18 -18
  340. package/pack/generated/devin/skills/devrites-frontend-craft/reference/quality-standards.md +6 -4
  341. package/pack/generated/devin/skills/devrites-interview/SKILL.md +4 -4
  342. package/pack/generated/devin/skills/devrites-lib/reference/parallel-dispatch.md +1 -1
  343. package/pack/generated/devin/skills/devrites-lib/reference/standards/afk-hitl.md +9 -1
  344. package/pack/generated/devin/skills/devrites-lib/reference/standards/agents.md +5 -2
  345. package/pack/generated/devin/skills/devrites-lib/reference/standards/gates.md +6 -5
  346. package/pack/generated/devin/skills/devrites-lib/reference/standards/spec-grammar.md +1 -1
  347. package/pack/generated/devin/skills/devrites-lib/reference/standards/verification-methods.md +3 -3
  348. package/pack/generated/devin/skills/devrites-lib/reference/standards/windows.md +3 -3
  349. package/pack/generated/devin/skills/devrites-lib/reference/standards/workflow-artifacts.md +2 -2
  350. package/pack/generated/devin/skills/devrites-lib/reference/visual-playbooks/outline-template.md +1 -1
  351. package/pack/generated/devin/skills/devrites-lib/reference/workspace-artifact-schema.md +4 -4
  352. package/pack/generated/devin/skills/devrites-ux-shape/SKILL.md +11 -12
  353. package/pack/generated/devin/skills/devrites-ux-shape/reference/visual-direction-probe.md +3 -1
  354. package/pack/generated/devin/skills/overhaul/SKILL.md +1 -1
  355. package/pack/generated/devin/skills/overhaul/references/orchestration.md +5 -3
  356. package/pack/generated/devin/skills/overhaul/references/records.md +39 -0
  357. package/pack/generated/devin/skills/overhaul/references/scoring.md +14 -4
  358. package/pack/generated/devin/skills/rite/reference/menu.md +1 -1
  359. package/pack/generated/devin/skills/rite-adopt/SKILL.md +8 -2
  360. package/pack/generated/devin/skills/rite-autocomplete/SKILL.md +11 -6
  361. package/pack/generated/devin/skills/rite-autocomplete/reference/decision-policy.md +3 -2
  362. package/pack/generated/devin/skills/rite-autocomplete/reference/loop.md +1 -1
  363. package/pack/generated/devin/skills/rite-autocomplete/reference/stop-conditions.md +6 -3
  364. package/pack/generated/devin/skills/rite-build/SKILL.md +2 -1
  365. package/pack/generated/devin/skills/rite-build/reference/afk-discipline.md +1 -1
  366. package/pack/generated/devin/skills/rite-build/reference/checkpoint-protocol.md +1 -1
  367. package/pack/generated/devin/skills/rite-build/reference/checkpoint.md +5 -0
  368. package/pack/generated/devin/skills/rite-build/reference/parallel-batch.md +2 -2
  369. package/pack/generated/devin/skills/rite-build/reference/wright-dispatch.md +6 -3
  370. package/pack/generated/devin/skills/rite-clarify/SKILL.md +5 -4
  371. package/pack/generated/devin/skills/rite-clarify/reference/anti-patterns.md +2 -2
  372. package/pack/generated/devin/skills/rite-clarify/reference/decision-coverage.md +3 -2
  373. package/pack/generated/devin/skills/rite-converge/SKILL.md +2 -0
  374. package/pack/generated/devin/skills/rite-customize/SKILL.md +6 -4
  375. package/pack/generated/devin/skills/rite-define/SKILL.md +2 -0
  376. package/pack/generated/devin/skills/rite-define/reference/gates.md +8 -7
  377. package/pack/generated/devin/skills/rite-explain/SKILL.md +1 -1
  378. package/pack/generated/devin/skills/rite-explain/reference/intake.md +1 -1
  379. package/pack/generated/devin/skills/rite-fast/SKILL.md +1 -1
  380. package/pack/generated/devin/skills/rite-plan/SKILL.md +4 -3
  381. package/pack/generated/devin/skills/rite-plan/reference/replan-and-repair.md +4 -4
  382. package/pack/generated/devin/skills/rite-polish/reference/anti-ai-slop.md +1 -1
  383. package/pack/generated/devin/skills/rite-prove/reference/browser-proof.md +5 -5
  384. package/pack/generated/devin/skills/rite-resolve/SKILL.md +3 -3
  385. package/pack/generated/devin/skills/rite-seal/SKILL.md +4 -3
  386. package/pack/generated/devin/skills/rite-seal/reference/phase-contract.md +3 -2
  387. package/pack/generated/devin/skills/rite-seal/reference/seal-template.md +2 -0
  388. package/pack/generated/devin/skills/rite-ship/SKILL.md +4 -2
  389. package/pack/generated/devin/skills/rite-ship/reference/git-ship.md +8 -2
  390. package/pack/generated/devin/skills/rite-ship/reference/ship-template.md +2 -1
  391. package/pack/generated/devin/skills/rite-spec/SKILL.md +2 -2
  392. package/pack/generated/devin/skills/rite-spec/reference/spec-checklists.md +1 -1
  393. package/pack/generated/devin/skills/rite-spec/reference/spec-template.md +7 -3
  394. package/pack/generated/devin/skills/rite-spec/reference/state-workspace.md +2 -0
  395. package/pack/generated/devin/skills/rite-temper/SKILL.md +12 -9
  396. package/pack/generated/devin/skills/rite-temper/reference/scope-modes.md +1 -1
  397. package/pack/generated/devin/skills/rite-temper/reference/significance.md +10 -3
  398. package/pack/generated/devin/skills/rite-temper/reference/strategy-template.md +13 -2
  399. package/pack/generated/devin/skills/rite-vet/SKILL.md +4 -3
  400. package/pack/generated/devin/skills/rite-vet/reference/artifacts.md +1 -1
  401. package/pack/generated/omp/agents/devrites-spec-reviewer.md +2 -2
  402. package/pack/generated/omp/commands/rite-define.md +1 -1
  403. package/pack/generated/omp/commands/rite-handoff.md +1 -1
  404. package/pack/generated/omp/commands/rite-plan.md +1 -1
  405. package/pack/generated/omp/commands/rite-pressure-test.md +1 -1
  406. package/pack/generated/omp/commands/rite-quick.md +1 -1
  407. package/pack/generated/omp/commands/rite-status.md +1 -1
  408. package/pack/generated/omp/commands/rite-temper.md +1 -1
  409. package/pack/generated/omp/commands/rite-zoom-out.md +1 -1
  410. package/pack/generated/omp/skills/devrites-browser-proof/SKILL.md +2 -2
  411. package/pack/generated/omp/skills/devrites-frontend-craft/reference/design-references.md +18 -18
  412. package/pack/generated/omp/skills/devrites-frontend-craft/reference/quality-standards.md +6 -4
  413. package/pack/generated/omp/skills/devrites-interview/SKILL.md +4 -4
  414. package/pack/generated/omp/skills/devrites-lib/reference/parallel-dispatch.md +1 -1
  415. package/pack/generated/omp/skills/devrites-lib/reference/standards/afk-hitl.md +9 -1
  416. package/pack/generated/omp/skills/devrites-lib/reference/standards/agents.md +5 -2
  417. package/pack/generated/omp/skills/devrites-lib/reference/standards/gates.md +6 -5
  418. package/pack/generated/omp/skills/devrites-lib/reference/standards/spec-grammar.md +1 -1
  419. package/pack/generated/omp/skills/devrites-lib/reference/standards/verification-methods.md +3 -3
  420. package/pack/generated/omp/skills/devrites-lib/reference/standards/windows.md +3 -3
  421. package/pack/generated/omp/skills/devrites-lib/reference/standards/workflow-artifacts.md +2 -2
  422. package/pack/generated/omp/skills/devrites-lib/reference/visual-playbooks/outline-template.md +1 -1
  423. package/pack/generated/omp/skills/devrites-lib/reference/workspace-artifact-schema.md +4 -4
  424. package/pack/generated/omp/skills/devrites-ux-shape/SKILL.md +11 -12
  425. package/pack/generated/omp/skills/devrites-ux-shape/reference/visual-direction-probe.md +3 -1
  426. package/pack/generated/omp/skills/overhaul/SKILL.md +1 -1
  427. package/pack/generated/omp/skills/overhaul/references/orchestration.md +5 -3
  428. package/pack/generated/omp/skills/overhaul/references/records.md +39 -0
  429. package/pack/generated/omp/skills/overhaul/references/scoring.md +14 -4
  430. package/pack/generated/omp/skills/rite/reference/menu.md +1 -1
  431. package/pack/generated/omp/skills/rite-adopt/SKILL.md +8 -2
  432. package/pack/generated/omp/skills/rite-autocomplete/SKILL.md +11 -6
  433. package/pack/generated/omp/skills/rite-autocomplete/reference/decision-policy.md +3 -2
  434. package/pack/generated/omp/skills/rite-autocomplete/reference/loop.md +1 -1
  435. package/pack/generated/omp/skills/rite-autocomplete/reference/stop-conditions.md +6 -3
  436. package/pack/generated/omp/skills/rite-build/SKILL.md +2 -1
  437. package/pack/generated/omp/skills/rite-build/reference/afk-discipline.md +1 -1
  438. package/pack/generated/omp/skills/rite-build/reference/checkpoint-protocol.md +1 -1
  439. package/pack/generated/omp/skills/rite-build/reference/checkpoint.md +5 -0
  440. package/pack/generated/omp/skills/rite-build/reference/parallel-batch.md +2 -2
  441. package/pack/generated/omp/skills/rite-build/reference/wright-dispatch.md +6 -3
  442. package/pack/generated/omp/skills/rite-clarify/SKILL.md +5 -4
  443. package/pack/generated/omp/skills/rite-clarify/reference/anti-patterns.md +2 -2
  444. package/pack/generated/omp/skills/rite-clarify/reference/decision-coverage.md +3 -2
  445. package/pack/generated/omp/skills/rite-converge/SKILL.md +2 -0
  446. package/pack/generated/omp/skills/rite-customize/SKILL.md +6 -4
  447. package/pack/generated/omp/skills/rite-define/SKILL.md +2 -0
  448. package/pack/generated/omp/skills/rite-define/reference/gates.md +8 -7
  449. package/pack/generated/omp/skills/rite-explain/SKILL.md +1 -1
  450. package/pack/generated/omp/skills/rite-explain/reference/intake.md +1 -1
  451. package/pack/generated/omp/skills/rite-fast/SKILL.md +1 -1
  452. package/pack/generated/omp/skills/rite-plan/SKILL.md +4 -3
  453. package/pack/generated/omp/skills/rite-plan/reference/replan-and-repair.md +4 -4
  454. package/pack/generated/omp/skills/rite-polish/reference/anti-ai-slop.md +1 -1
  455. package/pack/generated/omp/skills/rite-prove/reference/browser-proof.md +5 -5
  456. package/pack/generated/omp/skills/rite-resolve/SKILL.md +3 -3
  457. package/pack/generated/omp/skills/rite-seal/SKILL.md +4 -3
  458. package/pack/generated/omp/skills/rite-seal/reference/phase-contract.md +3 -2
  459. package/pack/generated/omp/skills/rite-seal/reference/seal-template.md +2 -0
  460. package/pack/generated/omp/skills/rite-ship/SKILL.md +4 -2
  461. package/pack/generated/omp/skills/rite-ship/reference/git-ship.md +8 -2
  462. package/pack/generated/omp/skills/rite-ship/reference/ship-template.md +2 -1
  463. package/pack/generated/omp/skills/rite-spec/SKILL.md +2 -2
  464. package/pack/generated/omp/skills/rite-spec/reference/spec-checklists.md +1 -1
  465. package/pack/generated/omp/skills/rite-spec/reference/spec-template.md +7 -3
  466. package/pack/generated/omp/skills/rite-spec/reference/state-workspace.md +2 -0
  467. package/pack/generated/omp/skills/rite-temper/SKILL.md +12 -9
  468. package/pack/generated/omp/skills/rite-temper/reference/scope-modes.md +1 -1
  469. package/pack/generated/omp/skills/rite-temper/reference/significance.md +10 -3
  470. package/pack/generated/omp/skills/rite-temper/reference/strategy-template.md +13 -2
  471. package/pack/generated/omp/skills/rite-vet/SKILL.md +4 -3
  472. package/pack/generated/omp/skills/rite-vet/reference/artifacts.md +1 -1
  473. package/pack/generated/pi/AGENTS.md +1 -1
  474. package/pack/generated/pi/agents/devrites-spec-reviewer.md +2 -2
  475. package/pack/generated/pi/prompts/rite-define.md +1 -1
  476. package/pack/generated/pi/prompts/rite-handoff.md +1 -1
  477. package/pack/generated/pi/prompts/rite-plan.md +1 -1
  478. package/pack/generated/pi/prompts/rite-pressure-test.md +1 -1
  479. package/pack/generated/pi/prompts/rite-quick.md +1 -1
  480. package/pack/generated/pi/prompts/rite-status.md +1 -1
  481. package/pack/generated/pi/prompts/rite-temper.md +1 -1
  482. package/pack/generated/pi/prompts/rite-zoom-out.md +1 -1
  483. package/pack/generated/pi/skills/devrites-browser-proof/SKILL.md +2 -2
  484. package/pack/generated/pi/skills/devrites-frontend-craft/reference/design-references.md +18 -18
  485. package/pack/generated/pi/skills/devrites-frontend-craft/reference/quality-standards.md +6 -4
  486. package/pack/generated/pi/skills/devrites-interview/SKILL.md +4 -4
  487. package/pack/generated/pi/skills/devrites-lib/reference/parallel-dispatch.md +1 -1
  488. package/pack/generated/pi/skills/devrites-lib/reference/standards/afk-hitl.md +9 -1
  489. package/pack/generated/pi/skills/devrites-lib/reference/standards/agents.md +5 -2
  490. package/pack/generated/pi/skills/devrites-lib/reference/standards/gates.md +6 -5
  491. package/pack/generated/pi/skills/devrites-lib/reference/standards/spec-grammar.md +1 -1
  492. package/pack/generated/pi/skills/devrites-lib/reference/standards/verification-methods.md +3 -3
  493. package/pack/generated/pi/skills/devrites-lib/reference/standards/windows.md +3 -3
  494. package/pack/generated/pi/skills/devrites-lib/reference/standards/workflow-artifacts.md +2 -2
  495. package/pack/generated/pi/skills/devrites-lib/reference/visual-playbooks/outline-template.md +1 -1
  496. package/pack/generated/pi/skills/devrites-lib/reference/workspace-artifact-schema.md +4 -4
  497. package/pack/generated/pi/skills/devrites-ux-shape/SKILL.md +11 -12
  498. package/pack/generated/pi/skills/devrites-ux-shape/reference/visual-direction-probe.md +3 -1
  499. package/pack/generated/pi/skills/overhaul/SKILL.md +1 -1
  500. package/pack/generated/pi/skills/overhaul/references/orchestration.md +5 -3
  501. package/pack/generated/pi/skills/overhaul/references/records.md +39 -0
  502. package/pack/generated/pi/skills/overhaul/references/scoring.md +14 -4
  503. package/pack/generated/pi/skills/rite/reference/menu.md +1 -1
  504. package/pack/generated/pi/skills/rite-adopt/SKILL.md +8 -2
  505. package/pack/generated/pi/skills/rite-autocomplete/SKILL.md +11 -6
  506. package/pack/generated/pi/skills/rite-autocomplete/reference/decision-policy.md +3 -2
  507. package/pack/generated/pi/skills/rite-autocomplete/reference/loop.md +1 -1
  508. package/pack/generated/pi/skills/rite-autocomplete/reference/stop-conditions.md +6 -3
  509. package/pack/generated/pi/skills/rite-build/SKILL.md +2 -1
  510. package/pack/generated/pi/skills/rite-build/reference/afk-discipline.md +1 -1
  511. package/pack/generated/pi/skills/rite-build/reference/checkpoint-protocol.md +1 -1
  512. package/pack/generated/pi/skills/rite-build/reference/checkpoint.md +5 -0
  513. package/pack/generated/pi/skills/rite-build/reference/parallel-batch.md +2 -2
  514. package/pack/generated/pi/skills/rite-build/reference/wright-dispatch.md +6 -3
  515. package/pack/generated/pi/skills/rite-clarify/SKILL.md +5 -4
  516. package/pack/generated/pi/skills/rite-clarify/reference/anti-patterns.md +2 -2
  517. package/pack/generated/pi/skills/rite-clarify/reference/decision-coverage.md +3 -2
  518. package/pack/generated/pi/skills/rite-converge/SKILL.md +2 -0
  519. package/pack/generated/pi/skills/rite-customize/SKILL.md +6 -4
  520. package/pack/generated/pi/skills/rite-define/SKILL.md +2 -0
  521. package/pack/generated/pi/skills/rite-define/reference/gates.md +8 -7
  522. package/pack/generated/pi/skills/rite-explain/SKILL.md +1 -1
  523. package/pack/generated/pi/skills/rite-explain/reference/intake.md +1 -1
  524. package/pack/generated/pi/skills/rite-fast/SKILL.md +1 -1
  525. package/pack/generated/pi/skills/rite-plan/SKILL.md +4 -3
  526. package/pack/generated/pi/skills/rite-plan/reference/replan-and-repair.md +4 -4
  527. package/pack/generated/pi/skills/rite-polish/reference/anti-ai-slop.md +1 -1
  528. package/pack/generated/pi/skills/rite-prove/reference/browser-proof.md +5 -5
  529. package/pack/generated/pi/skills/rite-resolve/SKILL.md +3 -3
  530. package/pack/generated/pi/skills/rite-seal/SKILL.md +4 -3
  531. package/pack/generated/pi/skills/rite-seal/reference/phase-contract.md +3 -2
  532. package/pack/generated/pi/skills/rite-seal/reference/seal-template.md +2 -0
  533. package/pack/generated/pi/skills/rite-ship/SKILL.md +4 -2
  534. package/pack/generated/pi/skills/rite-ship/reference/git-ship.md +8 -2
  535. package/pack/generated/pi/skills/rite-ship/reference/ship-template.md +2 -1
  536. package/pack/generated/pi/skills/rite-spec/SKILL.md +2 -2
  537. package/pack/generated/pi/skills/rite-spec/reference/spec-checklists.md +1 -1
  538. package/pack/generated/pi/skills/rite-spec/reference/spec-template.md +7 -3
  539. package/pack/generated/pi/skills/rite-spec/reference/state-workspace.md +2 -0
  540. package/pack/generated/pi/skills/rite-temper/SKILL.md +12 -9
  541. package/pack/generated/pi/skills/rite-temper/reference/scope-modes.md +1 -1
  542. package/pack/generated/pi/skills/rite-temper/reference/significance.md +10 -3
  543. package/pack/generated/pi/skills/rite-temper/reference/strategy-template.md +13 -2
  544. package/pack/generated/pi/skills/rite-vet/SKILL.md +4 -3
  545. package/pack/generated/pi/skills/rite-vet/reference/artifacts.md +1 -1
  546. package/package.json +5 -4
  547. package/scripts/build-host-artifacts.sh +15 -0
  548. package/scripts/codex-generate.sh +1 -1
  549. package/scripts/devin-generate.sh +1 -1
  550. package/scripts/expand-includes.py +46 -0
  551. package/scripts/install-lib.sh +42 -7
  552. package/scripts/omp-generate.sh +11 -0
  553. package/scripts/pi-generate.sh +12 -1
  554. package/update.sh +1 -2
  555. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/README.md +0 -7
  556. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/architecture.md +0 -3
  557. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/assumptions.md +0 -3
  558. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/brief.md +0 -3
  559. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/decision-coverage.md +0 -3
  560. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/decisions.md +0 -4
  561. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/eng-review.md +0 -3
  562. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/gates.md +0 -4
  563. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/plan.md +0 -5
  564. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/questions.md +0 -3
  565. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/spec.md +0 -6
  566. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/state.md +0 -8
  567. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/tasks.md +0 -1
  568. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/test-plan.md +0 -3
  569. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/traceability.md +0 -3
  570. package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/README.md +0 -7
  571. package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/assumptions.md +0 -3
  572. package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/brief.md +0 -3
  573. package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/decisions.md +0 -3
  574. package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/questions.md +0 -3
  575. package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/spec.md +0 -6
  576. package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/state.md +0 -3
  577. package/engine/testdata/golden/TestParityCloseOut/active_pointed_elsewhere.golden +0 -2
  578. package/engine/testdata/golden/TestParityCloseOut/active_slug_cleared.golden +0 -2
  579. package/engine/testdata/golden/TestParityCloseOut/archive_clobber.golden +0 -1
  580. package/engine/testdata/golden/TestParityCloseOut/missing_slug.golden +0 -1
  581. package/engine/testdata/golden/TestParityCloseOut/missing_workspace.golden +0 -1
  582. package/engine/testdata/golden/TestParityLearnings/add.golden +0 -2
  583. package/engine/testdata/golden/TestParityLearnings/list.golden +0 -5
  584. package/engine/testdata/golden/TestParityLearnings/mine-no-archive.golden +0 -2
  585. package/engine/testdata/golden/TestParityLearnings/mine.golden +0 -5
  586. package/engine/testdata/golden/TestParityLearnings/nudge.golden +0 -2
  587. package/engine/testdata/golden/TestParityLearnings/unknown.golden +0 -1
  588. package/engine/testdata/golden/TestParityPackageExistence/ghost-workspace.golden +0 -1
  589. package/engine/testdata/golden/TestParityPackageExistence/no-manifest.golden +0 -1
  590. package/engine/testdata/golden/TestParityPackageExistence/not-git.golden +0 -1
  591. package/engine/testdata/golden/TestParityPackageExistence/undeclared.golden +0 -1
  592. package/engine/testdata/golden/TestParityPreamble/afk/a.golden +0 -14
  593. package/engine/testdata/golden/TestParityPreamble/hitl/active.golden +0 -18
  594. package/engine/testdata/golden/TestParityPreamble/hitl/bare.golden +0 -10
  595. package/engine/testdata/golden/TestParityPreamble/hitl/full.golden +0 -18
  596. package/engine/testdata/golden/TestParityPreamble/hitl/ghost.golden +0 -2
  597. package/engine/testdata/golden/TestParityPreamble/hitl/nonl.golden +0 -11
  598. package/engine/testdata/golden/TestParityPreamble/hitl/openend.golden +0 -13
  599. package/engine/testdata/golden/TestParityProgress/arg=allbuilt.golden +0 -4
  600. package/engine/testdata/golden/TestParityProgress/arg=done.golden +0 -3
  601. package/engine/testdata/golden/TestParityProgress/arg=ghost.golden +0 -1
  602. package/engine/testdata/golden/TestParityProgress/arg=mid.golden +0 -4
  603. package/engine/testdata/golden/TestParityProgress/arg=nophase.golden +0 -3
  604. package/engine/testdata/golden/TestParityProgress/arg=noslice.golden +0 -3
  605. package/engine/testdata/golden/TestParityProgress/arg=plan.golden +0 -4
  606. package/engine/testdata/golden/TestParityProgress/arg=seal.golden +0 -3
  607. package/engine/testdata/golden/TestParityResolve/answer/questions.md.golden +0 -9
  608. package/engine/testdata/golden/TestParityResolve/answer/state.md.golden +0 -7
  609. package/engine/testdata/golden/TestParityResolve/answer.golden +0 -4
  610. package/engine/testdata/golden/TestParityResolve/batch/questions.md.golden +0 -9
  611. package/engine/testdata/golden/TestParityResolve/batch-unterminated/questions.md.golden +0 -9
  612. package/engine/testdata/golden/TestParityResolve/batch-unterminated.golden +0 -2
  613. package/engine/testdata/golden/TestParityResolve/batch.golden +0 -3
  614. package/engine/testdata/golden/TestParityResolve/drop/questions.md.golden +0 -9
  615. package/engine/testdata/golden/TestParityResolve/drop/state.md.golden +0 -9
  616. package/engine/testdata/golden/TestParityResolve/drop.golden +0 -4
  617. package/engine/testdata/golden/TestParityResolve/missing-answer.golden +0 -1
  618. package/engine/testdata/golden/TestParityResolve/no-workspace.golden +0 -1
  619. package/engine/testdata/golden/TestParityResolve/not-found.golden +0 -1
  620. package/engine/testdata/golden/TestParityResolve/not-open.golden +0 -1
  621. package/engine/testdata/golden/TestStatusLiveWorkspace.golden +0 -10
  622. package/engine/testdata/golden/status/auth-tokens.txt +0 -9
  623. package/engine/testdata/golden/status/search-ranking.txt +0 -9
  624. package/engine/testdata/visual/open-visual-smoke.html +0 -675
  625. package/engine/testdata/visual/open-visual-smoke.outline.md +0 -54
  626. package/engine/tests/adr_0003_gate_exit_code_test.go +0 -32
  627. package/engine/tests/adr_0004_required_by_phase_test.go +0 -54
  628. package/engine/tests/adr_0011_define_plan_test.go +0 -32
  629. package/engine/tests/adr_0027_readiness_binding_test.go +0 -107
  630. package/engine/tests/cli_test.go +0 -126
  631. package/engine/tests/gate_test.go +0 -669
  632. package/engine/tests/meta_test.go +0 -37
  633. package/engine/tests/parity_closeout_test.go +0 -105
  634. package/engine/tests/parity_githelpers_test.go +0 -41
  635. package/engine/tests/parity_resolve_test.go +0 -136
  636. package/engine/tests/parity_test.go +0 -86
  637. package/engine/tests/workspace_observation_migration_test.go +0 -1069
@@ -293,7 +293,7 @@ func manifestFallback(root string, slot string) (detectedCommand, bool) {
293
293
  }
294
294
  cmd, ok := m.cmds[slot]
295
295
  if !ok {
296
- return detectedCommand{}, false
296
+ continue
297
297
  }
298
298
  out := detectedCommand{Name: slot, Command: cmd, Source: m.source}
299
299
  binary := strings.Fields(cmd)[0]
@@ -68,6 +68,19 @@ func TestDetectManifestFallbackAndJSON(t *testing.T) {
68
68
  }
69
69
  }
70
70
 
71
+ func TestDetectManifestFallbackSkipsManifestWithoutSlot(t *testing.T) {
72
+ root := t.TempDir()
73
+ writeFile(t, root+"/setup.py", "from setuptools import setup\nsetup()\n")
74
+ writeFile(t, root+"/build.gradle", "plugins { id 'java' }\n")
75
+ code, out := runDetect(t, root)
76
+ if code != 0 {
77
+ t.Fatalf("code=%d out=%s", code, out)
78
+ }
79
+ if !strings.Contains(out, "gradle build") || !strings.Contains(out, "build.gradle") {
80
+ t.Fatalf("build slot should resolve from build.gradle:\n%s", out)
81
+ }
82
+ }
83
+
71
84
  func TestDetectEmptyRepoReportsUnresolved(t *testing.T) {
72
85
  root := t.TempDir()
73
86
  code, out := runDetect(t, root)
@@ -153,25 +153,35 @@ func (o diffScopeOpts) changedPaths() ([]string, error) {
153
153
  }
154
154
 
155
155
  // gitStatusPaths lists every path the writer touched: tracked modifications,
156
- // staged entries, renames (new name), and untracked files.
156
+ // staged entries, renames (new name, then the old name it replaced), and
157
+ // untracked files.
157
158
  func gitStatusPaths(dir string) ([]string, error) {
158
- out, err := runGitCommand(dir, nil, "status", "--porcelain", "-uall")
159
+ out, err := runGitCommand(dir, nil, "status", "--porcelain", "-uall", "-z")
159
160
  if err != nil {
160
161
  return nil, err
161
162
  }
162
163
  var paths []string
163
- for _, line := range splitLinesNoTrailing(out) {
164
- if len(line) < 4 {
164
+ // -z emits one NUL-terminated record per entry with paths unquoted; a
165
+ // rename or copy carries a second record, new name first, then the old.
166
+ records := strings.Split(string(out), "\x00")
167
+ for i := 0; i < len(records); i++ {
168
+ record := records[i]
169
+ if len(record) < 4 {
165
170
  continue
166
171
  }
167
- path := strings.TrimSpace(line[3:])
168
- if idx := strings.LastIndex(path, " -> "); idx >= 0 {
169
- path = path[idx+4:]
172
+ path := record[3:]
173
+ if path == "" {
174
+ continue
170
175
  }
171
- path = strings.Trim(path, `"`)
172
- if path != "" {
176
+ if strings.ContainsAny(record[:2], "RC") {
173
177
  paths = append(paths, path)
178
+ if i+1 < len(records) && records[i+1] != "" {
179
+ i++
180
+ paths = append(paths, records[i])
181
+ }
182
+ continue
174
183
  }
184
+ paths = append(paths, path)
175
185
  }
176
186
  return paths, nil
177
187
  }
@@ -262,13 +262,20 @@ func dispatchLocked(root, slug, sub, phase, wave, role, handle, reason string, r
262
262
 
263
263
  case "status":
264
264
  if wave == "" {
265
+ var incomplete []string
265
266
  for _, name := range sortedKeys(f.Waves) {
266
267
  w := f.Waves[name]
267
268
  fmt.Fprintf(stdout, "dispatch: wave %s state=%s roles=%d\n", name, w.State, len(w.Roles))
269
+ if w.State != "complete" {
270
+ incomplete = append(incomplete, name)
271
+ }
268
272
  }
269
273
  if len(f.Waves) == 0 {
270
274
  fmt.Fprintln(stdout, "dispatch: no waves")
271
275
  }
276
+ if len(incomplete) > 0 {
277
+ return fail("waves not complete: %s", strings.Join(incomplete, ","))
278
+ }
272
279
  return 0
273
280
  }
274
281
  w := getWave()
@@ -59,7 +59,6 @@ const (
59
59
  dupMaxPostings = 32
60
60
  dupMaxPairAnchors = 8192
61
61
  dupMergeGap = 4
62
- dupMaxClusters = 200
63
62
  dupHashLength = 12
64
63
  dupCrossDirMaxHops = 8
65
64
  dupCrossDirBoost = 0.15
@@ -440,6 +439,18 @@ func dupDistanceBoost(pathA, pathB string, seg dupSegment) float64 {
440
439
  // dupClusterUnits merges per-file ranges into units, unions segments into
441
440
  // transitive clusters, and orders clusters by their strongest boosted pair.
442
441
  func dupClusterUnits(segs []dupSegment, files []dupFile) ([]dupCluster, []dupUnit, []dupEdge) {
442
+ return dupClusterUnitsCounted(segs, files, nil)
443
+ }
444
+
445
+ // dupClusterUnitsCounted is dupClusterUnits with an optional counter of the
446
+ // units and edges it examines, so tests can assert on work done instead of
447
+ // wall time.
448
+ func dupClusterUnitsCounted(segs []dupSegment, files []dupFile, steps *int) ([]dupCluster, []dupUnit, []dupEdge) {
449
+ step := func() {
450
+ if steps != nil {
451
+ *steps++
452
+ }
453
+ }
443
454
  // Merge each file's overlapping/adjacent ranges into units.
444
455
  perFile := map[int][]dupUnit{}
445
456
  for _, s := range segs {
@@ -465,15 +476,22 @@ func dupClusterUnits(segs []dupSegment, files []dupFile) ([]dupCluster, []dupUni
465
476
  }
466
477
  }
467
478
  // Locate the unit covering a segment endpoint: same file, maximal overlap.
479
+ // Each file's units are already sorted by start, so only the units that
480
+ // reach the range are visited.
481
+ byFile := map[int][]int{}
482
+ for i, u := range units {
483
+ byFile[u.file] = append(byFile[u.file], i)
484
+ }
468
485
  unitIndex := func(file, start, end int) int {
486
+ idxs := byFile[file]
469
487
  best, bestOverlap := -1, 0
470
- for i, u := range units {
471
- if u.file != file {
472
- continue
473
- }
488
+ k := sort.Search(len(idxs), func(k int) bool { return units[idxs[k]].end >= start })
489
+ for ; k < len(idxs) && units[idxs[k]].start <= end; k++ {
490
+ step()
491
+ u := units[idxs[k]]
474
492
  ov := min(u.end, end) - max(u.start, start) + 1
475
493
  if ov > bestOverlap {
476
- best, bestOverlap = i, ov
494
+ best, bestOverlap = idxs[k], ov
477
495
  }
478
496
  }
479
497
  return best
@@ -503,19 +521,25 @@ func dupClusterUnits(segs []dupSegment, files []dupFile) ([]dupCluster, []dupUni
503
521
  }
504
522
  }
505
523
  groups := map[int][]int{}
524
+ seen := map[int]bool{}
525
+ best := map[int]float64{}
506
526
  for _, e := range edges {
527
+ step()
507
528
  r := find(e.a)
508
- if !containsInt(groups[r], e.a) {
509
- groups[r] = append(groups[r], e.a)
529
+ for _, u := range [2]int{e.a, e.b} {
530
+ if !seen[u] {
531
+ seen[u] = true
532
+ groups[r] = append(groups[r], u)
533
+ }
510
534
  }
511
- if !containsInt(groups[r], e.b) {
512
- groups[r] = append(groups[r], e.b)
535
+ if e.boosted > best[r] {
536
+ best[r] = e.boosted
513
537
  }
514
538
  }
515
539
  var clusters []dupCluster
516
- for _, members := range groups {
540
+ for r, members := range groups {
517
541
  sort.Ints(members)
518
- c := dupCluster{units: members}
542
+ c := dupCluster{units: members, score: best[r]}
519
543
  var sb strings.Builder
520
544
  var hashes []string
521
545
  for _, u := range members {
@@ -530,19 +554,6 @@ func dupClusterUnits(segs []dupSegment, files []dupFile) ([]dupCluster, []dupUni
530
554
  c.hash = hex.EncodeToString(sum[:])[:dupHashLength]
531
555
  clusters = append(clusters, c)
532
556
  }
533
- for i := range clusters {
534
- best := 0.0
535
- member := map[int]bool{}
536
- for _, u := range clusters[i].units {
537
- member[u] = true
538
- }
539
- for _, e := range edges {
540
- if member[e.a] && member[e.b] && e.boosted > best {
541
- best = e.boosted
542
- }
543
- }
544
- clusters[i].score = best
545
- }
546
557
  sort.Slice(clusters, func(i, j int) bool {
547
558
  if clusters[i].score != clusters[j].score {
548
559
  return clusters[i].score > clusters[j].score
@@ -583,15 +594,6 @@ func dupHeaderLine(tokens string) bool {
583
594
  return false
584
595
  }
585
596
 
586
- func containsInt(xs []int, v int) bool {
587
- for _, x := range xs {
588
- if x == v {
589
- return true
590
- }
591
- }
592
- return false
593
- }
594
-
595
597
  // dupChangedRanges parses `git diff -U0` output into per-file added-line
596
598
  // ranges on the new side. Deleted files contribute nothing.
597
599
  func dupChangedRanges(diff string) map[string][][2]int {
@@ -961,11 +963,12 @@ func RunCheckDup(root string, args []string, stdout, stderr io.Writer) int {
961
963
  fmt.Fprintln(stdout, "dup: ok (no similar regions)")
962
964
  }
963
965
  if opts.slug != "" {
964
- recordMetric(root, opts.slug, opts.phase, "dup", "", int64(len(clusters)))
966
+ recordMetric(root, opts.slug, opts.phase, "dup", "", 0)
965
967
  }
966
968
  return 0
967
969
  }
968
- fmt.Fprintf(stdout, "dup: %d cluster(s)", len(shown))
970
+ shownCount := len(shown)
971
+ fmt.Fprintf(stdout, "dup: %d cluster(s)", shownCount)
969
972
  if ignoredCount > 0 {
970
973
  fmt.Fprintf(stdout, " (%d ignored)", ignoredCount)
971
974
  }
@@ -1000,7 +1003,7 @@ func RunCheckDup(root string, args []string, stdout, stderr io.Writer) int {
1000
1003
  return 2
1001
1004
  }
1002
1005
  if opts.slug != "" {
1003
- recordMetric(root, opts.slug, opts.phase, "dup", "", int64(len(shown)))
1006
+ recordMetric(root, opts.slug, opts.phase, "dup", "", int64(shownCount))
1004
1007
  }
1005
1008
  return 0
1006
1009
  }
@@ -2,9 +2,11 @@ package lib
2
2
 
3
3
  import (
4
4
  "bytes"
5
+ "encoding/json"
5
6
  "os"
6
7
  "os/exec"
7
8
  "path/filepath"
9
+ "regexp"
8
10
  "strings"
9
11
  "testing"
10
12
 
@@ -486,3 +488,179 @@ func TestDupStagedQuotedPathAndDiffConfig(t *testing.T) {
486
488
  t.Fatalf("code=%d out=%s err=%s", code, &out, &errOut)
487
489
  }
488
490
  }
491
+
492
+ const dupBlockC = `func loadSettings(path string) (map[string]string, error) {
493
+ data, err := os.ReadFile(path)
494
+ if err != nil {
495
+ return nil, fmt.Errorf("read: %w", err)
496
+ }
497
+ out := map[string]string{}
498
+ for _, line := range strings.Split(string(data), "\n") {
499
+ k, v, ok := strings.Cut(line, "=")
500
+ if !ok {
501
+ continue
502
+ }
503
+ out[strings.TrimSpace(k)] = strings.TrimSpace(v)
504
+ }
505
+ return out, nil
506
+ }
507
+ `
508
+
509
+ const dupBlockD = `func readProfile(file string) (map[string]string, error) {
510
+ raw, err := os.ReadFile(file)
511
+ if err != nil {
512
+ return nil, fmt.Errorf("open: %w", err)
513
+ }
514
+ res := map[string]string{}
515
+ for _, row := range strings.Split(string(raw), "\n") {
516
+ name, val, found := strings.Cut(row, "=")
517
+ if !found {
518
+ continue
519
+ }
520
+ res[strings.TrimSpace(name)] = strings.TrimSpace(val)
521
+ }
522
+ return res, nil
523
+ }
524
+ `
525
+
526
+ func dupMetricBytes(t *testing.T, root, slug string) []int64 {
527
+ t.Helper()
528
+ data, err := os.ReadFile(filepath.Join(root, "work", slug, MetricsFile))
529
+ if err != nil {
530
+ t.Fatal(err)
531
+ }
532
+ var got []int64
533
+ for _, line := range strings.Split(strings.TrimSpace(string(data)), "\n") {
534
+ var ev metricEvent
535
+ if err := json.Unmarshal([]byte(line), &ev); err != nil {
536
+ t.Fatal(err)
537
+ }
538
+ got = append(got, ev.Bytes)
539
+ }
540
+ return got
541
+ }
542
+
543
+ func TestRunCheckDupMetricIgnoresLimitAndCountsOnlyShown(t *testing.T) {
544
+ project := t.TempDir()
545
+ initDupRepo(t, project)
546
+ testutil.WriteFile(t, filepath.Join(project, "a/one.go"), "package a\n\n"+dupBlockA)
547
+ testutil.WriteFile(t, filepath.Join(project, "c/three.go"), "package c\n\n"+dupBlockC)
548
+ testutil.WriteFile(t, filepath.Join(project, "b/two.go"), "package b\n\n"+dupBlockB)
549
+ testutil.WriteFile(t, filepath.Join(project, "d/four.go"), "package d\n\n"+dupBlockD)
550
+ runDupGit(t, project, "add", ".")
551
+ runDupGit(t, project, "commit", "-qm", "base")
552
+ root := filepath.Join(project, ".devrites")
553
+ if err := os.MkdirAll(filepath.Join(root, "work", "demo"), 0o755); err != nil {
554
+ t.Fatal(err)
555
+ }
556
+
557
+ var hashes []string
558
+ for _, limit := range []string{"1", "50"} {
559
+ var stdout, stderr bytes.Buffer
560
+ args := []string{"demo", "--all", "--min-lines", "5", "--limit", limit}
561
+ if code := RunCheckDup(root, args, &stdout, &stderr); code != 0 {
562
+ t.Fatalf("limit=%s code=%d stderr=%s", limit, code, stderr.String())
563
+ }
564
+ if limit == "50" {
565
+ for _, m := range regexp.MustCompile(`cluster \d+ \(([0-9a-f]+)\)`).FindAllStringSubmatch(stdout.String(), -1) {
566
+ hashes = append(hashes, m[1])
567
+ }
568
+ }
569
+ }
570
+ if len(hashes) != 2 {
571
+ t.Fatalf("want 2 clusters, got %v", hashes)
572
+ }
573
+ got := dupMetricBytes(t, root, "demo")
574
+ if len(got) != 2 || got[0] != got[1] {
575
+ t.Fatalf("metric depends on --limit: %v", got)
576
+ }
577
+
578
+ testutil.WriteFile(t, filepath.Join(root, "dup-ignore"), hashes[0]+"\n")
579
+ var stdout, stderr bytes.Buffer
580
+ if code := RunCheckDup(root, []string{"demo", "--all", "--min-lines", "5"}, &stdout, &stderr); code != 0 {
581
+ t.Fatalf("code=%d stderr=%s", code, stderr.String())
582
+ }
583
+ oneIgnored := dupMetricBytes(t, root, "demo")[2]
584
+
585
+ testutil.WriteFile(t, filepath.Join(root, "dup-ignore"), hashes[0]+"\n"+hashes[1]+"\n")
586
+ stdout.Reset()
587
+ if code := RunCheckDup(root, []string{"demo", "--all", "--min-lines", "5"}, &stdout, &stderr); code != 0 {
588
+ t.Fatalf("code=%d stderr=%s", code, stderr.String())
589
+ }
590
+ if allIgnored := dupMetricBytes(t, root, "demo")[3]; allIgnored > oneIgnored {
591
+ t.Fatalf("metric rose when findings were dismissed: %d -> %d", oneIgnored, allIgnored)
592
+ }
593
+ }
594
+
595
+ // dupGroupedInput builds nFiles files of 10 units each (lines 10u..10u+4)
596
+ // where every unit position is linked across consecutive files of each group.
597
+ func dupGroupedInput(nFiles, group int) ([]dupSegment, []dupFile) {
598
+ files := make([]dupFile, nFiles)
599
+ for i := range files {
600
+ files[i] = dupFile{path: "pkg" + string(rune('a'+i%26)) + "/f" + string(rune('a'+i/26%26)) + string(rune('a'+i/676)) + ".go", lines: make([]dupLine, 100)}
601
+ }
602
+ var segs []dupSegment
603
+ for base := 0; base+group <= nFiles; base += group {
604
+ for u := 0; u < 10; u++ {
605
+ for k := 0; k+1 < group; k++ {
606
+ segs = append(segs, dupSegment{
607
+ aFile: base + k, bFile: base + k + 1,
608
+ aStart: u * 10, aEnd: u*10 + 4, bStart: u * 10, bEnd: u*10 + 4,
609
+ boosted: float64((base+k+u)%7+1) / 10,
610
+ })
611
+ }
612
+ }
613
+ }
614
+ return segs, files
615
+ }
616
+
617
+ func TestDupClusterUnitsSmallCase(t *testing.T) {
618
+ files := []dupFile{
619
+ {path: "a/x.go", lines: make([]dupLine, 40)},
620
+ {path: "b/y.go", lines: make([]dupLine, 40)},
621
+ {path: "c/z.go", lines: make([]dupLine, 40)},
622
+ }
623
+ segs := []dupSegment{
624
+ {aFile: 0, bFile: 1, aStart: 0, aEnd: 4, bStart: 0, bEnd: 4, boosted: 0.5},
625
+ {aFile: 1, bFile: 2, aStart: 0, aEnd: 4, bStart: 0, bEnd: 4, boosted: 0.9},
626
+ {aFile: 0, bFile: 1, aStart: 20, aEnd: 24, bStart: 20, bEnd: 24, boosted: 0.3},
627
+ }
628
+ clusters, units, edges := dupClusterUnits(segs, files)
629
+ if len(units) != 5 || len(edges) != 3 || len(clusters) != 2 {
630
+ t.Fatalf("units=%d edges=%d clusters=%d", len(units), len(edges), len(clusters))
631
+ }
632
+ if len(clusters[0].units) != 3 || clusters[0].score != 0.9 {
633
+ t.Fatalf("first cluster = %d units score %v", len(clusters[0].units), clusters[0].score)
634
+ }
635
+ if len(clusters[1].units) != 2 || clusters[1].score != 0.3 {
636
+ t.Fatalf("second cluster = %d units score %v", len(clusters[1].units), clusters[1].score)
637
+ }
638
+ if clusters[0].hash == clusters[1].hash || len(clusters[0].hash) != dupHashLength {
639
+ t.Fatalf("hashes %q %q", clusters[0].hash, clusters[1].hash)
640
+ }
641
+ }
642
+
643
+ func TestDupClusterUnitsWorkIsLinear(t *testing.T) {
644
+ segs, files := dupGroupedInput(1600, 20)
645
+ var steps int
646
+ clusters, units, _ := dupClusterUnitsCounted(segs, files, &steps)
647
+ if len(clusters) != 80*10 {
648
+ t.Fatalf("clusters=%d, want 800", len(clusters))
649
+ }
650
+ for _, c := range clusters {
651
+ if len(c.units) != 20 {
652
+ t.Fatalf("cluster has %d units, want 20", len(c.units))
653
+ }
654
+ }
655
+ // Every unit sits alone in a 10-line slot, so each of the two endpoints of
656
+ // a segment overlaps exactly one unit, and the scoring pass visits each
657
+ // edge once. A path that scans all units or all edges per cluster does
658
+ // more work, and one that skips the counter does none.
659
+ groups, perGroup, unitsPerFile := 1600/20, 20, 10
660
+ if want := groups * unitsPerFile * (perGroup - 1); len(segs) != want {
661
+ t.Fatalf("fixture has %d segments, want %d", len(segs), want)
662
+ }
663
+ if want := 3 * len(segs); steps != want {
664
+ t.Fatalf("examined %d units/edges for %d segments and %d units, want exactly %d", steps, len(segs), len(units), want)
665
+ }
666
+ }
@@ -171,7 +171,7 @@ func walkSequenceChain(root, slug, workspace string) ([]string, [][]candidateRow
171
171
  chain := []string{}
172
172
  manifests := [][]candidateRow{}
173
173
  dir := workspace
174
- for len(chain) < maxSequenceChain {
174
+ for {
175
175
  raw, err := readBoundedRegularFile(filepath.Join(dir, "state.md"), maxCandidateManifestBytes)
176
176
  if err != nil {
177
177
  return nil, nil, fmt.Errorf("%s state.md: %w", filepath.Base(dir), err)
@@ -180,6 +180,9 @@ func walkSequenceChain(root, slug, workspace string) ([]string, [][]candidateRow
180
180
  if parent == "" {
181
181
  break
182
182
  }
183
+ if len(chain) == maxSequenceChain {
184
+ return nil, nil, fmt.Errorf("sequence chain longer than %d; pass an explicit ordered predecessor list", maxSequenceChain)
185
+ }
183
186
  if _, dup := visited[parent]; dup {
184
187
  return nil, nil, fmt.Errorf("sequence_parent cycle at %q", parent)
185
188
  }
@@ -2,6 +2,7 @@ package lib
2
2
 
3
3
  import (
4
4
  "bytes"
5
+ "fmt"
5
6
  "os"
6
7
  "path/filepath"
7
8
  "strings"
@@ -212,3 +213,39 @@ func TestMergeManifestRejectsParentMismatchAndMissingPredecessor(t *testing.T) {
212
213
  t.Fatalf("missing args: code=%d", code)
213
214
  }
214
215
  }
216
+
217
+ func TestSequenceChainBeyondBoundRefusesInsteadOfTruncating(t *testing.T) {
218
+ root := unionRoot(t)
219
+ n := maxSequenceChain + 6
220
+ for i := 0; i < n; i++ {
221
+ slug := fmt.Sprintf("feat-%03d", i)
222
+ unionWorkspace(t, root, slug, manifestBody(fmt.Sprintf("| present | `src/f%03d.ts` | SLICE-001 | Added. |", i)), false)
223
+ }
224
+ unionWorkspace(t, root, "release", manifestBody("| present | `src/r.ts` | SLICE-001 | Release. |"), false)
225
+ link := func(slug, parent string) {
226
+ statePath := filepath.Join(root, "work", slug, "state.md")
227
+ raw, _ := os.ReadFile(statePath)
228
+ raw = append(raw, []byte("| sequence_parent | "+parent+" |\n")...)
229
+ if err := os.WriteFile(statePath, raw, 0o644); err != nil {
230
+ t.Fatal(err)
231
+ }
232
+ }
233
+ for i := 1; i < n; i++ {
234
+ link(fmt.Sprintf("feat-%03d", i), fmt.Sprintf("feat-%03d", i-1))
235
+ }
236
+ link("release", fmt.Sprintf("feat-%03d", n-1))
237
+ statePath := filepath.Join(root, "work", "release", "state.md")
238
+ raw, _ := os.ReadFile(statePath)
239
+ raw = append(raw, []byte("| sequence_role | release |\n")...)
240
+ if err := os.WriteFile(statePath, raw, 0o644); err != nil {
241
+ t.Fatal(err)
242
+ }
243
+
244
+ var stdout, stderr bytes.Buffer
245
+ if code := RunMergeManifest(root, []string{"release"}, &stdout, &stderr); code == 0 {
246
+ t.Fatalf("merge over an over-long chain succeeded: stdout=%s", stdout.String())
247
+ }
248
+ if err := VerifyReleaseUnion(root, "release"); err == nil {
249
+ t.Fatal("release union passed over an over-long chain")
250
+ }
251
+ }
@@ -216,6 +216,10 @@ func metricsSummary(root string, args []string, stdout, stderr io.Writer) int {
216
216
  totalEvents++
217
217
  totalBytes += ev.Bytes
218
218
  }
219
+ if err := scanner.Err(); err != nil {
220
+ fmt.Fprintf(stderr, "metrics: ledger unreadable after %d events: %v\n", totalEvents, err)
221
+ return 2
222
+ }
219
223
  if len(phases) == 0 && bad == 0 {
220
224
  fmt.Fprintf(stdout, "metrics: no events recorded for %s\n", slug)
221
225
  return 0
@@ -0,0 +1,28 @@
1
+ package lib
2
+
3
+ import (
4
+ "bytes"
5
+ "os"
6
+ "path/filepath"
7
+ "strings"
8
+ "testing"
9
+ )
10
+
11
+ func TestMetricsSummaryFailsOnOversizedLine(t *testing.T) {
12
+ root, featureDir := newWorkspace(t, "build")
13
+ ev := func(e string) string {
14
+ return `{"ts":"2026-01-01T00:00:00Z","phase":"build","event":"` + e + `"}` + "\n"
15
+ }
16
+ ledger := ev("a") + strings.Repeat("x", 1<<20) + "\n" + ev("b") + ev("c")
17
+ if err := os.WriteFile(filepath.Join(featureDir, MetricsFile), []byte(ledger), 0o600); err != nil {
18
+ t.Fatal(err)
19
+ }
20
+ stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{}
21
+ code := RunMetrics(root, []string{"summary", "feat"}, stdout, stderr)
22
+ if code == 0 {
23
+ t.Fatalf("truncated ledger reported as complete:\n%s", stdout)
24
+ }
25
+ if !strings.Contains(stderr.String(), "ledger unreadable after 1 events") {
26
+ t.Fatalf("stderr missing truncation report: %q", stderr)
27
+ }
28
+ }
@@ -152,3 +152,62 @@ func TestMigrateCurrentAndNewerSchemaAreRefused(t *testing.T) {
152
152
  t.Fatalf("newer schema: code=%d stderr=%q", code, stderr.String())
153
153
  }
154
154
  }
155
+
156
+ // End to end through the `migrate` command path. migrate.go
157
+ // calls state.ConvertCursorToTable (apply, unconditional) and state.CursorForm
158
+ // (plan), so a fenced example in state.md — the phase-authority ledger — must
159
+ // survive a migration byte for byte, and a migration plan must not advertise a
160
+ // conversion that only fenced content would have performed.
161
+
162
+ func seedLedger(t *testing.T, body string) (root, workspace string) {
163
+ t.Helper()
164
+ root = t.TempDir()
165
+ workspace = filepath.Join(root, "work", "feature")
166
+ if err := os.MkdirAll(workspace, 0o755); err != nil {
167
+ t.Fatal(err)
168
+ }
169
+ if err := os.WriteFile(filepath.Join(workspace, "state.md"), []byte(body), 0o644); err != nil {
170
+ t.Fatal(err)
171
+ }
172
+ return root, workspace
173
+ }
174
+
175
+ const ledgerBody = "# State\n\n## Recovery note\n\nThe pre-v5 ledger looked like this:\n\n~~~md\n- Phase: build\n- Status: running\n~~~\n\n## Cursor\n\n- Phase: build\n- Status: running\n"
176
+
177
+ func TestMigrateLeavesFencedCursorExamplesByteIdentical(t *testing.T) {
178
+ root, workspace := seedLedger(t, ledgerBody)
179
+ var stdout, stderr strings.Builder
180
+ if code := Migrate(root, []string{"feature"}, &stdout, &stderr); code != 0 {
181
+ t.Fatalf("code=%d stdout=%q stderr=%q", code, stdout.String(), stderr.String())
182
+ }
183
+ body, err := os.ReadFile(filepath.Join(workspace, "state.md"))
184
+ if err != nil {
185
+ t.Fatal(err)
186
+ }
187
+ text := string(body)
188
+ if got := strings.Count(text, "- Phase: build"); got != 1 {
189
+ t.Fatalf("fenced example rewritten: %d surviving \"- Phase: build\" lines in\n%s", got, text)
190
+ }
191
+ if !strings.Contains(text, "| phase | build |") {
192
+ t.Fatalf("live cursor bullet not converted to the canonical table row:\n%s", text)
193
+ }
194
+ fenced := "~~~md\n- Phase: build\n- Status: running\n~~~"
195
+ if !strings.Contains(text, fenced) {
196
+ t.Fatalf("fenced block not preserved byte for byte:\n%s", text)
197
+ }
198
+ }
199
+
200
+ func TestMigratePlanDoesNotOfferConversionForFencedCursorsOnly(t *testing.T) {
201
+ body := "# State\n\n## Recovery note\n\n~~~md\n- Phase: build\n- Status: running\n~~~\n"
202
+ root, _ := seedLedger(t, body)
203
+ var stdout, stderr strings.Builder
204
+ if code := Migrate(root, []string{"feature", "--dry-run", "--answer", "phase=build"}, &stdout, &stderr); code != 0 {
205
+ t.Fatalf("code=%d stdout=%q stderr=%q", code, stdout.String(), stderr.String())
206
+ }
207
+ if strings.Contains(stdout.String(), "[cursor]") {
208
+ t.Fatalf("plan offers a cursor conversion for a ledger whose only legacy bullets are fenced:\n%s", stdout.String())
209
+ }
210
+ if !strings.Contains(stdout.String(), "[schema] record schema") {
211
+ t.Fatalf("plan incomplete:\n%s", stdout.String())
212
+ }
213
+ }
@@ -28,33 +28,48 @@ type BulkFile struct {
28
28
  }
29
29
 
30
30
  // observeArtifactBudgets lists canonical artifacts present in the workspace root with
31
- // their budget status, plus non-canonical files large enough to hurt a reader.
32
- func observeArtifactBudgets(workspace string) ([]ArtifactBudget, []BulkFile, error) {
31
+ // their budget status, plus non-canonical files large enough to hurt a reader. The
32
+ // third return names every entry it could not read: one unreadable artifact must not
33
+ // hide the budgets of every other artifact.
34
+ func observeArtifactBudgets(workspace string) ([]ArtifactBudget, []BulkFile, []string, error) {
33
35
  entries, err := os.ReadDir(workspace)
34
36
  if err != nil {
35
- return nil, nil, err
37
+ return nil, nil, nil, err
36
38
  }
37
39
  var budgets []ArtifactBudget
38
40
  var bulk []BulkFile
41
+ var unreadable []string
39
42
  for _, entry := range entries {
40
43
  if !entry.Type().IsRegular() {
41
44
  continue
42
45
  }
46
+ name := entry.Name()
43
47
  info, err := entry.Info()
44
48
  if err != nil {
45
- return nil, nil, err
49
+ unreadable = append(unreadable, name)
50
+ continue
46
51
  }
47
- name := entry.Name()
48
52
  if _, canonical := state.ArtifactLineBudget(name); !canonical {
49
53
  if info.Size() > bulkFileThreshold {
50
54
  bulk = append(bulk, BulkFile{File: name, Bytes: info.Size()})
51
55
  }
52
56
  continue
53
57
  }
54
- // #nosec G304 -- canonical workspace artifact name from a fixed table
55
- raw, err := os.ReadFile(filepath.Join(workspace, name))
58
+ if info.Size() > maxCandidateArtifactBytes {
59
+ measured, _ := state.ArtifactBudget(name, nil)
60
+ budgets = append(budgets, ArtifactBudget{
61
+ File: name,
62
+ Bytes: info.Size(),
63
+ LineBudget: measured.LineBudget,
64
+ ByteBudget: measured.ByteBudget,
65
+ Over: true,
66
+ })
67
+ continue
68
+ }
69
+ raw, err := readBoundedRegularFile(filepath.Join(workspace, name), maxCandidateArtifactBytes)
56
70
  if err != nil {
57
- return nil, nil, err
71
+ unreadable = append(unreadable, name)
72
+ continue
58
73
  }
59
74
  measured, _ := state.ArtifactBudget(name, raw)
60
75
  budgets = append(budgets, ArtifactBudget{
@@ -69,5 +84,6 @@ func observeArtifactBudgets(workspace string) ([]ArtifactBudget, []BulkFile, err
69
84
  }
70
85
  sort.Slice(budgets, func(i, j int) bool { return budgets[i].File < budgets[j].File })
71
86
  sort.Slice(bulk, func(i, j int) bool { return bulk[i].File < bulk[j].File })
72
- return budgets, bulk, nil
87
+ sort.Strings(unreadable)
88
+ return budgets, bulk, unreadable, nil
73
89
  }