* feat(#163): tighten gsd-roadmapper granularity defaults to reduce thin-phase fragmentation Tighten the Granularity Calibration buckets in gsd-roadmapper (Coarse 3-5->2-4, Standard 5-8->4-6, Fine 8-12->6-10) and append inline Key guidance naming the thin-phase failure pattern (single requirement / internal-quality goal / task-shaped success criteria) with instruction to fold into a neighbor rather than create a standalone phase. Implements the maintainer-approved proposal verbatim. Update the canonical English docs that hardcoded the old phase-count numbers: docs/CONFIGURATION.md and docs/FEATURES.md. Translated docs are community-maintained and are not updated per-PR (CONTRIBUTING.md language policy). Prompt/doc text only; no code, format, or downstream-consumer changes. Agent size-budget and skills-awareness tests pass; full suite green. Closes #163 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(#163): add Changed changeset for roadmapper granularity tightening Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(#163): lock tightened gsd-roadmapper granularity buckets source-text-is-the-product test asserting the Granularity Calibration table holds the tightened ranges (Coarse 2-4, Standard 4-6, Fine 6-10), that no row maps to an old bucket, and that the Key paragraph carries the thin-phase folding guidance. Would fail if the values regress. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
5
.changeset/quick-pumas-fly.md
Normal file
5
.changeset/quick-pumas-fly.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
type: Changed
|
||||
pr: 591
|
||||
---
|
||||
**`gsd-roadmapper` granularity defaults tightened to reduce thin-phase fragmentation.** Coarse 3-5 -> 2-4, Standard 4-6 (was 5-8), Fine 6-10 (was 8-12). New inline guidance below the Granularity Calibration table names the thin-phase failure pattern (single requirement, internal-quality goal, task-shaped success criteria) and instructs the agent to fold into the most-related neighbor rather than create a standalone phase. Affects `/gsd-new-project`, `/gsd-new-milestone`, and `/gsd-plan-milestone-gaps`.
|
||||
@@ -215,11 +215,11 @@ Read granularity from config.json. Granularity controls compression tolerance.
|
||||
|
||||
| Granularity | Typical Phases | What It Means |
|
||||
|-------------|----------------|---------------|
|
||||
| Coarse | 3-5 | Combine aggressively, critical path only |
|
||||
| Standard | 5-8 | Balanced grouping |
|
||||
| Fine | 8-12 | Let natural boundaries stand |
|
||||
| Coarse | 2-4 | Combine aggressively, critical path only |
|
||||
| Standard | 4-6 | Balanced grouping (tightened from 5-8 in 2026-05; downstream observation that the prior baseline encouraged ~15-20% over-fragmentation, often manifesting as thin "maintenance" phases that would have been better folded into a neighbor) |
|
||||
| Fine | 6-10 | Let natural boundaries stand |
|
||||
|
||||
**Key:** Derive phases from work, then apply granularity as compression guidance. Don't pad small projects or compress complex ones.
|
||||
**Key:** Derive phases from work, then apply granularity as compression guidance. Don't pad small projects or compress complex ones. When a phase you are about to write would have a single requirement, an internal-quality goal ("improve X", "refactor Y", "add tests for Z"), or success criteria that read as tasks rather than user-observable outcomes, prefer to fold it into the most-related neighbor instead of creating a standalone phase.
|
||||
|
||||
## Good Phase Patterns
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ GSD stores project settings in `.planning/config.json`. Created during `/gsd-new
|
||||
| Setting | Type | Options | Default | Description |
|
||||
|---------|------|---------|---------|-------------|
|
||||
| `mode` | enum | `interactive`, `yolo` | `interactive` | `yolo` auto-approves decisions; `interactive` confirms at each step |
|
||||
| `granularity` | enum | `coarse`, `standard`, `fine` | `standard` | Controls phase count: `coarse` (3-5), `standard` (5-8), `fine` (8-12) |
|
||||
| `granularity` | enum | `coarse`, `standard`, `fine` | `standard` | Controls phase count: `coarse` (2-4), `standard` (4-6), `fine` (6-10) |
|
||||
| `model_profile` | enum | `quality`, `balanced`, `budget`, `adaptive`, `inherit` | `balanced` | Model tier for each agent (see [Model Profiles](#model-profiles)). `adaptive` was added per [#1713](https://github.com/open-gsd/gsd-core/issues/1713) / [#1806](https://github.com/open-gsd/gsd-core/issues/1806) and resolves the same way as the other tiers under runtime-aware profiles. |
|
||||
| `runtime` | string | `claude`, `codex`, or any string | (none) | Active runtime for [runtime-aware profile resolution](#runtime-aware-profiles-2517). When set, profile tiers (opus/sonnet/haiku) resolve to runtime-native model IDs. Today only the Codex install path emits per-agent model IDs from this resolver; other runtimes (`opencode`, `gemini`, `qwen`, `copilot`, …) consume the resolver at spawn time and gain dedicated install-path support in [#2612](https://github.com/open-gsd/gsd-core/issues/2612). When unset (default), behavior is unchanged from prior versions. Added in v1.39 |
|
||||
| `model_profile_overrides.<runtime>.<tier>` | string \| object | per-runtime tier override | (none) | Override the runtime-aware tier mapping for a specific `(runtime, tier)`. Tier is one of `opus`, `sonnet`, `haiku`. Value is either a model ID string (e.g. `"gpt-5-pro"`) or `{ model, reasoning_effort }`. See [Runtime-Aware Profiles](#runtime-aware-profiles-2517). Added in v1.39 |
|
||||
|
||||
@@ -208,7 +208,7 @@
|
||||
**Functional Requirements:**
|
||||
- Questions adapt based on detected project type (web app, CLI, mobile, API, etc.)
|
||||
- Research agents have web search capability for current ecosystem information
|
||||
- Granularity setting controls phase count: `coarse` (3-5), `standard` (5-8), `fine` (8-12)
|
||||
- Granularity setting controls phase count: `coarse` (2-4), `standard` (4-6), `fine` (6-10)
|
||||
- `--auto` mode extracts all information from the provided document without interactive questioning
|
||||
- Existing codebase context (from `/gsd-map-codebase`) is loaded if present
|
||||
|
||||
|
||||
58
tests/roadmapper-granularity.test.cjs
Normal file
58
tests/roadmapper-granularity.test.cjs
Normal file
@@ -0,0 +1,58 @@
|
||||
// allow-test-rule: source-text-is-the-product
|
||||
// agents/gsd-roadmapper.md is the installed agent — the Granularity Calibration
|
||||
// table IS the deployed instruction. Asserting on its text asserts what runs in
|
||||
// production. Locks the tightened phase-count buckets from #163.
|
||||
'use strict';
|
||||
|
||||
const { describe, test } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const AGENTS_DIR = path.join(__dirname, '..', 'agents');
|
||||
|
||||
function readAgent(name) {
|
||||
return fs.readFileSync(path.join(AGENTS_DIR, `${name}.md`), 'utf8');
|
||||
}
|
||||
|
||||
// Extract the "## Granularity Calibration" section (up to the next "## " heading)
|
||||
// so number-range assertions are scoped and cannot be satisfied by unrelated text
|
||||
// elsewhere in the agent file.
|
||||
function granularitySection(content) {
|
||||
const start = content.indexOf('## Granularity Calibration');
|
||||
assert.ok(start !== -1, 'Granularity Calibration section must exist');
|
||||
const rest = content.slice(start + '## Granularity Calibration'.length);
|
||||
const nextHeading = rest.indexOf('\n## ');
|
||||
return nextHeading === -1 ? rest : rest.slice(0, nextHeading);
|
||||
}
|
||||
|
||||
describe('gsd-roadmapper granularity calibration (#163)', () => {
|
||||
const section = granularitySection(readAgent('gsd-roadmapper'));
|
||||
|
||||
test('Coarse bucket is tightened to 2-4', () => {
|
||||
assert.ok(/\|\s*Coarse\s*\|\s*2-4\s*\|/.test(section), 'Coarse must be 2-4');
|
||||
});
|
||||
|
||||
test('Standard bucket is tightened to 4-6', () => {
|
||||
assert.ok(/\|\s*Standard\s*\|\s*4-6\b/.test(section), 'Standard must be 4-6');
|
||||
});
|
||||
|
||||
test('Fine bucket is tightened to 6-10', () => {
|
||||
assert.ok(/\|\s*Fine\s*\|\s*6-10\s*\|/.test(section), 'Fine must be 6-10');
|
||||
});
|
||||
|
||||
test('no granularity row maps to an old bucket (3-5 / 5-8 / 8-12)', () => {
|
||||
// Scope to the second ("Typical Phases") column of each row so the approved
|
||||
// explanatory footnote mentioning "5-8" in the third column does not false-fail.
|
||||
assert.ok(!/\|\s*Coarse\s*\|\s*3-5\b/.test(section), 'Coarse must not map to 3-5');
|
||||
assert.ok(!/\|\s*Standard\s*\|\s*5-8\b/.test(section), 'Standard must not map to 5-8');
|
||||
assert.ok(!/\|\s*Fine\s*\|\s*8-12\b/.test(section), 'Fine must not map to 8-12');
|
||||
});
|
||||
|
||||
test('Key paragraph names the thin-phase pattern and prefers folding into a neighbor', () => {
|
||||
assert.ok(
|
||||
section.includes('fold it into the most-related neighbor'),
|
||||
'Key guidance must instruct folding thin phases into the most-related neighbor'
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user