savepoint 1.0.1 → 1.0.3

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 (278) hide show
  1. package/.claude/settings.local.json +15 -1
  2. package/.golangci.yml +11 -0
  3. package/.savepoint/Design.md +52 -46
  4. package/.savepoint/releases/v1/epics/E01-go-setup/tasks/T001-init-module.md +1 -1
  5. package/.savepoint/releases/v1/epics/E03-board-tui-core/tasks/T005-layout.md +1 -1
  6. package/.savepoint/releases/v1/epics/E04-board-components/tasks/T002-card.md +1 -1
  7. package/.savepoint/releases/v1/epics/E04-board-components/tasks/T006-help-overlay.md +1 -1
  8. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/{Design.md → E06-Detail.md} +5 -3
  9. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T002-header-and-dividers.md +1 -1
  10. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T003-footer-status-bar.md +1 -1
  11. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T004-component-refinement.md +1 -1
  12. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T010-auto-refresh-watcher.md +2 -0
  13. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/{Design.md → E01-Detail.md} +9 -1
  14. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/{T007-next-activity-header.md → T001-next-activity-header.md} +13 -12
  15. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T002-rename-epic-design-files.md +9 -9
  16. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T003-rename-release-prd.md +2 -2
  17. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T004-update-instruction-files.md +13 -12
  18. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T005-update-cross-references.md +14 -13
  19. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T006-column-and-detail-scrolling.md +25 -15
  20. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T007-column-focus-border-stability.md +57 -0
  21. package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/E02-Audit.md +124 -0
  22. package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/{Design.md → E02-Detail.md} +12 -3
  23. package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/tasks/T001-fix-makefile.md +11 -8
  24. package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/tasks/T002-linux-build-target.md +12 -7
  25. package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/tasks/T003-macos-build-target.md +9 -5
  26. package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/tasks/T004-smoke-tests-and-artifacts.md +30 -9
  27. package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/E03-Audit.md +195 -0
  28. package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/E03-Detail.md +45 -0
  29. package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/tasks/T001-border-resize-fix.md +40 -0
  30. package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/tasks/T002-next-activity-below-header.md +64 -0
  31. package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/tasks/T003-checkbox-rendering-fix.md +56 -0
  32. package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/tasks/T005-unify-status-glyphs.md +65 -0
  33. package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/tasks/T006-forced-256-color-profile.md +36 -0
  34. package/.savepoint/releases/v1.1/epics/E04-epic-navigation/E04-Audit.md +167 -0
  35. package/.savepoint/releases/v1.1/epics/E04-epic-navigation/E04-Detail.md +51 -0
  36. package/.savepoint/releases/v1.1/epics/E04-epic-navigation/tasks/T001-sidebar-focusable-navigation.md +65 -0
  37. package/.savepoint/releases/v1.1/epics/E04-epic-navigation/tasks/T002-epic-detail-overlay.md +73 -0
  38. package/.savepoint/releases/v1.1/epics/E04-epic-navigation/tasks/T003-epic-status-glyphs.md +73 -0
  39. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/E05-Audit.md +237 -0
  40. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/E05-Detail.md +54 -0
  41. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T001-update-agents-md.md +45 -0
  42. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T002-update-router-md.md +40 -0
  43. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T003-update-design-md.md +47 -0
  44. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T004-implement-m-hotkey.md +98 -0
  45. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T005-update-help-overlay.md +33 -0
  46. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T006-tests-and-quality-gates.md +62 -0
  47. package/.savepoint/releases/v1.1/epics/E06-audit-command/E06-Audit.md +56 -0
  48. package/.savepoint/releases/v1.1/epics/E06-audit-command/E06-Detail.md +63 -0
  49. package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T005-proposals.md +44 -0
  50. package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T007-apply-close.md +35 -0
  51. package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T009-integration.md +40 -0
  52. package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T010-audit-file-migration.md +45 -0
  53. package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T011-model-tab-state.md +26 -0
  54. package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T012-epic-audit-render.md +33 -0
  55. package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T013-handle-tab-keys.md +34 -0
  56. package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T014-tab-indicator.md +33 -0
  57. package/.savepoint/releases/v1.1/epics/E07-init-command/E07-Audit.md +336 -0
  58. package/.savepoint/releases/v1.1/epics/E07-init-command/E07-Detail.md +61 -0
  59. package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T001-cli-entrypoint.md +37 -0
  60. package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T002-target-validation.md +28 -0
  61. package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T003-scaffold-writer.md +46 -0
  62. package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T004-atomic-writes.md +27 -0
  63. package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T005-magic-prompt.md +25 -0
  64. package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T006-clipboard.md +26 -0
  65. package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T007-integration-test.md +26 -0
  66. package/.savepoint/releases/v1.1/epics/E08-board-command/E08-Audit.md +333 -0
  67. package/.savepoint/releases/v1.1/epics/E08-board-command/E08-Detail.md +68 -0
  68. package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T001-cli-entrypoint.md +26 -0
  69. package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T002-non-tty-fallback.md +27 -0
  70. package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T003-tui-app-shell.md +28 -0
  71. package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T004-board-model.md +29 -0
  72. package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T005-detail-pane.md +27 -0
  73. package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T006-status-transitions.md +29 -0
  74. package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T007-theme-fallbacks.md +29 -0
  75. package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T008-integration-test.md +27 -0
  76. package/.savepoint/releases/v1.1/epics/E09-doctor-command/E09-Audit.md +207 -0
  77. package/.savepoint/releases/v1.1/epics/E09-doctor-command/E09-Detail.md +65 -0
  78. package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T001-cli-entrypoint.md +24 -0
  79. package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T002-config-router-validation.md +28 -0
  80. package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T003-structure-checks.md +29 -0
  81. package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T004-dependency-checks.md +27 -0
  82. package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T005-audit-orphan-checks.md +28 -0
  83. package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T006-quality-gates-report.md +31 -0
  84. package/.savepoint/releases/v1.1/epics/E11-board-refresh-fix/E11-Detail.md +36 -0
  85. package/.savepoint/releases/v1.1/epics/E11-board-refresh-fix/tasks/T001-debug-logging.md +25 -0
  86. package/.savepoint/releases/v1.1/epics/E11-board-refresh-fix/tasks/T002-increase-debounce.md +21 -0
  87. package/.savepoint/releases/v1.1/epics/E11-board-refresh-fix/tasks/T003-error-handling.md +22 -0
  88. package/.savepoint/releases/v1.1/epics/E11-board-refresh-fix/tasks/T004-test-verify.md +29 -0
  89. package/.savepoint/releases/v1.1/epics/E12-validation-fix/E12-Audit.md +444 -0
  90. package/.savepoint/releases/v1.1/epics/E12-validation-fix/E12-Detail.md +45 -0
  91. package/.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T001-default-phase.md +35 -0
  92. package/.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T002-default-status.md +19 -0
  93. package/.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T003-better-errors.md +29 -0
  94. package/.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T004-validate-on-write.md +25 -0
  95. package/.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T005-tests.md +37 -0
  96. package/.savepoint/releases/v1.1/epics/E13-audit-remediation/E13-Audit.md +118 -0
  97. package/.savepoint/releases/v1.1/epics/E13-audit-remediation/E13-Detail.md +73 -0
  98. package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T001-safe-cleanup.md +66 -0
  99. package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T002-bug-fixes.md +35 -0
  100. package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T003-centralize-duplication.md +60 -0
  101. package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T004-infrastructure.md +33 -0
  102. package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T005-decompose-update.md +37 -0
  103. package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T006-async-io.md +40 -0
  104. package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T007-test-coverage.md +37 -0
  105. package/.savepoint/releases/v1.1/epics/E14-structural-improvements/E14-Audit.md +267 -0
  106. package/.savepoint/releases/v1.1/epics/E14-structural-improvements/E14-Detail.md +54 -0
  107. package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T001-group-model.md +39 -0
  108. package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T002-data-interfaces.md +42 -0
  109. package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T003-discover-orphans.md +33 -0
  110. package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T004-epic-panel-headings.md +35 -0
  111. package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T005-shell-tokenization.md +27 -0
  112. package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T006-unify-enums.md +29 -0
  113. package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T007-testutil-package.md +28 -0
  114. package/.savepoint/releases/v1.1/epics/E15-hardening/E15-Detail.md +43 -0
  115. package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T001-benchmarks.md +31 -0
  116. package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T002-fuzz-targets.md +28 -0
  117. package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T003-debug-flag.md +30 -0
  118. package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T004-dist-checksums.md +27 -0
  119. package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T005-windows-targets.md +28 -0
  120. package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T006-abbreviation-splitting.md +26 -0
  121. package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T007-root-test-allowlist.md +28 -0
  122. package/.savepoint/releases/v1.1/epics/_archived/T001-cli-entrypoint.md +25 -0
  123. package/.savepoint/releases/v1.1/epics/_archived/T002-quality-gates.md +27 -0
  124. package/.savepoint/releases/v1.1/epics/_archived/T003-snapshot.md +27 -0
  125. package/.savepoint/releases/v1.1/epics/_archived/T004-ai-reconcile.md +29 -0
  126. package/.savepoint/releases/v1.1/epics/_archived/T006-tui-review.md +31 -0
  127. package/.savepoint/releases/v1.1/epics/_archived/T008-skip-handling.md +34 -0
  128. package/.savepoint/releases/v1.1/v1.1-PRD.md +139 -0
  129. package/.savepoint/router.md +29 -108
  130. package/AGENTS.md +69 -111
  131. package/Makefile +19 -3
  132. package/README.md +6 -6
  133. package/agent-skills/savepoint-audit/SKILL.md +87 -35
  134. package/agent-skills/savepoint-build-task/SKILL.md +9 -4
  135. package/agent-skills/savepoint-create-plan/SKILL.md +10 -5
  136. package/agent-skills/savepoint-create-task/SKILL.md +44 -31
  137. package/agent-skills/savepoint-draft-prd/SKILL.md +8 -3
  138. package/agent-skills/savepoint-system-design/SKILL.md +8 -3
  139. package/agent_skills_test.go +91 -0
  140. package/cmd/board.go +59 -0
  141. package/cmd/board_test.go +137 -0
  142. package/cmd/doctor.go +53 -0
  143. package/cmd/doctor_test.go +146 -0
  144. package/cmd/init.go +63 -0
  145. package/cmd/init_test.go +104 -0
  146. package/internal/board/board.go +69 -49
  147. package/internal/board/board_test.go +83 -67
  148. package/internal/board/card.go +71 -20
  149. package/internal/board/card_test.go +141 -12
  150. package/internal/board/column.go +77 -11
  151. package/internal/board/column_test.go +63 -13
  152. package/internal/board/detail.go +107 -72
  153. package/internal/board/detail_test.go +117 -26
  154. package/internal/board/epic_panel.go +211 -18
  155. package/internal/board/epic_panel_test.go +637 -14
  156. package/internal/board/help.go +1 -0
  157. package/internal/board/help_test.go +1 -0
  158. package/internal/board/integration_test.go +266 -0
  159. package/internal/board/interfaces.go +65 -0
  160. package/internal/board/interfaces_test.go +114 -0
  161. package/internal/board/io.go +93 -0
  162. package/internal/board/layout.go +12 -2
  163. package/internal/board/layout_test.go +17 -0
  164. package/internal/board/model.go +130 -52
  165. package/internal/board/plain.go +88 -0
  166. package/internal/board/plain_test.go +117 -0
  167. package/internal/board/release.go +1 -9
  168. package/internal/board/release_test.go +6 -6
  169. package/internal/board/render_policy_test.go +77 -0
  170. package/internal/board/status.go +23 -0
  171. package/internal/board/theme.go +24 -0
  172. package/internal/board/theme_test.go +31 -0
  173. package/internal/board/transitions.go +113 -88
  174. package/internal/board/transitions_test.go +164 -141
  175. package/internal/board/tui.go +32 -0
  176. package/internal/board/update.go +472 -94
  177. package/internal/board/update_test.go +447 -0
  178. package/internal/board/util.go +76 -0
  179. package/internal/board/view.go +139 -22
  180. package/internal/board/view_test.go +171 -3
  181. package/internal/board/watch.go +57 -9
  182. package/internal/buildtool/main.go +211 -0
  183. package/internal/buildtool/main_test.go +46 -0
  184. package/internal/data/config.go +17 -3
  185. package/internal/data/config_test.go +49 -0
  186. package/internal/data/discover.go +26 -0
  187. package/internal/data/discover_test.go +34 -10
  188. package/internal/data/errors.go +4 -0
  189. package/internal/data/lifecycle.go +13 -6
  190. package/internal/data/lifecycle_test.go +14 -11
  191. package/internal/data/parser.go +29 -6
  192. package/internal/data/parser_test.go +66 -7
  193. package/internal/data/task.go +1 -0
  194. package/internal/data/write.go +85 -11
  195. package/internal/data/write_test.go +167 -0
  196. package/internal/doctor/checks.go +567 -0
  197. package/internal/doctor/checks_test.go +716 -0
  198. package/internal/doctor/gates.go +193 -0
  199. package/internal/doctor/gates_test.go +166 -0
  200. package/internal/doctor/interfaces.go +64 -0
  201. package/internal/doctor/interfaces_test.go +104 -0
  202. package/internal/doctor/repairs.go +80 -0
  203. package/internal/doctor/repairs_test.go +81 -0
  204. package/internal/doctor/report.go +157 -0
  205. package/internal/doctor/report_test.go +89 -0
  206. package/internal/init/clipboard.go +146 -0
  207. package/internal/init/clipboard_test.go +74 -0
  208. package/internal/init/install.go +16 -0
  209. package/internal/init/integration_test.go +197 -0
  210. package/internal/init/prompt.go +14 -0
  211. package/internal/init/prompt_test.go +77 -0
  212. package/internal/init/scaffold.go +59 -0
  213. package/internal/init/scaffold_test.go +179 -0
  214. package/internal/init/template_freshness_test.go +56 -0
  215. package/internal/init/validate.go +85 -0
  216. package/internal/init/validate_test.go +141 -0
  217. package/internal/init/write.go +73 -0
  218. package/internal/init/write_test.go +91 -0
  219. package/internal/styles/palette.go +3 -3
  220. package/internal/styles/styles.go +39 -12
  221. package/internal/styles/styles_test.go +133 -0
  222. package/internal/testutil/fixture.go +113 -0
  223. package/internal/testutil/fs.go +26 -0
  224. package/main.go +107 -1
  225. package/package.json +2 -2
  226. package/project-audit/audit_report_glm_5.1.md +411 -0
  227. package/project-audit/audit_report_opus_4.6 +406 -0
  228. package/project-audit/consolidated-audit-report.md +456 -0
  229. package/savepoint +0 -0
  230. package/templates/project/.savepoint/Design.md +2 -2
  231. package/templates/project/.savepoint/router.md +15 -14
  232. package/templates/project/AGENTS.md +56 -98
  233. package/templates/project/agent-skills/savepoint-audit/SKILL.md +87 -0
  234. package/templates/project/agent-skills/savepoint-build-task/SKILL.md +44 -0
  235. package/templates/project/agent-skills/savepoint-create-plan/SKILL.md +33 -0
  236. package/templates/project/agent-skills/savepoint-create-task/SKILL.md +44 -0
  237. package/templates/project/agent-skills/savepoint-draft-prd/SKILL.md +37 -0
  238. package/templates/project/agent-skills/savepoint-system-design/SKILL.md +38 -0
  239. package/templates/prompts/audit-reconciliation.prompt.md +35 -30
  240. package/templates/prompts/design.prompt.md +3 -1
  241. package/templates/prompts/epic-design.prompt.md +3 -3
  242. package/templates/prompts/task-breakdown.prompt.md +1 -1
  243. package/templates/prompts/task-building.prompt.md +1 -1
  244. package/templates/prompts/task-planning.prompt.md +1 -1
  245. package/.savepoint/audit/E01-go-setup/proposals.md +0 -166
  246. package/.savepoint/audit/E01-go-setup/snapshot.md +0 -71
  247. package/.savepoint/audit/E01-scaffolding/proposals/AGENTS.md +0 -66
  248. package/.savepoint/audit/E01-scaffolding/proposals/Design.md +0 -210
  249. package/.savepoint/audit/E01-scaffolding/proposals/epic-Design.md +0 -117
  250. package/.savepoint/audit/E01-scaffolding/proposals/quality-review.md +0 -101
  251. package/.savepoint/audit/E01-scaffolding/snapshot.md +0 -54
  252. package/.savepoint/audit/E02-data-model/snapshot.md +0 -128
  253. package/.savepoint/audit/E02-data-readers/proposals.md +0 -123
  254. package/.savepoint/audit/E02-data-readers/snapshot.md +0 -54
  255. package/.savepoint/audit/E03-board-tui-core/proposals.md +0 -146
  256. package/.savepoint/audit/E03-board-tui-core/snapshot.md +0 -57
  257. package/.savepoint/audit/E03-cli-foundation/snapshot.md +0 -106
  258. package/.savepoint/audit/E04-board-components/proposals.md +0 -118
  259. package/.savepoint/audit/E04-board-components/snapshot.md +0 -77
  260. package/.savepoint/audit/E04-templates-and-prompts/snapshot.md +0 -115
  261. package/.savepoint/audit/E05-init-command/snapshot.md +0 -125
  262. package/.savepoint/audit/E05-phase-transitions/proposals.md +0 -83
  263. package/.savepoint/audit/E05-phase-transitions/snapshot.md +0 -36
  264. package/.savepoint/audit/E06-atari-noir-layout/proposals.md +0 -130
  265. package/.savepoint/audit/E06-atari-noir-layout/snapshot.md +0 -84
  266. package/.savepoint/audit/E06-tui-board/snapshot.md +0 -64
  267. package/.savepoint/audit/E07-audit-pipeline/snapshot.md +0 -165
  268. package/.savepoint/audit/E08-board-workflow-cleanup/snapshot.md +0 -65
  269. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T001-border-resize-fix.md +0 -36
  270. package/ink-cli-ui-design.zip +0 -0
  271. package/main.exe +0 -0
  272. package/savepoint.exe +0 -0
  273. /package/.savepoint/releases/v1/epics/E01-go-setup/{Design.md → E01-Detail.md} +0 -0
  274. /package/.savepoint/releases/v1/epics/E02-data-readers/{Design.md → E02-Detail.md} +0 -0
  275. /package/.savepoint/releases/v1/epics/E03-board-tui-core/{Design.md → E03-Detail.md} +0 -0
  276. /package/.savepoint/releases/v1/epics/E04-board-components/{Design.md → E04-Detail.md} +0 -0
  277. /package/.savepoint/releases/v1/epics/E05-phase-transitions/{Design.md → E05-Detail.md} +0 -0
  278. /package/.savepoint/releases/v1/{PRD.md → v1-PRD.md} +0 -0
