Skip to content

Commit 762cca2

Browse files
committed
fix(guidance): read complete specs before coverage decisions
1 parent 80b9031 commit 762cca2

7 files changed

Lines changed: 34 additions & 3 deletions

File tree

.changeset/teach-spec-inventory-verb.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@
33
---
44

55
Teach the generated guidance how to find and read a project's specs. `openspec list --specs` appeared in no generated skill, command, or artifact instruction, while `openspec list --json` (the in-flight *change* list) appeared throughout, so an agent asked to read the existing specs first enumerated changes instead and reported the step complete against the wrong object. The explore skill and command now list the spec inventory alongside the change list and say which is which, and the spec-driven `proposal` and `specs` instructions name the command where they ask for existing capabilities to be researched and for a delta's path to match an existing one. Both steps carry `--store "<id>"`, and capabilities are read with `openspec show "<spec-id>" --type spec --json --no-scenarios` so the read resolves against the same root the listing came from. `docs/cli.md` now documents the `--store` option on `list` and `show`, which both already accepted it. Fixes #1689.
6+
7+
The filtered read is only an overview. Agents read relevant specs in full, including scenarios, before deciding what is already covered or what should change.

schemas/spec-driven/schema.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ artifacts:
2828
error. `openspec list` without `--specs` lists in-flight changes, not
2929
specs - it never shows what the project already covers. Reuse an existing
3030
capability's exact path instead of introducing a near-duplicate name.
31+
The filtered read is only an overview. Before deciding what is already
32+
covered or what should change, read each relevant spec in full, including
33+
scenarios, with `openspec show "<spec-id>" --type spec` (same `--store` rule).
3134
Each capability listed here will need a corresponding spec file.
3235
3336
Every change must either declare at least one capability (new or

skills/openspec-explore/SKILL.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ openspec list --specs
102102
```
103103
Add `--json` for ids and requirement counts, and append `--store "<id>"` only for a registered standalone store. This is the inventory of what the project already claims to do, and `openspec list` on its own never shows it. To look at one, run `openspec show "<spec-id>" --type spec --json --no-scenarios` (same `--store` rule) - it returns that capability's purpose and requirement texts without pulling the whole spec file into context, and `--type spec` stops a change of the same name from making it ambiguous.
104104

105+
The filtered read is only an overview. Before deciding what is already covered or what should change, read each relevant spec in full, including scenarios, with `openspec show "<spec-id>" --type spec` (same `--store` rule).
106+
105107
Then read the project's own context from the resolved root - `<root.path>/openspec/config.yaml` (or `config.yml`). Use the `root.path` returned above, and skip this if neither file exists:
106108
- `context`: project background - tech stack, conventions, constraints
107109
- `rules`: keyed by artifact id - the entries for an artifact apply only when you write that artifact

src/core/templates/workflows/explore.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ openspec list --specs
104104
\`\`\`
105105
Add \`--json\` for ids and requirement counts, and append \`--store "<id>"\` only for a registered standalone store. This is the inventory of what the project already claims to do, and \`openspec list\` on its own never shows it. To look at one, run \`openspec show "<spec-id>" --type spec --json --no-scenarios\` (same \`--store\` rule) - it returns that capability's purpose and requirement texts without pulling the whole spec file into context, and \`--type spec\` stops a change of the same name from making it ambiguous.
106106
107+
The filtered read is only an overview. Before deciding what is already covered or what should change, read each relevant spec in full, including scenarios, with \`openspec show "<spec-id>" --type spec\` (same \`--store\` rule).
108+
107109
Then read the project's own context from the resolved root - \`<root.path>/openspec/config.yaml\` (or \`config.yml\`). Use the \`root.path\` returned above, and skip this if neither file exists:
108110
- \`context\`: project background - tech stack, conventions, constraints
109111
- \`rules\`: keyed by artifact id - the entries for an artifact apply only when you write that artifact
@@ -428,6 +430,8 @@ openspec list --specs
428430
\`\`\`
429431
Add \`--json\` for ids and requirement counts, and append \`--store "<id>"\` only for a registered standalone store. This is the inventory of what the project already claims to do, and \`openspec list\` on its own never shows it. To look at one, run \`openspec show "<spec-id>" --type spec --json --no-scenarios\` (same \`--store\` rule) - it returns that capability's purpose and requirement texts without pulling the whole spec file into context, and \`--type spec\` stops a change of the same name from making it ambiguous.
430432
433+
The filtered read is only an overview. Before deciding what is already covered or what should change, read each relevant spec in full, including scenarios, with \`openspec show "<spec-id>" --type spec\` (same \`--store\` rule).
434+
431435
Then read the project's own context from the resolved root - \`<root.path>/openspec/config.yaml\` (or \`config.yml\`). Use the \`root.path\` returned above, and skip this if neither file exists:
432436
- \`context\`: project background - tech stack, conventions, constraints
433437
- \`rules\`: keyed by artifact id - the entries for an artifact apply only when you write that artifact

test/commands/store-root-selection.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,11 @@ describe('store root selection for normal commands', () => {
180180
requirements: [{ text: 'The system SHALL bill.', scenarios: [] }],
181181
root: json.root,
182182
});
183+
184+
// The overview omits scenarios; decisions use the complete spec.
185+
const full = await runCLI(['show', id, '--type', 'spec', ...storeArgs], { cwd: appRepo, env });
186+
expect(full.exitCode).toBe(0);
187+
expect(full.stdout.trim()).toBe(spec.trim());
183188
}
184189
},
185190
30_000

test/core/templates/skill-templates-parity.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,14 @@ import {
3838
import { STORE_SELECTION_GUIDANCE } from '../../../src/core/templates/workflows/store-selection.js';
3939

4040
const EXPECTED_FUNCTION_HASHES: Record<string, string> = {
41-
getExploreSkillTemplate: 'e20cc6ef9532458a62662a9fcd8834e62ea6684185bf096cbfb09b8ecddb9f03',
41+
getExploreSkillTemplate: '6dfb17fb7a29e8bac9a15f7aa6fd677fd4ce12c99284919983efb9f54a690729',
4242
getNewChangeSkillTemplate: 'eabd1e895c5881dcb17dcbaa3fb26098dd59e8eacb318e400820b4dc811ef781',
4343
getContinueChangeSkillTemplate: '012136f6411a99c8fa228e2f9444cb64b0a89e0f56fdeac2fe03b2f5bee0c5d7',
4444
getApplyChangeSkillTemplate: 'd1e7d5ceb85193c0964057dbb88e9651526754bd33f84020e2440ff0621d5dbb',
4545
getFfChangeSkillTemplate: '5501740e7ec36ab23ab8c3a0d6dd0655a5e2f35433c7b90e82904fef5e7a326a',
4646
getSyncSpecsSkillTemplate: 'b099e2ff31859c9b10d928066e662524f9aad9ecf2be12fceacb732d718c4146',
4747
getOnboardSkillTemplate: '3a836faae463d88c289a1c129cb7ee556a563b7e53e1a52a4711ff152a3b51f7',
48-
getOpsxExploreCommandTemplate: '391aaec5a45fcd4a55cdbfd8e1a38097a167a93896ffd507e7f94aa814d460e5',
48+
getOpsxExploreCommandTemplate: '0010dfbbd4f8506947ec25eeb132a5f840cfe51e49f9b53c05a730b6decdd939',
4949
getOpsxNewCommandTemplate: 'f2d30e569798a4c92ba932859d6ba4e0ad10e18feccbade1cfee0957597b3463',
5050
getOpsxContinueCommandTemplate: 'e50e50266efa1b8e64ff9b6274ee8254f0a240d6adc1b862d126e2f1c9d3a559',
5151
getOpsxApplyCommandTemplate: 'e3579ac78f2e2c75fa3d3a7ac7dc3e49c395e96f7323398f0f041d94f8de9bb0',
@@ -66,7 +66,7 @@ const EXPECTED_FUNCTION_HASHES: Record<string, string> = {
6666
};
6767

6868
const EXPECTED_GENERATED_SKILL_CONTENT_HASHES: Record<string, string> = {
69-
'openspec-explore': 'e46f4d9925509a7209da49a9c4e07b5b1417050c77a6b4002384198977a54cca',
69+
'openspec-explore': '05812dcf995542f53e937de9798534bb2751ce97e587325cd4b66c6ed247e814',
7070
'openspec-new-change': 'ec4529beef978e34634a6f7286fab55d68fad8fb374dceb45691d52caab33fbb',
7171
'openspec-continue-change': 'bb6194a16c54891cdb253678e8f70ce53b2af86735243980f366ce551d37e42e',
7272
'openspec-apply-change': '81ea96d9fa6ec8536cd23c1fe561ed28e1cc1cad0a8ceb700588e08974cc0e49',

test/core/templates/spec-inventory.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,19 @@ describe('spec inventory vocabulary (#1689)', () => {
139139
);
140140
}
141141
});
142+
143+
it('reads full relevant specs before deciding coverage or changes', () => {
144+
const sites: Array<[string, string]> = [
145+
...exploreBodies,
146+
['proposal instruction', instructionFor('proposal')],
147+
];
148+
149+
for (const [label, body] of sites) {
150+
const normalized = body.replace(/\s+/g, ' ');
151+
expect(normalized, label).toContain('The filtered read is only an overview.');
152+
expect(normalized, label).toContain(
153+
'Before deciding what is already covered or what should change, read each relevant spec in full, including scenarios, with `openspec show "<spec-id>" --type spec` (same `--store` rule).'
154+
);
155+
}
156+
});
142157
});

0 commit comments

Comments
 (0)