Skip to content

state add-decision persists a literal [Phase ?] when --phase is omitted, though STATE.md frontmatter carries current_phase #3231

Description

@LayerC0de

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

  • I have removed all secrets, tokens, API keys, and credentials
  • I have removed or anonymized personal/company identifying information

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingconfirmed-bugVerified reproducible bugduplicateThis issue or pull request already exists

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions