mirror of
https://github.com/Fission-AI/OpenSpec.git
synced 2026-10-02 05:24:34 +08:00
fix(tasks): separate completion from archive readiness (#1791)
* fix(tasks): separate completion from archive readiness * fix(tasks): clarify optional workflow follow-up * fix(tasks): keep profile-aware archive handoff and sync generated surfaces Rebased onto main: the apply template now reports tracked completion through the existing optional-workflow ARCHIVE_HANDOFF (#1775), so profiles without the archive workflow still get the CLI fallback. Regenerates the skills/ mirror and parity hashes, and syncs the docs-lab spec-driven reference with the updated tasks instruction (#1952). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Clay Good <hi@claygood.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
Clay Good
parent
3a34ea309d
commit
7056a58056
@@ -361,17 +361,22 @@ Before writing tasks, check design.md for Open Questions. If any of them
|
||||
would change what gets built, resolve them with the user first - do not
|
||||
bake an unstated assumption into the task list.
|
||||
|
||||
**IMPORTANT: Follow the template below exactly.** The apply phase parses
|
||||
**IMPORTANT: Follow the template below for tracked tasks.** The apply phase parses
|
||||
checkbox format to track progress. A box holding only `x` counts as done,
|
||||
upper or lower case and with any spacing, so `- [ x]` is done too. Every
|
||||
other marker, including `- [~]`, `- [-]` and an empty `- []`, reads as
|
||||
unfinished. A line with no checkbox is not tracked at all.
|
||||
|
||||
Guidelines:
|
||||
- Group related tasks under ## numbered headings
|
||||
- Each task MUST be a checkbox: `- [ ] X.Y Task description`
|
||||
- Group related tracked tasks under ## numbered headings
|
||||
- Each tracked task MUST be a checkbox: `- [ ] X.Y Task description`
|
||||
- Tasks should be small enough to complete in one session
|
||||
- Order tasks by dependency (what must be done first?)
|
||||
- Track implementation and verification work that can be completed before
|
||||
archive. If the requested workflow includes archive or work that requires
|
||||
this change to be archived, preserve those steps as plain bullets in an
|
||||
optional `## Workflow follow-up` section at the end of tasks.md. These
|
||||
bullets are reference information outside tracked task progress.
|
||||
- Each task MUST state how to verify completion (a test, command,
|
||||
observable behavior, or delivered artifact). Put the verification in
|
||||
that task's checkbox description. Use a separate verification task only
|
||||
@@ -401,6 +406,14 @@ Example:
|
||||
- [ ] 2.3 Document the export API in docs/export.md and verify the documented command runs as written
|
||||
```
|
||||
|
||||
When applicable, append workflow follow-up as plain bullets, for example:
|
||||
```
|
||||
## Workflow follow-up
|
||||
|
||||
- Archive the change after the project's review requirements are satisfied.
|
||||
- Verify the archived result.
|
||||
```
|
||||
|
||||
Reference specs for what needs to be built, design for how to build it.
|
||||
````
|
||||
|
||||
|
||||
@@ -195,17 +195,22 @@ artifacts:
|
||||
would change what gets built, resolve them with the user first - do not
|
||||
bake an unstated assumption into the task list.
|
||||
|
||||
**IMPORTANT: Follow the template below exactly.** The apply phase parses
|
||||
**IMPORTANT: Follow the template below for tracked tasks.** The apply phase parses
|
||||
checkbox format to track progress. A box holding only `x` counts as done,
|
||||
upper or lower case and with any spacing, so `- [ x]` is done too. Every
|
||||
other marker, including `- [~]`, `- [-]` and an empty `- []`, reads as
|
||||
unfinished. A line with no checkbox is not tracked at all.
|
||||
|
||||
Guidelines:
|
||||
- Group related tasks under ## numbered headings
|
||||
- Each task MUST be a checkbox: `- [ ] X.Y Task description`
|
||||
- Group related tracked tasks under ## numbered headings
|
||||
- Each tracked task MUST be a checkbox: `- [ ] X.Y Task description`
|
||||
- Tasks should be small enough to complete in one session
|
||||
- Order tasks by dependency (what must be done first?)
|
||||
- Track implementation and verification work that can be completed before
|
||||
archive. If the requested workflow includes archive or work that requires
|
||||
this change to be archived, preserve those steps as plain bullets in an
|
||||
optional `## Workflow follow-up` section at the end of tasks.md. These
|
||||
bullets are reference information outside tracked task progress.
|
||||
- Each task MUST state how to verify completion (a test, command,
|
||||
observable behavior, or delivered artifact). Put the verification in
|
||||
that task's checkbox description. Use a separate verification task only
|
||||
@@ -235,6 +240,14 @@ artifacts:
|
||||
- [ ] 2.3 Document the export API in docs/export.md and verify the documented command runs as written
|
||||
```
|
||||
|
||||
When applicable, append workflow follow-up as plain bullets, for example:
|
||||
```
|
||||
## Workflow follow-up
|
||||
|
||||
- Archive the change after the project's review requirements are satisfied.
|
||||
- Verify the archived result.
|
||||
```
|
||||
|
||||
Reference specs for what needs to be built, design for how to build it.
|
||||
requires:
|
||||
- specs
|
||||
|
||||
@@ -65,7 +65,7 @@ In both branches, never create the root as a side effect: do not run `openspec i
|
||||
- If `state: "blocked"`: show the message and pause implementation.
|
||||
- If `missingArtifacts` is non-empty: suggest using `/openspec-continue-change` to create them.
|
||||
- Otherwise, follow the CLI instruction to create or repair the schema-configured tracking file from existing planning artifacts. Do not assume another artifact is ready or start implementation while blocked.
|
||||
- If `state: "all_done"`: congratulate, suggest archive
|
||||
- If `state: "all_done"`: report that all tracked tasks are complete and suggest review or verification as appropriate before archiving
|
||||
- Otherwise: proceed to implementation
|
||||
|
||||
Treat `context` as a required prompt-level input. Read and consider it, and
|
||||
@@ -124,7 +124,7 @@ In both branches, never create the root as a side effect: do not run `openspec i
|
||||
Display:
|
||||
- Tasks completed this session
|
||||
- Overall progress: "N/M tasks complete"
|
||||
- If all done: suggest archive
|
||||
- If all done: report that tracked tasks are complete and suggest review or verification as appropriate before archiving
|
||||
- If paused: explain why and wait for guidance
|
||||
|
||||
**Output During Implementation**
|
||||
@@ -155,7 +155,8 @@ Working on task 4/7: <task description>
|
||||
- [x] Task 2
|
||||
...
|
||||
|
||||
All tasks complete! You can archive this change with `/openspec-archive-change`.
|
||||
All tracked tasks are complete. Review or verify the change as appropriate
|
||||
before archiving. You can archive this change with `/openspec-archive-change`.
|
||||
```
|
||||
|
||||
**Output On Pause (Issue Encountered)**
|
||||
|
||||
@@ -672,7 +672,7 @@ export async function generateApplyInstructions(
|
||||
total > 0
|
||||
) {
|
||||
state = 'all_done';
|
||||
instruction = 'All tasks are complete! This change is ready to be archived.\nConsider running tests and reviewing the changes before archiving.';
|
||||
instruction = 'All tracked tasks are complete.\nReview or verify the change as appropriate before archiving.';
|
||||
} else if (!tracksFile) {
|
||||
// No tracking file configured in schema - ready to apply
|
||||
state = 'ready';
|
||||
|
||||
@@ -84,7 +84,7 @@ ${PROJECT_ROOT_GUARD}
|
||||
- If \`state: "blocked"\`: show the message and pause implementation.
|
||||
- If \`missingArtifacts\` is non-empty: ${BLOCKED_STATE_HANDOFF}
|
||||
- Otherwise, follow the CLI instruction to create or repair the schema-configured tracking file from existing planning artifacts. Do not assume another artifact is ready or start implementation while blocked.
|
||||
- If \`state: "all_done"\`: congratulate, suggest archive
|
||||
- If \`state: "all_done"\`: report that all tracked tasks are complete and suggest review or verification as appropriate before archiving
|
||||
- Otherwise: proceed to implementation
|
||||
|
||||
Treat \`context\` as a required prompt-level input. Read and consider it, and
|
||||
@@ -143,7 +143,7 @@ ${PROJECT_ROOT_GUARD}
|
||||
Display:
|
||||
- Tasks completed this session
|
||||
- Overall progress: "N/M tasks complete"
|
||||
- If all done: suggest archive
|
||||
- If all done: report that tracked tasks are complete and suggest review or verification as appropriate before archiving
|
||||
- If paused: explain why and wait for guidance
|
||||
|
||||
**Output During Implementation**
|
||||
@@ -174,7 +174,8 @@ Working on task 4/7: <task description>
|
||||
- [x] Task 2
|
||||
...
|
||||
|
||||
All tasks complete! ${ARCHIVE_HANDOFF}
|
||||
All tracked tasks are complete. Review or verify the change as appropriate
|
||||
before archiving. ${ARCHIVE_HANDOFF}
|
||||
\`\`\`
|
||||
|
||||
**Output On Pause (Issue Encountered)**
|
||||
|
||||
@@ -1246,9 +1246,23 @@ operations:
|
||||
});
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.stdout).toContain('complete ✓');
|
||||
expect(result.stdout).toContain('ready to be archived');
|
||||
expect(result.stdout).toContain('All tracked tasks are complete');
|
||||
expect(result.stdout).toContain('as appropriate before archiving');
|
||||
expect(result.stdout).not.toContain('ready to be archived');
|
||||
expect(result.stdout).toContain('### Project Context (required instruction input)');
|
||||
expect(result.stdout).toContain('### Operation Guidance (advisory)');
|
||||
|
||||
const jsonResult = await runCLI(
|
||||
['instructions', 'apply', '--change', 'done-apply', '--json'],
|
||||
{ cwd: tempDir }
|
||||
);
|
||||
expect(jsonResult.exitCode).toBe(0);
|
||||
expect(jsonResult.stderr).toBe('');
|
||||
|
||||
const json = JSON.parse(jsonResult.stdout);
|
||||
expect(json.state).toBe('all_done');
|
||||
expect(json.progress).toEqual({ total: 2, complete: 2, remaining: 0 });
|
||||
expect(json.instruction).toContain('All tracked tasks are complete');
|
||||
});
|
||||
|
||||
it('uses spec-driven schema apply configuration', async () => {
|
||||
|
||||
@@ -21,6 +21,7 @@ import { getCommandContents } from '../../../src/core/shared/skill-generation.js
|
||||
import { MAX_CONTEXT_SIZE } from '../../../src/core/project-config.js';
|
||||
import { resolveOptionalWorkflows } from '../../../src/core/templates/optional-workflow.js';
|
||||
import { ALL_WORKFLOWS } from '../../../src/core/profiles.js';
|
||||
import { parseTaskLines } from '../../../src/utils/task-progress.js';
|
||||
|
||||
// Templates carry optional-workflow conditionals; a body only means anything
|
||||
// once resolved against a workflow set. Unless a test says otherwise, these are
|
||||
@@ -117,6 +118,28 @@ describe('default proposal guidance', () => {
|
||||
});
|
||||
|
||||
describe('default task guidance', () => {
|
||||
it('keeps tracked tasks within the pre-archive workflow (#1790)', () => {
|
||||
const tasks = defaultSchema.artifacts.find(artifact => artifact.id === 'tasks');
|
||||
expect(tasks).toBeDefined();
|
||||
expect(tasks!.instruction).toMatch(
|
||||
/Track implementation and verification work that can be completed before\s+archive/
|
||||
);
|
||||
expect(tasks!.instruction).toMatch(
|
||||
/preserve those steps as plain bullets in an\s+optional `## Workflow follow-up` section at the end of tasks.md/
|
||||
);
|
||||
|
||||
const examples = [...tasks!.instruction.matchAll(/```\s*([\s\S]*?)```/g)];
|
||||
expect(examples).toHaveLength(2);
|
||||
const implementation = examples[0][1];
|
||||
const followUp = examples[1][1];
|
||||
expect(followUp).toContain('## Workflow follow-up');
|
||||
expect(followUp).toContain('- Verify the archived result.');
|
||||
expect(parseTaskLines(followUp)).toEqual([]);
|
||||
expect(parseTaskLines(`${implementation}\n${followUp}`)).toEqual(
|
||||
parseTaskLines(implementation)
|
||||
);
|
||||
});
|
||||
|
||||
it('requires a concrete verification method in each task (#345)', () => {
|
||||
const tasks = defaultSchema.artifacts.find(artifact => artifact.id === 'tasks');
|
||||
expect(tasks).toBeDefined();
|
||||
|
||||
@@ -79,14 +79,14 @@ const EXPECTED_FUNCTION_HASHES: Record<string, string> = {
|
||||
getExploreSkillTemplate: 'c1fddb294758004936add586f5826694cb06175cff935b75fd3a8d92332332e6',
|
||||
getNewChangeSkillTemplate: '0e5035b7b42198afc430206a1dbc9579096650ef0813d85e837d5a6cd0b98a85',
|
||||
getContinueChangeSkillTemplate: 'c2c8a0ba7f8c8fc7b174793832cd50f7c404eb8e1f7d49c47000993d621633b6',
|
||||
getApplyChangeSkillTemplate: 'a6a9aa080ba062045533b33a71ad9358d9d5fef1756616f3feac7dea64aba289',
|
||||
getApplyChangeSkillTemplate: 'c4f02c29137e19b34bf1dc59146941b8c920214aa2cf3ce80ae7f1b982e976fc',
|
||||
getFfChangeSkillTemplate: 'd091600476a815ba99f69b446bcd46af5bf73d1c2810215a0c6196937d019cf6',
|
||||
getSyncSpecsSkillTemplate: 'bc80fe9b07eaa289e5eb8a3ce65eb7df722a16d864e37283c678220712e4f230',
|
||||
getOnboardSkillTemplate: '84258a06c0ca88de708a23dd74e9a17efe11eff63a071b3864c781dcd5a0a4b7',
|
||||
getOpsxExploreCommandTemplate: '5d11f8ecb4c457140a3e874a8bf7aa72674e922e698c208832b1f34d3c617719',
|
||||
getOpsxNewCommandTemplate: '6d504fef1e0d4ced7c423f4cc9d9d2cee11b1a6224edf685e06a3f0757e0ebff',
|
||||
getOpsxContinueCommandTemplate: '241c50f97d5d681412d456d6b982743c3a5babeb77017fc8099c418bcf0d92df',
|
||||
getOpsxApplyCommandTemplate: '21ccc013710ffb9f3a93ede9f9fb7e3eecaac292fcb3f3be30e2562cae9c4127',
|
||||
getOpsxApplyCommandTemplate: '9b733946aa7216b45d084bb5affd7071086db6f91282c3666731a17543825ff1',
|
||||
getOpsxFfCommandTemplate: '743a7304c7efc84aa87f556154c034e1e0e561c276c51870a30ada58f33eb9af',
|
||||
getArchiveChangeSkillTemplate: '04a029782fc4137971fad6f54cfda3685d7e0b835b06565f9a509b4878093099',
|
||||
getBulkArchiveChangeSkillTemplate: '44dbd3c7a347e5f8339b2141393f2ac36017527cce251483fe70f2059c1e286e',
|
||||
@@ -107,7 +107,7 @@ const EXPECTED_GENERATED_SKILL_CONTENT_HASHES: Record<string, string> = {
|
||||
'openspec-explore': '7d80caf9cd25a2565ba190b1297f1631c7f2c2db5e614597b4284abc0118ea70',
|
||||
'openspec-new-change': '27e09d43785953827efc9a98bb9d6cf06db48fe6abe7e1c049409fe5b5061323',
|
||||
'openspec-continue-change': '1f92fad53022270e96f8ea34de75f7c12c08225edd5a9e8f4e864b63b5ef79c5',
|
||||
'openspec-apply-change': '4bcd8c1dc3d0d86a2e4c605fc60d6ee54e0e0433f248c1d602c12927935e6963',
|
||||
'openspec-apply-change': '991a1d4687f1c8c7147d8c3b68cf14e9c515c6c53bb026356dac3b80e909b827',
|
||||
'openspec-ff-change': 'a7ab656d46f04d45dff0c8888df4a126a2e62288b7336f7445bce4d1715055f5',
|
||||
'openspec-sync-specs': '3909936a236a21a9a6d5bf495f90b396b3b68fc9220d7b2c1894668653beb2e4',
|
||||
'openspec-archive-change': '5f0d131a885dcdcd9ba2172ea9a42bc6748125e24b8c4eecb7c86f1a4aea83af',
|
||||
@@ -1267,6 +1267,13 @@ describe('apply skill/command shared instruction core', () => {
|
||||
expect(getApplyChangeSkillTemplate().instructions).toBe(core);
|
||||
expect(getOpsxApplyCommandTemplate().content).toBe(core);
|
||||
});
|
||||
|
||||
it('keeps task completion distinct from archive readiness (#1790)', () => {
|
||||
const core = getApplyInstructions();
|
||||
expect(core).toContain('All tracked tasks are complete');
|
||||
expect(core).toMatch(/Review or verify the change as appropriate\s+before archiving/);
|
||||
expect(core).not.toContain('All tasks complete! You can archive');
|
||||
});
|
||||
});
|
||||
|
||||
describe('workflow guidance matches the packaged templates (#1138)', () => {
|
||||
|
||||
Reference in New Issue
Block a user