From b66878fe369fc043fd59ab9176722be8810d7fb7 Mon Sep 17 00:00:00 2001 From: Marzx13 <28298824+Marzx13@users.noreply.github.com> Date: Fri, 4 Sep 2026 15:34:43 -0400 Subject: [PATCH 1/3] fix(tasks): separate completion from archive readiness --- schemas/spec-driven/schema.yaml | 3 +++ src/commands/workflow/instructions.ts | 2 +- src/core/templates/workflows/apply-change.ts | 7 ++++--- test/commands/artifact-workflow.test.ts | 16 +++++++++++++++- test/core/templates/propose.test.ts | 11 +++++++++++ .../templates/skill-templates-parity.test.ts | 7 +++++++ 6 files changed, 41 insertions(+), 5 deletions(-) diff --git a/schemas/spec-driven/schema.yaml b/schemas/spec-driven/schema.yaml index 766f9b02be..614fc1ac25 100644 --- a/schemas/spec-driven/schema.yaml +++ b/schemas/spec-driven/schema.yaml @@ -206,6 +206,9 @@ artifacts: - Each 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. Present archive and work that depends on the change already + being archived as subsequent workflow steps, not task checkboxes. - 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 diff --git a/src/commands/workflow/instructions.ts b/src/commands/workflow/instructions.ts index 02297c4772..8cd8dedbc7 100644 --- a/src/commands/workflow/instructions.ts +++ b/src/commands/workflow/instructions.ts @@ -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'; diff --git a/src/core/templates/workflows/apply-change.ts b/src/core/templates/workflows/apply-change.ts index 6605f60d50..087911b0bd 100644 --- a/src/core/templates/workflows/apply-change.ts +++ b/src/core/templates/workflows/apply-change.ts @@ -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: - [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)** diff --git a/test/commands/artifact-workflow.test.ts b/test/commands/artifact-workflow.test.ts index dc7b1eb7c2..4774d3664d 100644 --- a/test/commands/artifact-workflow.test.ts +++ b/test/commands/artifact-workflow.test.ts @@ -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 () => { diff --git a/test/core/templates/propose.test.ts b/test/core/templates/propose.test.ts index 502671a8dc..edd4e7856b 100644 --- a/test/core/templates/propose.test.ts +++ b/test/core/templates/propose.test.ts @@ -117,6 +117,17 @@ 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( + /Present archive and work that depends on the change already\s+being archived as subsequent workflow steps, not task checkboxes/ + ); + }); + it('requires a concrete verification method in each task (#345)', () => { const tasks = defaultSchema.artifacts.find(artifact => artifact.id === 'tasks'); expect(tasks).toBeDefined(); diff --git a/test/core/templates/skill-templates-parity.test.ts b/test/core/templates/skill-templates-parity.test.ts index 427b3b7abd..fce6dcd433 100644 --- a/test/core/templates/skill-templates-parity.test.ts +++ b/test/core/templates/skill-templates-parity.test.ts @@ -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)', () => { From 0d88241b9777213113c4bd4670a7eb18d0c61abf Mon Sep 17 00:00:00 2001 From: Marzx13 <28298824+Marzx13@users.noreply.github.com> Date: Fri, 4 Sep 2026 16:47:59 -0400 Subject: [PATCH 2/3] fix(tasks): clarify optional workflow follow-up --- schemas/spec-driven/schema.yaml | 20 +++++++++++++++----- test/core/templates/propose.test.ts | 14 +++++++++++++- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/schemas/spec-driven/schema.yaml b/schemas/spec-driven/schema.yaml index 614fc1ac25..8e1994d79f 100644 --- a/schemas/spec-driven/schema.yaml +++ b/schemas/spec-driven/schema.yaml @@ -195,20 +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. Present archive and work that depends on the change already - being archived as subsequent workflow steps, not task checkboxes. + 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 @@ -238,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 diff --git a/test/core/templates/propose.test.ts b/test/core/templates/propose.test.ts index edd4e7856b..34c9b86852 100644 --- a/test/core/templates/propose.test.ts +++ b/test/core/templates/propose.test.ts @@ -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 @@ -124,7 +125,18 @@ describe('default task guidance', () => { /Track implementation and verification work that can be completed before\s+archive/ ); expect(tasks!.instruction).toMatch( - /Present archive and work that depends on the change already\s+being archived as subsequent workflow steps, not task checkboxes/ + /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) ); }); From c9bb3f0d39c7e141e582ffa81b60461c34ccda23 Mon Sep 17 00:00:00 2001 From: Clay Good Date: Thu, 1 Oct 2026 13:10:33 -0500 Subject: [PATCH 3/3] 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 --- .../reference/schemas/spec-driven/index.md | 19 ++++++++++++++++--- skills/openspec-apply-change/SKILL.md | 7 ++++--- .../templates/skill-templates-parity.test.ts | 6 +++--- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/docs-lab/reference/schemas/spec-driven/index.md b/docs-lab/reference/schemas/spec-driven/index.md index 3fdabad440..d60ab35a48 100644 --- a/docs-lab/reference/schemas/spec-driven/index.md +++ b/docs-lab/reference/schemas/spec-driven/index.md @@ -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. ```` diff --git a/skills/openspec-apply-change/SKILL.md b/skills/openspec-apply-change/SKILL.md index 7be7c7421f..25419a286c 100644 --- a/skills/openspec-apply-change/SKILL.md +++ b/skills/openspec-apply-change/SKILL.md @@ -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: - [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)** diff --git a/test/core/templates/skill-templates-parity.test.ts b/test/core/templates/skill-templates-parity.test.ts index fce6dcd433..e3d1c48dc7 100644 --- a/test/core/templates/skill-templates-parity.test.ts +++ b/test/core/templates/skill-templates-parity.test.ts @@ -79,14 +79,14 @@ const EXPECTED_FUNCTION_HASHES: Record = { 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 = { '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',