Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [Unreleased]

### Fixed
- `/gsd:new-milestone` no longer overwrites `workflow.research` config — milestone research decision is now per-invocation, persistent preference only changes via `/gsd:settings`
- `/gsd:health --repair` now creates config.json with correct nested `workflow` structure matching `config-ensure-section` canonical format

## [1.22.4] - 2026-03-03

### Added
Expand Down
12 changes: 9 additions & 3 deletions get-shit-done/bin/lib/verify.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -728,10 +728,16 @@ function cmdValidateHealth(cwd, options, raw) {
commit_docs: true,
search_gitignored: false,
branching_strategy: 'none',
research: true,
plan_checker: true,
verifier: true,
phase_branch_template: 'gsd/phase-{phase}-{slug}',
milestone_branch_template: 'gsd/{milestone}-{slug}',
workflow: {
research: true,
plan_check: true,
verifier: true,
nyquist_validation: true,
},
parallelization: true,
brave_search: false,
};
fs.writeFileSync(configPath, JSON.stringify(defaults, null, 2), 'utf-8');
repairActions.push({ action: repair, success: true, path: 'config.json' });
Expand Down
20 changes: 11 additions & 9 deletions get-shit-done/workflows/new-milestone.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,21 +86,23 @@ Extract from init JSON: `researcher_model`, `synthesizer_model`, `roadmapper_mod

## 8. Research Decision

Check `research_enabled` from init JSON (loaded from config).

**If `research_enabled` is `true`:**

AskUserQuestion: "Research the domain ecosystem for new features before defining requirements?"
- "Research first (Recommended)" — Discover patterns, features, architecture for NEW capabilities
- "Skip research" — Go straight to requirements
- "Skip research for this milestone" — Go straight to requirements (does not change your default)

**Persist choice to config** (so future `/gsd:plan-phase` honors it):
**If `research_enabled` is `false`:**

```bash
# If "Research first": persist true
node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config-set workflow.research true
AskUserQuestion: "Research the domain ecosystem for new features before defining requirements?"
- "Skip research (current default)" — Go straight to requirements
- "Research first" — Discover patterns, features, architecture for NEW capabilities

# If "Skip research": persist false
node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config-set workflow.research false
```
**IMPORTANT:** Do NOT persist this choice to config.json. The `workflow.research` setting is a persistent user preference that controls plan-phase behavior across the project. Changing it here would silently alter future `/gsd:plan-phase` behavior. To change the default, use `/gsd:settings`.

**If "Research first":**
**If user chose "Research first":**

```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Expand Down
13 changes: 12 additions & 1 deletion tests/verify-health.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,15 @@ describe('validate health --repair command', () => {
assert.ok(fs.existsSync(configPath), 'config.json should now exist on disk');
const diskConfig = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
assert.strictEqual(diskConfig.model_profile, 'balanced', 'default model_profile should be balanced');
// Verify nested workflow structure matches config.cjs canonical format
assert.ok(diskConfig.workflow, 'config should have nested workflow object');
assert.strictEqual(diskConfig.workflow.research, true, 'workflow.research should default to true');
assert.strictEqual(diskConfig.workflow.plan_check, true, 'workflow.plan_check should default to true');
assert.strictEqual(diskConfig.workflow.verifier, true, 'workflow.verifier should default to true');
assert.strictEqual(diskConfig.workflow.nyquist_validation, true, 'workflow.nyquist_validation should default to true');
// Verify branch templates are present
assert.strictEqual(diskConfig.phase_branch_template, 'gsd/phase-{phase}-{slug}');
assert.strictEqual(diskConfig.milestone_branch_template, 'gsd/{milestone}-{slug}');
});

test('resets config.json when JSON is invalid', () => {
Expand All @@ -545,9 +554,11 @@ describe('validate health --repair command', () => {
const resetAction = output.repairs_performed.find(r => r.action === 'resetConfig');
assert.ok(resetAction, `Expected resetConfig action: ${JSON.stringify(output.repairs_performed)}`);

// Verify config.json is now valid JSON
// Verify config.json is now valid JSON with correct nested structure
const diskConfig = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
assert.ok(typeof diskConfig === 'object', 'config.json should be valid JSON after repair');
assert.ok(diskConfig.workflow, 'reset config should have nested workflow object');
assert.strictEqual(diskConfig.workflow.research, true, 'workflow.research should be true after reset');
});

test('regenerates STATE.md when missing', () => {
Expand Down