@@ -1,88 +1,113 @@
1
- package board
2
-
3
- import (
4
- "fmt"
5
-
6
- "github.com/opencode/savepoint/internal/data"
7
- )
8
-
9
- // Advance moves a task forward through the phase lifecycle.
10
- func Advance(t *data.Task) {
11
- switch t.Column {
12
- case data.ColumnPlanned:
13
- t.Column = data.ColumnInProgress
14
- t.Stage = data.StageBuild
15
- case data.ColumnInProgress:
16
- switch t.Stage {
17
- case data.StageBuild:
18
- t.Stage = data.StageTest
19
- case data.StageTest:
20
- t.Stage = data.StageAudit
21
- case data.StageAudit:
22
- t.Column = data.ColumnDone
23
- t.Stage = ""
24
- }
25
- }
26
- }
27
-
28
- // Retreat moves a task backward through the phase lifecycle.
29
- func Retreat(t *data.Task) {
30
- switch t.Column {
31
- case data.ColumnDone:
32
- t.Column = data.ColumnInProgress
33
- t.Stage = data.StageAudit
34
- case data.ColumnInProgress:
35
- switch t.Stage {
36
- case data.StageAudit:
37
- t.Stage = data.StageTest
38
- case data.StageTest:
39
- t.Stage = data.StageBuild
40
- case data.StageBuild:
41
- t.Column = data.ColumnPlanned
42
- t.Stage = ""
43
- }
44
- }
45
- }
46
-
47
- // CanAdvance checks whether a task is allowed to advance to its next phase.
48
- // It validates phase adjacency and dependency completion.
49
- // Returns (true, "") if allowed, or (false, reason) if blocked.
50
- func CanAdvance(t *data.Task, allTasks []data.Task) (bool, string) {
51
- switch t.Column {
52
- case data.ColumnPlanned:
53
- return true, ""
54
- case data.ColumnInProgress:
55
- switch t.Stage {
56
- case data.StageBuild:
57
- return true, ""
58
- case data.StageTest:
59
- return true, ""
60
- case data.StageAudit:
61
- for _, depID := range t.DependsOn {
62
- dep := findTask(depID, allTasks)
63
- if dep == nil {
64
- return false, fmt.Sprintf("dependency %q not found", depID)
65
- }
66
- if dep.Column != data.ColumnDone {
67
- return false, fmt.Sprintf("dependency %q is not done", depID)
68
- }
69
- }
70
- return true, ""
71
- default:
72
- return false, fmt.Sprintf("unknown stage %q", t.Stage)
73
- }
74
- case data.ColumnDone:
75
- return false, "task is already done"
76
- default:
77
- return false, fmt.Sprintf("unknown column %q", t.Column)
78
- }
79
- }
80
-
81
- func findTask(id string, tasks []data.Task) *data.Task {
82
- for i := range tasks {
83
- if tasks[i].ID == id {
84
- return &tasks[i]
85
- }
86
- }
87
- return nil
88
- }
1
+ package board
2
+
3
+ import (
4
+ "fmt"
5
+
6
+ "github.com/opencode/savepoint/internal/data"
7
+ )
8
+
9
+ // Advance moves a task forward through the phase lifecycle.
10
+ func Advance(t *data.Task) {
11
+ stage := t.Stage
12
+ if stage == "" {
13
+ stage = data.StageBuild
14
+ }
15
+ switch t.Column {
16
+ case data.ColumnPlanned:
17
+ t.Column = data.ColumnInProgress
18
+ t.Stage = data.StageBuild
19
+ case data.ColumnInProgress:
20
+ switch stage {
21
+ case data.StageBuild:
22
+ t.Stage = data.StageTest
23
+ case data.StageTest:
24
+ t.Stage = data.StageAudit
25
+ case data.StageAudit:
26
+ t.Column = data.ColumnDone
27
+ t.Stage = ""
28
+ }
29
+ }
30
+ t.Status = string(t.Column)
31
+ }
32
+
33
+ // Retreat moves a task backward through the phase lifecycle.
34
+ func Retreat(t *data.Task) {
35
+ stage := t.Stage
36
+ if stage == "" {
37
+ stage = data.StageBuild
38
+ }
39
+ switch t.Column {
40
+ case data.ColumnDone:
41
+ t.Column = data.ColumnInProgress
42
+ t.Stage = data.StageAudit
43
+ case data.ColumnInProgress:
44
+ switch stage {
45
+ case data.StageAudit:
46
+ t.Stage = data.StageTest
47
+ case data.StageTest:
48
+ t.Stage = data.StageBuild
49
+ case data.StageBuild:
50
+ t.Column = data.ColumnPlanned
51
+ t.Stage = ""
52
+ }
53
+ }
54
+ t.Status = string(t.Column)
55
+ }
56
+
57
+ func taskTransitionMessage(prefix string, task data.Task) string {
58
+ if task.Column == data.ColumnInProgress {
59
+ return fmt.Sprintf("%s %s to %s", prefix, shortID(task.ID), task.Stage)
60
+ }
61
+ return fmt.Sprintf("%s %s to %s", prefix, shortID(task.ID), task.Column)
62
+ }
63
+
64
+ // CanAdvance checks whether a task is allowed to advance to its next phase.
65
+ // It validates phase adjacency and dependency completion.
66
+ // Returns (true, "") if allowed, or (false, reason) if blocked.
67
+ func CanAdvance(t *data.Task, allTasks []data.Task) (bool, string) {
68
+ switch t.Column {
69
+ case data.ColumnPlanned:
70
+ return dependenciesDone(t, allTasks)
71
+ case data.ColumnInProgress:
72
+ stage := t.Stage
73
+ if stage == "" {
74
+ stage = data.StageBuild
75
+ }
76
+ switch stage {
77
+ case data.StageBuild:
78
+ return true, ""
79
+ case data.StageTest:
80
+ return true, ""
81
+ case data.StageAudit:
82
+ return dependenciesDone(t, allTasks)
83
+ default:
84
+ return false, fmt.Sprintf("unknown stage %q", stage)
85
+ }
86
+ case data.ColumnDone:
87
+ return false, "task is already done"
88
+ default:
89
+ return false, fmt.Sprintf("unknown column %q", t.Column)
90
+ }
91
+ }
92
+
93
+ func dependenciesDone(t *data.Task, allTasks []data.Task) (bool, string) {
94
+ for _, depID := range t.DependsOn {
95
+ dep := findTask(depID, allTasks)
96
+ if dep == nil {
97
+ return false, fmt.Sprintf("dependency %q not found", depID)
98
+ }
99
+ if dep.Column != data.ColumnDone {
100
+ return false, fmt.Sprintf("dependency %q is not done", depID)
101
+ }
102
+ }
103
+ return true, ""
104
+ }
105
+
106
+ func findTask(id string, tasks []data.Task) *data.Task {
107
+ for i := range tasks {
108
+ if tasks[i].ID == id {
109
+ return &tasks[i]
110
+ }
111
+ }
112
+ return nil
113
+ }
@@ -1,141 +1,164 @@
1
- package board
2
-
3
- import (
4
- "testing"
5
-
6
- "github.com/opencode/savepoint/internal/data"
7
- )
8
-
9
- func TestAdvance(t *testing.T) {
10
- tests := []struct {
11
- name string
12
- initialCol data.ColumnType
13
- initialSt data.ProgressStage
14
- expectCol data.ColumnType
15
- expectSt data.ProgressStage
16
- }{
17
- {"planned to in_progress/build", data.ColumnPlanned, "", data.ColumnInProgress, data.StageBuild},
18
- {"in_progress/build to test", data.ColumnInProgress, data.StageBuild, data.ColumnInProgress, data.StageTest},
19
- {"in_progress/test to audit", data.ColumnInProgress, data.StageTest, data.ColumnInProgress, data.StageAudit},
20
- {"in_progress/audit to done", data.ColumnInProgress, data.StageAudit, data.ColumnDone, ""},
21
- }
22
-
23
- for _, tt := range tests {
24
- t.Run(tt.name, func(t *testing.T) {
25
- task := data.Task{Column: tt.initialCol, Stage: tt.initialSt}
26
- Advance(&task)
27
- if task.Column != tt.expectCol || task.Stage != tt.expectSt {
28
- t.Errorf("Advance() = %v/%v, want %v/%v", task.Column, task.Stage, tt.expectCol, tt.expectSt)
29
- }
30
- })
31
- }
32
- }
33
-
34
- func TestRetreat(t *testing.T) {
35
- tests := []struct {
36
- name string
37
- initialCol data.ColumnType
38
- initialSt data.ProgressStage
39
- expectCol data.ColumnType
40
- expectSt data.ProgressStage
41
- }{
42
- {"done to in_progress/audit", data.ColumnDone, "", data.ColumnInProgress, data.StageAudit},
43
- {"in_progress/audit to test", data.ColumnInProgress, data.StageAudit, data.ColumnInProgress, data.StageTest},
44
- {"in_progress/test to build", data.ColumnInProgress, data.StageTest, data.ColumnInProgress, data.StageBuild},
45
- {"in_progress/build to planned", data.ColumnInProgress, data.StageBuild, data.ColumnPlanned, ""},
46
- }
47
-
48
- for _, tt := range tests {
49
- t.Run(tt.name, func(t *testing.T) {
50
- task := data.Task{Column: tt.initialCol, Stage: tt.initialSt}
51
- Retreat(&task)
52
- if task.Column != tt.expectCol || task.Stage != tt.expectSt {
53
- t.Errorf("Retreat() = %v/%v, want %v/%v", task.Column, task.Stage, tt.expectCol, tt.expectSt)
54
- }
55
- })
56
- }
57
- }
58
-
59
- func TestCanAdvance_plannedAlwaysAllowed(t *testing.T) {
60
- task := data.Task{ID: "T1", Column: data.ColumnPlanned}
61
- ok, reason := CanAdvance(&task, nil)
62
- if !ok {
63
- t.Errorf("CanAdvance(planned) = false %q, want true", reason)
64
- }
65
- }
66
-
67
- func TestCanAdvance_buildAlwaysAllowed(t *testing.T) {
68
- task := data.Task{ID: "T1", Column: data.ColumnInProgress, Stage: data.StageBuild}
69
- ok, reason := CanAdvance(&task, nil)
70
- if !ok {
71
- t.Errorf("CanAdvance(build) = false %q, want true", reason)
72
- }
73
- }
74
-
75
- func TestCanAdvance_testAlwaysAllowed(t *testing.T) {
76
- task := data.Task{ID: "T1", Column: data.ColumnInProgress, Stage: data.StageTest}
77
- ok, reason := CanAdvance(&task, nil)
78
- if !ok {
79
- t.Errorf("CanAdvance(test) = false %q, want true", reason)
80
- }
81
- }
82
-
83
- func TestCanAdvance_auditDoneBlockedByDependency(t *testing.T) {
84
- allTasks := []data.Task{
85
- {ID: "T1", Column: data.ColumnInProgress, Stage: data.StageAudit, DependsOn: []string{"T2"}},
86
- {ID: "T2", Column: data.ColumnInProgress, Stage: data.StageBuild},
87
- }
88
- ok, reason := CanAdvance(&allTasks[0], allTasks)
89
- if ok {
90
- t.Fatal("CanAdvance(audit with undep) = true, want false")
91
- }
92
- if reason == "" {
93
- t.Fatal("expected non-empty reason string")
94
- }
95
- }
96
-
97
- func TestCanAdvance_auditDoneAllowedWhenDepsDone(t *testing.T) {
98
- allTasks := []data.Task{
99
- {ID: "T1", Column: data.ColumnInProgress, Stage: data.StageAudit, DependsOn: []string{"T2"}},
100
- {ID: "T2", Column: data.ColumnDone},
101
- }
102
- ok, reason := CanAdvance(&allTasks[0], allTasks)
103
- if !ok {
104
- t.Errorf("CanAdvance(audit with dep done) = false %q, want true", reason)
105
- }
106
- }
107
-
108
- func TestCanAdvance_doneBlocked(t *testing.T) {
109
- task := data.Task{ID: "T1", Column: data.ColumnDone}
110
- ok, reason := CanAdvance(&task, nil)
111
- if ok {
112
- t.Fatal("CanAdvance(done) = true, want false")
113
- }
114
- if reason == "" {
115
- t.Fatal("expected non-empty reason string")
116
- }
117
- }
118
-
119
- func TestCanAdvance_unknownStageBlocked(t *testing.T) {
120
- task := data.Task{ID: "T1", Column: data.ColumnInProgress, Stage: "invalid"}
121
- ok, reason := CanAdvance(&task, nil)
122
- if ok {
123
- t.Fatal("CanAdvance(unknown stage) = true, want false")
124
- }
125
- if reason == "" {
126
- t.Fatal("expected non-empty reason string")
127
- }
128
- }
129
-
130
- func TestCanAdvance_auditDepsNotFoundBlocked(t *testing.T) {
131
- allTasks := []data.Task{
132
- {ID: "T1", Column: data.ColumnInProgress, Stage: data.StageAudit, DependsOn: []string{"T2"}},
133
- }
134
- ok, reason := CanAdvance(&allTasks[0], allTasks)
135
- if ok {
136
- t.Fatal("CanAdvance(audit missing dep) = true, want false")
137
- }
138
- if reason == "" {
139
- t.Fatal("expected non-empty reason string")
140
- }
141
- }
1
+ package board
2
+
3
+ import (
4
+ "testing"
5
+
6
+ "github.com/opencode/savepoint/internal/data"
7
+ )
8
+
9
+ func TestAdvance(t *testing.T) {
10
+ tests := []struct {
11
+ name string
12
+ initialCol data.ColumnType
13
+ initialSt data.ProgressStage
14
+ expectCol data.ColumnType
15
+ expectSt data.ProgressStage
16
+ }{
17
+ {"planned to in_progress/build", data.ColumnPlanned, "", data.ColumnInProgress, data.StageBuild},
18
+ {"in_progress/build to test", data.ColumnInProgress, data.StageBuild, data.ColumnInProgress, data.StageTest},
19
+ {"in_progress/test to audit", data.ColumnInProgress, data.StageTest, data.ColumnInProgress, data.StageAudit},
20
+ {"in_progress/audit to done", data.ColumnInProgress, data.StageAudit, data.ColumnDone, ""},
21
+ }
22
+
23
+ for _, tt := range tests {
24
+ t.Run(tt.name, func(t *testing.T) {
25
+ task := data.Task{Column: tt.initialCol, Stage: tt.initialSt}
26
+ Advance(&task)
27
+ if task.Column != tt.expectCol || task.Stage != tt.expectSt {
28
+ t.Errorf("Advance() = %v/%v, want %v/%v", task.Column, task.Stage, tt.expectCol, tt.expectSt)
29
+ }
30
+ if task.Status != string(tt.expectCol) {
31
+ t.Errorf("Advance() status = %q, want %q", task.Status, tt.expectCol)
32
+ }
33
+ })
34
+ }
35
+ }
36
+
37
+ func TestRetreat(t *testing.T) {
38
+ tests := []struct {
39
+ name string
40
+ initialCol data.ColumnType
41
+ initialSt data.ProgressStage
42
+ expectCol data.ColumnType
43
+ expectSt data.ProgressStage
44
+ }{
45
+ {"done to in_progress/audit", data.ColumnDone, "", data.ColumnInProgress, data.StageAudit},
46
+ {"in_progress/audit to test", data.ColumnInProgress, data.StageAudit, data.ColumnInProgress, data.StageTest},
47
+ {"in_progress/test to build", data.ColumnInProgress, data.StageTest, data.ColumnInProgress, data.StageBuild},
48
+ {"in_progress/build to planned", data.ColumnInProgress, data.StageBuild, data.ColumnPlanned, ""},
49
+ }
50
+
51
+ for _, tt := range tests {
52
+ t.Run(tt.name, func(t *testing.T) {
53
+ task := data.Task{Status: string(tt.initialCol), Column: tt.initialCol, Stage: tt.initialSt}
54
+ Retreat(&task)
55
+ if task.Column != tt.expectCol || task.Stage != tt.expectSt {
56
+ t.Errorf("Retreat() = %v/%v, want %v/%v", task.Column, task.Stage, tt.expectCol, tt.expectSt)
57
+ }
58
+ if task.Status != string(tt.expectCol) {
59
+ t.Errorf("Retreat() status = %q, want %q", task.Status, tt.expectCol)
60
+ }
61
+ })
62
+ }
63
+ }
64
+
65
+ func TestCanAdvance_plannedAllowedWhenDependenciesDone(t *testing.T) {
66
+ allTasks := []data.Task{
67
+ {ID: "T1", Column: data.ColumnPlanned, DependsOn: []string{"T2"}},
68
+ {ID: "T2", Column: data.ColumnDone},
69
+ }
70
+ ok, reason := CanAdvance(&allTasks[0], allTasks)
71
+ if !ok {
72
+ t.Errorf("CanAdvance(planned with done dep) = false %q, want true", reason)
73
+ }
74
+ }
75
+
76
+ func TestCanAdvance_plannedBlockedByDependency(t *testing.T) {
77
+ allTasks := []data.Task{
78
+ {ID: "T1", Column: data.ColumnPlanned, DependsOn: []string{"T2"}},
79
+ {ID: "T2", Column: data.ColumnInProgress},
80
+ }
81
+ ok, reason := CanAdvance(&allTasks[0], allTasks)
82
+ if ok {
83
+ t.Fatal("CanAdvance(planned with unfinished dep) = true, want false")
84
+ }
85
+ if reason != "dependency \"T2\" is not done" {
86
+ t.Errorf("reason = %q, want dependency warning", reason)
87
+ }
88
+ }
89
+
90
+ func TestCanAdvance_buildAlwaysAllowed(t *testing.T) {
91
+ task := data.Task{ID: "T1", Column: data.ColumnInProgress, Stage: data.StageBuild}
92
+ ok, reason := CanAdvance(&task, nil)
93
+ if !ok {
94
+ t.Errorf("CanAdvance(build) = false %q, want true", reason)
95
+ }
96
+ }
97
+
98
+ func TestCanAdvance_testAlwaysAllowed(t *testing.T) {
99
+ task := data.Task{ID: "T1", Column: data.ColumnInProgress, Stage: data.StageTest}
100
+ ok, reason := CanAdvance(&task, nil)
101
+ if !ok {
102
+ t.Errorf("CanAdvance(test) = false %q, want true", reason)
103
+ }
104
+ }
105
+
106
+ func TestCanAdvance_auditDoneBlockedByDependency(t *testing.T) {
107
+ allTasks := []data.Task{
108
+ {ID: "T1", Column: data.ColumnInProgress, Stage: data.StageAudit, DependsOn: []string{"T2"}},
109
+ {ID: "T2", Column: data.ColumnInProgress, Stage: data.StageBuild},
110
+ }
111
+ ok, reason := CanAdvance(&allTasks[0], allTasks)
112
+ if ok {
113
+ t.Fatal("CanAdvance(audit with undep) = true, want false")
114
+ }
115
+ if reason == "" {
116
+ t.Fatal("expected non-empty reason string")
117
+ }
118
+ }
119
+
120
+ func TestCanAdvance_auditDoneAllowedWhenDepsDone(t *testing.T) {
121
+ allTasks := []data.Task{
122
+ {ID: "T1", Column: data.ColumnInProgress, Stage: data.StageAudit, DependsOn: []string{"T2"}},
123
+ {ID: "T2", Column: data.ColumnDone},
124
+ }
125
+ ok, reason := CanAdvance(&allTasks[0], allTasks)
126
+ if !ok {
127
+ t.Errorf("CanAdvance(audit with dep done) = false %q, want true", reason)
128
+ }
129
+ }
130
+
131
+ func TestCanAdvance_doneBlocked(t *testing.T) {
132
+ task := data.Task{ID: "T1", Column: data.ColumnDone}
133
+ ok, reason := CanAdvance(&task, nil)
134
+ if ok {
135
+ t.Fatal("CanAdvance(done) = true, want false")
136
+ }
137
+ if reason == "" {
138
+ t.Fatal("expected non-empty reason string")
139
+ }
140
+ }
141
+
142
+ func TestCanAdvance_unknownStageBlocked(t *testing.T) {
143
+ task := data.Task{ID: "T1", Column: data.ColumnInProgress, Stage: "invalid"}
144
+ ok, reason := CanAdvance(&task, nil)
145
+ if ok {
146
+ t.Fatal("CanAdvance(unknown stage) = true, want false")
147
+ }
148
+ if reason == "" {
149
+ t.Fatal("expected non-empty reason string")
150
+ }
151
+ }
152
+
153
+ func TestCanAdvance_auditDepsNotFoundBlocked(t *testing.T) {
154
+ allTasks := []data.Task{
155
+ {ID: "T1", Column: data.ColumnInProgress, Stage: data.StageAudit, DependsOn: []string{"T2"}},
156
+ }
157
+ ok, reason := CanAdvance(&allTasks[0], allTasks)
158
+ if ok {
159
+ t.Fatal("CanAdvance(audit missing dep) = true, want false")
160
+ }
161
+ if reason == "" {
162
+ t.Fatal("expected non-empty reason string")
163
+ }
164
+ }
@@ -0,0 +1,32 @@
1
+ package board
2
+
3
+ import (
4
+ "fmt"
5
+ "os"
6
+ "path/filepath"
7
+
8
+ tea "github.com/charmbracelet/bubbletea"
9
+ )
10
+
11
+ // RunTUI launches the Bubble Tea board with optional release/epic filters.
12
+ func RunTUI(release, epic string) error {
13
+ model, err := newProjectModel(".", release, epic)
14
+ if err != nil {
15
+ return err
16
+ }
17
+
18
+ cfg, err := model.Dependencies.ConfigReader.Read(filepath.Join(model.Root, "config.yml"))
19
+ if err != nil {
20
+ return err
21
+ }
22
+ model.Theme = cfg.Theme
23
+
24
+ applyColorProfile()
25
+
26
+ p := tea.NewProgram(model, tea.WithAltScreen())
27
+ if _, err := p.Run(); err != nil {
28
+ fmt.Fprintf(os.Stderr, "Error: %v\n", err)
29
+ return err
30
+ }
31
+ return nil
32
+ }