savepoint 1.0.4 → 1.0.7

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 (425) hide show
  1. package/package.json +11 -4
  2. package/savepoint.exe +0 -0
  3. package/.claude/settings.local.json +0 -38
  4. package/.github/workflows/ci.yml +0 -20
  5. package/.golangci.yml +0 -11
  6. package/.prettierignore +0 -4
  7. package/.savepoint/Design.md +0 -207
  8. package/.savepoint/PRD.md +0 -58
  9. package/.savepoint/config.yml +0 -27
  10. package/.savepoint/releases/v1/epics/E01-go-setup/E01-Detail.md +0 -39
  11. package/.savepoint/releases/v1/epics/E01-go-setup/tasks/T001-init-module.md +0 -42
  12. package/.savepoint/releases/v1/epics/E01-go-setup/tasks/T002-entrypoint.md +0 -23
  13. package/.savepoint/releases/v1/epics/E01-go-setup/tasks/T003-directory-structure.md +0 -24
  14. package/.savepoint/releases/v1/epics/E01-go-setup/tasks/T004-makefile.md +0 -23
  15. package/.savepoint/releases/v1/epics/E02-data-readers/E02-Detail.md +0 -61
  16. package/.savepoint/releases/v1/epics/E02-data-readers/tasks/T001-task-struct.md +0 -29
  17. package/.savepoint/releases/v1/epics/E02-data-readers/tasks/T002-frontmatter-parser.md +0 -30
  18. package/.savepoint/releases/v1/epics/E02-data-readers/tasks/T003-router-reader.md +0 -29
  19. package/.savepoint/releases/v1/epics/E02-data-readers/tasks/T004-config-reader.md +0 -29
  20. package/.savepoint/releases/v1/epics/E02-data-readers/tasks/T005-discovery.md +0 -30
  21. package/.savepoint/releases/v1/epics/E03-board-tui-core/E03-Detail.md +0 -38
  22. package/.savepoint/releases/v1/epics/E03-board-tui-core/tasks/T001-model.md +0 -29
  23. package/.savepoint/releases/v1/epics/E03-board-tui-core/tasks/T002-update-loop.md +0 -30
  24. package/.savepoint/releases/v1/epics/E03-board-tui-core/tasks/T003-view.md +0 -34
  25. package/.savepoint/releases/v1/epics/E03-board-tui-core/tasks/T004-styles.md +0 -29
  26. package/.savepoint/releases/v1/epics/E03-board-tui-core/tasks/T005-layout.md +0 -42
  27. package/.savepoint/releases/v1/epics/E04-board-components/E04-Detail.md +0 -44
  28. package/.savepoint/releases/v1/epics/E04-board-components/tasks/T001-column.md +0 -34
  29. package/.savepoint/releases/v1/epics/E04-board-components/tasks/T002-card.md +0 -33
  30. package/.savepoint/releases/v1/epics/E04-board-components/tasks/T003-epic-panel.md +0 -49
  31. package/.savepoint/releases/v1/epics/E04-board-components/tasks/T004-detail-overlay.md +0 -40
  32. package/.savepoint/releases/v1/epics/E04-board-components/tasks/T005-release-dropdown.md +0 -33
  33. package/.savepoint/releases/v1/epics/E04-board-components/tasks/T006-help-overlay.md +0 -34
  34. package/.savepoint/releases/v1/epics/E05-phase-transitions/E05-Detail.md +0 -38
  35. package/.savepoint/releases/v1/epics/E05-phase-transitions/tasks/T001-phase-stepping.md +0 -29
  36. package/.savepoint/releases/v1/epics/E05-phase-transitions/tasks/T002-gates.md +0 -31
  37. package/.savepoint/releases/v1/epics/E05-phase-transitions/tasks/T003-write-task.md +0 -31
  38. package/.savepoint/releases/v1/epics/E05-phase-transitions/tasks/T004-write-router.md +0 -31
  39. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/E06-Detail.md +0 -62
  40. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T001-color-system.md +0 -39
  41. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T002-header-and-dividers.md +0 -52
  42. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T003-footer-status-bar.md +0 -52
  43. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T004-component-refinement.md +0 -53
  44. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T005-restore-nav-hints.md +0 -39
  45. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T007-detail-card-fixes.md +0 -36
  46. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T008-checkbox-states.md +0 -40
  47. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T009-router-priority-marker.md +0 -48
  48. package/.savepoint/releases/v1/epics/E06-atari-noir-layout/tasks/T010-auto-refresh-watcher.md +0 -66
  49. package/.savepoint/releases/v1/epics/_archived/E01-archive-and-reset/Design.md +0 -39
  50. package/.savepoint/releases/v1/epics/_archived/E01-archive-and-reset/tasks/T001-archive-epics.md +0 -20
  51. package/.savepoint/releases/v1/epics/_archived/E01-archive-and-reset/tasks/T002-rewrite-prd.md +0 -22
  52. package/.savepoint/releases/v1/epics/_archived/E01-archive-and-reset/tasks/T003-create-epic-stubs.md +0 -24
  53. package/.savepoint/releases/v1/epics/_archived/E01-archive-and-reset/tasks/T004-update-router.md +0 -22
  54. package/.savepoint/releases/v1/epics/_archived/E01-scaffolding/Design.md +0 -118
  55. package/.savepoint/releases/v1/epics/_archived/E01-scaffolding/handoff.md +0 -9
  56. package/.savepoint/releases/v1/epics/_archived/E01-scaffolding/tasks/T001-package-baseline.md +0 -45
  57. package/.savepoint/releases/v1/epics/_archived/E01-scaffolding/tasks/T002-typescript-build.md +0 -48
  58. package/.savepoint/releases/v1/epics/_archived/E01-scaffolding/tasks/T003-vitest-smoke.md +0 -43
  59. package/.savepoint/releases/v1/epics/_archived/E01-scaffolding/tasks/T004-lint-format-gates.md +0 -45
  60. package/.savepoint/releases/v1/epics/_archived/E01-scaffolding/tasks/T005-scaffold-verification.md +0 -40
  61. package/.savepoint/releases/v1/epics/_archived/E02-data-model/Design.md +0 -142
  62. package/.savepoint/releases/v1/epics/_archived/E02-data-model/tasks/T001-domain-ids-status.md +0 -27
  63. package/.savepoint/releases/v1/epics/_archived/E02-data-model/tasks/T002-markdown-frontmatter-boundary.md +0 -28
  64. package/.savepoint/releases/v1/epics/_archived/E02-data-model/tasks/T003-task-documents.md +0 -29
  65. package/.savepoint/releases/v1/epics/_archived/E02-data-model/tasks/T004-release-epic-router-config-readers.md +0 -30
  66. package/.savepoint/releases/v1/epics/_archived/E02-data-model/tasks/T005-dependency-validation.md +0 -29
  67. package/.savepoint/releases/v1/epics/_archived/E02-data-model/tasks/T006-epic-task-set-reader.md +0 -29
  68. package/.savepoint/releases/v1/epics/_archived/E02-data-model/tasks/T007-quality-gates.md +0 -31
  69. package/.savepoint/releases/v1/epics/_archived/E02-domain-phase-model/Design.md +0 -40
  70. package/.savepoint/releases/v1/epics/_archived/E02-domain-phase-model/tasks/T001-phase-types.md +0 -27
  71. package/.savepoint/releases/v1/epics/_archived/E02-domain-phase-model/tasks/T002-phase-frontmatter.md +0 -25
  72. package/.savepoint/releases/v1/epics/_archived/E02-domain-phase-model/tasks/T003-simplify-config.md +0 -26
  73. package/.savepoint/releases/v1/epics/_archived/E02-domain-phase-model/tasks/T004-simplify-router-domain.md +0 -24
  74. package/.savepoint/releases/v1/epics/_archived/E03-cli-foundation/Design.md +0 -122
  75. package/.savepoint/releases/v1/epics/_archived/E03-cli-foundation/tasks/T001-argument-parser-contract.md +0 -28
  76. package/.savepoint/releases/v1/epics/_archived/E03-cli-foundation/tasks/T002-help-text-generation.md +0 -28
  77. package/.savepoint/releases/v1/epics/_archived/E03-cli-foundation/tasks/T003-terminal-environment-detection.md +0 -27
  78. package/.savepoint/releases/v1/epics/_archived/E03-cli-foundation/tasks/T004-command-stub-modules.md +0 -29
  79. package/.savepoint/releases/v1/epics/_archived/E03-cli-foundation/tasks/T005-cli-runner-dispatch.md +0 -34
  80. package/.savepoint/releases/v1/epics/_archived/E03-cli-foundation/tasks/T006-entrypoint-quality-gates.md +0 -32
  81. package/.savepoint/releases/v1/epics/_archived/E03-cli-simplify/Design.md +0 -43
  82. package/.savepoint/releases/v1/epics/_archived/E03-cli-simplify/tasks/T001-strip-args.md +0 -26
  83. package/.savepoint/releases/v1/epics/_archived/E03-cli-simplify/tasks/T002-strip-help.md +0 -23
  84. package/.savepoint/releases/v1/epics/_archived/E03-cli-simplify/tasks/T003-strip-run.md +0 -23
  85. package/.savepoint/releases/v1/epics/_archived/E03-cli-simplify/tasks/T004-delete-commands.md +0 -24
  86. package/.savepoint/releases/v1/epics/_archived/E03-cli-simplify/tasks/T005-update-cli-tests.md +0 -22
  87. package/.savepoint/releases/v1/epics/_archived/E04-board-phase-integration/Design.md +0 -48
  88. package/.savepoint/releases/v1/epics/_archived/E04-board-phase-integration/tasks/T001-board-data-phases.md +0 -26
  89. package/.savepoint/releases/v1/epics/_archived/E04-board-phase-integration/tasks/T002-phase-rendering.md +0 -28
  90. package/.savepoint/releases/v1/epics/_archived/E04-board-phase-integration/tasks/T003-detail-pane-phases.md +0 -27
  91. package/.savepoint/releases/v1/epics/_archived/E04-board-phase-integration/tasks/T004-phase-transitions.md +0 -42
  92. package/.savepoint/releases/v1/epics/_archived/E04-board-phase-integration/tasks/T005-phase-gates.md +0 -24
  93. package/.savepoint/releases/v1/epics/_archived/E04-board-phase-integration/tasks/T006-phase-write-back.md +0 -24
  94. package/.savepoint/releases/v1/epics/_archived/E04-board-phase-integration/tasks/T007-remove-audit-flow.md +0 -27
  95. package/.savepoint/releases/v1/epics/_archived/E04-board-phase-integration/tasks/T008-board-tests.md +0 -25
  96. package/.savepoint/releases/v1/epics/_archived/E04-templates-and-prompts/Design.md +0 -85
  97. package/.savepoint/releases/v1/epics/_archived/E04-templates-and-prompts/tasks/T001-project-template-assets.md +0 -17
  98. package/.savepoint/releases/v1/epics/_archived/E04-templates-and-prompts/tasks/T002-release-and-prompt-assets.md +0 -20
  99. package/.savepoint/releases/v1/epics/_archived/E04-templates-and-prompts/tasks/T003-template-registry-renderer.md +0 -22
  100. package/.savepoint/releases/v1/epics/_archived/E04-templates-and-prompts/tasks/T004-template-integrity-tests.md +0 -17
  101. package/.savepoint/releases/v1/epics/_archived/E04-templates-and-prompts/tasks/T005-template-closeout-quality-gates.md +0 -16
  102. package/.savepoint/releases/v1/epics/_archived/E05-init-command/Design.md +0 -88
  103. package/.savepoint/releases/v1/epics/_archived/E05-init-command/tasks/T001-init-cli-contract.md +0 -22
  104. package/.savepoint/releases/v1/epics/_archived/E05-init-command/tasks/T002-target-validation.md +0 -23
  105. package/.savepoint/releases/v1/epics/_archived/E05-init-command/tasks/T003-scaffold-writer.md +0 -24
  106. package/.savepoint/releases/v1/epics/_archived/E05-init-command/tasks/T004-magic-prompt-and-clipboard.md +0 -23
  107. package/.savepoint/releases/v1/epics/_archived/E05-init-command/tasks/T005-dev-deps-install-option.md +0 -24
  108. package/.savepoint/releases/v1/epics/_archived/E05-init-command/tasks/T006-init-command-integration.md +0 -28
  109. package/.savepoint/releases/v1/epics/_archived/E05-project-cleanup/Design.md +0 -53
  110. package/.savepoint/releases/v1/epics/_archived/E05-project-cleanup/tasks/T001-delete-dead-src.md +0 -23
  111. package/.savepoint/releases/v1/epics/_archived/E05-project-cleanup/tasks/T002-delete-dead-tests.md +0 -26
  112. package/.savepoint/releases/v1/epics/_archived/E05-project-cleanup/tasks/T003-delete-assets.md +0 -25
  113. package/.savepoint/releases/v1/epics/_archived/E05-project-cleanup/tasks/T004-clean-savepoint.md +0 -28
  114. package/.savepoint/releases/v1/epics/_archived/E05-project-cleanup/tasks/T005-rewrite-agents-md.md +0 -28
  115. package/.savepoint/releases/v1/epics/_archived/E05-project-cleanup/tasks/T006-clean-package-json.md +0 -23
  116. package/.savepoint/releases/v1/epics/_archived/E05-project-cleanup/tasks/T007-verify.md +0 -25
  117. package/.savepoint/releases/v1/epics/_archived/E06-tui-board/Design.md +0 -104
  118. package/.savepoint/releases/v1/epics/_archived/E06-tui-board/tasks/T001-board-command-data.md +0 -23
  119. package/.savepoint/releases/v1/epics/_archived/E06-tui-board/tasks/T002-board-view-state.md +0 -24
  120. package/.savepoint/releases/v1/epics/_archived/E06-tui-board/tasks/T003-transition-gates-and-writes.md +0 -25
  121. package/.savepoint/releases/v1/epics/_archived/E06-tui-board/tasks/T004-terminal-theme.md +0 -23
  122. package/.savepoint/releases/v1/epics/_archived/E06-tui-board/tasks/T005-ink-board-ui.md +0 -26
  123. package/.savepoint/releases/v1/epics/_archived/E06-tui-board/tasks/T006-board-integration-audit-entry.md +0 -24
  124. package/.savepoint/releases/v1/epics/_archived/E07-audit-pipeline/Design.md +0 -88
  125. package/.savepoint/releases/v1/epics/_archived/E07-audit-pipeline/tasks/T001-audit-cli-contract.md +0 -23
  126. package/.savepoint/releases/v1/epics/_archived/E07-audit-pipeline/tasks/T002-quality-gate-runner.md +0 -23
  127. package/.savepoint/releases/v1/epics/_archived/E07-audit-pipeline/tasks/T003-snapshot-and-prompt.md +0 -23
  128. package/.savepoint/releases/v1/epics/_archived/E07-audit-pipeline/tasks/T004-audit-orchestration-router.md +0 -27
  129. package/.savepoint/releases/v1/epics/_archived/E07-audit-pipeline/tasks/T005-proposal-validation-apply.md +0 -25
  130. package/.savepoint/releases/v1/epics/_archived/E07-audit-pipeline/tasks/T006-audit-review-state.md +0 -24
  131. package/.savepoint/releases/v1/epics/_archived/E07-audit-pipeline/tasks/T007-audit-review-ui.md +0 -26
  132. package/.savepoint/releases/v1/epics/_archived/E07-audit-pipeline/tasks/T008-audit-pipeline-integration.md +0 -24
  133. package/.savepoint/releases/v1/epics/_archived/E08-board-workflow-cleanup/Design.md +0 -103
  134. package/.savepoint/releases/v1/epics/_archived/E08-board-workflow-cleanup/tasks/T001-acceptance-criteria-model.md +0 -30
  135. package/.savepoint/releases/v1/epics/_archived/E08-board-workflow-cleanup/tasks/T002-release-task-set-reader.md +0 -33
  136. package/.savepoint/releases/v1/epics/_archived/E08-board-workflow-cleanup/tasks/T003-board-data-and-plain-output.md +0 -34
  137. package/.savepoint/releases/v1/epics/_archived/E08-board-workflow-cleanup/tasks/T004-board-selection-state.md +0 -33
  138. package/.savepoint/releases/v1/epics/_archived/E08-board-workflow-cleanup/tasks/T005-ink-board-layout-cleanup.md +0 -37
  139. package/.savepoint/releases/v1/epics/_archived/E08-board-workflow-cleanup/tasks/T006-task-detail-popup.md +0 -36
  140. package/.savepoint/releases/v1/epics/_archived/E08-board-workflow-cleanup/tasks/T007-templates-acceptance-criteria.md +0 -34
  141. package/.savepoint/releases/v1/epics/_archived/E08-board-workflow-cleanup/tasks/T008-board-workflow-integration.md +0 -41
  142. package/.savepoint/releases/v1/epics/_archived/E09-doctor-command/Design.md +0 -70
  143. package/.savepoint/releases/v1/epics/_archived/E10-docs-and-packaging/Design.md +0 -68
  144. package/.savepoint/releases/v1/epics/_archived/E11-release-validation/Design.md +0 -68
  145. package/.savepoint/releases/v1/v1-PRD.md +0 -66
  146. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/E01-Detail.md +0 -40
  147. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T001-next-activity-header.md +0 -56
  148. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T002-rename-epic-design-files.md +0 -38
  149. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T003-rename-release-prd.md +0 -28
  150. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T004-update-instruction-files.md +0 -51
  151. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T005-update-cross-references.md +0 -45
  152. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T006-column-and-detail-scrolling.md +0 -68
  153. package/.savepoint/releases/v1.1/epics/E01-tui-optimisation/tasks/T007-column-focus-border-stability.md +0 -57
  154. package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/E02-Audit.md +0 -124
  155. package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/E02-Detail.md +0 -49
  156. package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/tasks/T001-fix-makefile.md +0 -37
  157. package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/tasks/T002-linux-build-target.md +0 -38
  158. package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/tasks/T003-macos-build-target.md +0 -36
  159. package/.savepoint/releases/v1.1/epics/E02-cross-platform-compatibility/tasks/T004-smoke-tests-and-artifacts.md +0 -59
  160. package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/E03-Audit.md +0 -195
  161. package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/E03-Detail.md +0 -45
  162. package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/tasks/T001-border-resize-fix.md +0 -40
  163. package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/tasks/T002-next-activity-below-header.md +0 -64
  164. package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/tasks/T003-checkbox-rendering-fix.md +0 -56
  165. package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/tasks/T005-unify-status-glyphs.md +0 -65
  166. package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/tasks/T006-forced-256-color-profile.md +0 -36
  167. package/.savepoint/releases/v1.1/epics/E04-epic-navigation/E04-Audit.md +0 -167
  168. package/.savepoint/releases/v1.1/epics/E04-epic-navigation/E04-Detail.md +0 -51
  169. package/.savepoint/releases/v1.1/epics/E04-epic-navigation/tasks/T001-sidebar-focusable-navigation.md +0 -65
  170. package/.savepoint/releases/v1.1/epics/E04-epic-navigation/tasks/T002-epic-detail-overlay.md +0 -73
  171. package/.savepoint/releases/v1.1/epics/E04-epic-navigation/tasks/T003-epic-status-glyphs.md +0 -73
  172. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/E05-Audit.md +0 -237
  173. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/E05-Detail.md +0 -54
  174. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T001-update-agents-md.md +0 -45
  175. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T002-update-router-md.md +0 -40
  176. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T003-update-design-md.md +0 -47
  177. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T004-implement-m-hotkey.md +0 -98
  178. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T005-update-help-overlay.md +0 -33
  179. package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T006-tests-and-quality-gates.md +0 -62
  180. package/.savepoint/releases/v1.1/epics/E06-audit-command/E06-Audit.md +0 -56
  181. package/.savepoint/releases/v1.1/epics/E06-audit-command/E06-Detail.md +0 -63
  182. package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T005-proposals.md +0 -44
  183. package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T007-apply-close.md +0 -35
  184. package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T009-integration.md +0 -40
  185. package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T010-audit-file-migration.md +0 -45
  186. package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T011-model-tab-state.md +0 -26
  187. package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T012-epic-audit-render.md +0 -33
  188. package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T013-handle-tab-keys.md +0 -34
  189. package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T014-tab-indicator.md +0 -33
  190. package/.savepoint/releases/v1.1/epics/E07-init-command/E07-Audit.md +0 -336
  191. package/.savepoint/releases/v1.1/epics/E07-init-command/E07-Detail.md +0 -61
  192. package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T001-cli-entrypoint.md +0 -37
  193. package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T002-target-validation.md +0 -28
  194. package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T003-scaffold-writer.md +0 -46
  195. package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T004-atomic-writes.md +0 -27
  196. package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T005-magic-prompt.md +0 -25
  197. package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T006-clipboard.md +0 -26
  198. package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T007-integration-test.md +0 -26
  199. package/.savepoint/releases/v1.1/epics/E08-board-command/E08-Audit.md +0 -333
  200. package/.savepoint/releases/v1.1/epics/E08-board-command/E08-Detail.md +0 -68
  201. package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T001-cli-entrypoint.md +0 -26
  202. package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T002-non-tty-fallback.md +0 -27
  203. package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T003-tui-app-shell.md +0 -28
  204. package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T004-board-model.md +0 -29
  205. package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T005-detail-pane.md +0 -27
  206. package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T006-status-transitions.md +0 -29
  207. package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T007-theme-fallbacks.md +0 -29
  208. package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T008-integration-test.md +0 -27
  209. package/.savepoint/releases/v1.1/epics/E09-doctor-command/E09-Audit.md +0 -207
  210. package/.savepoint/releases/v1.1/epics/E09-doctor-command/E09-Detail.md +0 -65
  211. package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T001-cli-entrypoint.md +0 -24
  212. package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T002-config-router-validation.md +0 -28
  213. package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T003-structure-checks.md +0 -29
  214. package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T004-dependency-checks.md +0 -27
  215. package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T005-audit-orphan-checks.md +0 -28
  216. package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T006-quality-gates-report.md +0 -31
  217. package/.savepoint/releases/v1.1/epics/E11-board-refresh-fix/E11-Detail.md +0 -36
  218. package/.savepoint/releases/v1.1/epics/E11-board-refresh-fix/tasks/T001-debug-logging.md +0 -25
  219. package/.savepoint/releases/v1.1/epics/E11-board-refresh-fix/tasks/T002-increase-debounce.md +0 -21
  220. package/.savepoint/releases/v1.1/epics/E11-board-refresh-fix/tasks/T003-error-handling.md +0 -22
  221. package/.savepoint/releases/v1.1/epics/E11-board-refresh-fix/tasks/T004-test-verify.md +0 -29
  222. package/.savepoint/releases/v1.1/epics/E12-validation-fix/E12-Audit.md +0 -444
  223. package/.savepoint/releases/v1.1/epics/E12-validation-fix/E12-Detail.md +0 -45
  224. package/.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T001-default-phase.md +0 -35
  225. package/.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T002-default-status.md +0 -19
  226. package/.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T003-better-errors.md +0 -29
  227. package/.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T004-validate-on-write.md +0 -25
  228. package/.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T005-tests.md +0 -37
  229. package/.savepoint/releases/v1.1/epics/E13-audit-remediation/E13-Audit.md +0 -118
  230. package/.savepoint/releases/v1.1/epics/E13-audit-remediation/E13-Detail.md +0 -73
  231. package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T001-safe-cleanup.md +0 -66
  232. package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T002-bug-fixes.md +0 -35
  233. package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T003-centralize-duplication.md +0 -60
  234. package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T004-infrastructure.md +0 -33
  235. package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T005-decompose-update.md +0 -37
  236. package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T006-async-io.md +0 -40
  237. package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T007-test-coverage.md +0 -37
  238. package/.savepoint/releases/v1.1/epics/E14-structural-improvements/E14-Audit.md +0 -267
  239. package/.savepoint/releases/v1.1/epics/E14-structural-improvements/E14-Detail.md +0 -54
  240. package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T001-group-model.md +0 -39
  241. package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T002-data-interfaces.md +0 -42
  242. package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T003-discover-orphans.md +0 -33
  243. package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T004-epic-panel-headings.md +0 -35
  244. package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T005-shell-tokenization.md +0 -27
  245. package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T006-unify-enums.md +0 -29
  246. package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T007-testutil-package.md +0 -28
  247. package/.savepoint/releases/v1.1/epics/E15-hardening/E15-Audit.md +0 -272
  248. package/.savepoint/releases/v1.1/epics/E15-hardening/E15-Detail.md +0 -60
  249. package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T001-benchmarks.md +0 -31
  250. package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T002-fuzz-targets.md +0 -34
  251. package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T003-debug-flag.md +0 -30
  252. package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T004-dist-checksums.md +0 -27
  253. package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T005-windows-targets.md +0 -28
  254. package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T006-abbreviation-splitting.md +0 -26
  255. package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T007-root-test-allowlist.md +0 -33
  256. package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T008-ci-and-release-automation.md +0 -46
  257. package/.savepoint/releases/v1.1/epics/_archived/T001-cli-entrypoint.md +0 -25
  258. package/.savepoint/releases/v1.1/epics/_archived/T002-quality-gates.md +0 -27
  259. package/.savepoint/releases/v1.1/epics/_archived/T003-snapshot.md +0 -27
  260. package/.savepoint/releases/v1.1/epics/_archived/T004-ai-reconcile.md +0 -29
  261. package/.savepoint/releases/v1.1/epics/_archived/T006-tui-review.md +0 -31
  262. package/.savepoint/releases/v1.1/epics/_archived/T008-skip-handling.md +0 -34
  263. package/.savepoint/releases/v1.1/v1.1-PRD.md +0 -139
  264. package/.savepoint/router.md +0 -57
  265. package/.savepoint/visual-identity.md +0 -125
  266. package/AGENTS.md +0 -99
  267. package/CLAUDE.md +0 -1
  268. package/GEMINI.md +0 -1
  269. package/Makefile +0 -31
  270. package/agent-skills/ink-tui-design/SKILL.md +0 -309
  271. package/agent-skills/ink-tui-design/references/component-patterns.md +0 -371
  272. package/agent-skills/ink-tui-design/references/hooks-guide.md +0 -436
  273. package/agent-skills/ink-tui-design/references/ink-gotchas.md +0 -330
  274. package/agent-skills/ink-tui-design/references/testing-patterns.md +0 -384
  275. package/agent-skills/savepoint-audit/SKILL.md +0 -87
  276. package/agent-skills/savepoint-build-task/SKILL.md +0 -44
  277. package/agent-skills/savepoint-create-plan/SKILL.md +0 -33
  278. package/agent-skills/savepoint-create-task/SKILL.md +0 -44
  279. package/agent-skills/savepoint-draft-prd/SKILL.md +0 -37
  280. package/agent-skills/savepoint-system-design/SKILL.md +0 -38
  281. package/agent-skills/superpowers/brainstorming/SKILL.md +0 -165
  282. package/agent-skills/superpowers/brainstorming/visual-companion.md +0 -304
  283. package/agent-skills/superpowers/dispatching-parallel-agents/SKILL.md +0 -193
  284. package/agent-skills/superpowers/executing-plans/SKILL.md +0 -77
  285. package/agent-skills/superpowers/finishing-a-development-branch/SKILL.md +0 -213
  286. package/agent-skills/superpowers/receiving-code-review/SKILL.md +0 -226
  287. package/agent-skills/superpowers/requesting-code-review/SKILL.md +0 -115
  288. package/agent-skills/superpowers/requesting-code-review/code-reviewer.md +0 -160
  289. package/agent-skills/superpowers/subagent-driven-development/SKILL.md +0 -292
  290. package/agent-skills/superpowers/subagent-driven-development/code-quality-reviewer-prompt.md +0 -27
  291. package/agent-skills/superpowers/subagent-driven-development/implementer-prompt.md +0 -113
  292. package/agent-skills/superpowers/subagent-driven-development/spec-reviewer-prompt.md +0 -61
  293. package/agent-skills/superpowers/systematic-debugging/SKILL.md +0 -305
  294. package/agent-skills/superpowers/systematic-debugging/condition-based-waiting.md +0 -122
  295. package/agent-skills/superpowers/systematic-debugging/defense-in-depth.md +0 -130
  296. package/agent-skills/superpowers/systematic-debugging/root-cause-tracing.md +0 -183
  297. package/agent-skills/superpowers/test-driven-development/SKILL.md +0 -389
  298. package/agent-skills/superpowers/test-driven-development/testing-anti-patterns.md +0 -317
  299. package/agent-skills/superpowers/verification-before-completion/SKILL.md +0 -147
  300. package/agent-skills/superpowers/writing-plans/SKILL.md +0 -159
  301. package/agent-skills/superpowers/writing-plans/plan-document-reviewer-prompt.md +0 -49
  302. package/agent_skills_test.go +0 -91
  303. package/assets/banner.png +0 -0
  304. package/assets/logo.png +0 -0
  305. package/assets/strawman.png +0 -0
  306. package/cmd/board.go +0 -59
  307. package/cmd/board_test.go +0 -137
  308. package/cmd/doctor.go +0 -53
  309. package/cmd/doctor_test.go +0 -146
  310. package/cmd/init.go +0 -63
  311. package/cmd/init_test.go +0 -104
  312. package/go.mod +0 -36
  313. package/go.sum +0 -75
  314. package/internal/board/board.go +0 -181
  315. package/internal/board/board_test.go +0 -168
  316. package/internal/board/card.go +0 -129
  317. package/internal/board/card_test.go +0 -287
  318. package/internal/board/column.go +0 -156
  319. package/internal/board/column_test.go +0 -210
  320. package/internal/board/debug.go +0 -26
  321. package/internal/board/debug_test.go +0 -108
  322. package/internal/board/detail.go +0 -218
  323. package/internal/board/detail_test.go +0 -388
  324. package/internal/board/epic_panel.go +0 -264
  325. package/internal/board/epic_panel_test.go +0 -869
  326. package/internal/board/help.go +0 -41
  327. package/internal/board/help_test.go +0 -86
  328. package/internal/board/integration_test.go +0 -266
  329. package/internal/board/interfaces.go +0 -65
  330. package/internal/board/interfaces_test.go +0 -114
  331. package/internal/board/io.go +0 -93
  332. package/internal/board/layout.go +0 -68
  333. package/internal/board/layout_test.go +0 -106
  334. package/internal/board/model.go +0 -235
  335. package/internal/board/model_test.go +0 -67
  336. package/internal/board/plain.go +0 -88
  337. package/internal/board/plain_test.go +0 -117
  338. package/internal/board/release.go +0 -34
  339. package/internal/board/release_test.go +0 -177
  340. package/internal/board/render_policy_test.go +0 -77
  341. package/internal/board/status.go +0 -23
  342. package/internal/board/theme.go +0 -24
  343. package/internal/board/theme_test.go +0 -31
  344. package/internal/board/transitions.go +0 -113
  345. package/internal/board/transitions_test.go +0 -164
  346. package/internal/board/tui.go +0 -32
  347. package/internal/board/update.go +0 -575
  348. package/internal/board/update_test.go +0 -602
  349. package/internal/board/util.go +0 -76
  350. package/internal/board/view.go +0 -317
  351. package/internal/board/view_test.go +0 -377
  352. package/internal/board/watch.go +0 -136
  353. package/internal/buildtool/main.go +0 -249
  354. package/internal/buildtool/main_test.go +0 -224
  355. package/internal/data/config.go +0 -101
  356. package/internal/data/config_test.go +0 -122
  357. package/internal/data/discover.go +0 -178
  358. package/internal/data/discover_test.go +0 -130
  359. package/internal/data/errors.go +0 -13
  360. package/internal/data/fuzz_test.go +0 -75
  361. package/internal/data/lifecycle.go +0 -44
  362. package/internal/data/lifecycle_test.go +0 -41
  363. package/internal/data/parser.go +0 -243
  364. package/internal/data/parser_test.go +0 -281
  365. package/internal/data/router.go +0 -52
  366. package/internal/data/router_test.go +0 -35
  367. package/internal/data/task.go +0 -57
  368. package/internal/data/task_test.go +0 -51
  369. package/internal/data/testdata/fuzz/FuzzSplitFrontmatterBody/68eb66b0fe91e7e3 +0 -2
  370. package/internal/data/write.go +0 -221
  371. package/internal/data/write_test.go +0 -623
  372. package/internal/doctor/checks.go +0 -567
  373. package/internal/doctor/checks_test.go +0 -716
  374. package/internal/doctor/gates.go +0 -193
  375. package/internal/doctor/gates_test.go +0 -166
  376. package/internal/doctor/interfaces.go +0 -64
  377. package/internal/doctor/interfaces_test.go +0 -104
  378. package/internal/doctor/repairs.go +0 -80
  379. package/internal/doctor/repairs_test.go +0 -81
  380. package/internal/doctor/report.go +0 -157
  381. package/internal/doctor/report_test.go +0 -89
  382. package/internal/init/clipboard.go +0 -146
  383. package/internal/init/clipboard_test.go +0 -74
  384. package/internal/init/install.go +0 -16
  385. package/internal/init/integration_test.go +0 -197
  386. package/internal/init/prompt.go +0 -14
  387. package/internal/init/prompt_test.go +0 -77
  388. package/internal/init/scaffold.go +0 -59
  389. package/internal/init/scaffold_test.go +0 -179
  390. package/internal/init/template_freshness_test.go +0 -56
  391. package/internal/init/validate.go +0 -85
  392. package/internal/init/validate_test.go +0 -141
  393. package/internal/init/write.go +0 -73
  394. package/internal/init/write_test.go +0 -91
  395. package/internal/styles/palette.go +0 -49
  396. package/internal/styles/styles.go +0 -139
  397. package/internal/styles/styles_test.go +0 -133
  398. package/internal/testutil/fixture.go +0 -113
  399. package/internal/testutil/fs.go +0 -26
  400. package/main.go +0 -136
  401. package/project-audit/audit_report_glm_5.1.md +0 -411
  402. package/project-audit/audit_report_opus_4.6.md +0 -406
  403. package/project-audit/consolidated-audit-report.md +0 -456
  404. package/scripts/vitest-preload.cjs +0 -95
  405. package/templates/project/.savepoint/Design.md +0 -47
  406. package/templates/project/.savepoint/PRD.md +0 -34
  407. package/templates/project/.savepoint/config.yml +0 -27
  408. package/templates/project/.savepoint/router.md +0 -153
  409. package/templates/project/.savepoint/visual-identity.md +0 -122
  410. package/templates/project/AGENTS.md +0 -88
  411. package/templates/project/agent-skills/savepoint-audit/SKILL.md +0 -87
  412. package/templates/project/agent-skills/savepoint-build-task/SKILL.md +0 -44
  413. package/templates/project/agent-skills/savepoint-create-plan/SKILL.md +0 -33
  414. package/templates/project/agent-skills/savepoint-create-task/SKILL.md +0 -44
  415. package/templates/project/agent-skills/savepoint-draft-prd/SKILL.md +0 -37
  416. package/templates/project/agent-skills/savepoint-system-design/SKILL.md +0 -38
  417. package/templates/prompts/audit-reconciliation.prompt.md +0 -72
  418. package/templates/prompts/design.prompt.md +0 -45
  419. package/templates/prompts/epic-design.prompt.md +0 -43
  420. package/templates/prompts/magic-prompt.prompt.md +0 -7
  421. package/templates/prompts/prd.prompt.md +0 -42
  422. package/templates/prompts/task-breakdown.prompt.md +0 -54
  423. package/templates/prompts/task-building.prompt.md +0 -38
  424. package/templates/prompts/task-planning.prompt.md +0 -53
  425. package/templates/release/v1/PRD.md +0 -37
