savepoint 1.0.2 → 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.
- package/.claude/settings.local.json +12 -1
- package/.golangci.yml +11 -0
- package/.savepoint/Design.md +37 -36
- package/.savepoint/{audit/v1.1/E02-cross-platform-compatibility/proposals.md → releases/v1.1/epics/E02-cross-platform-compatibility/E02-Audit.md} +48 -38
- package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/E03-Audit.md +195 -0
- package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/E03-Detail.md +14 -1
- package/.savepoint/releases/v1.1/epics/E03-ui-visual-refinement/tasks/T006-forced-256-color-profile.md +3 -3
- package/.savepoint/{audit/v1.1/E04-epic-navigation/proposals.md → releases/v1.1/epics/E04-epic-navigation/E04-Audit.md} +65 -54
- package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/E05-Audit.md +237 -0
- package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/E05-Detail.md +25 -16
- package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T001-update-agents-md.md +17 -6
- package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T002-update-router-md.md +15 -5
- package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T003-update-design-md.md +19 -5
- package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T004-implement-m-hotkey.md +11 -1
- package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T005-update-help-overlay.md +9 -6
- package/.savepoint/releases/v1.1/epics/E05-tasking-permissions/tasks/T006-tests-and-quality-gates.md +29 -13
- package/.savepoint/releases/v1.1/epics/E06-audit-command/E06-Audit.md +56 -0
- package/.savepoint/releases/v1.1/epics/E06-audit-command/E06-Detail.md +63 -0
- package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T005-proposals.md +44 -0
- package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T007-apply-close.md +35 -0
- package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T009-integration.md +40 -0
- package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T010-audit-file-migration.md +45 -0
- package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T011-model-tab-state.md +26 -0
- package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T012-epic-audit-render.md +33 -0
- package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T013-handle-tab-keys.md +34 -0
- package/.savepoint/releases/v1.1/epics/E06-audit-command/tasks/T014-tab-indicator.md +33 -0
- package/.savepoint/releases/v1.1/epics/E07-init-command/E07-Audit.md +336 -0
- package/.savepoint/releases/v1.1/epics/E07-init-command/E07-Detail.md +61 -0
- package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T001-cli-entrypoint.md +37 -0
- package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T002-target-validation.md +28 -0
- package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T003-scaffold-writer.md +46 -0
- package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T004-atomic-writes.md +27 -0
- package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T005-magic-prompt.md +25 -0
- package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T006-clipboard.md +26 -0
- package/.savepoint/releases/v1.1/epics/E07-init-command/tasks/T007-integration-test.md +26 -0
- package/.savepoint/releases/v1.1/epics/E08-board-command/E08-Audit.md +333 -0
- package/.savepoint/releases/v1.1/epics/E08-board-command/E08-Detail.md +68 -0
- package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T001-cli-entrypoint.md +26 -0
- package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T002-non-tty-fallback.md +27 -0
- package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T003-tui-app-shell.md +28 -0
- package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T004-board-model.md +29 -0
- package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T005-detail-pane.md +27 -0
- package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T006-status-transitions.md +29 -0
- package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T007-theme-fallbacks.md +29 -0
- package/.savepoint/releases/v1.1/epics/E08-board-command/tasks/T008-integration-test.md +27 -0
- package/.savepoint/releases/v1.1/epics/E09-doctor-command/E09-Audit.md +207 -0
- package/.savepoint/releases/v1.1/epics/E09-doctor-command/E09-Detail.md +65 -0
- package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T001-cli-entrypoint.md +24 -0
- package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T002-config-router-validation.md +28 -0
- package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T003-structure-checks.md +29 -0
- package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T004-dependency-checks.md +27 -0
- package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T005-audit-orphan-checks.md +28 -0
- package/.savepoint/releases/v1.1/epics/E09-doctor-command/tasks/T006-quality-gates-report.md +31 -0
- package/.savepoint/releases/v1.1/epics/E11-board-refresh-fix/E11-Detail.md +36 -0
- package/.savepoint/releases/v1.1/epics/E11-board-refresh-fix/tasks/T001-debug-logging.md +25 -0
- package/.savepoint/releases/v1.1/epics/E11-board-refresh-fix/tasks/T002-increase-debounce.md +21 -0
- package/.savepoint/releases/v1.1/epics/E11-board-refresh-fix/tasks/T003-error-handling.md +22 -0
- package/.savepoint/releases/v1.1/epics/E11-board-refresh-fix/tasks/T004-test-verify.md +29 -0
- package/.savepoint/releases/v1.1/epics/E12-validation-fix/E12-Audit.md +444 -0
- package/.savepoint/releases/v1.1/epics/E12-validation-fix/E12-Detail.md +45 -0
- package/.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T001-default-phase.md +35 -0
- package/.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T002-default-status.md +19 -0
- package/.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T003-better-errors.md +29 -0
- package/.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T004-validate-on-write.md +25 -0
- package/.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T005-tests.md +37 -0
- package/.savepoint/releases/v1.1/epics/E13-audit-remediation/E13-Audit.md +118 -0
- package/.savepoint/releases/v1.1/epics/E13-audit-remediation/E13-Detail.md +73 -0
- package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T001-safe-cleanup.md +66 -0
- package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T002-bug-fixes.md +35 -0
- package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T003-centralize-duplication.md +60 -0
- package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T004-infrastructure.md +33 -0
- package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T005-decompose-update.md +37 -0
- package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T006-async-io.md +40 -0
- package/.savepoint/releases/v1.1/epics/E13-audit-remediation/tasks/T007-test-coverage.md +37 -0
- package/.savepoint/releases/v1.1/epics/E14-structural-improvements/E14-Audit.md +267 -0
- package/.savepoint/releases/v1.1/epics/E14-structural-improvements/E14-Detail.md +54 -0
- package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T001-group-model.md +39 -0
- package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T002-data-interfaces.md +42 -0
- package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T003-discover-orphans.md +33 -0
- package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T004-epic-panel-headings.md +35 -0
- package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T005-shell-tokenization.md +27 -0
- package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T006-unify-enums.md +29 -0
- package/.savepoint/releases/v1.1/epics/E14-structural-improvements/tasks/T007-testutil-package.md +28 -0
- package/.savepoint/releases/v1.1/epics/E15-hardening/E15-Detail.md +43 -0
- package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T001-benchmarks.md +31 -0
- package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T002-fuzz-targets.md +28 -0
- package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T003-debug-flag.md +30 -0
- package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T004-dist-checksums.md +27 -0
- package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T005-windows-targets.md +28 -0
- package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T006-abbreviation-splitting.md +26 -0
- package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T007-root-test-allowlist.md +28 -0
- package/.savepoint/releases/v1.1/epics/_archived/T001-cli-entrypoint.md +25 -0
- package/.savepoint/releases/v1.1/epics/_archived/T002-quality-gates.md +27 -0
- package/.savepoint/releases/v1.1/epics/_archived/T003-snapshot.md +27 -0
- package/.savepoint/releases/v1.1/epics/_archived/T004-ai-reconcile.md +29 -0
- package/.savepoint/releases/v1.1/epics/_archived/T006-tui-review.md +31 -0
- package/.savepoint/releases/v1.1/epics/_archived/T008-skip-handling.md +34 -0
- package/.savepoint/releases/v1.1/v1.1-PRD.md +67 -7
- package/.savepoint/router.md +9 -16
- package/AGENTS.md +38 -23
- package/README.md +0 -1
- package/agent-skills/savepoint-audit/SKILL.md +86 -34
- package/agent-skills/savepoint-build-task/SKILL.md +7 -2
- package/agent-skills/savepoint-create-plan/SKILL.md +7 -2
- package/agent-skills/savepoint-create-task/SKILL.md +44 -31
- package/agent-skills/savepoint-draft-prd/SKILL.md +7 -2
- package/agent-skills/savepoint-system-design/SKILL.md +7 -2
- package/agent_skills_test.go +91 -0
- package/cmd/board.go +59 -0
- package/cmd/board_test.go +137 -0
- package/cmd/doctor.go +53 -0
- package/cmd/doctor_test.go +146 -0
- package/cmd/init.go +63 -0
- package/cmd/init_test.go +104 -0
- package/internal/board/board.go +40 -36
- package/internal/board/board_test.go +27 -82
- package/internal/board/card.go +43 -23
- package/internal/board/card_test.go +41 -5
- package/internal/board/column.go +44 -13
- package/internal/board/column_test.go +5 -2
- package/internal/board/detail.go +0 -47
- package/internal/board/epic_panel.go +118 -22
- package/internal/board/epic_panel_test.go +302 -17
- package/internal/board/help.go +1 -0
- package/internal/board/help_test.go +1 -0
- package/internal/board/integration_test.go +266 -0
- package/internal/board/interfaces.go +65 -0
- package/internal/board/interfaces_test.go +114 -0
- package/internal/board/io.go +93 -0
- package/internal/board/model.go +79 -118
- package/internal/board/plain.go +88 -0
- package/internal/board/plain_test.go +117 -0
- package/internal/board/release.go +1 -9
- package/internal/board/release_test.go +6 -6
- package/internal/board/status.go +4 -4
- package/internal/board/theme.go +24 -0
- package/internal/board/theme_test.go +31 -0
- package/internal/board/transitions.go +113 -88
- package/internal/board/transitions_test.go +164 -141
- package/internal/board/tui.go +32 -0
- package/internal/board/update.go +325 -215
- package/internal/board/update_test.go +299 -18
- package/internal/board/util.go +76 -0
- package/internal/board/view.go +31 -28
- package/internal/board/view_test.go +12 -2
- package/internal/board/watch.go +35 -5
- package/internal/buildtool/main.go +2 -10
- package/internal/buildtool/main_test.go +46 -0
- package/internal/data/config.go +17 -3
- package/internal/data/config_test.go +49 -0
- package/internal/data/discover.go +26 -0
- package/internal/data/discover_test.go +34 -10
- package/internal/data/errors.go +4 -0
- package/internal/data/lifecycle.go +13 -6
- package/internal/data/lifecycle_test.go +14 -11
- package/internal/data/parser.go +21 -6
- package/internal/data/parser_test.go +31 -7
- package/internal/data/task.go +0 -9
- package/internal/data/write.go +85 -11
- package/internal/data/write_test.go +167 -0
- package/internal/doctor/checks.go +567 -0
- package/internal/doctor/checks_test.go +716 -0
- package/internal/doctor/gates.go +193 -0
- package/internal/doctor/gates_test.go +166 -0
- package/internal/doctor/interfaces.go +64 -0
- package/internal/doctor/interfaces_test.go +104 -0
- package/internal/doctor/repairs.go +80 -0
- package/internal/doctor/repairs_test.go +81 -0
- package/internal/doctor/report.go +157 -0
- package/internal/doctor/report_test.go +89 -0
- package/internal/init/clipboard.go +146 -0
- package/internal/init/clipboard_test.go +74 -0
- package/internal/init/install.go +16 -0
- package/internal/init/integration_test.go +197 -0
- package/internal/init/prompt.go +14 -0
- package/internal/init/prompt_test.go +77 -0
- package/internal/init/scaffold.go +59 -0
- package/internal/init/scaffold_test.go +179 -0
- package/internal/init/template_freshness_test.go +56 -0
- package/internal/init/validate.go +85 -0
- package/internal/init/validate_test.go +141 -0
- package/internal/init/write.go +73 -0
- package/internal/init/write_test.go +91 -0
- package/internal/styles/styles_test.go +133 -0
- package/internal/testutil/fixture.go +113 -0
- package/internal/testutil/fs.go +26 -0
- package/main.go +101 -4
- package/package.json +2 -2
- package/project-audit/audit_report_glm_5.1.md +411 -0
- package/project-audit/audit_report_opus_4.6 +406 -0
- package/project-audit/consolidated-audit-report.md +456 -0
- package/savepoint +0 -0
- package/templates/project/.savepoint/Design.md +2 -2
- package/templates/project/.savepoint/router.md +10 -10
- package/templates/project/AGENTS.md +33 -21
- package/templates/project/agent-skills/savepoint-audit/SKILL.md +87 -0
- package/templates/project/agent-skills/savepoint-build-task/SKILL.md +44 -0
- package/templates/project/agent-skills/savepoint-create-plan/SKILL.md +33 -0
- package/templates/project/agent-skills/savepoint-create-task/SKILL.md +44 -0
- package/templates/project/agent-skills/savepoint-draft-prd/SKILL.md +37 -0
- package/templates/project/agent-skills/savepoint-system-design/SKILL.md +38 -0
- package/templates/prompts/audit-reconciliation.prompt.md +33 -28
- package/templates/prompts/design.prompt.md +3 -1
- package/.savepoint/audit/v1/E01/proposals.md +0 -168
- package/.savepoint/audit/v1/E01/snapshot.md +0 -78
- package/.savepoint/audit/v1/E01-go-setup/proposals.md +0 -166
- package/.savepoint/audit/v1/E01-go-setup/snapshot.md +0 -71
- package/.savepoint/audit/v1/E01-scaffolding/proposals/AGENTS.md +0 -66
- package/.savepoint/audit/v1/E01-scaffolding/proposals/Design.md +0 -210
- package/.savepoint/audit/v1/E01-scaffolding/proposals/epic-Design.md +0 -117
- package/.savepoint/audit/v1/E01-scaffolding/proposals/quality-review.md +0 -101
- package/.savepoint/audit/v1/E01-scaffolding/snapshot.md +0 -54
- package/.savepoint/audit/v1/E02-data-model/snapshot.md +0 -128
- package/.savepoint/audit/v1/E02-data-readers/proposals.md +0 -123
- package/.savepoint/audit/v1/E02-data-readers/snapshot.md +0 -54
- package/.savepoint/audit/v1/E03-board-tui-core/proposals.md +0 -146
- package/.savepoint/audit/v1/E03-board-tui-core/snapshot.md +0 -57
- package/.savepoint/audit/v1/E03-cli-foundation/snapshot.md +0 -106
- package/.savepoint/audit/v1/E04-board-components/proposals.md +0 -118
- package/.savepoint/audit/v1/E04-board-components/snapshot.md +0 -77
- package/.savepoint/audit/v1/E04-templates-and-prompts/snapshot.md +0 -115
- package/.savepoint/audit/v1/E05-init-command/snapshot.md +0 -125
- package/.savepoint/audit/v1/E05-phase-transitions/proposals.md +0 -83
- package/.savepoint/audit/v1/E05-phase-transitions/snapshot.md +0 -36
- package/.savepoint/audit/v1/E06-atari-noir-layout/proposals.md +0 -130
- package/.savepoint/audit/v1/E06-atari-noir-layout/snapshot.md +0 -84
- package/.savepoint/audit/v1/E06-tui-board/snapshot.md +0 -64
- package/.savepoint/audit/v1/E07-audit-pipeline/snapshot.md +0 -165
- package/.savepoint/audit/v1/E08-board-workflow-cleanup/snapshot.md +0 -65
- package/.savepoint/audit/v1.1/E02-cross-platform-compatibility/snapshot.md +0 -41
- package/.savepoint/audit/v1.1/E04-epic-navigation/snapshot.md +0 -48
- package/ink-cli-ui-design.zip +0 -0
- package/savepoint.exe +0 -0
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
# Consolidated Audit Report — Savepoint
|
|
2
|
+
|
|
3
|
+
This report merges findings from two independent audits (Opus 4.6 and GLM 5.1) of the Savepoint codebase. Where both audits identified the same issue, the finding is merged with combined evidence. Where only one audit identified an issue, it is included with attribution. Severity disagreements are resolved in favour of the higher rating, per the principle that the more cautious assessment should prevail.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Executive Summary
|
|
8
|
+
|
|
9
|
+
**Savepoint** is a Go CLI/TUI tool (~5,600 lines of production code, ~5,600 lines of tests, 264 tests across 39 test files) that implements a file-based project state machine with a kanban-style terminal board. It uses the Charmbracelet stack (Bubble Tea, Lip Gloss) for TUI rendering, fsnotify for file watching, and YAML-frontmatter markdown files as its data layer.
|
|
10
|
+
|
|
11
|
+
### What is working well
|
|
12
|
+
|
|
13
|
+
- **File-per-responsibility** is followed diligently. Nearly every `.go` file does one job. The `board/` package splits cleanly into `model.go`, `view.go`, `update.go`, `card.go`, `column.go`, etc.
|
|
14
|
+
- **Test coverage is strong.** 264 tests across 39 files — roughly 1.6× the production code. All packages except `buildtool` and `styles` have tests, and integration tests exist for `board` and `init`.
|
|
15
|
+
- **Clean dependency tree.** Only 2 direct dependencies (`bubbletea`, `fsnotify`) plus the Charmbracelet ecosystem for rendering. No framework bloat.
|
|
16
|
+
- **All tests pass.** `go test ./...` reports zero failures.
|
|
17
|
+
- **Data model is honest.** The `data` package cleanly separates parsing, lifecycle validation, writing, and discovery.
|
|
18
|
+
- **Dependency injection in `cmd/`** via function types (`InitRunner`, `BoardRunner`, `DoctorRunner`) makes commands trivially testable.
|
|
19
|
+
- **Atomic file writes** in `data/write.go` and `init/write.go` prevent corruption.
|
|
20
|
+
- **Policy tests** (`render_policy_test.go`) enforce cross-cutting visual constraints.
|
|
21
|
+
- **Responsive TUI layout** with clean breakpoint-based column rendering.
|
|
22
|
+
|
|
23
|
+
### Biggest risks
|
|
24
|
+
|
|
25
|
+
1. **Synchronous file I/O inside the Bubble Tea `Update()` loop** freezes the TUI on slow disks. This is the highest-impact architectural issue.
|
|
26
|
+
2. **`update.go` is a 521-line monolith** with a deeply nested key-dispatch switch. Hard to extend or test in isolation.
|
|
27
|
+
3. **Duplicated YAML frontmatter read/write/parse logic** appears in `write.go`, `parser.go`, `board.go`, `checks.go`, and `epic_panel.go`.
|
|
28
|
+
4. **Cycle detection in `checks.go` produces inaccurate paths** — a correctness bug.
|
|
29
|
+
5. **No interfaces used for I/O boundaries** — all data-access types are concrete structs, making them impossible to mock without disk fixtures.
|
|
30
|
+
|
|
31
|
+
### Extensibility
|
|
32
|
+
|
|
33
|
+
The project is easy to extend for new checks, overlays, and commands. It is harder to extend for new data sources or rendering backends because I/O is baked into concrete functions. The main risk to extensibility is the `update.go` monolith.
|
|
34
|
+
|
|
35
|
+
### Architecture fit
|
|
36
|
+
|
|
37
|
+
The architecture (flat `internal/` packages, Elm-like TUI model, embedded templates) is well-suited for a small-to-medium CLI tool. No over-engineering is evident. The main architectural debts are the I/O-in-update anti-pattern and the `update.go` size.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 2. Severity-Ranked Recommendations
|
|
42
|
+
|
|
43
|
+
### Critical
|
|
44
|
+
|
|
45
|
+
#### C1. Synchronous file I/O in the TUI update loop
|
|
46
|
+
|
|
47
|
+
- **Finding:** `update.go` performs filesystem reads and writes directly inside `Update()`: `writeTaskStatus()`, `writeRouterTask()`, `writeRouterReleaseEpic()`, `readEpicDetailFile()`, `selectEpicPanelEpic()`. These block the TUI event loop.
|
|
48
|
+
- **Why it matters:** Any disk latency (network drives, slow SSDs, virus scanners) freezes the entire TUI. Bubble Tea's design intent is for `Update()` to be pure — I/O should happen in `tea.Cmd` functions that return messages.
|
|
49
|
+
- **Evidence:** `internal/board/update.go` (writeRouterTask, writeRouterReleaseEpic, readEpicDetailFile), `internal/board/model.go:235-280` (writeRouterReleaseEpic, writeRouterTask)
|
|
50
|
+
- **Recommended fix:** Extract I/O operations into `tea.Cmd` functions. E.g., `writeTaskStatusCmd(task, path, mtime) tea.Cmd` returns a `tea.Msg` on completion. `Update()` dispatches the command and handles the result message. This is the standard Bubble Tea pattern.
|
|
51
|
+
- **Estimated effort:** Medium
|
|
52
|
+
|
|
53
|
+
#### C2. Cycle detection produces inaccurate paths
|
|
54
|
+
|
|
55
|
+
- **Finding:** `detectCycles` in `checks.go` uses a `parent` map that gets overwritten when a node is visited from multiple paths. When a cycle is found, the path reconstructed via `parent` may not represent the actual cycle.
|
|
56
|
+
- **Why it matters:** Users could be shown a cycle path that doesn't actually exist, causing confusion or incorrect doctor reports.
|
|
57
|
+
- **Evidence:** `internal/doctor/checks.go` — the DFS `parent` map is a simple `map[string]string` that gets overwritten per-visit
|
|
58
|
+
- **Recommended fix:** Use a stack-based cycle reconstruction (track the current DFS path as a slice) or validate the reconstructed path actually forms a cycle before reporting it.
|
|
59
|
+
- **Estimated effort:** Small
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
### High
|
|
64
|
+
|
|
65
|
+
#### H1. Committed binaries in the repository
|
|
66
|
+
|
|
67
|
+
- **Finding:** `savepoint` (5.5 MB), `savepoint.exe` (6.0 MB), `dist/`, and `ink-cli-ui-design.zip` are tracked in Git.
|
|
68
|
+
- **Why it matters:** Bloats clone size (12+ MB), causes merge noise, and risks accidentally shipping stale binaries.
|
|
69
|
+
- **Evidence:** `.gitignore` does not exclude root binaries. `dist/` is also checked in.
|
|
70
|
+
- **Recommended fix:** Add `savepoint`, `savepoint.exe`, `dist/`, and `*.zip` to `.gitignore`. Run `git rm --cached` on tracked binaries. Build in CI only.
|
|
71
|
+
- **Estimated effort:** Small
|
|
72
|
+
|
|
73
|
+
#### H2. `update.go` complexity: God-method `Update()`
|
|
74
|
+
|
|
75
|
+
- **Finding:** `update.go` is 521 lines. The `Update()` method alone spans ~190 lines with 4+ levels of nesting inside `case tea.KeyMsg`. Overlay handling mixes 5 overlay types in one `updateOverlay()` function.
|
|
76
|
+
- **Why it matters:** Adding a new keybinding or overlay requires editing a deeply nested switch. Bug surface area grows with each addition. The space-bar and backspace handlers have nearly identical structure — find-task-by-ID, mutate, write, refresh.
|
|
77
|
+
- **Evidence:** `internal/board/update.go` — lines 19–192 for `Update()`, lines 132–158 and 159–181 for near-duplicate handler structure
|
|
78
|
+
- **Recommended fix:** Extract key handlers into named methods: `handleAdvanceTask()`, `handleRetreatTask()`, `handleSetPriority()`. Extract `updateBoardKeys()` and `updateOverlayKeys()` from the top-level switch. Split the overlay update into per-type handlers.
|
|
79
|
+
- **Estimated effort:** Medium
|
|
80
|
+
|
|
81
|
+
#### H3. Duplicated frontmatter body-extraction logic
|
|
82
|
+
|
|
83
|
+
- **Finding:** The pattern "extract frontmatter → unmarshal YAML → compute body start offset → reconstruct file" appears in:
|
|
84
|
+
- `write.go:updateFrontmatterField` (lines 40–83)
|
|
85
|
+
- `write.go:WriteTaskStatus` (lines 85–150)
|
|
86
|
+
- `board.go` (router reading)
|
|
87
|
+
- `checks.go` (task validation)
|
|
88
|
+
- `epic_panel.go:epicDetailBody` and `epicAuditBody` (frontmatter stripping, lines 50–60 and 125–134)
|
|
89
|
+
- The magic `delimLen := 4; bodyStart := delimLen + len(raw) + delimLen` appears in two places
|
|
90
|
+
- **Why it matters:** A change to frontmatter format must be patched in 4+ places. The body-offset calculation is fragile.
|
|
91
|
+
- **Evidence:** `internal/data/write.go:74-79` and `internal/data/write.go:140-145` are identical; `internal/board/epic_panel.go:51-59` and `internal/board/epic_panel.go:126-133` are identical
|
|
92
|
+
- **Recommended fix:** Extract `SplitFrontmatterBody(content string) (yaml string, body string, err error)` in the `data` package. Extract `stripFrontmatter(content string) string` for `epic_panel.go`.
|
|
93
|
+
- **Estimated effort:** Small
|
|
94
|
+
|
|
95
|
+
#### H4. No interfaces for data-access types
|
|
96
|
+
|
|
97
|
+
- **Finding:** `Discover`, `Parser`, `ConfigReader`, `RouterReader` are all concrete structs. Every consumer calls `data.NewDiscover()`, `data.NewParser()`, etc. directly.
|
|
98
|
+
- **Why it matters:** Test helpers in `board` and `doctor` must create real filesystem fixtures to test business logic. This is expensive and brittle.
|
|
99
|
+
- **Evidence:** `internal/board/board.go` lines 37, 85, 130 all call `data.NewDiscover()` with no injection point. `internal/doctor/checks.go` lines 116, 293, 454, 525 do the same.
|
|
100
|
+
- **Recommended fix:** Define interfaces at the consumer side (e.g., `type taskDiscoverer interface { ListReleases(root string) ([]data.ReleaseInfo, error) ... }`). Keep the existing structs as production implementations. Accept the interface in board/doctor constructors.
|
|
101
|
+
- **Estimated effort:** Medium
|
|
102
|
+
|
|
103
|
+
#### H5. Stdlib reimplementation in `repairs.go` and `buildtool/main.go`
|
|
104
|
+
|
|
105
|
+
- **Finding:** `contains()` and `indexOf()` in `repairs.go` reimplement `strings.Contains` and `strings.Index`. `trimSpace()` in `buildtool/main.go` reimplements `bytes.TrimSpace`.
|
|
106
|
+
- **Why it matters:** Makes code harder to read for Go developers expecting standard library calls. The custom implementations may have subtle differences from the stdlib versions (e.g., Unicode whitespace handling).
|
|
107
|
+
- **Evidence:** `internal/doctor/repairs.go:50-61`, `internal/buildtool/main.go:211-219`
|
|
108
|
+
- **Recommended fix:** Replace with `strings.Contains`, `strings.Index`, and `strings.TrimSpace` respectively.
|
|
109
|
+
- **Estimated effort:** Small
|
|
110
|
+
|
|
111
|
+
#### H6. Fragile repair suggestion matching via substring search on error messages
|
|
112
|
+
|
|
113
|
+
- **Finding:** `SuggestRepair()` pattern-matches against error message substrings to suggest fixes. If error messages change format, repairs silently break.
|
|
114
|
+
- **Evidence:** `internal/doctor/repairs.go:8-66` — hard-coded substring matching against messages like `"not found"`, `"invalid"`, `"missing"`
|
|
115
|
+
- **Recommended fix:** Define typed error codes or sentinel errors in `data/` and `doctor/`, and match on error type rather than substring.
|
|
116
|
+
- **Estimated effort:** Medium
|
|
117
|
+
|
|
118
|
+
#### H7. Quality gate command execution has no timeout
|
|
119
|
+
|
|
120
|
+
- **Finding:** `RunQualityGates` executes commands from `config.yml` with no timeout. A hung command blocks the doctor indefinitely.
|
|
121
|
+
- **Evidence:** `internal/doctor/gates.go:32-55` — `exec.Command` with `Run()` and no `Context` timeout
|
|
122
|
+
- **Recommended fix:** Use `exec.CommandContext(ctx, ...)` with a configurable timeout (default: 60s). Add a `gate_timeout` config option.
|
|
123
|
+
- **Estimated effort:** Small
|
|
124
|
+
|
|
125
|
+
#### H8. `\r\n` normalization is scattered across files
|
|
126
|
+
|
|
127
|
+
- **Finding:** `strings.ReplaceAll(content, "\r\n", "\n")` appears in `parser.go`, `write.go` (3 times), `epic_panel.go`, and likely elsewhere.
|
|
128
|
+
- **Why it matters:** If the normalization logic changes (e.g., also handling `\r` alone), every call site must be found and updated. Missing a call site causes subtle cross-platform bugs.
|
|
129
|
+
- **Evidence:** `internal/data/parser.go:92`, `internal/data/write.go:22,46,104`, `internal/board/epic_panel.go`
|
|
130
|
+
- **Recommended fix:** Create a `normalizeLineEndings(s string) string` function in `internal/data/` and use it everywhere.
|
|
131
|
+
- **Estimated effort:** Small
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
### Medium
|
|
136
|
+
|
|
137
|
+
#### M1. `Discover`, `Parser`, `ConfigReader` are stateless singletons instantiated repeatedly
|
|
138
|
+
|
|
139
|
+
- **Finding:** `data.NewDiscover()`, `data.NewParser()`, `data.NewConfigReader()`, `data.NewRouterReader()` all return pointers to zero-value structs. `Discover` is re-created 7 times across `board.go`, `checks.go`, and `main.go`.
|
|
140
|
+
- **Recommended fix:** Either convert to package-level functions (since there's no state) or introduce consumer-side interfaces per H4.
|
|
141
|
+
- **Estimated effort:** Small
|
|
142
|
+
|
|
143
|
+
#### M2. `newProgramModel()` contains hardcoded epic slug
|
|
144
|
+
|
|
145
|
+
- **Finding:** `board.go:33` — `NewModel(nil, "v1", "E03-board-tui-core")` is a development leftover.
|
|
146
|
+
- **Recommended fix:** Delete `newProgramModel()` or replace hardcoded values with empty strings.
|
|
147
|
+
- **Estimated effort:** Small
|
|
148
|
+
|
|
149
|
+
#### M3. Hardcoded state constants in `checks.go` duplicate `data/` definitions
|
|
150
|
+
|
|
151
|
+
- **Finding:** `validStates` map in `checks.go` duplicates state names defined in `data/lifecycle.go`.
|
|
152
|
+
- **Recommended fix:** Remove `validStates` and use `data.IsCanonicalColumn()` / `data.IsCanonicalStage()`.
|
|
153
|
+
- **Estimated effort:** Small
|
|
154
|
+
|
|
155
|
+
#### M4. Inconsistent directory abstraction: `CheckOrphans` bypasses `data.Discover`
|
|
156
|
+
|
|
157
|
+
- **Finding:** Most of `checks.go` uses `data.Discover` for filesystem traversal, but `CheckOrphans` uses `os.ReadDir` directly.
|
|
158
|
+
- **Recommended fix:** Add a `ListRootDirs()` method to `data.Discover` and use it in `CheckOrphans`.
|
|
159
|
+
- **Estimated effort:** Small
|
|
160
|
+
|
|
161
|
+
#### M5. Layout constants split between `layout.go` and `column.go`
|
|
162
|
+
|
|
163
|
+
- **Finding:** `colOverhead` is defined in `column.go` (value 4) and used in both `column.go` and `layout.go`.
|
|
164
|
+
- **Recommended fix:** Move all layout constants to `layout.go`.
|
|
165
|
+
- **Estimated effort:** Small
|
|
166
|
+
|
|
167
|
+
#### M6. `AtomicWrite` cross-device rename fallback is broken
|
|
168
|
+
|
|
169
|
+
- **Finding:** `replaceFile()` tries `os.Rename` first, then falls back to creating a backup and renaming, but `os.Rename` will still fail on cross-device moves in the fallback path.
|
|
170
|
+
- **Evidence:** `internal/init/write.go:52-63`
|
|
171
|
+
- **Recommended fix:** Use `os.Open` + `io.Copy` + `os.Remove` for cross-filesystem fallback.
|
|
172
|
+
- **Estimated effort:** Small
|
|
173
|
+
|
|
174
|
+
#### M7. Ad-hoc Markdown parsing in `epic_panel.go` is fragile
|
|
175
|
+
|
|
176
|
+
- **Finding:** `epicDetailBody()` and `epicAuditBody()` skip headings containing "component" or "files" via substring matching. Any heading with those substrings will be silently hidden.
|
|
177
|
+
- **Recommended fix:** Use exact heading matches with a configurable allowlist/blocklist rather than substring matching.
|
|
178
|
+
- **Estimated effort:** Small (exact match) / Medium (markdown parser)
|
|
179
|
+
|
|
180
|
+
#### M8. `Config.Theme` defaults not filling individual accent colors
|
|
181
|
+
|
|
182
|
+
- **Finding:** `fillThemeDefaults()` fills base theme colors when empty, but for accents it's all-or-nothing: `len(theme.Accents) == 0` triggers the default.
|
|
183
|
+
- **Recommended fix:** Fill missing accent keys individually from `defaultTheme.Accents`.
|
|
184
|
+
- **Estimated effort:** Small
|
|
185
|
+
|
|
186
|
+
#### M9. `splitCommand` in `gates.go` is a naïve shell tokenizer
|
|
187
|
+
|
|
188
|
+
- **Finding:** Only handles double-quote grouping — no escaping, no single quotes, no backslash-escapes.
|
|
189
|
+
- **Recommended fix:** Document the limitation or use `shellwords` parsing.
|
|
190
|
+
- **Estimated effort:** Small
|
|
191
|
+
|
|
192
|
+
#### M10. No Windows build target in `buildtool`
|
|
193
|
+
|
|
194
|
+
- **Finding:** `targets` list only includes Linux and Darwin. No Windows target despite a `localExecutable()` Windows branch.
|
|
195
|
+
- **Recommended fix:** Add Windows amd64 and arm64 targets. Add `.exe` suffix handling.
|
|
196
|
+
- **Estimated effort:** Small
|
|
197
|
+
|
|
198
|
+
#### M11. `buildtool` has no tests
|
|
199
|
+
|
|
200
|
+
- **Finding:** Only production package with `[no test files]`.
|
|
201
|
+
- **Recommended fix:** Add tests for `run()`, `version()`, and `writeTarGz()`.
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
### Low
|
|
206
|
+
|
|
207
|
+
#### L1. `ColumnType` and `TaskStatus` are parallel enumerations for the same concept
|
|
208
|
+
|
|
209
|
+
- **Recommended fix:** Consider unifying into a single status type. Low priority.
|
|
210
|
+
- **Estimated effort:** Medium
|
|
211
|
+
|
|
212
|
+
#### L2. `package.json` test script is misleading
|
|
213
|
+
|
|
214
|
+
- **Finding:** `"test": "savepoint init"` — `npm test` scaffolds a project instead of running tests.
|
|
215
|
+
- **Recommended fix:** Change to `"test": "echo \"Run 'make test' for Go tests\""`.
|
|
216
|
+
|
|
217
|
+
#### L3. Dead code: `taskLabel()`, `loadAllTasks()`, `newProgramModel()`, `CheckResult`
|
|
218
|
+
|
|
219
|
+
- **Recommended fix:** Delete all four.
|
|
220
|
+
|
|
221
|
+
#### L4. `shortID` and `shortRouterID` are near-duplicates
|
|
222
|
+
|
|
223
|
+
- **Recommended fix:** Consolidate into one `ShortID(full string) string` function.
|
|
224
|
+
|
|
225
|
+
#### L5. `epicIndex` and `releaseIndex` are identical functions
|
|
226
|
+
|
|
227
|
+
- **Recommended fix:** Extract `sliceIndex(items []string, target string) int`.
|
|
228
|
+
|
|
229
|
+
#### L6. No linter configured
|
|
230
|
+
|
|
231
|
+
- **Recommended fix:** Add `.golangci.yml` with `unused`, `errcheck`, `staticcheck`, `govet`, `ineffassign`.
|
|
232
|
+
|
|
233
|
+
#### L7. No distribution checksums
|
|
234
|
+
|
|
235
|
+
- **Finding:** `dist()` creates tar.gz archives but no SHA256 checksums file.
|
|
236
|
+
- **Recommended fix:** Generate `checksums.txt` during `dist`.
|
|
237
|
+
|
|
238
|
+
#### L8. `agent_skills_test.go` hardcodes expected skill count of 6
|
|
239
|
+
|
|
240
|
+
- **Recommended fix:** Remove count assertion or derive from directory listing.
|
|
241
|
+
|
|
242
|
+
#### L9. Test helper duplication across packages
|
|
243
|
+
|
|
244
|
+
- **Recommended fix:** Create `internal/testutil` package with shared fixtures.
|
|
245
|
+
|
|
246
|
+
#### L10. `splitChecklistSentences` doesn't handle abbreviations
|
|
247
|
+
|
|
248
|
+
- **Recommended fix:** Skip periods preceded by known abbreviations (e.g., "e.g.", "i.e.").
|
|
249
|
+
|
|
250
|
+
#### L11. `package main` test file at root level
|
|
251
|
+
|
|
252
|
+
- **Recommended fix:** Move to a dedicated test package.
|
|
253
|
+
|
|
254
|
+
#### L12. Audit section allowlist should be configurable
|
|
255
|
+
|
|
256
|
+
- **Recommended fix:** Extract `allowedSections` to a named constant with documentation.
|
|
257
|
+
|
|
258
|
+
#### L13. `reloadTasks` silently swallows errors
|
|
259
|
+
|
|
260
|
+
- **Finding:** `watch.go` returns `nil` on error, causing the board to silently stop refreshing.
|
|
261
|
+
- **Recommended fix:** Return an `errorMsg` so the TUI can surface it.
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## 3. Complexity & Modularity Review
|
|
266
|
+
|
|
267
|
+
### Overly large files
|
|
268
|
+
|
|
269
|
+
| File | Lines | Concern |
|
|
270
|
+
|------|-------|---------|
|
|
271
|
+
| `internal/board/update.go` | 521 | `Update()` is 190 lines; `updateOverlay()` is 100 lines |
|
|
272
|
+
| `internal/doctor/checks.go` | 585 | Single-file check aggregator; could be split by check type |
|
|
273
|
+
| `internal/data/write.go` | 216 | Two large functions with duplicated body-offset arithmetic |
|
|
274
|
+
| `internal/board/epic_panel.go` | 256 | Sidebar, detail, audit, and dropdown rendering mixed |
|
|
275
|
+
|
|
276
|
+
### Tight coupling
|
|
277
|
+
|
|
278
|
+
- **Board → data:** Appropriate for project size. Clean boundary.
|
|
279
|
+
- **Board → os:** `model.go` and `update.go` call `os.ReadFile`, `os.Stat`, `os.WriteFile` directly. Couples UI logic to filesystem.
|
|
280
|
+
|
|
281
|
+
### Repeated logic
|
|
282
|
+
|
|
283
|
+
1. Frontmatter stripping (3+ places)
|
|
284
|
+
2. `\r\n` normalization (4+ call sites across 2 packages)
|
|
285
|
+
3. `shortID` extraction (2 near-duplicate implementations)
|
|
286
|
+
4. `indexOf`/`epicIndex`/`releaseIndex` pattern (3 implementations)
|
|
287
|
+
5. Space-bar and Backspace handlers in `update.go` (nearly identical structure)
|
|
288
|
+
6. `Discover` instantiation (7 separate `NewDiscover()` calls)
|
|
289
|
+
|
|
290
|
+
### Unclear data flow
|
|
291
|
+
|
|
292
|
+
- **`Task.Status` vs `Task.Column`:** Two representations of the same state. `syncTaskStatus` manually keeps them in sync.
|
|
293
|
+
- **`watch.go` silent error swallowing:** `reloadTasks` returns `nil` on error.
|
|
294
|
+
|
|
295
|
+
### Excessive abstraction
|
|
296
|
+
|
|
297
|
+
- `Discover`, `Parser`, `ConfigReader`, `RouterReader` are empty structs used as method namespaces. Should be package-level functions or interfaces.
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## 4. Architecture Review
|
|
302
|
+
|
|
303
|
+
### Folder organisation ✅
|
|
304
|
+
|
|
305
|
+
```
|
|
306
|
+
savepoint/
|
|
307
|
+
├── cmd/ # CLI arg parsing — clean separation from execution
|
|
308
|
+
├── internal/
|
|
309
|
+
│ ├── board/ # TUI model/view/update — Elm architecture
|
|
310
|
+
│ ├── buildtool/ # Standalone Go binary for build automation
|
|
311
|
+
│ ├── data/ # Models, parsing, writing, discovery
|
|
312
|
+
│ ├── doctor/ # Read-only diagnostics
|
|
313
|
+
│ ├── init/ # Scaffolding
|
|
314
|
+
│ └── styles/ # Centralised palette + styles
|
|
315
|
+
├── templates/ # Embedded scaffold templates
|
|
316
|
+
└── agent-skills/ # Prompt documents for AI agents
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Domain boundaries ✅
|
|
320
|
+
|
|
321
|
+
- **Data layer** (`internal/data/`): Task lifecycle, parsing, discovery, config, writing. Correct boundary.
|
|
322
|
+
- **Board/TUI** (`internal/board/`): Rendering, interaction, file watching. Largest package.
|
|
323
|
+
- **Doctor** (`internal/doctor/`): Checks, gates, repairs, report. Each file has one job. Clean.
|
|
324
|
+
- **Init** (`internal/init/`): Scaffold, validate, write, clipboard, prompt. Clean.
|
|
325
|
+
|
|
326
|
+
### State management
|
|
327
|
+
|
|
328
|
+
Bubble Tea `Model` with 27 fields. Upper bound of manageable. Consider grouping related fields into sub-structs if TUI grows. I/O-in-Update is the main concern (C1).
|
|
329
|
+
|
|
330
|
+
### Configuration approach ✅
|
|
331
|
+
|
|
332
|
+
`config.yml` with defaults baked into Go code. Three-tier color support. `QualityGates` for project-specific commands. Clean.
|
|
333
|
+
|
|
334
|
+
### Error handling
|
|
335
|
+
|
|
336
|
+
Generally good — errors wrapped with `fmt.Errorf("context: %w", err)`. Key exceptions:
|
|
337
|
+
- `reloadTasks` silently swallows errors (L13)
|
|
338
|
+
- `SuggestRepair` relies on substring matching (H6)
|
|
339
|
+
- Quality gates have no timeout (H7)
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## 5. Best-Practice Review
|
|
344
|
+
|
|
345
|
+
### Framework conventions ⚠️
|
|
346
|
+
|
|
347
|
+
Bubble Tea conventions generally followed. I/O in `Update()` violates the framework's core principle — see C1.
|
|
348
|
+
|
|
349
|
+
### Type security ✅
|
|
350
|
+
|
|
351
|
+
Custom types (`ColumnType`, `ProgressStage`, `OverlayType`) used appropriately. `ColumnType`/`TaskStatus` duality is a minor concern (L1).
|
|
352
|
+
|
|
353
|
+
### Linting/formatting ⚠️
|
|
354
|
+
|
|
355
|
+
No linter configured. Add `.golangci.yml` with `unused`, `errcheck`, `staticcheck`, `govet`, `ineffassign`.
|
|
356
|
+
|
|
357
|
+
### Testing ✅
|
|
358
|
+
|
|
359
|
+
Strong coverage (264 tests, ~1.6:1 test-to-code ratio). Missing: `buildtool`, `styles`, benchmarks, fuzz tests.
|
|
360
|
+
|
|
361
|
+
### Dependency management ✅
|
|
362
|
+
|
|
363
|
+
`go.mod` is clean. 2 direct dependencies. All indirect deps from Charmbracelet ecosystem.
|
|
364
|
+
|
|
365
|
+
### Build/deployment ⚠️
|
|
366
|
+
|
|
367
|
+
- No CI configuration visible
|
|
368
|
+
- Binaries committed to repo (H1)
|
|
369
|
+
- No Windows build target (M10)
|
|
370
|
+
- No distribution checksums (L7)
|
|
371
|
+
|
|
372
|
+
### Logging/debugging ⚠️
|
|
373
|
+
|
|
374
|
+
No logging. TUI uses `StatusMessage` for user feedback. No `--debug` flag. Recommend adding `SAVEPOINT_DEBUG` env var.
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
## 6. Refactor Roadmap
|
|
379
|
+
|
|
380
|
+
### Phase 1 — Safe cleanup
|
|
381
|
+
|
|
382
|
+
**Objective:** Remove dead code, fix obvious duplication, improve hygiene.
|
|
383
|
+
|
|
384
|
+
| Task | Risk |
|
|
385
|
+
|------|------|
|
|
386
|
+
| Add binaries to `.gitignore` and remove from Git tracking | None |
|
|
387
|
+
| Delete `taskLabel()`, `loadAllTasks()`, `newProgramModel()`, `CheckResult` | None |
|
|
388
|
+
| Remove unused `exitCode` param from `GateSuggestion` | None |
|
|
389
|
+
| Replace `contains()`/`indexOf()` with `strings.Contains`/`strings.Index` | None |
|
|
390
|
+
| Replace `trimSpace()` with `strings.TrimSpace` | None |
|
|
391
|
+
| Replace `validStates` map with `data.IsCanonicalColumn()`/`data.IsCanonicalStage()` | Low |
|
|
392
|
+
| Fix `package.json` test script | None |
|
|
393
|
+
| Add `.golangci.yml` with basic linters | None |
|
|
394
|
+
| Co-locate layout constants (`colOverhead`) into `layout.go` | Low |
|
|
395
|
+
| Consolidate `shortID`/`shortRouterID` and `epicIndex`/`releaseIndex` | Low |
|
|
396
|
+
|
|
397
|
+
**Expected benefit:** Cleaner codebase, smaller repo, automated lint catches.
|
|
398
|
+
**Risk level:** Very low.
|
|
399
|
+
|
|
400
|
+
### Phase 2 — Structural improvements
|
|
401
|
+
|
|
402
|
+
**Objective:** Reduce duplication, improve modularity, make key files easier to extend.
|
|
403
|
+
|
|
404
|
+
| Task | Risk |
|
|
405
|
+
|------|------|
|
|
406
|
+
| Extract `SplitFrontmatterBody()` in `data` package | Low — well-tested |
|
|
407
|
+
| Extract `stripFrontmatter()` for `epic_panel.go` | Low |
|
|
408
|
+
| Add `normalizeLineEndings()` to `data` package; use everywhere | Low |
|
|
409
|
+
| Split `Update()` into `handleBoardKey()`, `handleOverlayKey()`, named methods | Medium |
|
|
410
|
+
| Group `Model` fields into sub-structs | Medium |
|
|
411
|
+
| Convert stateless data types to package-level functions or consumer-defined interfaces | Medium |
|
|
412
|
+
| Fix `AtomicWrite` cross-device fallback | Low |
|
|
413
|
+
| Make `Config.Theme.Accents` fill missing keys individually | Low |
|
|
414
|
+
|
|
415
|
+
**Expected benefit:** Easier to add features, reduced duplication, more testable.
|
|
416
|
+
**Risk level:** Medium.
|
|
417
|
+
|
|
418
|
+
### Phase 3 — Hardening
|
|
419
|
+
|
|
420
|
+
**Objective:** Fix the I/O-in-update anti-pattern, add missing tests, improve error handling.
|
|
421
|
+
|
|
422
|
+
| Task | Risk |
|
|
423
|
+
|------|------|
|
|
424
|
+
| Extract all filesystem I/O from `update.go` into `tea.Cmd` functions | Medium-High |
|
|
425
|
+
| Add `tea.Cmd`-based router writing for priority key and epic selection | Medium |
|
|
426
|
+
| Handle `reloadTasks` errors by emitting `errorMsg` | Low |
|
|
427
|
+
| Add timeout to quality gate execution | Low |
|
|
428
|
+
| Convert `SuggestRepair` to typed error matching | Medium |
|
|
429
|
+
| Add tests for `buildtool/` | Low |
|
|
430
|
+
| Add tests for `styles/` | Low |
|
|
431
|
+
| Add benchmark tests for render functions | Low |
|
|
432
|
+
| Add fuzz targets for YAML frontmatter parsing | Low |
|
|
433
|
+
| Add `--debug`/`SAVEPOINT_DEBUG` flag | Low |
|
|
434
|
+
| Fix cycle detection path reconstruction | Low |
|
|
435
|
+
|
|
436
|
+
**Expected benefit:** TUI responsiveness, correct error propagation, test coverage completeness.
|
|
437
|
+
**Risk level:** Low–Medium.
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
## 7. Top 10 Action List
|
|
442
|
+
|
|
443
|
+
- [ ] **1. Extract I/O from `update.go` into `tea.Cmd` functions** — Critical — `internal/board/update.go`, `internal/board/model.go` — Prevents TUI freezes on slow disk; follows Bubble Tea conventions; highest architectural impact
|
|
444
|
+
- [ ] **2. Remove committed binaries from the repository** — High — `.gitignore`, `savepoint`, `savepoint.exe`, `dist/`, `ink-cli-ui-design.zip` — Repo shrinks by 12 MB+, eliminates merge noise
|
|
445
|
+
- [ ] **3. Fix cycle detection path reconstruction in `checks.go`** — High (bug) — `internal/doctor/checks.go` — Produces inaccurate error messages today
|
|
446
|
+
- [ ] **4. Extract `SplitFrontmatterBody()` to deduplicate write logic** — High — `internal/data/write.go`, `internal/board/epic_panel.go` — Single source of truth for frontmatter reconstruction
|
|
447
|
+
- [ ] **5. Split `Update()` into named key-handler methods** — High — `internal/board/update.go` — 190-line method becomes 5–6 focused methods; easier to extend
|
|
448
|
+
- [ ] **6. Replace stdlib reimplementations (`contains`, `indexOf`, `trimSpace`)** — High — `internal/doctor/repairs.go`, `internal/buildtool/main.go` — Eliminates confusing custom code
|
|
449
|
+
- [ ] **7. Add timeout to quality gate execution** — High — `internal/doctor/gates.go` — Prevents indefinite blocking
|
|
450
|
+
- [ ] **8. Centralize `\r\n` normalization and frontmatter stripping** — Medium — `internal/data/parser.go`, `internal/data/write.go`, `internal/board/epic_panel.go` — Single source of truth for cross-platform line endings and body extraction
|
|
451
|
+
- [ ] **9. Consolidate layout constants, shared utilities, and duplicate functions** — Medium — `internal/board/column.go`, `internal/board/layout.go`, `internal/board/card.go`, `internal/board/view.go`, `internal/board/detail.go`, `internal/board/epic_panel.go`, `internal/board/release.go` — Reduces scatter and makes logic findable
|
|
452
|
+
- [ ] **10. Fix `AtomicWrite` cross-device rename fallback** — Medium — `internal/init/write.go` — Prevents silent data loss on cross-filesystem moves
|
|
453
|
+
|
|
454
|
+
---
|
|
455
|
+
|
|
456
|
+
*Consolidated from audits by Opus 4.6 and GLM 5.1 on 2026-05-03.*
|
package/savepoint
CHANGED
|
Binary file
|
|
@@ -34,9 +34,9 @@ last_audited: never
|
|
|
34
34
|
|
|
35
35
|
<!-- Commands and flags if applicable. -->
|
|
36
36
|
|
|
37
|
-
## 7.
|
|
37
|
+
## 7. Agent audit workflow
|
|
38
38
|
|
|
39
|
-
<!--
|
|
39
|
+
<!-- Savepoint audit is agent-led and skill-driven, not a CLI pipeline. At epic close, a fresh audit agent writes one epic-local `E##-Audit.md` with exactly these user-facing sections: `## Main Findings` and `## Code Style Review`. File-specific `### Target File` / `### Replace` / `### With` blocks belong under a separate `## Proposed Changes` admin section so the TUI Audit tab can omit them. -->
|
|
40
40
|
|
|
41
41
|
## 8. Testing strategy
|
|
42
42
|
|
|
@@ -27,11 +27,12 @@ next_action: "The project has its PRD and Design locked but no epics defined yet
|
|
|
27
27
|
|
|
28
28
|
If the user explicitly asks you to audit an epic, perform the audit for that epic even if the router has not reached `state: audit-pending` yet.
|
|
29
29
|
|
|
30
|
-
Persist the audit
|
|
30
|
+
Persist the audit artifact before replying:
|
|
31
31
|
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
32
|
+
- Write exactly one `.savepoint/releases/{release}/epics/{E##-epic}/E##-Audit.md`.
|
|
33
|
+
- Include `## Main Findings`, `## Code Style Review`, and `## Proposed Changes`.
|
|
34
|
+
- Keep file-specific `### Target File` / `### Replace` / `### With` blocks under `## Proposed Changes`.
|
|
35
|
+
- Do not apply proposals or mark the epic audited until the user says `apply audit`.
|
|
35
36
|
|
|
36
37
|
## State → next action
|
|
37
38
|
|
|
@@ -102,12 +103,11 @@ The last task in an epic is `done`. Audit must run before the next epic starts.
|
|
|
102
103
|
|
|
103
104
|
**Context gate:** If you just built this epic in the current session, you **must not** audit it. Close this session. The user should start a new session for the audit.
|
|
104
105
|
|
|
105
|
-
**Next action (fresh session only):**
|
|
106
|
+
**Next action (fresh session only):** Read the epic's `E##-Detail.md`, task files, drift notes, `.savepoint/Design.md`, `AGENTS.md`, and scoped changed files. Write one epic-local audit file to `.savepoint/releases/{release}/epics/{E##-epic}/E##-Audit.md`:
|
|
106
107
|
|
|
107
|
-
- `
|
|
108
|
-
-
|
|
109
|
-
- `
|
|
110
|
-
- `Quality Review` section — semantic-review findings against the 10 Code Style rules.
|
|
108
|
+
- `## Main Findings` — user-facing AC verification, important drift, and notable risks.
|
|
109
|
+
- `## Code Style Review` — checklist against the 10 AGENTS.md code style rules.
|
|
110
|
+
- `## Proposed Changes` — admin/apply metadata using `### Target File`, `### Replace`, and `### With`.
|
|
111
111
|
|
|
112
112
|
Prefer delta-only edits (`Insert After`, `Replace`, `Delete`) anchored to exact text. Do not quote and replace entire large sections unless the whole section genuinely changed.
|
|
113
113
|
|
|
@@ -139,7 +139,7 @@ Quality review section format:
|
|
|
139
139
|
## Already Fixed
|
|
140
140
|
```
|
|
141
141
|
|
|
142
|
-
After proposals are approved, apply approved proposals to live files, mark the epic `
|
|
142
|
+
After proposals are approved, apply approved proposals to live files, mark the epic `E##-Detail.md` as `status: audited`, update project `Design.md` `last_audited`, refresh `AGENTS.md` Codebase Map if needed, and advance this router to the next epic state.
|
|
143
143
|
|
|
144
144
|
Stop. The user reviews proposals in the TUI before commit actions.
|
|
145
145
|
|
|
@@ -16,25 +16,29 @@
|
|
|
16
16
|
| task-building | savepoint-build-task |
|
|
17
17
|
| audit-pending | savepoint-audit |
|
|
18
18
|
|
|
19
|
-
Use `skill` tool.
|
|
19
|
+
Use the `skill` tool when the listed skill is available. If the agent says the skill is not found, read `agent-skills/{skill}/SKILL.md` directly and follow it as the active skill.
|
|
20
20
|
|
|
21
21
|
Read `.savepoint/PRD.md` only for vision changes, `.savepoint/Design.md` only for architecture/audit.
|
|
22
22
|
|
|
23
23
|
## Task Status
|
|
24
24
|
|
|
25
25
|
- `status`: only `planned`, `in_progress`, or `done`
|
|
26
|
-
- `
|
|
26
|
+
- `stage` (build/test/audit): **required** when `status: in_progress` — omitting it is a parse error
|
|
27
27
|
- Never: todo, doing, blocked, review, audit
|
|
28
|
+
- Agents may set a task to `status: in_progress` when starting implementation.
|
|
29
|
+
- Only the user may set a task to `status: done` or retreat a task to an earlier status.
|
|
28
30
|
|
|
29
31
|
## Implementation
|
|
30
32
|
|
|
31
|
-
1. Read task's `##
|
|
32
|
-
2.
|
|
33
|
-
3.
|
|
34
|
-
4.
|
|
35
|
-
5.
|
|
36
|
-
6.
|
|
37
|
-
7.
|
|
33
|
+
1. Read task's `## Context Files` using `Read` tool — one call per file, no explore, no glob
|
|
34
|
+
2. Read task's `## Acceptance Criteria` + `## Implementation Plan`
|
|
35
|
+
3. When starting implementation, set task frontmatter to `status: in_progress` + `stage: build` (both required together)
|
|
36
|
+
4. After setting `in_progress`, press `p` in the TUI to mark the focused task as router priority
|
|
37
|
+
5. Execute in order, tick checkboxes
|
|
38
|
+
6. Verify every AC has passing test/outcome
|
|
39
|
+
7. Run quality gates (build + test)
|
|
40
|
+
8. Update router.md: next task or `audit-pending`
|
|
41
|
+
9. **Stop. Prompt user before continuing.**
|
|
38
42
|
|
|
39
43
|
## Drift Check
|
|
40
44
|
|
|
@@ -47,23 +51,31 @@ If yes → append `## Drift Notes` to task file.
|
|
|
47
51
|
|
|
48
52
|
The agent that builds an epic **must not audit it**. Start a fresh session.
|
|
49
53
|
|
|
54
|
+
## Audit File Structure
|
|
55
|
+
|
|
56
|
+
- Audit is agent-led via `savepoint-audit`, not a `savepoint audit` CLI pipeline.
|
|
57
|
+
- Write exactly one `.savepoint/releases/{release}/epics/{E##-slug}/E##-Audit.md`.
|
|
58
|
+
- The TUI Audit tab renders `## Main Findings` and `## Code Style Review` only.
|
|
59
|
+
- Keep file-specific `### Target File` / `### Replace` / `### With` blocks under `## Proposed Changes` so admin apply details do not appear in the Epic Detail panel.
|
|
60
|
+
- During audit apply/close, update the same `E##-Audit.md` visible sections so `## Main Findings` and `## Code Style Review` describe the applied outcome, not stale pre-apply blockers.
|
|
61
|
+
|
|
50
62
|
## Code Style
|
|
51
63
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
- **One job per file** — split files when responsibilities mix.
|
|
65
|
+
- **One job per function** — small, named, testable units.
|
|
66
|
+
- **Test branches** — cover meaningful conditionals and edge cases.
|
|
67
|
+
- **Types document intent** — prefer explicit types over comments.
|
|
68
|
+
- **Build only what is needed** — no speculative abstractions.
|
|
69
|
+
- **Handle errors at boundaries** — validate inputs, APIs, IO, and external data.
|
|
70
|
+
- **One source of truth** — no duplicated rules, constants, state, or config.
|
|
71
|
+
- **Comments explain why** — not what the code already says.
|
|
72
|
+
- **Content lives in data** — keep copy/config out of logic.
|
|
73
|
+
- **Small diffs** — minimal, reviewable, behaviour-preserving changes.
|
|
62
74
|
|
|
63
75
|
## Build
|
|
64
76
|
|
|
65
77
|
```bash
|
|
66
|
-
|
|
78
|
+
make build && make test
|
|
67
79
|
```
|
|
68
80
|
|
|
69
81
|
## Codebase Map
|
|
@@ -73,4 +85,4 @@ npm run build && npm run test
|
|
|
73
85
|
|
|
74
86
|
## CLI Rules
|
|
75
87
|
|
|
76
|
-
**Never run `savepoint` commands.** The CLI is for the human. Edit files directly.
|
|
88
|
+
**Never run `savepoint` commands.** The CLI is for the human. Edit files directly.
|