GSD Version
1.10.0 (reproduced on next @ 86bebce; originally hit on an installed 1.6.0)
Runtime
Claude Code
Operating System
Windows
Node.js Version
v24.18.0
Shell
Git Bash
Installation Method
npx @opengsd/gsd-core
What happened?
state add-decision without --phase writes a literal - [Phase ?]: entry into
STATE.md, even when STATE.md's own frontmatter carries current_phase.
The ? is persisted. A decision entry is a permanent record, so the provenance of
that decision is lost unless a human notices the placeholder and hand-edits it.
I hit this across an 11-plan phase. Every executor agent that recorded a decision
without passing --phase produced a [Phase ?] entry, and each one hand-repaired
it — so the same defect was silently paid for eleven times. The information needed
to render it correctly was in the file being written, three lines above the
insertion point.
Root cause is a single expression at src/state.cts:835:
const entry = `- [Phase ${phase || '?'}]: ${summaryText}...`;
cmdStateAddDecision never consults frontmatter. state-command-router.cts:142
passes --phase straight through with no fallback. The codebase already has the
resolution it needs — cmdStatePruneDecisions reads fm.current_phase with a
scalar guard at src/state.cts:3111-3117 — it just isn't reused here.
What did you expect?
--phase omitted → resolve from frontmatter current_phase, so the entry reads
- [Phase 3]: ....
Explicit --phase should still win. And ? should still be written when no phase
is resolvable anywhere — an unknown phase should stay visibly unknown, never
guessed.
Steps to reproduce
mkdir -p repro/.planning && cd repro
cat > .planning/STATE.md <<'MD'
---
gsd_state_version: 1.0
current_phase: 3
current_phase_name: Sourcing Coverage
status: executing
---
# Project State
## Decisions
MD
gsd-tools state add-decision --summary "Dedup threshold set to 0.85 by sweep"
sed -n '/^## Decisions/,$p' .planning/STATE.md
Error output / logs
{
"added": true,
"decision": "- [Phase ?]: Dedup threshold set to 0.85 by sweep"
}
## Decisions
- [Phase ?]: Dedup threshold set to 0.85 by sweep
Expected - [Phase 3]: ... — current_phase: 3 is in the frontmatter of the very
file being written.
GSD Configuration
Not relevant — reproduces on a bare STATE.md with no config.json present.
GSD State (if relevant)
The four-line STATE.md in the repro above is sufficient.
How often does this happen?
Always
Impact
Medium — wrong output, silently persisted
Workaround (if any)
Always pass --phase explicitly, or hand-edit STATE.md afterwards. Neither
survives an agent that does not know to do it, which is the case the command is
mostly invoked from.
Additional context
This is the same family as #2400 (frontmatter/body state contradictions), which
was fixed for one writer. It may also be in scope for the #3180 epic on giving the
.planning/ semantic model a single owner — happy to close this if it is already
covered there.
I have a fix ready (three-line resolution reusing the existing scalar guard, plus
three regression tests: the fix, an explicit---phase precedence guard, and a
control asserting ? is retained when genuinely unresolvable). Full state.test.cjs
passes at 330/330. Will open a PR once this carries confirmed-bug, per
CONTRIBUTING.
Privacy Checklist
GSD Version
1.10.0 (reproduced on
next@ 86bebce; originally hit on an installed 1.6.0)Runtime
Claude Code
Operating System
Windows
Node.js Version
v24.18.0
Shell
Git Bash
Installation Method
npx @opengsd/gsd-core
What happened?
state add-decisionwithout--phasewrites a literal- [Phase ?]:entry intoSTATE.md, even when STATE.md's own frontmatter carries
current_phase.The
?is persisted. A decision entry is a permanent record, so the provenance ofthat decision is lost unless a human notices the placeholder and hand-edits it.
I hit this across an 11-plan phase. Every executor agent that recorded a decision
without passing
--phaseproduced a[Phase ?]entry, and each one hand-repairedit — so the same defect was silently paid for eleven times. The information needed
to render it correctly was in the file being written, three lines above the
insertion point.
Root cause is a single expression at
src/state.cts:835:cmdStateAddDecisionnever consults frontmatter.state-command-router.cts:142passes
--phasestraight through with no fallback. The codebase already has theresolution it needs —
cmdStatePruneDecisionsreadsfm.current_phasewith ascalar guard at
src/state.cts:3111-3117— it just isn't reused here.What did you expect?
--phaseomitted → resolve from frontmattercurrent_phase, so the entry reads- [Phase 3]: ....Explicit
--phaseshould still win. And?should still be written when no phaseis resolvable anywhere — an unknown phase should stay visibly unknown, never
guessed.
Steps to reproduce
Error output / logs
Expected
- [Phase 3]: ...—current_phase: 3is in the frontmatter of the veryfile being written.
GSD Configuration
Not relevant — reproduces on a bare STATE.md with no config.json present.
GSD State (if relevant)
The four-line STATE.md in the repro above is sufficient.
How often does this happen?
Always
Impact
Medium — wrong output, silently persisted
Workaround (if any)
Always pass
--phaseexplicitly, or hand-edit STATE.md afterwards. Neithersurvives an agent that does not know to do it, which is the case the command is
mostly invoked from.
Additional context
This is the same family as #2400 (frontmatter/body state contradictions), which
was fixed for one writer. It may also be in scope for the #3180 epic on giving the
.planning/semantic model a single owner — happy to close this if it is alreadycovered there.
I have a fix ready (three-line resolution reusing the existing scalar guard, plus
three regression tests: the fix, an explicit-
--phaseprecedence guard, and acontrol asserting
?is retained when genuinely unresolvable). Fullstate.test.cjspasses at 330/330. Will open a PR once this carries
confirmed-bug, perCONTRIBUTING.
Privacy Checklist