feat(domain): add goal phase field (spec 024)#54
Merged
Conversation
There was a problem hiding this comment.
Based on my review, the implementation is solid. Let me compile the findings:
Summary: The PR correctly implements spec 024 — adding a GoalPhase enum with 4 canonical values (todo/planning/execution/done) and a phase frontmatter field on goals. The lazy-migration strategy (tolerate legacy hand-typed values on read, reject them on re-write) is intentional and well-documented. The nil pointer deletion behavior is correct. Test coverage is thorough, including regression tests for unrelated mutations. No security concerns.
{
"verdict": "approve",
"summary": "The GoalPhase feature is correctly implemented per spec 024. The 4-value enum mirrors TaskPhase shape without aliases, the lazy-migration strategy correctly tolerates legacy values on read while rejecting them on write, and test coverage is comprehensive including regression tests. One minor issue: goal_phase.go has a 2025 copyright year despite being a new file committed in 2026.",
"comments": [
{
"file": "pkg/domain/goal_phase.go",
"line": 1,
"severity": "minor",
"message": "Copyright year is 2025 but file was committed in 2026 — should be (c) 2026"
}
],
"concerns_addressed": [
"correctness: Phase() getter returns raw value without validation on read — confirmed intentional, clearly documented in method comment, legacy hand-typed values survive display and round-trip",
"correctness: setPhaseField validates on write but not on read — confirmed intentional lazy-migration strategy, test at goal_frontmatter_test.go:350-355 verifies legacy alias rejection on re-set",
"correctness: GoalPhase mirrors TaskPhase shape but has no aliases (4-of-7 subset) — confirmed intentional per spec 024, goal_phase_test.go:49-55 verifies in_progress is correctly rejected",
"correctness: nil pointer to SetPhase deletes the key — confirmed correct behavior, goal_frontmatter_test.go:320-325 verifies deletion removes the key entirely",
"tests: legacy goal round-trip regression test — confirmed sufficient, goal_frontmatter_test.go:375-383 verifies no phase injection on unrelated mutations",
"security: Validate uses closed 4-value enum, no user-controlled string interpolation in error messages — confirmed, errors.Wrapf at goal_phase.go:53 safely formats the phase value without concatenation"
]
}
bborbe
added a commit
that referenced
this pull request
Jul 16, 2026
feat(domain): add goal phase field (spec 024)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a validated
phasefrontmatter field to the Goal domain type (todo/planning/execution/done), mirroring the task-sidePhase. Data-layer foundation for the Phase-Gated Goal Flow workflow — theplan-goal/execute-goalgate commands (separate, markdown) consume this field.What
GoalPhaseenum type +Validate(pkg/domain/goal_phase.go), shape-mirrored fromTaskPhase, no aliasesPhase()getter /SetPhasesetter +phasecase inGoalFrontmatterget/set (pkg/domain/goal_frontmatter.go) — rides the existinggoal set/goal showcommands, no new commandphaseparse and operate unchanged; missing reads as empty (no default-to-todo)Phasetype left byte-for-byte unchangedTesting
pkg/domainGinkgo suite — enumDescribeTable(4 canonical +bogus/empty/in_progressrejections) + frontmatter getter/setter round-tripmake precommitgreen (golangci-lint 0 issues, security clean, CHANGELOG valid)verify-spec 024: all 9 acceptance criteria proven against a freshly-built binary — field writes to frontmatter,--output jsonsurfaces it,bogusrejected with file unchanged, no-phase goal cleanSpec
dark-factory spec
024-goal-phase-field(seespecs/completed/).