@@ -1,869 +0,0 @@
1
- package board
2
-
3
- import (
4
- "os"
5
- "path/filepath"
6
- "strings"
7
- "testing"
8
-
9
- tea "github.com/charmbracelet/bubbletea"
10
- "github.com/opencode/savepoint/internal/data"
11
- )
12
-
13
- func TestRenderEpicSidebar_containsEpicsHeader(t *testing.T) {
14
- got := RenderEpicSidebar([]string{"E01", "E02"}, "E01", 28, false, 0, nil, 999)
15
- if !strings.Contains(got, "EPICS") {
16
- t.Error("RenderEpicSidebar missing EPICS header")
17
- }
18
- }
19
-
20
- func TestRenderEpicSidebar_activeEpicMarked(t *testing.T) {
21
- got := RenderEpicSidebar([]string{"E01", "E02"}, "E01", 28, false, 0, nil, 999)
22
- if !strings.Contains(got, epicActiveMarker) {
23
- t.Errorf("RenderEpicSidebar missing active marker %q", epicActiveMarker)
24
- }
25
- }
26
-
27
- func TestRenderEpicSidebar_focusedCursorMarked(t *testing.T) {
28
- got := RenderEpicSidebar([]string{"E01", "E02"}, "E01", 28, true, 1, nil, 999)
29
- if !strings.Contains(got, epicActiveMarker+" E02") {
30
- t.Errorf("RenderEpicSidebar focused cursor missing marker, got %q", got)
31
- }
32
- }
33
-
34
- func TestRenderEpicSidebar_allEpicsPresent(t *testing.T) {
35
- epics := []string{"E01-foo", "E02-bar", "E03-baz"}
36
- got := RenderEpicSidebar(epics, "E01-foo", 32, false, 0, nil, 999)
37
- for _, e := range epics {
38
- if !strings.Contains(got, e) {
39
- t.Errorf("RenderEpicSidebar missing epic %q", e)
40
- }
41
- }
42
- }
43
-
44
- func TestRenderEpicSidebar_emptyEpicsFallback(t *testing.T) {
45
- got := RenderEpicSidebar(nil, "E03", 28, false, 0, nil, 999)
46
- if !strings.Contains(got, "E03") {
47
- t.Error("RenderEpicSidebar with empty list should show selected epic")
48
- }
49
- }
50
-
51
- func TestRenderEpicSidebar_emptyBothShowsNone(t *testing.T) {
52
- got := RenderEpicSidebar(nil, "", 28, false, 0, nil, 999)
53
- if !strings.Contains(got, "(none)") {
54
- t.Error("RenderEpicSidebar with no epics and no selected should show (none)")
55
- }
56
- }
57
-
58
- func TestRenderEpicDropdown_containsHeader(t *testing.T) {
59
- got := RenderEpicDropdown([]string{"E01", "E02"}, 0, 32)
60
- if !strings.Contains(got, "SELECT EPIC") {
61
- t.Error("RenderEpicDropdown missing SELECT EPIC header")
62
- }
63
- }
64
-
65
- func TestRenderEpicDropdown_cursorMarked(t *testing.T) {
66
- got := RenderEpicDropdown([]string{"E01", "E02"}, 1, 32)
67
- if !strings.Contains(got, epicActiveMarker) {
68
- t.Errorf("RenderEpicDropdown missing cursor marker %q", epicActiveMarker)
69
- }
70
- }
71
-
72
- func TestRenderEpicDropdown_containsHint(t *testing.T) {
73
- got := RenderEpicDropdown([]string{"E01"}, 0, 32)
74
- if !strings.Contains(got, "esc") {
75
- t.Error("RenderEpicDropdown missing esc hint")
76
- }
77
- }
78
-
79
- func TestRenderEpicDropdown_emptyShowsNone(t *testing.T) {
80
- got := RenderEpicDropdown(nil, 0, 32)
81
- if !strings.Contains(got, "(none)") {
82
- t.Error("RenderEpicDropdown with no epics should show (none)")
83
- }
84
- }
85
-
86
- func TestSliceIndex_found(t *testing.T) {
87
- epics := []string{"E01", "E02", "E03"}
88
- if got := sliceIndex(epics, "E02"); got != 1 {
89
- t.Errorf("sliceIndex = %d, want 1", got)
90
- }
91
- }
92
-
93
- func TestSliceIndex_notFound(t *testing.T) {
94
- if got := sliceIndex([]string{"E01"}, "E99"); got != 0 {
95
- t.Errorf("sliceIndex not-found = %d, want 0", got)
96
- }
97
- }
98
-
99
- func TestSliceIndex_empty(t *testing.T) {
100
- if got := sliceIndex(nil, "E01"); got != 0 {
101
- t.Errorf("sliceIndex empty = %d, want 0", got)
102
- }
103
- }
104
-
105
- // Update integration tests for epic dropdown
106
-
107
- func TestUpdate_eKeyOpensDropdownNarrow(t *testing.T) {
108
- m := NewModel(nil, "v1", "E03")
109
- m.Width = 80 // narrow: < 120
110
- m.Epics = []string{"E01", "E03"}
111
- got, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("e")})
112
- updated := requireModel(t, got)
113
- if updated.Overlay != OverlayEpic {
114
- t.Errorf("Overlay = %q, want %q", updated.Overlay, OverlayEpic)
115
- }
116
- }
117
-
118
- func TestUpdate_eKeyOpensDropdownWide(t *testing.T) {
119
- m := NewModel(nil, "v1", "E03")
120
- m.Width = 120
121
- m.Epics = []string{"E01", "E03"}
122
- got, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("e")})
123
- updated := requireModel(t, got)
124
- if updated.Overlay != OverlayEpic {
125
- t.Errorf("Overlay = %q, want %q", updated.Overlay, OverlayEpic)
126
- }
127
- }
128
-
129
- func TestUpdate_epicDropdownEscCloses(t *testing.T) {
130
- m := NewModel(nil, "v1", "E03")
131
- m.Overlay = OverlayEpic
132
- got, _ := m.Update(tea.KeyMsg{Type: tea.KeyEsc})
133
- updated := requireModel(t, got)
134
- if updated.Overlay != OverlayNone {
135
- t.Errorf("Overlay = %q after esc, want none", updated.Overlay)
136
- }
137
- }
138
-
139
- func TestUpdate_epicDropdownDownMovesCursor(t *testing.T) {
140
- m := NewModel(nil, "v1", "E01")
141
- m.Overlay = OverlayEpic
142
- m.Epics = []string{"E01", "E02", "E03"}
143
- m.EpicCursor = 0
144
- got, _ := m.Update(tea.KeyMsg{Type: tea.KeyDown})
145
- updated := requireModel(t, got)
146
- if updated.EpicCursor != 1 {
147
- t.Errorf("EpicCursor = %d, want 1", updated.EpicCursor)
148
- }
149
- }
150
-
151
- func TestUpdate_epicDropdownUpMovesCursor(t *testing.T) {
152
- m := NewModel(nil, "v1", "E02")
153
- m.Overlay = OverlayEpic
154
- m.Epics = []string{"E01", "E02", "E03"}
155
- m.EpicCursor = 2
156
- got, _ := m.Update(tea.KeyMsg{Type: tea.KeyUp})
157
- updated := requireModel(t, got)
158
- if updated.EpicCursor != 1 {
159
- t.Errorf("EpicCursor = %d, want 1", updated.EpicCursor)
160
- }
161
- }
162
-
163
- func TestUpdate_epicDropdownEnterSelectsEpic(t *testing.T) {
164
- tasks := []data.Task{
165
- {ID: "T1", Epic: "E01", Release: "v1", Column: data.ColumnPlanned},
166
- {ID: "T3", Epic: "E03", Release: "v1", Column: data.ColumnPlanned},
167
- }
168
- m := NewModel(tasks, "v1", "E01")
169
- m.Overlay = OverlayEpic
170
- m.Epics = []string{"E01", "E02", "E03"}
171
- m.EpicCursor = 2
172
- got, _ := m.Update(tea.KeyMsg{Type: tea.KeyEnter})
173
- updated := requireModel(t, got)
174
- if updated.SelectedEpic != "E03" {
175
- t.Errorf("SelectedEpic = %q, want %q", updated.SelectedEpic, "E03")
176
- }
177
- if updated.Overlay != OverlayNone {
178
- t.Errorf("Overlay = %q after enter, want none", updated.Overlay)
179
- }
180
- if got := len(updated.Tasks[data.ColumnPlanned]); got != 1 {
181
- t.Errorf("planned task count = %d, want 1 after epic selection", got)
182
- }
183
- if updated.Tasks[data.ColumnPlanned][0].ID != "T3" {
184
- t.Errorf("visible task = %q, want T3", updated.Tasks[data.ColumnPlanned][0].ID)
185
- }
186
- }
187
-
188
- func TestUpdate_epicDropdownDownClampedAtEnd(t *testing.T) {
189
- m := NewModel(nil, "v1", "E03")
190
- m.Overlay = OverlayEpic
191
- m.Epics = []string{"E01", "E02"}
192
- m.EpicCursor = 1
193
- got, _ := m.Update(tea.KeyMsg{Type: tea.KeyDown})
194
- updated := requireModel(t, got)
195
- if updated.EpicCursor != 1 {
196
- t.Errorf("EpicCursor = %d, want 1 (clamped)", updated.EpicCursor)
197
- }
198
- }
199
-
200
- func TestUpdate_epicDropdownUpClampedAtStart(t *testing.T) {
201
- m := NewModel(nil, "v1", "E01")
202
- m.Overlay = OverlayEpic
203
- m.Epics = []string{"E01", "E02"}
204
- m.EpicCursor = 0
205
- got, _ := m.Update(tea.KeyMsg{Type: tea.KeyUp})
206
- updated := requireModel(t, got)
207
- if updated.EpicCursor != 0 {
208
- t.Errorf("EpicCursor = %d, want 0 (clamped)", updated.EpicCursor)
209
- }
210
- }
211
-
212
- func TestUpdate_overlayBlocksColumnNav(t *testing.T) {
213
- m := NewModel(nil, "v1", "E01")
214
- m.Overlay = OverlayEpic
215
- m.FocusedColumn = "planned"
216
- got, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("l")})
217
- updated := requireModel(t, got)
218
- if updated.FocusedColumn != "planned" {
219
- t.Error("column nav should be blocked when overlay is open")
220
- }
221
- }
222
-
223
- func TestUpdate_leftFromPlannedFocusesEpicPanelWide(t *testing.T) {
224
- m := NewModel(nil, "v1", "E02")
225
- m.Width = 120
226
- m.Epics = []string{"E01", "E02", "E03"}
227
-
228
- got, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("h")})
229
- updated := requireModel(t, got)
230
-
231
- if !updated.EpicPanelFocus {
232
- t.Fatal("EpicPanelFocus = false, want true")
233
- }
234
- if updated.EpicPanelCursor != 1 {
235
- t.Errorf("EpicPanelCursor = %d, want 1", updated.EpicPanelCursor)
236
- }
237
- if updated.FocusedColumn != data.ColumnPlanned {
238
- t.Errorf("FocusedColumn = %q, want planned", updated.FocusedColumn)
239
- }
240
- }
241
-
242
- func TestUpdate_leftFromPlannedDoesNotFocusEpicPanelNarrow(t *testing.T) {
243
- m := NewModel(nil, "v1", "E02")
244
- m.Width = 100
245
- m.Epics = []string{"E01", "E02"}
246
-
247
- got, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("h")})
248
- updated := requireModel(t, got)
249
-
250
- if updated.EpicPanelFocus {
251
- t.Fatal("EpicPanelFocus = true, want false")
252
- }
253
- if updated.FocusedColumn != data.ColumnDone {
254
- t.Errorf("FocusedColumn = %q, want done", updated.FocusedColumn)
255
- }
256
- }
257
-
258
- func TestUpdate_leftFromPlannedDoesNotFocusEmptyEpicPanel(t *testing.T) {
259
- m := NewModel(nil, "v1", "")
260
- m.Width = 120
261
-
262
- got, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("h")})
263
- updated := requireModel(t, got)
264
-
265
- if updated.EpicPanelFocus {
266
- t.Fatal("EpicPanelFocus = true, want false with no epics")
267
- }
268
- if updated.EpicPanelCursor != 0 {
269
- t.Errorf("EpicPanelCursor = %d, want 0", updated.EpicPanelCursor)
270
- }
271
- }
272
-
273
- func TestUpdate_windowResizeClearsEpicPanelFocusWhenHidden(t *testing.T) {
274
- m := NewModel(nil, "v1", "E01")
275
- m.Width = 120
276
- m.Epics = []string{"E01"}
277
- m.EpicPanelFocus = true
278
-
279
- got, _ := m.Update(tea.WindowSizeMsg{Width: 100, Height: 24})
280
- updated := requireModel(t, got)
281
-
282
- if updated.EpicPanelFocus {
283
- t.Fatal("EpicPanelFocus = true, want false when panel is hidden")
284
- }
285
- }
286
-
287
- func TestUpdate_epicPanelDownUpClamped(t *testing.T) {
288
- m := NewModel(nil, "v1", "E01")
289
- m.Width = 120
290
- m.EpicPanelFocus = true
291
- m.Epics = []string{"E01", "E02"}
292
-
293
- got, _ := m.Update(tea.KeyMsg{Type: tea.KeyDown})
294
- updated := requireModel(t, got)
295
- if updated.EpicPanelCursor != 1 {
296
- t.Errorf("EpicPanelCursor after down = %d, want 1", updated.EpicPanelCursor)
297
- }
298
-
299
- got, _ = updated.Update(tea.KeyMsg{Type: tea.KeyDown})
300
- updated = requireModel(t, got)
301
- if updated.EpicPanelCursor != 1 {
302
- t.Errorf("EpicPanelCursor after clamped down = %d, want 1", updated.EpicPanelCursor)
303
- }
304
-
305
- got, _ = updated.Update(tea.KeyMsg{Type: tea.KeyUp})
306
- updated = requireModel(t, got)
307
- if updated.EpicPanelCursor != 0 {
308
- t.Errorf("EpicPanelCursor after up = %d, want 0", updated.EpicPanelCursor)
309
- }
310
- }
311
-
312
- func TestUpdate_epicPanelEnterOpensDetailOverlay(t *testing.T) {
313
- tasks := []data.Task{
314
- {ID: "T1", Epic: "E01", Release: "v1", Column: data.ColumnPlanned},
315
- {ID: "T2", Epic: "E02", Release: "v1", Column: data.ColumnPlanned},
316
- }
317
- m := NewModel(tasks, "v1", "E01")
318
- m.Width = 120
319
- m.Epics = []string{"E01", "E02"}
320
- m.EpicPanelFocus = true
321
- m.EpicPanelCursor = 1
322
- m.FocusedTask = 3
323
- m.DetailOffset = 2
324
-
325
- got, _ := m.Update(tea.KeyMsg{Type: tea.KeyEnter})
326
- updated := requireModel(t, got)
327
-
328
- if updated.Overlay != OverlayEpicDetail {
329
- t.Errorf("Overlay = %q, want %q", updated.Overlay, OverlayEpicDetail)
330
- }
331
- if updated.EpicDetailOffset != 0 {
332
- t.Errorf("EpicDetailOffset = %d, want 0", updated.EpicDetailOffset)
333
- }
334
- if !updated.EpicPanelFocus {
335
- t.Error("EpicPanelFocus should remain true after enter")
336
- }
337
- // SelectedEpic unchanged; Enter now opens detail, not selects
338
- if updated.SelectedEpic != "E01" {
339
- t.Errorf("SelectedEpic = %q, want E01 (unchanged)", updated.SelectedEpic)
340
- }
341
- }
342
-
343
- func TestUpdate_epicPanelRightReturnsToPlanned(t *testing.T) {
344
- m := NewModel(nil, "v1", "E01")
345
- m.Width = 120
346
- m.EpicPanelFocus = true
347
- m.Epics = []string{"E01"}
348
- m.FocusedColumn = data.ColumnDone
349
- m.FocusedTask = 2
350
-
351
- got, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("l")})
352
- updated := requireModel(t, got)
353
-
354
- if updated.EpicPanelFocus {
355
- t.Fatal("EpicPanelFocus = true, want false")
356
- }
357
- if updated.FocusedColumn != data.ColumnPlanned {
358
- t.Errorf("FocusedColumn = %q, want planned", updated.FocusedColumn)
359
- }
360
- if updated.FocusedTask != 0 {
361
- t.Errorf("FocusedTask = %d, want 0", updated.FocusedTask)
362
- }
363
- }
364
-
365
- func TestUpdate_overlayBlocksEpicPanelNav(t *testing.T) {
366
- m := NewModel(nil, "v1", "E01")
367
- m.Width = 120
368
- m.EpicPanelFocus = true
369
- m.Epics = []string{"E01", "E02"}
370
- m.Overlay = OverlayHelp
371
-
372
- got, _ := m.Update(tea.KeyMsg{Type: tea.KeyDown})
373
- updated := requireModel(t, got)
374
-
375
- if updated.EpicPanelCursor != 0 {
376
- t.Errorf("EpicPanelCursor = %d, want 0 while overlay open", updated.EpicPanelCursor)
377
- }
378
- }
379
-
380
- func TestUpdate_epicPanelFocusAllowsGlobalQuit(t *testing.T) {
381
- m := NewModel(nil, "v1", "E01")
382
- m.Width = 120
383
- m.EpicPanelFocus = true
384
- m.Epics = []string{"E01"}
385
-
386
- _, cmd := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("q")})
387
- if cmd == nil {
388
- t.Fatal("expected tea.Quit cmd from q while epic panel focused, got nil")
389
- }
390
- }
391
-
392
- func TestUpdate_epicPanelFocusAllowsEpicDropdown(t *testing.T) {
393
- m := NewModel(nil, "v1", "E02")
394
- m.Width = 120
395
- m.EpicPanelFocus = true
396
- m.Epics = []string{"E01", "E02"}
397
-
398
- got, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("e")})
399
- updated := requireModel(t, got)
400
-
401
- if updated.Overlay != OverlayEpic {
402
- t.Errorf("Overlay = %q, want %q", updated.Overlay, OverlayEpic)
403
- }
404
- if updated.EpicCursor != 1 {
405
- t.Errorf("EpicCursor = %d, want 1", updated.EpicCursor)
406
- }
407
- }
408
-
409
- func TestUpdate_epicPanelFocusAllowsReleaseDropdown(t *testing.T) {
410
- m := NewModel(nil, "v1", "E01")
411
- m.Width = 120
412
- m.EpicPanelFocus = true
413
- m.Epics = []string{"E01"}
414
- m.Releases = []string{"v1", "v1.1"}
415
- m.SelectedRelease = "v1.1"
416
-
417
- got, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("r")})
418
- updated := requireModel(t, got)
419
-
420
- if updated.Overlay != OverlayRelease {
421
- t.Errorf("Overlay = %q, want %q", updated.Overlay, OverlayRelease)
422
- }
423
- if updated.ReleaseCursor != 1 {
424
- t.Errorf("ReleaseCursor = %d, want 1", updated.ReleaseCursor)
425
- }
426
- }
427
-
428
- func TestView_epicDropdownOverlayRendered(t *testing.T) {
429
- m := NewModel(nil, "v1", "E01")
430
- m.Width = 80
431
- m.Height = 24
432
- m.Overlay = OverlayEpic
433
- m.Epics = []string{"E01", "E02"}
434
- got := m.View()
435
- if !strings.Contains(got, "SELECT EPIC") {
436
- t.Error("View() with OverlayEpic missing SELECT EPIC")
437
- }
438
- }
439
-
440
- func TestView_epicDropdownKeepsBoardBehind(t *testing.T) {
441
- m := NewModel(nil, "v1", "E01")
442
- m.Width = 100
443
- m.Height = 24
444
- m.Overlay = OverlayEpic
445
- m.Epics = []string{"E01", "E02"}
446
- got := m.View()
447
- if !strings.Contains(got, "S A V E P O I N T") {
448
- t.Error("View() with OverlayEpic should keep board visible behind overlay")
449
- }
450
- }
451
-
452
- func TestView_epicSidebarOnWide(t *testing.T) {
453
- m := NewModel(nil, "v1", "E03")
454
- m.Width = 120
455
- m.Epics = []string{"E01", "E03"}
456
- got := m.View()
457
- if !strings.Contains(got, "EPICS") {
458
- t.Error("View() at width>=120 missing EPICS header in sidebar")
459
- }
460
- }
461
-
462
- func TestUpdate_epicDetailOverlayEscCloses(t *testing.T) {
463
- m := NewModel(nil, "v1", "E01")
464
- m.Width = 120
465
- m.EpicPanelFocus = true
466
- m.Epics = []string{"E01"}
467
- m.Overlay = OverlayEpicDetail
468
- m.EpicDetailContent = "# E01 Detail"
469
- m.EpicDetailOffset = 3
470
-
471
- got, _ := m.Update(tea.KeyMsg{Type: tea.KeyEsc})
472
- updated := requireModel(t, got)
473
-
474
- if updated.Overlay != OverlayNone {
475
- t.Errorf("Overlay = %q, want none after esc", updated.Overlay)
476
- }
477
- if !updated.EpicPanelFocus {
478
- t.Error("EpicPanelFocus should remain true after closing detail overlay")
479
- }
480
- }
481
-
482
- func TestUpdate_epicDetailOverlayScrollUpDown(t *testing.T) {
483
- m := NewModel(nil, "v1", "E01")
484
- m.Width = 120
485
- m.EpicPanelFocus = true
486
- m.Epics = []string{"E01"}
487
- m.Overlay = OverlayEpicDetail
488
- m.EpicDetailOffset = 2
489
-
490
- got, _ := m.Update(tea.KeyMsg{Type: tea.KeyDown})
491
- updated := requireModel(t, got)
492
- if updated.EpicDetailOffset != 3 {
493
- t.Errorf("EpicDetailOffset after down = %d, want 3", updated.EpicDetailOffset)
494
- }
495
-
496
- got, _ = updated.Update(tea.KeyMsg{Type: tea.KeyUp})
497
- updated = requireModel(t, got)
498
- if updated.EpicDetailOffset != 2 {
499
- t.Errorf("EpicDetailOffset after up = %d, want 2", updated.EpicDetailOffset)
500
- }
501
-
502
- got, _ = updated.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("k")})
503
- updated = requireModel(t, got)
504
- if updated.EpicDetailOffset != 1 {
505
- t.Errorf("EpicDetailOffset after k = %d, want 1", updated.EpicDetailOffset)
506
- }
507
-
508
- // Clamp at 0
509
- updated.EpicDetailOffset = 0
510
- got, _ = updated.Update(tea.KeyMsg{Type: tea.KeyUp})
511
- updated = requireModel(t, got)
512
- if updated.EpicDetailOffset != 0 {
513
- t.Errorf("EpicDetailOffset should not go below 0, got %d", updated.EpicDetailOffset)
514
- }
515
- }
516
-
517
- func TestUpdate_epicDetailOverlayPgUpDown(t *testing.T) {
518
- m := NewModel(nil, "v1", "E01")
519
- m.Width = 120
520
- m.Height = 30
521
- m.Epics = []string{"E01"}
522
- m.Overlay = OverlayEpicDetail
523
- m.EpicDetailOffset = 20
524
-
525
- got, _ := m.Update(tea.KeyMsg{Type: tea.KeyPgUp})
526
- updated := requireModel(t, got)
527
- if updated.EpicDetailOffset >= 20 {
528
- t.Errorf("EpicDetailOffset after pgup = %d, should decrease from 20", updated.EpicDetailOffset)
529
- }
530
- if updated.EpicDetailOffset < 0 {
531
- t.Errorf("EpicDetailOffset = %d, should not go below 0", updated.EpicDetailOffset)
532
- }
533
-
534
- updated.EpicDetailOffset = 0
535
- got, _ = updated.Update(tea.KeyMsg{Type: tea.KeyPgDown})
536
- updated = requireModel(t, got)
537
- if updated.EpicDetailOffset <= 0 {
538
- t.Errorf("EpicDetailOffset after pgdown = %d, should increase from 0", updated.EpicDetailOffset)
539
- }
540
- }
541
-
542
- func TestView_epicDetailOverlayRendered(t *testing.T) {
543
- m := NewModel(nil, "v1", "E01")
544
- m.Width = 120
545
- m.Height = 30
546
- m.Epics = []string{"E01"}
547
- m.Overlay = OverlayEpicDetail
548
- m.EpicDetailContent = "# My Epic\n\n## Purpose\nDoes things."
549
-
550
- got := m.View()
551
- if !strings.Contains(got, "EPIC DETAIL") {
552
- t.Error("View() with OverlayEpicDetail missing EPIC DETAIL header")
553
- }
554
- }
555
-
556
- func TestView_epicDetailOverlayNoContent(t *testing.T) {
557
- m := NewModel(nil, "v1", "E01")
558
- m.Width = 120
559
- m.Height = 30
560
- m.Epics = []string{"E01"}
561
- m.Overlay = OverlayEpicDetail
562
- m.EpicDetailContent = "(no detail available)"
563
-
564
- got := m.View()
565
- if !strings.Contains(got, "no detail available") {
566
- t.Error("View() with missing epic detail should show 'no detail available'")
567
- }
568
- }
569
-
570
- func TestRenderEpicDetail_stripsMarkdownHeadings(t *testing.T) {
571
- content := "---\ntype: epic-design\n---\n# Epic E01\n\n## Purpose\nDoes things."
572
- got := RenderEpicDetail("E01-test", content, 60, 40, 0, 0)
573
- if !strings.Contains(got, "EPIC DETAIL") {
574
- t.Error("RenderEpicDetail missing EPIC DETAIL header")
575
- }
576
- if strings.Contains(got, "# Epic E01") {
577
- t.Error("RenderEpicDetail should strip raw markdown heading prefix")
578
- }
579
- }
580
-
581
- func TestRenderEpicDetail_noDetailFallback(t *testing.T) {
582
- got := RenderEpicDetail("E01-test", "(no detail available)", 60, 40, 0, 0)
583
- if !strings.Contains(got, "no detail available") {
584
- t.Error("RenderEpicDetail fallback message missing")
585
- }
586
- }
587
-
588
- func TestRenderEpicDetail_tabIndicatorDetailActive(t *testing.T) {
589
- got := RenderEpicDetail("E01-test", "content", 60, 40, 0, 0)
590
- if !strings.Contains(got, "DETAIL [1]") {
591
- t.Error("RenderEpicDetail tab=0: missing DETAIL [1] indicator")
592
- }
593
- if !strings.Contains(got, "AUDIT [2]") {
594
- t.Error("RenderEpicDetail tab=0: missing AUDIT [2] indicator")
595
- }
596
- }
597
-
598
- func TestRenderEpicDetail_tabIndicatorAuditActive(t *testing.T) {
599
- got := RenderEpicDetail("E01-test", "content", 60, 40, 0, 1)
600
- if !strings.Contains(got, "DETAIL [1]") {
601
- t.Error("RenderEpicDetail tab=1: missing DETAIL [1] indicator")
602
- }
603
- if !strings.Contains(got, "AUDIT [2]") {
604
- t.Error("RenderEpicDetail tab=1: missing AUDIT [2] indicator")
605
- }
606
- }
607
-
608
- func TestRenderEpicAuditTab_header(t *testing.T) {
609
- got := RenderEpicAuditTab("E06-test", "# Audit\n\n## Main Findings\nAll good.", 60, 40, 0, 1)
610
- if !strings.Contains(got, "EPIC AUDIT") {
611
- t.Error("RenderEpicAuditTab missing EPIC AUDIT header")
612
- }
613
- }
614
-
615
- func TestRenderEpicAuditTab_noContent(t *testing.T) {
616
- got := RenderEpicAuditTab("E06-test", "(no audit available)", 60, 40, 0, 1)
617
- if !strings.Contains(got, "no audit available") {
618
- t.Error("RenderEpicAuditTab fallback message missing")
619
- }
620
- }
621
-
622
- func TestRenderEpicAuditTab_emptyContent(t *testing.T) {
623
- got := RenderEpicAuditTab("E06-test", "", 60, 40, 0, 1)
624
- if !strings.Contains(got, "no audit available") {
625
- t.Error("RenderEpicAuditTab empty content should show fallback")
626
- }
627
- }
628
-
629
- func TestRenderEpicAuditTab_stripsFrontmatter(t *testing.T) {
630
- content := "---\ntype: audit\n---\n# E06 Audit\n\n## Main Findings\nLooks good."
631
- got := RenderEpicAuditTab("E06-test", content, 60, 40, 0, 1)
632
- if strings.Contains(got, "type: audit") {
633
- t.Error("RenderEpicAuditTab should strip frontmatter")
634
- }
635
- if !strings.Contains(got, "EPIC AUDIT") {
636
- t.Error("RenderEpicAuditTab missing header after frontmatter strip")
637
- }
638
- }
639
-
640
- func TestRenderEpicAuditTab_checkboxDonePresent(t *testing.T) {
641
- content := "## Code Style Review\n- [x] One job per file\n- [ ] One-sentence functions"
642
- got := RenderEpicAuditTab("E06-test", content, 60, 40, 0, 1)
643
- if !strings.Contains(got, "One job per file") {
644
- t.Error("RenderEpicAuditTab missing done checkbox text")
645
- }
646
- if !strings.Contains(got, "One-sentence functions") {
647
- t.Error("RenderEpicAuditTab missing undone checkbox text")
648
- }
649
- }
650
-
651
- func TestRenderEpicAuditTab_scrollFooter(t *testing.T) {
652
- got := RenderEpicAuditTab("E06-test", "# Audit", 60, 40, 0, 1)
653
- if !strings.Contains(got, "esc:close") {
654
- t.Error("RenderEpicAuditTab missing esc:close footer")
655
- }
656
- }
657
-
658
- func TestRenderEpicAuditTab_tabIndicator(t *testing.T) {
659
- got := RenderEpicAuditTab("E06-test", "# Audit", 60, 40, 0, 1)
660
- if !strings.Contains(got, "DETAIL [1]") {
661
- t.Error("RenderEpicAuditTab missing DETAIL [1] indicator")
662
- }
663
- if !strings.Contains(got, "AUDIT [2]") {
664
- t.Error("RenderEpicAuditTab missing AUDIT [2] indicator")
665
- }
666
- }
667
-
668
- func TestRenderEpicAuditTab_mainFindingsVisible(t *testing.T) {
669
- content := "## Main Findings\nAudit summary is visible.\n\n## Proposed Changes\n### Target File\nAGENTS.md\n"
670
- got := RenderEpicAuditTab("E06-test", content, 80, 50, 0, 1)
671
- if !strings.Contains(got, "Audit summary is visible") {
672
- t.Error("RenderEpicAuditTab should render Main Findings body")
673
- }
674
- if strings.Contains(got, "Target File") || strings.Contains(got, "AGENTS.md") {
675
- t.Error("RenderEpicAuditTab should not render Proposed Changes admin blocks")
676
- }
677
- }
678
-
679
- func TestRenderEpicAuditTab_qualityReviewHidden(t *testing.T) {
680
- content := "## Quality Review\nOld quality section.\n\n## Code Style Review\n- [ ] One job per file\n"
681
- got := RenderEpicAuditTab("E06-test", content, 80, 50, 0, 1)
682
- if strings.Contains(got, "Old quality section") {
683
- t.Error("RenderEpicAuditTab should not render superseded Quality Review section")
684
- }
685
- if !strings.Contains(got, "One job per file") {
686
- t.Error("RenderEpicAuditTab should render Code Style Review")
687
- }
688
- }
689
-
690
- func TestRenderEpicAuditTab_hiddenHeadingsRequireExactMatch(t *testing.T) {
691
- content := "## Proposed Changes Appendix\nNear-match section is visible.\n\n## Proposed Changes\nHidden admin section.\n"
692
- got := RenderEpicAuditTab("E06-test", content, 80, 50, 0, 1)
693
- if !strings.Contains(got, "Near-match section is visible") {
694
- t.Error("RenderEpicAuditTab should render headings that only partially match hidden headings")
695
- }
696
- if strings.Contains(got, "Hidden admin section") {
697
- t.Error("RenderEpicAuditTab should hide exact Proposed Changes section")
698
- }
699
- }
700
-
701
- func TestRenderEpicAuditTab_allCodeStyleRules(t *testing.T) {
702
- rules := []string{
703
- "One job per file",
704
- "One-sentence functions",
705
- "Test branches",
706
- "Types are documentation",
707
- "Build, don't speculate",
708
- "Errors at boundaries",
709
- "One source of truth",
710
- "Comments explain WHY",
711
- "Content in data files",
712
- "Small diffs",
713
- }
714
- content := "## Code Style Review\n"
715
- for _, r := range rules {
716
- content += "- [ ] " + r + "\n"
717
- }
718
- got := RenderEpicAuditTab("E06-test", content, 80, 50, 0, 1)
719
- for _, r := range rules {
720
- if !strings.Contains(got, r) {
721
- t.Errorf("RenderEpicAuditTab missing code style rule %q", r)
722
- }
723
- }
724
- }
725
-
726
- // TestView_epicAuditTabRendered verifies View() uses RenderEpicAuditTab when EpicDetailTab=1.
727
- func TestView_epicAuditTabRendered(t *testing.T) {
728
- m := NewModel(nil, "v1.1", "E06-audit-command")
729
- m.Width = 120
730
- m.Height = 30
731
- m.Epics = []string{"E06-audit-command"}
732
- m.Overlay = OverlayEpicDetail
733
- m.EpicDetailTab = 1
734
- m.EpicAuditContent = "# Audit Findings: E06\n\n## Main Findings\nAll good.\n\n## Code Style Review\n- [x] One job per file\n"
735
-
736
- got := m.View()
737
- if !strings.Contains(got, "EPIC AUDIT") {
738
- t.Error("View() with EpicDetailTab=1 missing EPIC AUDIT header")
739
- }
740
- if strings.Contains(got, "EPIC DETAIL") {
741
- t.Error("View() with EpicDetailTab=1 should not render EPIC DETAIL header")
742
- }
743
- }
744
-
745
- // TestAuditWorkflow_fullEndToEnd exercises the full audit workflow:
746
- // create E##-Audit.md on disk, open overlay, press 2, verify content loads and renders.
747
- func TestAuditWorkflow_fullEndToEnd(t *testing.T) {
748
- root := t.TempDir()
749
- epicSlug := "E06-audit-command"
750
- epicDir := filepath.Join(root, "releases", "v1.1", "epics", epicSlug)
751
- if err := os.MkdirAll(epicDir, 0755); err != nil {
752
- t.Fatal(err)
753
- }
754
-
755
- auditContent := `---
756
- type: audit-findings
757
- audited: 2026-05-03
758
- ---
759
- # Audit Findings: E06 Agent Audit + Audit Tab
760
-
761
- ## Main Findings
762
- All acceptance criteria met.
763
-
764
- ## Code Style Review
765
- - [x] One job per file
766
- - [x] One-sentence functions
767
- - [x] Test branches
768
- - [x] Types are documentation
769
- - [x] Build, don't speculate
770
- - [x] Errors at boundaries
771
- - [x] One source of truth
772
- - [x] Comments explain WHY
773
- - [x] Content in data files
774
- - [x] Small diffs
775
- `
776
- if err := os.WriteFile(filepath.Join(epicDir, "E06-Audit.md"), []byte(auditContent), 0644); err != nil {
777
- t.Fatal(err)
778
- }
779
-
780
- tasks := []data.Task{
781
- {ID: "E06-audit-command/T009-integration", Release: "v1.1", Epic: epicSlug, Column: data.ColumnPlanned},
782
- }
783
- m := NewModel(tasks, "v1.1", epicSlug)
784
- m.Root = root
785
- m.Epics = []string{epicSlug}
786
- m.EpicPanelCursor = 0
787
- m.Width = 120
788
- m.Height = 40
789
-
790
- // Open detail overlay (tab=0)
791
- m.openEpicDetailOverlay()
792
- if m.Overlay != OverlayEpicDetail {
793
- t.Fatal("overlay not opened")
794
- }
795
- if m.EpicDetailTab != 0 {
796
- t.Errorf("EpicDetailTab = %d, want 0 on open", m.EpicDetailTab)
797
- }
798
-
799
- // Press 2 → switch to audit tab, load content
800
- got, cmd := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("2")})
801
- updated := requireModel(t, got)
802
-
803
- if updated.EpicDetailTab != 1 {
804
- t.Errorf("EpicDetailTab = %d, want 1 after pressing 2", updated.EpicDetailTab)
805
- }
806
-
807
- msg := cmd()
808
- got2, _ := updated.Update(msg)
809
- updated2 := requireModel(t, got2)
810
- if updated2.EpicAuditContent == "" || updated2.EpicAuditContent == "(no audit available)" {
811
- t.Errorf("EpicAuditContent not loaded: %q", updated2.EpicAuditContent)
812
- }
813
-
814
- // Verify View() renders audit content
815
- view := updated2.View()
816
- if !strings.Contains(view, "EPIC AUDIT") {
817
- t.Error("View() after tab switch missing EPIC AUDIT")
818
- }
819
- if !strings.Contains(view, "One job per file") {
820
- t.Error("View() after tab switch missing code style rule")
821
- }
822
-
823
- // Press 1 → switch back to detail tab
824
- got, _ = updated2.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("1")})
825
- updated = requireModel(t, got)
826
- if updated.EpicDetailTab != 0 {
827
- t.Errorf("EpicDetailTab = %d, want 0 after pressing 1", updated.EpicDetailTab)
828
- }
829
-
830
- // Press esc → overlay closes
831
- got, _ = updated.Update(tea.KeyMsg{Type: tea.KeyEsc})
832
- updated = requireModel(t, got)
833
- if updated.Overlay != OverlayNone {
834
- t.Errorf("Overlay = %q, want none after esc", updated.Overlay)
835
- }
836
- }
837
-
838
- func TestRenderEpicAuditTab_v11AuditFiles(t *testing.T) {
839
- files := []struct {
840
- path string
841
- want string
842
- }{
843
- {filepath.Join("..", "..", ".savepoint", "releases", "v1.1", "epics", "E02-cross-platform-compatibility", "E02-Audit.md"), "cross-platform build work"},
844
- {filepath.Join("..", "..", ".savepoint", "releases", "v1.1", "epics", "E03-ui-visual-refinement", "E03-Audit.md"), "visual refinement work"},
845
- {filepath.Join("..", "..", ".savepoint", "releases", "v1.1", "epics", "E04-epic-navigation", "E04-Audit.md"), "wide-screen epic navigation"},
846
- {filepath.Join("..", "..", ".savepoint", "releases", "v1.1", "epics", "E05-tasking-permissions", "E05-Audit.md"), "tasking-permissions shift"},
847
- {filepath.Join("..", "..", ".savepoint", "releases", "v1.1", "epics", "E06-audit-command", "E06-Audit.md"), "agent-led"},
848
- }
849
-
850
- for _, tt := range files {
851
- content, err := os.ReadFile(tt.path)
852
- if err != nil {
853
- t.Fatalf("read %s: %v", tt.path, err)
854
- }
855
- if !strings.Contains(string(content), tt.want) {
856
- t.Fatalf("fixture %s missing %q", tt.path, tt.want)
857
- }
858
- got := RenderEpicAuditTab(filepath.Base(filepath.Dir(tt.path)), string(content), 80, 40, 0, 1)
859
- if !strings.Contains(got, tt.want) {
860
- t.Errorf("RenderEpicAuditTab(%s) missing %q", tt.path, tt.want)
861
- }
862
- if strings.Contains(got, "Target File") {
863
- t.Errorf("RenderEpicAuditTab(%s) should not render Proposed Changes", tt.path)
864
- }
865
- if strings.Contains(got, "Boundaries") || strings.Contains(got, "Implemented as") || strings.Contains(got, "Implemented As") {
866
- t.Errorf("RenderEpicAuditTab(%s) should only render visible audit sections", tt.path)
867
- }
868
- }
869
- }