savepoint 1.0.2 → 1.0.4
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/.github/workflows/ci.yml +20 -0
- package/.golangci.yml +11 -0
- package/.savepoint/Design.md +40 -38
- 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-Audit.md +272 -0
- package/.savepoint/releases/v1.1/epics/E15-hardening/E15-Detail.md +60 -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 +34 -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 +33 -0
- package/.savepoint/releases/v1.1/epics/E15-hardening/tasks/T008-ci-and-release-automation.md +46 -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 +10 -17
- package/AGENTS.md +39 -24
- package/Makefile +3 -1
- 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 +44 -36
- package/internal/board/board_test.go +27 -82
- package/internal/board/card.go +43 -23
- package/internal/board/card_test.go +74 -5
- package/internal/board/column.go +75 -15
- package/internal/board/column_test.go +76 -2
- package/internal/board/debug.go +26 -0
- package/internal/board/debug_test.go +108 -0
- package/internal/board/detail.go +33 -47
- package/internal/board/detail_test.go +48 -0
- package/internal/board/epic_panel.go +120 -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 +344 -215
- package/internal/board/update_test.go +326 -18
- package/internal/board/util.go +76 -0
- package/internal/board/view.go +31 -28
- package/internal/board/view_test.go +74 -2
- package/internal/board/watch.go +41 -5
- package/internal/buildtool/main.go +45 -15
- package/internal/buildtool/main_test.go +224 -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/fuzz_test.go +75 -0
- package/internal/data/lifecycle.go +13 -6
- package/internal/data/lifecycle_test.go +14 -11
- package/internal/data/parser.go +22 -6
- package/internal/data/parser_test.go +31 -7
- package/internal/data/task.go +0 -9
- package/internal/data/testdata/fuzz/FuzzSplitFrontmatterBody/68eb66b0fe91e7e3 +2 -0
- package/internal/data/write.go +88 -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 +120 -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.md +406 -0
- package/project-audit/consolidated-audit-report.md +456 -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 +0 -0
- package/savepoint.exe +0 -0
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: audit-findings
|
|
3
|
+
audited: 2026-05-03
|
|
4
|
+
---
|
|
5
|
+
# Audit Findings: E12 Task File Validation & Auto-Fix
|
|
6
|
+
|
|
7
|
+
## Main Findings
|
|
8
|
+
|
|
9
|
+
E12 is closed. Parser-side defaults are in place: missing task status parses as `planned`, missing phase/stage on parsed `in_progress` tasks becomes `build`, and invalid status/phase messages include actionable hints.
|
|
10
|
+
|
|
11
|
+
The write-time lifecycle gap found during audit was fixed. `ValidateTaskLifecycle` now receives a task pointer, so defaulting an `in_progress` task to `StageBuild` persists for callers such as `WriteTaskStatus`. Regression coverage now verifies both lifecycle defaulting and persisted `phase: build` writes.
|
|
12
|
+
|
|
13
|
+
The E12 task files now include the required `## Context Files` sections. Architecture and agent documentation were reconciled to describe `internal/data` lifecycle validation/defaulting.
|
|
14
|
+
|
|
15
|
+
Verification after applying proposals: `go build ./...` passed and `go test ./...` passed.
|
|
16
|
+
|
|
17
|
+
## Code Style Review
|
|
18
|
+
|
|
19
|
+
- [x] One job per file
|
|
20
|
+
- [x] One-sentence functions
|
|
21
|
+
- [x] Test branches
|
|
22
|
+
- [x] Types are documentation
|
|
23
|
+
- [x] Build, don't speculate
|
|
24
|
+
- [x] Errors at boundaries
|
|
25
|
+
- [x] One source of truth
|
|
26
|
+
- [x] Comments explain WHY
|
|
27
|
+
- [x] Content in data files
|
|
28
|
+
- [x] Small diffs
|
|
29
|
+
|
|
30
|
+
## Proposed Changes
|
|
31
|
+
|
|
32
|
+
### Target File
|
|
33
|
+
internal/data/lifecycle.go
|
|
34
|
+
|
|
35
|
+
### Replace
|
|
36
|
+
```go
|
|
37
|
+
func ValidateTaskLifecycle(task Task) error {
|
|
38
|
+
if !IsCanonicalColumn(task.Column) {
|
|
39
|
+
return fmt.Errorf("invalid status %q: use planned, in_progress, or done. Add 'status: planned' or 'status: in_progress' to task frontmatter", task.Column)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if task.Column == ColumnInProgress {
|
|
43
|
+
if task.Stage == "" {
|
|
44
|
+
task.Stage = StageBuild
|
|
45
|
+
return nil
|
|
46
|
+
}
|
|
47
|
+
if !IsCanonicalStage(task.Stage) {
|
|
48
|
+
return fmt.Errorf("invalid phase %q: use build, test, or audit. Add 'phase: build' to task frontmatter", task.Stage)
|
|
49
|
+
}
|
|
50
|
+
return nil
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if task.Stage != "" {
|
|
54
|
+
return fmt.Errorf("phase field %q is only valid when status is in_progress. Remove 'phase' or change status to in_progress", task.Stage)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return nil
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### With
|
|
62
|
+
```go
|
|
63
|
+
func ValidateTaskLifecycle(task *Task) error {
|
|
64
|
+
if !IsCanonicalColumn(task.Column) {
|
|
65
|
+
return fmt.Errorf("invalid status %q: use planned, in_progress, or done. Add 'status: planned' or 'status: in_progress' to task frontmatter", task.Column)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if task.Column == ColumnInProgress {
|
|
69
|
+
if task.Stage == "" {
|
|
70
|
+
task.Stage = StageBuild
|
|
71
|
+
return nil
|
|
72
|
+
}
|
|
73
|
+
if !IsCanonicalStage(task.Stage) {
|
|
74
|
+
return fmt.Errorf("invalid phase %q: use build, test, or audit. Add 'phase: build' to task frontmatter", task.Stage)
|
|
75
|
+
}
|
|
76
|
+
return nil
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if task.Stage != "" {
|
|
80
|
+
return fmt.Errorf("phase field %q is only valid when status is in_progress. Remove 'phase' or change status to in_progress", task.Stage)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return nil
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Target File
|
|
88
|
+
internal/data/write.go
|
|
89
|
+
|
|
90
|
+
### Replace
|
|
91
|
+
```go
|
|
92
|
+
if err := ValidateTaskLifecycle(*task); err != nil {
|
|
93
|
+
return err
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### With
|
|
98
|
+
```go
|
|
99
|
+
if err := ValidateTaskLifecycle(task); err != nil {
|
|
100
|
+
return err
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Target File
|
|
105
|
+
internal/data/lifecycle_test.go
|
|
106
|
+
|
|
107
|
+
### Replace
|
|
108
|
+
```go
|
|
109
|
+
func TestValidateTaskLifecycle_allowsPlannedWithoutPhase(t *testing.T) {
|
|
110
|
+
task := Task{Column: ColumnPlanned}
|
|
111
|
+
if err := ValidateTaskLifecycle(task); err != nil {
|
|
112
|
+
t.Fatalf("ValidateTaskLifecycle() error = %v", err)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
func TestValidateTaskLifecycle_allowsInProgressWithPhase(t *testing.T) {
|
|
117
|
+
task := Task{Column: ColumnInProgress, Stage: StageAudit}
|
|
118
|
+
if err := ValidateTaskLifecycle(task); err != nil {
|
|
119
|
+
t.Fatalf("ValidateTaskLifecycle() error = %v", err)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
func TestValidateTaskLifecycle_rejectsUnknownStatus(t *testing.T) {
|
|
124
|
+
task := Task{Column: "review"}
|
|
125
|
+
if err := ValidateTaskLifecycle(task); err == nil {
|
|
126
|
+
t.Fatal("ValidateTaskLifecycle() expected unknown status error")
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
func TestValidateTaskLifecycle_rejectsPhaseOutsideInProgress(t *testing.T) {
|
|
131
|
+
task := Task{Column: ColumnPlanned, Stage: StageBuild}
|
|
132
|
+
if err := ValidateTaskLifecycle(task); err == nil {
|
|
133
|
+
t.Fatal("ValidateTaskLifecycle() expected phase/status error")
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### With
|
|
139
|
+
```go
|
|
140
|
+
func TestValidateTaskLifecycle_allowsPlannedWithoutPhase(t *testing.T) {
|
|
141
|
+
task := Task{Column: ColumnPlanned}
|
|
142
|
+
if err := ValidateTaskLifecycle(&task); err != nil {
|
|
143
|
+
t.Fatalf("ValidateTaskLifecycle() error = %v", err)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
func TestValidateTaskLifecycle_defaultsInProgressWithoutPhase(t *testing.T) {
|
|
148
|
+
task := Task{Column: ColumnInProgress}
|
|
149
|
+
if err := ValidateTaskLifecycle(&task); err != nil {
|
|
150
|
+
t.Fatalf("ValidateTaskLifecycle() error = %v", err)
|
|
151
|
+
}
|
|
152
|
+
if task.Stage != StageBuild {
|
|
153
|
+
t.Fatalf("Task.Stage = %q, want %q", task.Stage, StageBuild)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
func TestValidateTaskLifecycle_allowsInProgressWithPhase(t *testing.T) {
|
|
158
|
+
task := Task{Column: ColumnInProgress, Stage: StageAudit}
|
|
159
|
+
if err := ValidateTaskLifecycle(&task); err != nil {
|
|
160
|
+
t.Fatalf("ValidateTaskLifecycle() error = %v", err)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
func TestValidateTaskLifecycle_rejectsUnknownStatus(t *testing.T) {
|
|
165
|
+
task := Task{Column: "review"}
|
|
166
|
+
if err := ValidateTaskLifecycle(&task); err == nil {
|
|
167
|
+
t.Fatal("ValidateTaskLifecycle() expected unknown status error")
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
func TestValidateTaskLifecycle_rejectsPhaseOutsideInProgress(t *testing.T) {
|
|
172
|
+
task := Task{Column: ColumnPlanned, Stage: StageBuild}
|
|
173
|
+
if err := ValidateTaskLifecycle(&task); err == nil {
|
|
174
|
+
t.Fatal("ValidateTaskLifecycle() expected phase/status error")
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Target File
|
|
180
|
+
internal/data/write_test.go
|
|
181
|
+
|
|
182
|
+
### Replace
|
|
183
|
+
```go
|
|
184
|
+
func TestWriteTaskStatus_addsPhaseWhenStagePresent(t *testing.T) {
|
|
185
|
+
dir := t.TempDir()
|
|
186
|
+
path := filepath.Join(dir, "task.md")
|
|
187
|
+
content := `---
|
|
188
|
+
id: E01/T005
|
|
189
|
+
status: in_progress
|
|
190
|
+
objective: "No phase yet"
|
|
191
|
+
---`
|
|
192
|
+
|
|
193
|
+
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
|
|
194
|
+
t.Fatal(err)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
fi, _ := os.Stat(path)
|
|
198
|
+
|
|
199
|
+
task := &Task{
|
|
200
|
+
ID: "E01/T005",
|
|
201
|
+
Column: ColumnInProgress,
|
|
202
|
+
Stage: StageAudit,
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if err := WriteTaskStatus(path, task, fi.ModTime()); err != nil {
|
|
206
|
+
t.Fatalf("WriteTaskStatus() error = %v", err)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
result, _ := os.ReadFile(path)
|
|
210
|
+
|
|
211
|
+
if !strings.Contains(string(result), "phase: audit") {
|
|
212
|
+
t.Error("phase field should be added when stage is set")
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### With
|
|
218
|
+
```go
|
|
219
|
+
func TestWriteTaskStatus_addsPhaseWhenStagePresent(t *testing.T) {
|
|
220
|
+
dir := t.TempDir()
|
|
221
|
+
path := filepath.Join(dir, "task.md")
|
|
222
|
+
content := `---
|
|
223
|
+
id: E01/T005
|
|
224
|
+
status: in_progress
|
|
225
|
+
objective: "No phase yet"
|
|
226
|
+
---`
|
|
227
|
+
|
|
228
|
+
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
|
|
229
|
+
t.Fatal(err)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
fi, _ := os.Stat(path)
|
|
233
|
+
|
|
234
|
+
task := &Task{
|
|
235
|
+
ID: "E01/T005",
|
|
236
|
+
Column: ColumnInProgress,
|
|
237
|
+
Stage: StageAudit,
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if err := WriteTaskStatus(path, task, fi.ModTime()); err != nil {
|
|
241
|
+
t.Fatalf("WriteTaskStatus() error = %v", err)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
result, _ := os.ReadFile(path)
|
|
245
|
+
|
|
246
|
+
if !strings.Contains(string(result), "phase: audit") {
|
|
247
|
+
t.Error("phase field should be added when stage is set")
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
func TestWriteTaskStatus_defaultsInProgressPhaseWhenStageMissing(t *testing.T) {
|
|
252
|
+
dir := t.TempDir()
|
|
253
|
+
path := filepath.Join(dir, "task.md")
|
|
254
|
+
content := `---
|
|
255
|
+
id: E01/T010
|
|
256
|
+
status: planned
|
|
257
|
+
objective: "No phase yet"
|
|
258
|
+
---`
|
|
259
|
+
|
|
260
|
+
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
|
|
261
|
+
t.Fatal(err)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
fi, _ := os.Stat(path)
|
|
265
|
+
|
|
266
|
+
task := &Task{
|
|
267
|
+
ID: "E01/T010",
|
|
268
|
+
Column: ColumnInProgress,
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if err := WriteTaskStatus(path, task, fi.ModTime()); err != nil {
|
|
272
|
+
t.Fatalf("WriteTaskStatus() error = %v", err)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
result, _ := os.ReadFile(path)
|
|
276
|
+
|
|
277
|
+
if !strings.Contains(string(result), "phase: build") {
|
|
278
|
+
t.Error("phase field should default to build for in_progress writes")
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Target File
|
|
284
|
+
.savepoint/Design.md
|
|
285
|
+
|
|
286
|
+
### Replace
|
|
287
|
+
```md
|
|
288
|
+
- **Go data-reader boundary:** established in epic `E02-data-readers` (2026-05-01). `internal/data` owns Savepoint file parsing and discovery for the Go implementation: task frontmatter models, markdown YAML extraction, router state parsing, config theme defaults, release/epic/task directory listing, and boundary error sentinels.
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### With
|
|
292
|
+
```md
|
|
293
|
+
- **Go data-reader boundary:** established in epic `E02-data-readers` (2026-05-01). `internal/data` owns Savepoint file parsing and discovery for the Go implementation: task frontmatter models, markdown YAML extraction, router state parsing, config theme defaults, release/epic/task directory listing, task lifecycle validation/defaulting, write-time status validation, and boundary error sentinels.
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Target File
|
|
297
|
+
AGENTS.md
|
|
298
|
+
|
|
299
|
+
### Replace
|
|
300
|
+
```md
|
|
301
|
+
| `internal/data/` | Task/router models, frontmatter parsing, discovery |
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### With
|
|
305
|
+
```md
|
|
306
|
+
| `internal/data/` | Task/router models, frontmatter parsing, lifecycle validation/defaulting, discovery |
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Target File
|
|
310
|
+
.savepoint/releases/v1.1/epics/E12-validation-fix/E12-Detail.md
|
|
311
|
+
|
|
312
|
+
### Replace
|
|
313
|
+
```md
|
|
314
|
+
**Out of scope:**
|
|
315
|
+
- New UI features
|
|
316
|
+
- New commands
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### With
|
|
320
|
+
```md
|
|
321
|
+
**Out of scope:**
|
|
322
|
+
- New UI features
|
|
323
|
+
- New commands
|
|
324
|
+
|
|
325
|
+
## Implemented as
|
|
326
|
+
|
|
327
|
+
- Parser-side defaults live in `internal/data/parser.go`: empty status/column normalizes to `planned`, and parsed `in_progress` tasks without phase/stage default to `build`.
|
|
328
|
+
- Lifecycle validation and user-facing hints live in `internal/data/lifecycle.go`.
|
|
329
|
+
- Status writes validate lifecycle rules through `internal/data/write.go`; audit follow-up must persist the default `phase: build` when callers write `in_progress` with no stage.
|
|
330
|
+
- Regression coverage lives in `internal/data/parser_test.go`, `internal/data/lifecycle_test.go`, and `internal/data/write_test.go`.
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Target File
|
|
334
|
+
.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T001-default-phase.md
|
|
335
|
+
|
|
336
|
+
### Replace
|
|
337
|
+
```md
|
|
338
|
+
# T001: Default Phase to Build
|
|
339
|
+
|
|
340
|
+
## Acceptance Criteria
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### With
|
|
344
|
+
```md
|
|
345
|
+
# T001: Default Phase to Build
|
|
346
|
+
|
|
347
|
+
## Context Files
|
|
348
|
+
|
|
349
|
+
- `internal/data/parser.go`
|
|
350
|
+
- `internal/data/lifecycle.go`
|
|
351
|
+
- `internal/data/parser_test.go`
|
|
352
|
+
|
|
353
|
+
## Acceptance Criteria
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Target File
|
|
357
|
+
.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T002-default-status.md
|
|
358
|
+
|
|
359
|
+
### Replace
|
|
360
|
+
```md
|
|
361
|
+
# T002: Default Status to Planned
|
|
362
|
+
|
|
363
|
+
## Acceptance Criteria
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### With
|
|
367
|
+
```md
|
|
368
|
+
# T002: Default Status to Planned
|
|
369
|
+
|
|
370
|
+
## Context Files
|
|
371
|
+
|
|
372
|
+
- `internal/data/parser.go`
|
|
373
|
+
- `internal/data/parser_test.go`
|
|
374
|
+
|
|
375
|
+
## Acceptance Criteria
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### Target File
|
|
379
|
+
.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T003-better-errors.md
|
|
380
|
+
|
|
381
|
+
### Replace
|
|
382
|
+
```md
|
|
383
|
+
# T003: Better Error Messages
|
|
384
|
+
|
|
385
|
+
## Acceptance Criteria
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### With
|
|
389
|
+
```md
|
|
390
|
+
# T003: Better Error Messages
|
|
391
|
+
|
|
392
|
+
## Context Files
|
|
393
|
+
|
|
394
|
+
- `internal/data/lifecycle.go`
|
|
395
|
+
- `internal/data/lifecycle_test.go`
|
|
396
|
+
|
|
397
|
+
## Acceptance Criteria
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### Target File
|
|
401
|
+
.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T004-validate-on-write.md
|
|
402
|
+
|
|
403
|
+
### Replace
|
|
404
|
+
```md
|
|
405
|
+
# T004: Validate On Write
|
|
406
|
+
|
|
407
|
+
## Acceptance Criteria
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### With
|
|
411
|
+
```md
|
|
412
|
+
# T004: Validate On Write
|
|
413
|
+
|
|
414
|
+
## Context Files
|
|
415
|
+
|
|
416
|
+
- `internal/data/write.go`
|
|
417
|
+
- `internal/data/write_test.go`
|
|
418
|
+
- `internal/data/lifecycle.go`
|
|
419
|
+
|
|
420
|
+
## Acceptance Criteria
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
### Target File
|
|
424
|
+
.savepoint/releases/v1.1/epics/E12-validation-fix/tasks/T005-tests.md
|
|
425
|
+
|
|
426
|
+
### Replace
|
|
427
|
+
```md
|
|
428
|
+
# T005: Tests
|
|
429
|
+
|
|
430
|
+
## Acceptance Criteria
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### With
|
|
434
|
+
```md
|
|
435
|
+
# T005: Tests
|
|
436
|
+
|
|
437
|
+
## Context Files
|
|
438
|
+
|
|
439
|
+
- `internal/data/parser_test.go`
|
|
440
|
+
- `internal/data/lifecycle_test.go`
|
|
441
|
+
- `internal/data/write_test.go`
|
|
442
|
+
|
|
443
|
+
## Acceptance Criteria
|
|
444
|
+
```
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: epic-design
|
|
3
|
+
status: audited
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# E12: Task File Validation & Auto-Fix
|
|
7
|
+
|
|
8
|
+
## Purpose
|
|
9
|
+
|
|
10
|
+
Fix task file parsing to provide helpful defaults and clear error messages when validation fails. Currently tasks fail with cryptic errors like `"invalid in_progress phase "": use build, test, or audit"`.
|
|
11
|
+
|
|
12
|
+
## What this epic adds
|
|
13
|
+
|
|
14
|
+
- Default `phase: build` when status=in_progress but phase missing
|
|
15
|
+
- Default `status: planned` when both status/column missing
|
|
16
|
+
- Better error hints with suggested fixes
|
|
17
|
+
- Validate on write with helpful messages
|
|
18
|
+
|
|
19
|
+
## Components
|
|
20
|
+
|
|
21
|
+
| Module | Purpose |
|
|
22
|
+
|--------|---------|
|
|
23
|
+
| `internal/data/parser.go` | Default phase to "build" in firstStage() |
|
|
24
|
+
| `internal/data/parser.go` | Default status to "planned" in normalizeColumn() |
|
|
25
|
+
| `internal/data/lifecycle.go` | Better error messages with hints |
|
|
26
|
+
| `internal/data/write.go` | Validate on write |
|
|
27
|
+
|
|
28
|
+
## Boundaries
|
|
29
|
+
|
|
30
|
+
**In scope:**
|
|
31
|
+
- Default phase for in_progress tasks
|
|
32
|
+
- Default status for tasks
|
|
33
|
+
- Better error messages
|
|
34
|
+
- Write-time validation
|
|
35
|
+
|
|
36
|
+
**Out of scope:**
|
|
37
|
+
- New UI features
|
|
38
|
+
- New commands
|
|
39
|
+
|
|
40
|
+
## Implemented as
|
|
41
|
+
|
|
42
|
+
- Parser-side defaults live in `internal/data/parser.go`: empty status/column normalizes to `planned`, and parsed `in_progress` tasks without phase/stage default to `build`.
|
|
43
|
+
- Lifecycle validation and user-facing hints live in `internal/data/lifecycle.go`.
|
|
44
|
+
- Status writes validate lifecycle rules through `internal/data/write.go` and persist the default `phase: build` when callers write `in_progress` with no stage.
|
|
45
|
+
- Regression coverage lives in `internal/data/parser_test.go`, `internal/data/lifecycle_test.go`, and `internal/data/write_test.go`.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: E12-validation-fix/T001-default-phase
|
|
3
|
+
status: done
|
|
4
|
+
objective: Default phase to build when status=in_progress but phase missing
|
|
5
|
+
depends_on: []
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# T001: Default Phase to Build
|
|
9
|
+
|
|
10
|
+
## Context Files
|
|
11
|
+
|
|
12
|
+
- `internal/data/parser.go`
|
|
13
|
+
- `internal/data/lifecycle.go`
|
|
14
|
+
- `internal/data/parser_test.go`
|
|
15
|
+
|
|
16
|
+
## Acceptance Criteria
|
|
17
|
+
|
|
18
|
+
- [x] firstStage() returns "build" when both stage and phase are empty
|
|
19
|
+
- [x] Tasks without phase field parse successfully
|
|
20
|
+
- [x] Make build passes
|
|
21
|
+
- [x] go test ./... passes
|
|
22
|
+
|
|
23
|
+
## Implementation Plan
|
|
24
|
+
|
|
25
|
+
- [x] Set default at parse time after validation:
|
|
26
|
+
- In parser.go: ParseTaskFile() line 60-63
|
|
27
|
+
- After validation: `if task.Column == in_progress && task.Stage == "" { task.Stage = StageBuild }`
|
|
28
|
+
- [x] Run `make build` to verify change
|
|
29
|
+
- [x] Run `go test ./...` all pass
|
|
30
|
+
|
|
31
|
+
Notes:
|
|
32
|
+
- Default set at parsing time, not validation time
|
|
33
|
+
- Works with both planned and done tasks (phase ignored)
|
|
34
|
+
- Better error messages in lifecycle.go already implemented
|
|
35
|
+
- Tests added for new behavior
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: E12-validation-fix/T002-default-status
|
|
3
|
+
status: done
|
|
4
|
+
objective: Default status to planned when both status and column missing
|
|
5
|
+
depends_on:
|
|
6
|
+
- E12-validation-fix/T001-default-phase
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# T002: Default Status to Planned
|
|
10
|
+
|
|
11
|
+
## Context Files
|
|
12
|
+
|
|
13
|
+
- `internal/data/parser.go`
|
|
14
|
+
- `internal/data/parser_test.go`
|
|
15
|
+
|
|
16
|
+
## Acceptance Criteria
|
|
17
|
+
|
|
18
|
+
- [x] Already implemented in normalizeColumn() - returns ColumnPlanned for empty
|
|
19
|
+
- [x] go test ./... passes
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: E12-validation-fix/T003-better-errors
|
|
3
|
+
status: done
|
|
4
|
+
objective: Improve error messages with hints for common issues
|
|
5
|
+
depends_on:
|
|
6
|
+
- E12-validation-fix/T001-default-phase
|
|
7
|
+
- E12-validation-fix/T002-default-status
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# T003: Better Error Messages
|
|
11
|
+
|
|
12
|
+
## Context Files
|
|
13
|
+
|
|
14
|
+
- `internal/data/lifecycle.go`
|
|
15
|
+
- `internal/data/lifecycle_test.go`
|
|
16
|
+
|
|
17
|
+
## Acceptance Criteria
|
|
18
|
+
|
|
19
|
+
- [x] Error messages include suggested fix
|
|
20
|
+
- [x] Show which field is problematic with context
|
|
21
|
+
- [x] go test ./... passes
|
|
22
|
+
|
|
23
|
+
## Implementation Plan
|
|
24
|
+
|
|
25
|
+
- [x] Improved errors in lifecycle.go (lines 7, 16, 22)
|
|
26
|
+
- Examples of better messages:
|
|
27
|
+
- `invalid status %q: use planned, in_progress, or done. Add 'status: planned'...`
|
|
28
|
+
- `invalid phase %q: use build, test, or audit. Add 'phase: build'...`
|
|
29
|
+
- [x] Tests pass
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: E12-validation-fix/T004-validate-on-write
|
|
3
|
+
status: done
|
|
4
|
+
objective: Add validation when writing task files
|
|
5
|
+
depends_on:
|
|
6
|
+
- E12-validation-fix/T003-better-errors
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# T004: Validate On Write
|
|
10
|
+
|
|
11
|
+
## Context Files
|
|
12
|
+
|
|
13
|
+
- `internal/data/write.go`
|
|
14
|
+
- `internal/data/write_test.go`
|
|
15
|
+
- `internal/data/lifecycle.go`
|
|
16
|
+
|
|
17
|
+
## Acceptance Criteria
|
|
18
|
+
|
|
19
|
+
- [x] Already validated in write path via WriteTaskStatus
|
|
20
|
+
- [x] go test ./... passes
|
|
21
|
+
|
|
22
|
+
## Implementation Plan
|
|
23
|
+
|
|
24
|
+
- [x] Validation already runs via ParseTaskFile in write.go
|
|
25
|
+
- [x] Tests pass
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: E12-validation-fix/T005-tests
|
|
3
|
+
status: done
|
|
4
|
+
objective: Verify all validation scenarios work correctly
|
|
5
|
+
depends_on:
|
|
6
|
+
- E12-validation-fix/T001-default-phase
|
|
7
|
+
- E12-validation-fix/T002-default-status
|
|
8
|
+
- E12-validation-fix/T003-better-errors
|
|
9
|
+
- E12-validation-fix/T004-validate-on-write
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# T005: Tests
|
|
13
|
+
|
|
14
|
+
## Context Files
|
|
15
|
+
|
|
16
|
+
- `internal/data/parser_test.go`
|
|
17
|
+
- `internal/data/lifecycle_test.go`
|
|
18
|
+
- `internal/data/write_test.go`
|
|
19
|
+
|
|
20
|
+
## Acceptance Criteria
|
|
21
|
+
|
|
22
|
+
- [x] All new defaults work in tests
|
|
23
|
+
- [x] Error messages tested
|
|
24
|
+
- [x] go test ./... passes
|
|
25
|
+
|
|
26
|
+
## Implementation Plan
|
|
27
|
+
|
|
28
|
+
- [x] Run `go test ./...` - all pass
|
|
29
|
+
- [x] Updated lifecycle tests for new behavior:
|
|
30
|
+
- TestValidateTaskLifecycle_allowsPlannedWithoutPhase ✓
|
|
31
|
+
- TestValidateTaskLifecycle_allowsInProgressWithPhase ✓
|
|
32
|
+
- TestValidateTaskLifecycle_rejectsUnknownStatus ✓
|
|
33
|
+
- TestValidateTaskLifecycle_rejectsPhaseOutsideInProgress ✓
|
|
34
|
+
- [x] Updated parser tests:
|
|
35
|
+
- TestParseTaskFile_includesDefaultBuildForInProgress ✓
|
|
36
|
+
- TestParseTaskFile_allowsPhaseOutsideInProgress ✓
|
|
37
|
+
- [x] All other existing tests pass
|