diff --git a/.changeset/apply-surface-deferred-scope.md b/.changeset/apply-surface-deferred-scope.md new file mode 100644 index 0000000000..daafa643db --- /dev/null +++ b/.changeset/apply-surface-deferred-scope.md @@ -0,0 +1,5 @@ +--- +"@fission-ai/openspec": patch +--- + +Apply workflow now tells agents to surface unexpected scope instead of hiding it. When a task needs work beyond what the spec describes, the `/opsx:apply` skill and command guidance direct the agent to pause and report the added scope rather than silently narrowing, deferring, or simplifying away specified behavior, and to mark a task complete only when its specified behavior is fully implemented. Fixes #1529. diff --git a/skills/openspec-apply-change/SKILL.md b/skills/openspec-apply-change/SKILL.md index cd00a8b444..e9dc213b6b 100644 --- a/skills/openspec-apply-change/SKILL.md +++ b/skills/openspec-apply-change/SKILL.md @@ -99,6 +99,7 @@ Implement tasks from an OpenSpec change. **Pause if:** - Task is unclear → ask for clarification - Implementation reveals a design issue → suggest updating artifacts + - A task needs work beyond what the spec and tasks describe, or you are tempted to drop, narrow, defer, or accept exceptions to specified behavior to make it fit → surface the added scope and ask; do not absorb it silently - Error or blocker encountered → report and wait for guidance - User interrupts @@ -169,6 +170,8 @@ What would you like to do? - Keep code changes minimal and scoped to each task - Update task checkbox immediately after completing each task - Pause on errors, blockers, or unclear requirements - don't guess +- When a task needs work beyond what the spec describes, surface the added scope and pause - never silently narrow, defer, or simplify away specified behavior +- Only mark a task `- [x]` when its specified behavior is fully implemented, not when it is partially done or deferred - Use contextFiles from CLI output, don't assume specific file names - Do not use context or operation guidance as proof that a task is complete - Apply relevant project context; report conflicts with controlling workflow inputs diff --git a/src/core/templates/workflows/apply-change.ts b/src/core/templates/workflows/apply-change.ts index 931f6e7b68..d7ebe2f4eb 100644 --- a/src/core/templates/workflows/apply-change.ts +++ b/src/core/templates/workflows/apply-change.ts @@ -107,6 +107,7 @@ ${STORE_SELECTION_GUIDANCE} **Pause if:** - Task is unclear → ask for clarification - Implementation reveals a design issue → suggest updating artifacts + - A task needs work beyond what the spec and tasks describe, or you are tempted to drop, narrow, defer, or accept exceptions to specified behavior to make it fit → surface the added scope and ask; do not absorb it silently - Error or blocker encountered → report and wait for guidance - User interrupts @@ -177,6 +178,8 @@ What would you like to do? - Keep code changes minimal and scoped to each task - Update task checkbox immediately after completing each task - Pause on errors, blockers, or unclear requirements - don't guess +- When a task needs work beyond what the spec describes, surface the added scope and pause - never silently narrow, defer, or simplify away specified behavior +- Only mark a task \`- [x]\` when its specified behavior is fully implemented, not when it is partially done or deferred - Use contextFiles from CLI output, don't assume specific file names - Do not use context or operation guidance as proof that a task is complete - Apply relevant project context; report conflicts with controlling workflow inputs diff --git a/test/core/templates/apply-defer-guardrail.test.ts b/test/core/templates/apply-defer-guardrail.test.ts new file mode 100644 index 0000000000..51fc85f340 --- /dev/null +++ b/test/core/templates/apply-defer-guardrail.test.ts @@ -0,0 +1,36 @@ +import { describe, it, expect } from 'vitest'; +import { + getApplyInstructions, + getApplyChangeSkillTemplate, + getOpsxApplyCommandTemplate, +} from '../../../src/core/templates/workflows/apply-change.js'; + +// #1529: agents were silently simplifying or deferring work mid-apply and +// marking tasks done anyway. The apply instructions must tell the agent to +// surface unexpected scope instead of absorbing it, on both surfaces. +describe('apply instructions surface deferred scope (#1529)', () => { + const instructions = getApplyInstructions(); + + it('tells the agent to surface added scope rather than defer or simplify', () => { + expect(instructions).toContain('surface the added scope'); + expect(instructions).toContain('beyond what the spec describes'); + expect(instructions).toMatch(/never silently narrow, defer/); + }); + + it('requires pausing, not just reporting and continuing', () => { + // The agent must hand control back, not surface the scope and press on. + expect(instructions).toContain('surface the added scope and pause'); + }); + + it('forbids marking a task complete when it is only partially done', () => { + expect(instructions).toMatch( + /Only mark a task .* when its specified behavior is fully implemented/ + ); + }); + + it('carries the same guidance on both the skill and command surfaces', () => { + const needle = 'surface the added scope'; + expect(getApplyChangeSkillTemplate().instructions).toContain(needle); + expect(getOpsxApplyCommandTemplate().content).toContain(needle); + }); +}); diff --git a/test/core/templates/skill-templates-parity.test.ts b/test/core/templates/skill-templates-parity.test.ts index e0a1995752..8c267c617a 100644 --- a/test/core/templates/skill-templates-parity.test.ts +++ b/test/core/templates/skill-templates-parity.test.ts @@ -41,14 +41,14 @@ const EXPECTED_FUNCTION_HASHES: Record = { getExploreSkillTemplate: 'fec38ba01c5c20695aca0ec7eff78c26e278ead21459cab8ec1562af51053427', getNewChangeSkillTemplate: '935f6335e2d4b7d1bd4f0538c88386350c25e8b16e11b627556262229583ca51', getContinueChangeSkillTemplate: 'ed41e2356af7aad6ef760f60fad19c6843cefe436d8f90084dcba4dbc6bf7272', - getApplyChangeSkillTemplate: '0de84d3e414c0bc72b21a47384257a1b3bc754336538e245db55af307d7eda99', + getApplyChangeSkillTemplate: 'a8d2529741849723ef160726648173e8ca8b42f5bb6f3d89ec547268adce2846', getFfChangeSkillTemplate: 'fc2a45a08533ee9c7ab30fdab5f832b7d440070048e2a153f03db1620dc379bb', getSyncSpecsSkillTemplate: 'd43b112a3c74bc951b094d220c8e75cca26bb00640d404b78af0752af1ff7bd9', getOnboardSkillTemplate: 'a9f6134b187ec4f3a5aa6c7c181e51a15fec11b7ac1044a076fdfe79b47fbc80', getOpsxExploreCommandTemplate: 'e2d470148708a9070675edddd1e783f1c71c96625d08cff4fe7a9994e0d292c0', getOpsxNewCommandTemplate: '08e784e52ac2c146975a874257c589d88e93efbd83dc4d79253c8525f5c3064f', getOpsxContinueCommandTemplate: 'ae964cd00f6ca332fd7f9428a577ade75be279f50431d5f60ece8172e8d1a4b1', - getOpsxApplyCommandTemplate: 'd27ad905657dd3797571eccee2b6416495fa9b39759d36b43a9871a301757979', + getOpsxApplyCommandTemplate: '860b55e4ffc055bb6f7339eeb65eaa695e09be7738dff2260726403647a57a18', getOpsxFfCommandTemplate: '012610f85576a7055dfec2aaabba6bfc245454ce91fb6214587ae9316dc2b864', getArchiveChangeSkillTemplate: '5ef19163f73997fdda1c69dc8bca710c16c50b052b481821d916f4084bb42a64', getBulkArchiveChangeSkillTemplate: '03cc44a0ce9bdb3ba2668a9d43946596308901600aa29a728c4a71fc76e86de3', @@ -69,7 +69,7 @@ const EXPECTED_GENERATED_SKILL_CONTENT_HASHES: Record = { 'openspec-explore': '80109dec3abf1505ab1037f7196baac4fcdf175ca954411e8d439e5da881bf62', 'openspec-new-change': '579d432771703f947a331a6ed288bf9c6660ca015fcd376d76f19b6ac7683082', 'openspec-continue-change': '5c34be8194cdb4c5158335e47aece71143e8a22bfb4179dba47fd8aaf436d395', - 'openspec-apply-change': 'a1c79d1104255f7655df120d3ebf362cc14a2bb23ae6e857ba430dea2f8bc8bc', + 'openspec-apply-change': '2709759f101b455dbd93779005f6692966a37c7c71336af9dc5752906046d4f8', 'openspec-ff-change': '19315644df7c582d920acfb67f3c500ca4e06fccc900265b3ac39621d85f7cdb', 'openspec-sync-specs': '6e85521de10858bb020885eb657aa843e5746b2f09c846aa44545694f456cda9', 'openspec-archive-change': '019d580a13eee5892cc9233a899919b572a3abfc6a05c1f0aabf9c4ba9bf3d4d',