fix(config): prevent workflow.research reset during milestone transitions
Two bugs caused workflow.research to silently reset to false: 1. new-milestone.md unconditionally overwrote workflow.research via config-set after asking about research — coupling a per-milestone decision to a persistent user preference. Now the research question is per-invocation only; persistent config changes via /gsd:settings. 2. verify.cjs health repair (createConfig/resetConfig) used flat keys (research, plan_checker, verifier) instead of the canonical nested workflow object from config.cjs, also missing branch templates, nyquist_validation, and brave_search fields. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,10 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||
### Added
|
||||
- **Node repair operator** (`workflows/node-repair.md`) — autonomous recovery when task verification fails. Instead of immediately asking the user, the executor attempts structured repair: RETRY (different approach), DECOMPOSE (break into sub-tasks), or PRUNE (skip with justification). Only escalates to the user when the repair budget is exhausted or an architectural decision is needed. Repair budget defaults to 2 attempts per task; configurable via `workflow.node_repair_budget`. Disable entirely with `workflow.node_repair: false` to restore original behavior.
|
||||
|
||||
### 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
|
||||
|
||||
@@ -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' });
|
||||
|
||||
@@ -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":**
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
@@ -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', () => {
|
||||
@@ -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', () => {
|
||||
|
||||
Reference in New Issue
Block a user