Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/apply-surface-deferred-scope.md
Original file line number Diff line number Diff line change
@@ -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.
3 changes: 3 additions & 0 deletions skills/openspec-apply-change/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/core/templates/workflows/apply-change.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
36 changes: 36 additions & 0 deletions test/core/templates/apply-defer-guardrail.test.ts
Original file line number Diff line number Diff line change
@@ -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');
});
Comment on lines +14 to +18

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert the pause requirement explicitly.

The test checks that the instructions surface added scope and prohibit silent simplification or deferment. It does not check that the agent pauses. The test could pass if the workflow reports the added scope and then continues without waiting for guidance.

Add surface the added scope and pause to the single-surface assertion and use the same phrase for both rendered surfaces. The production requirement is in src/core/templates/workflows/apply-change.ts, Lines 181-182.

Proposed assertion update
-    expect(instructions).toContain('surface the added scope');
+    expect(instructions).toContain('surface the added scope and pause');
...
-    const needle = 'surface the added scope';
+    const needle = 'surface the added scope and pause';

This follows the PR objective that unexpected scope must pause the workflow.

Also applies to: 23-26

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/core/templates/apply-defer-guardrail.test.ts` around lines 14 - 17,
Update the assertions in the apply-defer guardrail test to require the exact
phrase “surface the added scope and pause” for both rendered instruction
surfaces, while preserving the existing checks prohibiting silent simplification
or deferment. Align the test with the corresponding requirement in the
apply-change workflow template.


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);
});
});
6 changes: 3 additions & 3 deletions test/core/templates/skill-templates-parity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ const EXPECTED_FUNCTION_HASHES: Record<string, string> = {
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',
Expand All @@ -69,7 +69,7 @@ const EXPECTED_GENERATED_SKILL_CONTENT_HASHES: Record<string, string> = {
'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',
Expand Down
Loading