From 6d975f8d74c69214ead4f67f6607d63c57ce90f9 Mon Sep 17 00:00:00 2001 From: Clay Good Date: Thu, 10 Sep 2026 11:45:40 -0500 Subject: [PATCH 1/5] fix(explore): make the capture request the write confirmation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Explore's write-confirmation rule named `openspec new change` as an action requiring a separate yes/no, while the capture branch told the agent to transition "seamlessly" into running it. Both readings were defensible, so the same request either wrote files immediately or stopped and asked. State the resolution in all three places: an explicit capture request is the confirmation, for the change and artifacts that request names. The guardrail keeps its teeth where #1715 reported the problem — an agent-proposed capture, or work beyond the requested scope, still asks. Closes #1828 Co-Authored-By: Claude Opus 5 --- ...explore-capture-request-is-confirmation.md | 5 ++ skills/openspec-explore/SKILL.md | 6 +- src/core/templates/workflows/explore.ts | 12 +-- test/core/templates/explore.test.ts | 73 +++++++++++++++++++ .../templates/skill-templates-parity.test.ts | 6 +- 5 files changed, 90 insertions(+), 12 deletions(-) create mode 100644 .changeset/explore-capture-request-is-confirmation.md diff --git a/.changeset/explore-capture-request-is-confirmation.md b/.changeset/explore-capture-request-is-confirmation.md new file mode 100644 index 0000000000..649bdc0181 --- /dev/null +++ b/.changeset/explore-capture-request-is-confirmation.md @@ -0,0 +1,5 @@ +--- +'@fission-ai/openspec': patch +--- + +Resolve the contradiction that left explore mode's capture branch without a governing rule. Explore states twice that the agent must ask a direct yes/no question and wait for confirmation in a separate user message before its first write-capable action, naming `openspec new change` as an example, while the capture branch tells the agent to transition "seamlessly" into running `openspec new change` and creating artifacts with no confirmation step. Both readings were defensible from the text, so the same "capture this as a change" request either wrote `.openspec.yaml` plus several artifacts immediately or stopped and asked, depending on which passage the agent weighed — which made the #1715 guarantee unenforceable in the one explore path that writes files. An explicit capture request is now stated to be that confirmation, covering the change and the artifacts the request names and nothing else. The guardrail keeps its teeth for the case #1715 actually reported: when the agent is the one proposing the capture, or when the work would go beyond the requested scope, it still asks first, and answers to design or clarifying questions are still never consent to write. Both explore delivery surfaces and the committed skill carry the same wording. Fixes #1828. diff --git a/skills/openspec-explore/SKILL.md b/skills/openspec-explore/SKILL.md index 2ff15bfed1..6b067e5ea0 100644 --- a/skills/openspec-explore/SKILL.md +++ b/skills/openspec-explore/SKILL.md @@ -11,7 +11,7 @@ metadata: Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes. -**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, investigate the codebase, and run read-only commands or tools without confirmation, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create or update OpenSpec change artifacts (proposals, designs, specs) within a confirmed scope—that's capturing thinking, not implementing. Answering design or clarifying questions is never consent to write. Before the first write-capable action, name the artifacts or files you would change and what you would do, ask a direct yes/no question, and wait for the user's confirmation in a separate message. Confirmation covers only the scope you described; ask again before expanding it. For a new change, scaffold it first as described below. +**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, investigate the codebase, and run read-only commands or tools without confirmation, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create or update OpenSpec change artifacts (proposals, designs, specs) within a confirmed scope—that's capturing thinking, not implementing. Answering design or clarifying questions is never consent to write. Before the first write-capable action, name the artifacts or files you would change and what you would do, ask a direct yes/no question, and wait for the user's confirmation in a separate message. Confirmation covers only the scope you described; ask again before expanding it. An explicit request to capture the exploration as a new change is itself that confirmation, covering the change and the artifacts that request names; scaffold it first as described below. **This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore. @@ -141,7 +141,7 @@ Think freely. When insights crystallize, you might offer: - "This feels solid enough to start a change. Want me to create a proposal?" - Or keep exploring - no pressure to formalize -If the user asks you to capture the exploration as a new change, transition seamlessly into the requested capture: +If the user asks you to capture the exploration as a new change, that request is the confirmation required above. It covers scaffolding the change and creating the artifacts the request names, and nothing else. Do not ask for a second confirmation; transition seamlessly into the requested capture: 1. Run `openspec new change ""` (with `--store ` when applicable) before creating any artifacts. Never create a new change directory under `openspec/changes/` by hand; the CLI scaffold creates required metadata such as `.openspec.yaml`. Keep the selected `--store ` on every applicable follow-up `status` and `instructions` command. 2. Run `openspec status --change "" --json` (append the confirmed `--store ""` only for a registered standalone store), then process the requested artifacts in dependency order. For each requested artifact that is `ready`, run `openspec instructions "" --change "" --json` (append the confirmed `--store ""` only for a registered standalone store). Before creating a requested artifact, evaluate any condition in its own `instruction` against the explored change; record a deliberate skip instead when the condition does not apply. If a requested artifact is blocked by a direct prerequisite the user did not request, run `openspec instructions "" --change "" --json` (append the confirmed `--store ""` only for a registered standalone store) for that prerequisite whether it is `ready` or `blocked`. If its own `instruction` states a condition, evaluate that condition against the explored change and record a deliberate skip only when the condition does not apply. If the condition applies, or the prerequisite is not conditional, treat it as a normal prerequisite and ask before expanding the capture. Do not create an unrequested prerequisite unless the user approves. @@ -335,7 +335,7 @@ But this summary is optional. Sometimes the thinking IS the value. - **Don't fake understanding** - If something is unclear, dig deeper - **Don't rush** - Discovery is thinking time, not task time - **Don't force structure** - Let patterns emerge naturally -- **Don't auto-capture** - Offer to save insights, don't just do it. Read-only commands and tools need no confirmation. Before the first write-capable action—including `openspec new change` or another command that writes files—name the artifacts or files and proposed changes, ask a direct yes/no question, and wait for explicit confirmation in a separate user message. That confirmation covers only the described scope; ask again before expanding it. Answers to design or clarifying questions are never consent to write. +- **Don't auto-capture** - Offer to save insights, don't just do it. Read-only commands and tools need no confirmation. Before the first write-capable action—including `openspec new change` or another command that writes files—name the artifacts or files and proposed changes, ask a direct yes/no question, and wait for explicit confirmation in a separate user message. That confirmation covers only the described scope; ask again before expanding it. Answers to design or clarifying questions are never consent to write. That rule governs `openspec new change` whenever you are the one proposing the capture. When the user explicitly asks you to capture the exploration as a new change, their request is the confirmation for scaffolding it and creating the artifacts the request names, so run the capture transition without asking again and ask before writing anything outside that scope. - **Don't manually scaffold changes** - Never create a new change directory under `openspec/changes/` by hand. Always use `openspec new change ""` (with `--store ` when applicable) so required metadata such as `.openspec.yaml` is created before writing artifacts. - **Do visualize** - A good diagram is worth many paragraphs - **Do explore the codebase** - Ground discussions in reality diff --git a/src/core/templates/workflows/explore.ts b/src/core/templates/workflows/explore.ts index 20db318293..356bfcfd08 100644 --- a/src/core/templates/workflows/explore.ts +++ b/src/core/templates/workflows/explore.ts @@ -35,7 +35,7 @@ export function getExploreSkillTemplate(): SkillTemplate { description: 'Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements. Use when the user wants to think through something before or during a change.', instructions: `Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes. -**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, investigate the codebase, and run read-only commands or tools without confirmation, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create or update OpenSpec change artifacts (proposals, designs, specs) within a confirmed scope—that's capturing thinking, not implementing. Answering design or clarifying questions is never consent to write. Before the first write-capable action, name the artifacts or files you would change and what you would do, ask a direct yes/no question, and wait for the user's confirmation in a separate message. Confirmation covers only the scope you described; ask again before expanding it. For a new change, scaffold it first as described below. +**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, investigate the codebase, and run read-only commands or tools without confirmation, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create or update OpenSpec change artifacts (proposals, designs, specs) within a confirmed scope—that's capturing thinking, not implementing. Answering design or clarifying questions is never consent to write. Before the first write-capable action, name the artifacts or files you would change and what you would do, ask a direct yes/no question, and wait for the user's confirmation in a separate message. Confirmation covers only the scope you described; ask again before expanding it. An explicit request to capture the exploration as a new change is itself that confirmation, covering the change and the artifacts that request names; scaffold it first as described below. **This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore. @@ -145,7 +145,7 @@ Think freely. When insights crystallize, you might offer: - "This feels solid enough to start a change. Want me to create a proposal?" - Or keep exploring - no pressure to formalize -If the user asks you to capture the exploration as a new change, transition seamlessly into the requested capture: +If the user asks you to capture the exploration as a new change, that request is the confirmation required above. It covers scaffolding the change and creating the artifacts the request names, and nothing else. Do not ask for a second confirmation; transition seamlessly into the requested capture: 1. Run \`openspec new change ""\` (with \`--store \` when applicable) before creating any artifacts. Never create a new change directory under \`openspec/changes/\` by hand; the CLI scaffold creates required metadata such as \`.openspec.yaml\`. Keep the selected \`--store \` on every applicable follow-up \`status\` and \`instructions\` command. 2. Run \`openspec status --change "" --json\` (append the confirmed \`--store ""\` only for a registered standalone store), then process the requested artifacts in dependency order. For each requested artifact that is \`ready\`, run \`openspec instructions "" --change "" --json\` (append the confirmed \`--store ""\` only for a registered standalone store). Before creating a requested artifact, evaluate any condition in its own \`instruction\` against the explored change; record a deliberate skip instead when the condition does not apply. If a requested artifact is blocked by a direct prerequisite the user did not request, run \`openspec instructions "" --change "" --json\` (append the confirmed \`--store ""\` only for a registered standalone store) for that prerequisite whether it is \`ready\` or \`blocked\`. If its own \`instruction\` states a condition, evaluate that condition against the explored change and record a deliberate skip only when the condition does not apply. If the condition applies, or the prerequisite is not conditional, treat it as a normal prerequisite and ask before expanding the capture. Do not create an unrequested prerequisite unless the user approves. @@ -339,7 +339,7 @@ But this summary is optional. Sometimes the thinking IS the value. - **Don't fake understanding** - If something is unclear, dig deeper - **Don't rush** - Discovery is thinking time, not task time - **Don't force structure** - Let patterns emerge naturally -- **Don't auto-capture** - Offer to save insights, don't just do it. Read-only commands and tools need no confirmation. Before the first write-capable action—including \`openspec new change\` or another command that writes files—name the artifacts or files and proposed changes, ask a direct yes/no question, and wait for explicit confirmation in a separate user message. That confirmation covers only the described scope; ask again before expanding it. Answers to design or clarifying questions are never consent to write. +- **Don't auto-capture** - Offer to save insights, don't just do it. Read-only commands and tools need no confirmation. Before the first write-capable action—including \`openspec new change\` or another command that writes files—name the artifacts or files and proposed changes, ask a direct yes/no question, and wait for explicit confirmation in a separate user message. That confirmation covers only the described scope; ask again before expanding it. Answers to design or clarifying questions are never consent to write. That rule governs \`openspec new change\` whenever you are the one proposing the capture. When the user explicitly asks you to capture the exploration as a new change, their request is the confirmation for scaffolding it and creating the artifacts the request names, so run the capture transition without asking again and ask before writing anything outside that scope. - **Don't manually scaffold changes** - Never create a new change directory under \`openspec/changes/\` by hand. Always use \`openspec new change ""\` (with \`--store \` when applicable) so required metadata such as \`.openspec.yaml\` is created before writing artifacts. - **Do visualize** - A good diagram is worth many paragraphs - **Do explore the codebase** - Ground discussions in reality @@ -358,7 +358,7 @@ export function getOpsxExploreCommandTemplate(): CommandTemplate { tags: ['workflow', 'explore', 'experimental', 'thinking'], content: `Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes. -**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, investigate the codebase, and run read-only commands or tools without confirmation, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create or update OpenSpec change artifacts (proposals, designs, specs) within a confirmed scope—that's capturing thinking, not implementing. Answering design or clarifying questions is never consent to write. Before the first write-capable action, name the artifacts or files you would change and what you would do, ask a direct yes/no question, and wait for the user's confirmation in a separate message. Confirmation covers only the scope you described; ask again before expanding it. For a new change, scaffold it first as described below. +**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, investigate the codebase, and run read-only commands or tools without confirmation, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create or update OpenSpec change artifacts (proposals, designs, specs) within a confirmed scope—that's capturing thinking, not implementing. Answering design or clarifying questions is never consent to write. Before the first write-capable action, name the artifacts or files you would change and what you would do, ask a direct yes/no question, and wait for the user's confirmation in a separate message. Confirmation covers only the scope you described; ask again before expanding it. An explicit request to capture the exploration as a new change is itself that confirmation, covering the change and the artifacts that request names; scaffold it first as described below. **This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore. @@ -477,7 +477,7 @@ Think freely. When insights crystallize, you might offer: - "This feels solid enough to start a change. Want me to create a proposal?" - Or keep exploring - no pressure to formalize -If the user asks you to capture the exploration as a new change, transition seamlessly into the requested capture: +If the user asks you to capture the exploration as a new change, that request is the confirmation required above. It covers scaffolding the change and creating the artifacts the request names, and nothing else. Do not ask for a second confirmation; transition seamlessly into the requested capture: 1. Run \`openspec new change ""\` (with \`--store \` when applicable) before creating any artifacts. Never create a new change directory under \`openspec/changes/\` by hand; the CLI scaffold creates required metadata such as \`.openspec.yaml\`. Keep the selected \`--store \` on every applicable follow-up \`status\` and \`instructions\` command. 2. Run \`openspec status --change "" --json\` (append the confirmed \`--store ""\` only for a registered standalone store), then process the requested artifacts in dependency order. For each requested artifact that is \`ready\`, run \`openspec instructions "" --change "" --json\` (append the confirmed \`--store ""\` only for a registered standalone store). Before creating a requested artifact, evaluate any condition in its own \`instruction\` against the explored change; record a deliberate skip instead when the condition does not apply. If a requested artifact is blocked by a direct prerequisite the user did not request, run \`openspec instructions "" --change "" --json\` (append the confirmed \`--store ""\` only for a registered standalone store) for that prerequisite whether it is \`ready\` or \`blocked\`. If its own \`instruction\` states a condition, evaluate that condition against the explored change and record a deliberate skip only when the condition does not apply. If the condition applies, or the prerequisite is not conditional, treat it as a normal prerequisite and ask before expanding the capture. Do not create an unrequested prerequisite unless the user approves. @@ -551,7 +551,7 @@ When things crystallize, you might offer a summary - but it's optional. Sometime - **Don't fake understanding** - If something is unclear, dig deeper - **Don't rush** - Discovery is thinking time, not task time - **Don't force structure** - Let patterns emerge naturally -- **Don't auto-capture** - Offer to save insights, don't just do it. Read-only commands and tools need no confirmation. Before the first write-capable action—including \`openspec new change\` or another command that writes files—name the artifacts or files and proposed changes, ask a direct yes/no question, and wait for explicit confirmation in a separate user message. That confirmation covers only the described scope; ask again before expanding it. Answers to design or clarifying questions are never consent to write. +- **Don't auto-capture** - Offer to save insights, don't just do it. Read-only commands and tools need no confirmation. Before the first write-capable action—including \`openspec new change\` or another command that writes files—name the artifacts or files and proposed changes, ask a direct yes/no question, and wait for explicit confirmation in a separate user message. That confirmation covers only the described scope; ask again before expanding it. Answers to design or clarifying questions are never consent to write. That rule governs \`openspec new change\` whenever you are the one proposing the capture. When the user explicitly asks you to capture the exploration as a new change, their request is the confirmation for scaffolding it and creating the artifacts the request names, so run the capture transition without asking again and ask before writing anything outside that scope. - **Don't manually scaffold changes** - Never create a new change directory under \`openspec/changes/\` by hand. Always use \`openspec new change ""\` (with \`--store \` when applicable) so required metadata such as \`.openspec.yaml\` is created before writing artifacts. - **Do visualize** - A good diagram is worth many paragraphs - **Do explore the codebase** - Ground discussions in reality diff --git a/test/core/templates/explore.test.ts b/test/core/templates/explore.test.ts index b6ec059403..0f8462a9af 100644 --- a/test/core/templates/explore.test.ts +++ b/test/core/templates/explore.test.ts @@ -192,6 +192,79 @@ describe('explore templates', () => { } }); + // Regression for #1828: the #1715 write-confirmation rule named + // `openspec new change` as something that needs a separate yes/no, while + // the capture branch told the agent to transition "seamlessly" into + // running it. Both readings were defensible, so the same request either + // wrote files immediately or stopped and asked. The rule now resolves the + // conflict in one direction: an explicit capture request IS the + // confirmation, for the scope that request names. + it('treats an explicit capture request as the write confirmation (#1828)', () => { + for (const [label, body] of bodies) { + expect(body, label).toContain( + 'An explicit request to capture the exploration as a new change is itself that confirmation, covering the change and the artifacts that request names' + ); + } + }); + + it('resolves the capture carve-out in the guardrail that names `openspec new change` (#1828)', () => { + for (const [label, body] of bodies) { + // The general rule must survive: an agent proposing the capture on its + // own initiative still owes the user a separate yes/no. + expect(body, label).toContain( + 'including `openspec new change` or another command that writes files' + ); + expect(body, label).toContain( + 'That rule governs `openspec new change` whenever you are the one proposing the capture' + ); + expect(body, label).toContain( + 'When the user explicitly asks you to capture the exploration as a new change, their request is the confirmation for scaffolding it and creating the artifacts the request names, so run the capture transition without asking again' + ); + expect(body, label).toContain( + 'ask before writing anything outside that scope' + ); + } + }); + + it('states the carve-out at the head of the capture branch, before the scaffold step (#1828)', () => { + for (const [label, body] of bodies) { + const transition = newChangeTransition(body, label); + const carveOut = transition.indexOf( + 'that request is the confirmation required above' + ); + const scaffold = transition.indexOf('1. Run `openspec new change ""`'); + + expect(carveOut, label).toBeGreaterThanOrEqual(0); + expect(scaffold, label).toBeGreaterThan(carveOut); + expect(transition, label).toContain( + 'It covers scaffolding the change and creating the artifacts the request names, and nothing else' + ); + expect(transition, label).toContain('Do not ask for a second confirmation'); + } + }); + + // The carve-out must not become a blanket write permit: #1715's guarantee + // survives only if everything outside the requested scope still stops. + it('keeps the carve-out scoped to what the request named (#1828, #1715)', () => { + for (const [label, body] of bodies) { + const transition = newChangeTransition(body, label); + + expect(body, label).toContain( + 'Confirmation covers only the scope you described; ask again before expanding it' + ); + expect(body, label).toContain( + 'Answering design or clarifying questions is never consent to write' + ); + expect(body, label).toContain( + 'Accepting an answer or a batch of recommendations is not permission to write' + ); + expect(transition, label).toContain( + 'Do not create an unrequested prerequisite unless the user approves' + ); + expect(transition, label).toContain('ask before expanding the capture'); + } + }); + it('scaffolds a new change before capturing exploration artifacts (#668, #720)', () => { for (const [label, body] of bodies) { const transition = newChangeTransition(body, label); diff --git a/test/core/templates/skill-templates-parity.test.ts b/test/core/templates/skill-templates-parity.test.ts index 4d2e717cae..7ac2e7e918 100644 --- a/test/core/templates/skill-templates-parity.test.ts +++ b/test/core/templates/skill-templates-parity.test.ts @@ -38,14 +38,14 @@ import { import { STORE_SELECTION_GUIDANCE } from '../../../src/core/templates/workflows/store-selection.js'; const EXPECTED_FUNCTION_HASHES: Record = { - getExploreSkillTemplate: '06aba775c621e61f00995a9ebc3a02fe873ddcc9bf024e416c4adaf91ccce115', + getExploreSkillTemplate: '880d6417dd7223a374d0569f0335abec87bea109f6d4805829dd8dbb643507d3', getNewChangeSkillTemplate: 'eabd1e895c5881dcb17dcbaa3fb26098dd59e8eacb318e400820b4dc811ef781', getContinueChangeSkillTemplate: '012136f6411a99c8fa228e2f9444cb64b0a89e0f56fdeac2fe03b2f5bee0c5d7', getApplyChangeSkillTemplate: 'd1e7d5ceb85193c0964057dbb88e9651526754bd33f84020e2440ff0621d5dbb', getFfChangeSkillTemplate: 'efa6a70c111b18b61a7720250b9622afa9a212fb64edf609cf80e2182a9bdf8c', getSyncSpecsSkillTemplate: 'b099e2ff31859c9b10d928066e662524f9aad9ecf2be12fceacb732d718c4146', getOnboardSkillTemplate: '3a836faae463d88c289a1c129cb7ee556a563b7e53e1a52a4711ff152a3b51f7', - getOpsxExploreCommandTemplate: '8046003e97d885a86ed392d4fb522bb78544a02872b042e51347a5021cc10523', + getOpsxExploreCommandTemplate: 'dd3062f557ad34f169bd2ed17113f539e827bf90c51b290311c21af66a255dc2', getOpsxNewCommandTemplate: 'f2d30e569798a4c92ba932859d6ba4e0ad10e18feccbade1cfee0957597b3463', getOpsxContinueCommandTemplate: 'e50e50266efa1b8e64ff9b6274ee8254f0a240d6adc1b862d126e2f1c9d3a559', getOpsxApplyCommandTemplate: 'e3579ac78f2e2c75fa3d3a7ac7dc3e49c395e96f7323398f0f041d94f8de9bb0', @@ -66,7 +66,7 @@ const EXPECTED_FUNCTION_HASHES: Record = { }; const EXPECTED_GENERATED_SKILL_CONTENT_HASHES: Record = { - 'openspec-explore': '32b20cfbcc7d51ff526bb19571ff3dc3d0c616a5911b8de74cf6d9b15650cf3e', + 'openspec-explore': '17969500e9c81580c1397bebc7592f7d695c737d8ed73610321f25843ca98744', 'openspec-new-change': 'ec4529beef978e34634a6f7286fab55d68fad8fb374dceb45691d52caab33fbb', 'openspec-continue-change': 'bb6194a16c54891cdb253678e8f70ce53b2af86735243980f366ce551d37e42e', 'openspec-apply-change': '81ea96d9fa6ec8536cd23c1fe561ed28e1cc1cad0a8ceb700588e08974cc0e49', From 55eeac6145b9c8ac3ba35616efe924a83bca44b1 Mon Sep 17 00:00:00 2001 From: Clay Good Date: Thu, 10 Sep 2026 11:50:42 -0500 Subject: [PATCH 2/5] test(explore): guard the capture branch against a re-added confirmation gate Also disambiguate the scope fence in the IMPORTANT block: "the artifacts that request names" parses as a relative clause, and it is the sentence an agent weighs first. Match the article used by both restatements. Co-Authored-By: Claude Opus 5 --- skills/openspec-explore/SKILL.md | 2 +- src/core/templates/workflows/explore.ts | 4 ++-- test/core/templates/explore.test.ts | 11 ++++++++++- test/core/templates/skill-templates-parity.test.ts | 6 +++--- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/skills/openspec-explore/SKILL.md b/skills/openspec-explore/SKILL.md index 6b067e5ea0..7d88db266c 100644 --- a/skills/openspec-explore/SKILL.md +++ b/skills/openspec-explore/SKILL.md @@ -11,7 +11,7 @@ metadata: Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes. -**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, investigate the codebase, and run read-only commands or tools without confirmation, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create or update OpenSpec change artifacts (proposals, designs, specs) within a confirmed scope—that's capturing thinking, not implementing. Answering design or clarifying questions is never consent to write. Before the first write-capable action, name the artifacts or files you would change and what you would do, ask a direct yes/no question, and wait for the user's confirmation in a separate message. Confirmation covers only the scope you described; ask again before expanding it. An explicit request to capture the exploration as a new change is itself that confirmation, covering the change and the artifacts that request names; scaffold it first as described below. +**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, investigate the codebase, and run read-only commands or tools without confirmation, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create or update OpenSpec change artifacts (proposals, designs, specs) within a confirmed scope—that's capturing thinking, not implementing. Answering design or clarifying questions is never consent to write. Before the first write-capable action, name the artifacts or files you would change and what you would do, ask a direct yes/no question, and wait for the user's confirmation in a separate message. Confirmation covers only the scope you described; ask again before expanding it. An explicit request to capture the exploration as a new change is itself that confirmation, covering the change and the artifacts the request names; scaffold it first as described below. **This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore. diff --git a/src/core/templates/workflows/explore.ts b/src/core/templates/workflows/explore.ts index 356bfcfd08..6faec2aa63 100644 --- a/src/core/templates/workflows/explore.ts +++ b/src/core/templates/workflows/explore.ts @@ -35,7 +35,7 @@ export function getExploreSkillTemplate(): SkillTemplate { description: 'Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements. Use when the user wants to think through something before or during a change.', instructions: `Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes. -**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, investigate the codebase, and run read-only commands or tools without confirmation, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create or update OpenSpec change artifacts (proposals, designs, specs) within a confirmed scope—that's capturing thinking, not implementing. Answering design or clarifying questions is never consent to write. Before the first write-capable action, name the artifacts or files you would change and what you would do, ask a direct yes/no question, and wait for the user's confirmation in a separate message. Confirmation covers only the scope you described; ask again before expanding it. An explicit request to capture the exploration as a new change is itself that confirmation, covering the change and the artifacts that request names; scaffold it first as described below. +**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, investigate the codebase, and run read-only commands or tools without confirmation, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create or update OpenSpec change artifacts (proposals, designs, specs) within a confirmed scope—that's capturing thinking, not implementing. Answering design or clarifying questions is never consent to write. Before the first write-capable action, name the artifacts or files you would change and what you would do, ask a direct yes/no question, and wait for the user's confirmation in a separate message. Confirmation covers only the scope you described; ask again before expanding it. An explicit request to capture the exploration as a new change is itself that confirmation, covering the change and the artifacts the request names; scaffold it first as described below. **This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore. @@ -358,7 +358,7 @@ export function getOpsxExploreCommandTemplate(): CommandTemplate { tags: ['workflow', 'explore', 'experimental', 'thinking'], content: `Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes. -**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, investigate the codebase, and run read-only commands or tools without confirmation, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create or update OpenSpec change artifacts (proposals, designs, specs) within a confirmed scope—that's capturing thinking, not implementing. Answering design or clarifying questions is never consent to write. Before the first write-capable action, name the artifacts or files you would change and what you would do, ask a direct yes/no question, and wait for the user's confirmation in a separate message. Confirmation covers only the scope you described; ask again before expanding it. An explicit request to capture the exploration as a new change is itself that confirmation, covering the change and the artifacts that request names; scaffold it first as described below. +**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, investigate the codebase, and run read-only commands or tools without confirmation, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create or update OpenSpec change artifacts (proposals, designs, specs) within a confirmed scope—that's capturing thinking, not implementing. Answering design or clarifying questions is never consent to write. Before the first write-capable action, name the artifacts or files you would change and what you would do, ask a direct yes/no question, and wait for the user's confirmation in a separate message. Confirmation covers only the scope you described; ask again before expanding it. An explicit request to capture the exploration as a new change is itself that confirmation, covering the change and the artifacts the request names; scaffold it first as described below. **This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore. diff --git a/test/core/templates/explore.test.ts b/test/core/templates/explore.test.ts index 0f8462a9af..95ca41d56d 100644 --- a/test/core/templates/explore.test.ts +++ b/test/core/templates/explore.test.ts @@ -202,7 +202,7 @@ describe('explore templates', () => { it('treats an explicit capture request as the write confirmation (#1828)', () => { for (const [label, body] of bodies) { expect(body, label).toContain( - 'An explicit request to capture the exploration as a new change is itself that confirmation, covering the change and the artifacts that request names' + 'An explicit request to capture the exploration as a new change is itself that confirmation, covering the change and the artifacts the request names' ); } }); @@ -249,6 +249,15 @@ describe('explore templates', () => { for (const [label, body] of bodies) { const transition = newChangeTransition(body, label); + // String containment alone cannot catch #1828's actual failure mode: a + // second, contradictory instruction added elsewhere. The capture branch + // is resolved only while it carries no confirmation gate of its own. + expect(transition, label).not.toContain('ask a direct yes/no question'); + expect(transition, label).not.toContain( + "wait for the user's confirmation" + ); + expect(transition, label).not.toContain('wait for explicit confirmation'); + expect(body, label).toContain( 'Confirmation covers only the scope you described; ask again before expanding it' ); diff --git a/test/core/templates/skill-templates-parity.test.ts b/test/core/templates/skill-templates-parity.test.ts index 7ac2e7e918..46156e5703 100644 --- a/test/core/templates/skill-templates-parity.test.ts +++ b/test/core/templates/skill-templates-parity.test.ts @@ -38,14 +38,14 @@ import { import { STORE_SELECTION_GUIDANCE } from '../../../src/core/templates/workflows/store-selection.js'; const EXPECTED_FUNCTION_HASHES: Record = { - getExploreSkillTemplate: '880d6417dd7223a374d0569f0335abec87bea109f6d4805829dd8dbb643507d3', + getExploreSkillTemplate: 'ecc0a0dbc55e8e5058529622302bda0f26058377e149a8f370b854d79f586a61', getNewChangeSkillTemplate: 'eabd1e895c5881dcb17dcbaa3fb26098dd59e8eacb318e400820b4dc811ef781', getContinueChangeSkillTemplate: '012136f6411a99c8fa228e2f9444cb64b0a89e0f56fdeac2fe03b2f5bee0c5d7', getApplyChangeSkillTemplate: 'd1e7d5ceb85193c0964057dbb88e9651526754bd33f84020e2440ff0621d5dbb', getFfChangeSkillTemplate: 'efa6a70c111b18b61a7720250b9622afa9a212fb64edf609cf80e2182a9bdf8c', getSyncSpecsSkillTemplate: 'b099e2ff31859c9b10d928066e662524f9aad9ecf2be12fceacb732d718c4146', getOnboardSkillTemplate: '3a836faae463d88c289a1c129cb7ee556a563b7e53e1a52a4711ff152a3b51f7', - getOpsxExploreCommandTemplate: 'dd3062f557ad34f169bd2ed17113f539e827bf90c51b290311c21af66a255dc2', + getOpsxExploreCommandTemplate: '519ea6a8d6801d0f7d54595ce548a9bb3e776066bbf718895b4225658dee78f0', getOpsxNewCommandTemplate: 'f2d30e569798a4c92ba932859d6ba4e0ad10e18feccbade1cfee0957597b3463', getOpsxContinueCommandTemplate: 'e50e50266efa1b8e64ff9b6274ee8254f0a240d6adc1b862d126e2f1c9d3a559', getOpsxApplyCommandTemplate: 'e3579ac78f2e2c75fa3d3a7ac7dc3e49c395e96f7323398f0f041d94f8de9bb0', @@ -66,7 +66,7 @@ const EXPECTED_FUNCTION_HASHES: Record = { }; const EXPECTED_GENERATED_SKILL_CONTENT_HASHES: Record = { - 'openspec-explore': '17969500e9c81580c1397bebc7592f7d695c737d8ed73610321f25843ca98744', + 'openspec-explore': '974cd6c923d1ceaa50fb7d335a0373979d8c5c06d58a29dede6c48bda59327fc', 'openspec-new-change': 'ec4529beef978e34634a6f7286fab55d68fad8fb374dceb45691d52caab33fbb', 'openspec-continue-change': 'bb6194a16c54891cdb253678e8f70ce53b2af86735243980f366ce551d37e42e', 'openspec-apply-change': '81ea96d9fa6ec8536cd23c1fe561ed28e1cc1cad0a8ceb700588e08974cc0e49', From 5985f2fde56fb803d180793de47a4320685b32d8 Mon Sep 17 00:00:00 2001 From: Clay Good Date: Thu, 10 Sep 2026 12:27:15 -0500 Subject: [PATCH 3/5] fix(explore): put the capture discriminators where the decision is made MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four hardening findings from review of the first pass: - A yes to an offer the agent made looks identical to a user-initiated capture request at the point the branch decides. The discriminator sat 190 lines away in Guardrails. Move it into the branch, and require the offer to name what it would create. - "Do not ask for a second confirmation" contradicted step 2 nine lines below it, which requires asking before expanding the capture. Narrow it to re-asking for what was already asked for. - Scope the carve-out to change artifacts, so it cannot be read to reach the workflow configuration #1715 reported an agent editing. - The Guardrails bullet restated the whole contract a third time, in a quick-reference list whose next-longest entry is 43 words. Replace with a pointer to the branch that owns it. Tests: the three not.toContain guards could not see a gate phrased in words they did not anticipate. Replace with a structural check that collects every consent-bearing sentence and requires each to be sanctioned — inside the capture branch with no topic filter, since a gate written there is about the capture whether or not it says so. Mutation testing: kills 6 of 8 contradiction mutations that survived before, and all three sites stay independently pinned. The two survivors reverse the resolution without any consent word and are noted as review-only. Co-Authored-By: Claude Opus 5 --- skills/openspec-explore/SKILL.md | 6 +- src/core/templates/workflows/explore.ts | 12 +- test/core/templates/explore.test.ts | 146 ++++++++++++++---- .../templates/skill-templates-parity.test.ts | 6 +- 4 files changed, 132 insertions(+), 38 deletions(-) diff --git a/skills/openspec-explore/SKILL.md b/skills/openspec-explore/SKILL.md index 7d88db266c..4384a9a739 100644 --- a/skills/openspec-explore/SKILL.md +++ b/skills/openspec-explore/SKILL.md @@ -11,7 +11,7 @@ metadata: Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes. -**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, investigate the codebase, and run read-only commands or tools without confirmation, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create or update OpenSpec change artifacts (proposals, designs, specs) within a confirmed scope—that's capturing thinking, not implementing. Answering design or clarifying questions is never consent to write. Before the first write-capable action, name the artifacts or files you would change and what you would do, ask a direct yes/no question, and wait for the user's confirmation in a separate message. Confirmation covers only the scope you described; ask again before expanding it. An explicit request to capture the exploration as a new change is itself that confirmation, covering the change and the artifacts the request names; scaffold it first as described below. +**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, investigate the codebase, and run read-only commands or tools without confirmation, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create or update OpenSpec change artifacts (proposals, designs, specs) within a confirmed scope—that's capturing thinking, not implementing. Answering design or clarifying questions is never consent to write. Before the first write-capable action, name the artifacts or files you would change and what you would do, ask a direct yes/no question, and wait for the user's confirmation in a separate message. Confirmation covers only the scope you described; ask again before expanding it. An explicit request from the user to capture the exploration as a new change is itself that confirmation, covering the change and the change artifacts that request names; scaffold it first as described below. **This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore. @@ -141,7 +141,7 @@ Think freely. When insights crystallize, you might offer: - "This feels solid enough to start a change. Want me to create a proposal?" - Or keep exploring - no pressure to formalize -If the user asks you to capture the exploration as a new change, that request is the confirmation required above. It covers scaffolding the change and creating the artifacts the request names, and nothing else. Do not ask for a second confirmation; transition seamlessly into the requested capture: +If the user asks you to capture the exploration as a new change, that request is the confirmation required above. It covers scaffolding that change and creating the change artifacts the request names, and nothing else. This holds only when the request is theirs: a yes to an offer you made confirms only the scope your offer itself named, so name the change and the artifacts in the offer. Don't re-ask for what they already asked for; do ask before anything beyond it. Transition seamlessly into the requested capture: 1. Run `openspec new change ""` (with `--store ` when applicable) before creating any artifacts. Never create a new change directory under `openspec/changes/` by hand; the CLI scaffold creates required metadata such as `.openspec.yaml`. Keep the selected `--store ` on every applicable follow-up `status` and `instructions` command. 2. Run `openspec status --change "" --json` (append the confirmed `--store ""` only for a registered standalone store), then process the requested artifacts in dependency order. For each requested artifact that is `ready`, run `openspec instructions "" --change "" --json` (append the confirmed `--store ""` only for a registered standalone store). Before creating a requested artifact, evaluate any condition in its own `instruction` against the explored change; record a deliberate skip instead when the condition does not apply. If a requested artifact is blocked by a direct prerequisite the user did not request, run `openspec instructions "" --change "" --json` (append the confirmed `--store ""` only for a registered standalone store) for that prerequisite whether it is `ready` or `blocked`. If its own `instruction` states a condition, evaluate that condition against the explored change and record a deliberate skip only when the condition does not apply. If the condition applies, or the prerequisite is not conditional, treat it as a normal prerequisite and ask before expanding the capture. Do not create an unrequested prerequisite unless the user approves. @@ -335,7 +335,7 @@ But this summary is optional. Sometimes the thinking IS the value. - **Don't fake understanding** - If something is unclear, dig deeper - **Don't rush** - Discovery is thinking time, not task time - **Don't force structure** - Let patterns emerge naturally -- **Don't auto-capture** - Offer to save insights, don't just do it. Read-only commands and tools need no confirmation. Before the first write-capable action—including `openspec new change` or another command that writes files—name the artifacts or files and proposed changes, ask a direct yes/no question, and wait for explicit confirmation in a separate user message. That confirmation covers only the described scope; ask again before expanding it. Answers to design or clarifying questions are never consent to write. That rule governs `openspec new change` whenever you are the one proposing the capture. When the user explicitly asks you to capture the exploration as a new change, their request is the confirmation for scaffolding it and creating the artifacts the request names, so run the capture transition without asking again and ask before writing anything outside that scope. +- **Don't auto-capture** - Offer to save insights, don't just do it. Read-only commands and tools need no confirmation. Before the first write-capable action—including `openspec new change` or another command that writes files—name the artifacts or files and proposed changes, ask a direct yes/no question, and wait for explicit confirmation in a separate user message. That confirmation covers only the described scope; ask again before expanding it. Answers to design or clarifying questions are never consent to write. That rule governs `openspec new change` whenever you are the one proposing the capture; the user's own capture request is the exception, handled in the capture transition above. - **Don't manually scaffold changes** - Never create a new change directory under `openspec/changes/` by hand. Always use `openspec new change ""` (with `--store ` when applicable) so required metadata such as `.openspec.yaml` is created before writing artifacts. - **Do visualize** - A good diagram is worth many paragraphs - **Do explore the codebase** - Ground discussions in reality diff --git a/src/core/templates/workflows/explore.ts b/src/core/templates/workflows/explore.ts index 6faec2aa63..d5ab78c36b 100644 --- a/src/core/templates/workflows/explore.ts +++ b/src/core/templates/workflows/explore.ts @@ -35,7 +35,7 @@ export function getExploreSkillTemplate(): SkillTemplate { description: 'Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements. Use when the user wants to think through something before or during a change.', instructions: `Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes. -**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, investigate the codebase, and run read-only commands or tools without confirmation, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create or update OpenSpec change artifacts (proposals, designs, specs) within a confirmed scope—that's capturing thinking, not implementing. Answering design or clarifying questions is never consent to write. Before the first write-capable action, name the artifacts or files you would change and what you would do, ask a direct yes/no question, and wait for the user's confirmation in a separate message. Confirmation covers only the scope you described; ask again before expanding it. An explicit request to capture the exploration as a new change is itself that confirmation, covering the change and the artifacts the request names; scaffold it first as described below. +**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, investigate the codebase, and run read-only commands or tools without confirmation, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create or update OpenSpec change artifacts (proposals, designs, specs) within a confirmed scope—that's capturing thinking, not implementing. Answering design or clarifying questions is never consent to write. Before the first write-capable action, name the artifacts or files you would change and what you would do, ask a direct yes/no question, and wait for the user's confirmation in a separate message. Confirmation covers only the scope you described; ask again before expanding it. An explicit request from the user to capture the exploration as a new change is itself that confirmation, covering the change and the change artifacts that request names; scaffold it first as described below. **This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore. @@ -145,7 +145,7 @@ Think freely. When insights crystallize, you might offer: - "This feels solid enough to start a change. Want me to create a proposal?" - Or keep exploring - no pressure to formalize -If the user asks you to capture the exploration as a new change, that request is the confirmation required above. It covers scaffolding the change and creating the artifacts the request names, and nothing else. Do not ask for a second confirmation; transition seamlessly into the requested capture: +If the user asks you to capture the exploration as a new change, that request is the confirmation required above. It covers scaffolding that change and creating the change artifacts the request names, and nothing else. This holds only when the request is theirs: a yes to an offer you made confirms only the scope your offer itself named, so name the change and the artifacts in the offer. Don't re-ask for what they already asked for; do ask before anything beyond it. Transition seamlessly into the requested capture: 1. Run \`openspec new change ""\` (with \`--store \` when applicable) before creating any artifacts. Never create a new change directory under \`openspec/changes/\` by hand; the CLI scaffold creates required metadata such as \`.openspec.yaml\`. Keep the selected \`--store \` on every applicable follow-up \`status\` and \`instructions\` command. 2. Run \`openspec status --change "" --json\` (append the confirmed \`--store ""\` only for a registered standalone store), then process the requested artifacts in dependency order. For each requested artifact that is \`ready\`, run \`openspec instructions "" --change "" --json\` (append the confirmed \`--store ""\` only for a registered standalone store). Before creating a requested artifact, evaluate any condition in its own \`instruction\` against the explored change; record a deliberate skip instead when the condition does not apply. If a requested artifact is blocked by a direct prerequisite the user did not request, run \`openspec instructions "" --change "" --json\` (append the confirmed \`--store ""\` only for a registered standalone store) for that prerequisite whether it is \`ready\` or \`blocked\`. If its own \`instruction\` states a condition, evaluate that condition against the explored change and record a deliberate skip only when the condition does not apply. If the condition applies, or the prerequisite is not conditional, treat it as a normal prerequisite and ask before expanding the capture. Do not create an unrequested prerequisite unless the user approves. @@ -339,7 +339,7 @@ But this summary is optional. Sometimes the thinking IS the value. - **Don't fake understanding** - If something is unclear, dig deeper - **Don't rush** - Discovery is thinking time, not task time - **Don't force structure** - Let patterns emerge naturally -- **Don't auto-capture** - Offer to save insights, don't just do it. Read-only commands and tools need no confirmation. Before the first write-capable action—including \`openspec new change\` or another command that writes files—name the artifacts or files and proposed changes, ask a direct yes/no question, and wait for explicit confirmation in a separate user message. That confirmation covers only the described scope; ask again before expanding it. Answers to design or clarifying questions are never consent to write. That rule governs \`openspec new change\` whenever you are the one proposing the capture. When the user explicitly asks you to capture the exploration as a new change, their request is the confirmation for scaffolding it and creating the artifacts the request names, so run the capture transition without asking again and ask before writing anything outside that scope. +- **Don't auto-capture** - Offer to save insights, don't just do it. Read-only commands and tools need no confirmation. Before the first write-capable action—including \`openspec new change\` or another command that writes files—name the artifacts or files and proposed changes, ask a direct yes/no question, and wait for explicit confirmation in a separate user message. That confirmation covers only the described scope; ask again before expanding it. Answers to design or clarifying questions are never consent to write. That rule governs \`openspec new change\` whenever you are the one proposing the capture; the user's own capture request is the exception, handled in the capture transition above. - **Don't manually scaffold changes** - Never create a new change directory under \`openspec/changes/\` by hand. Always use \`openspec new change ""\` (with \`--store \` when applicable) so required metadata such as \`.openspec.yaml\` is created before writing artifacts. - **Do visualize** - A good diagram is worth many paragraphs - **Do explore the codebase** - Ground discussions in reality @@ -358,7 +358,7 @@ export function getOpsxExploreCommandTemplate(): CommandTemplate { tags: ['workflow', 'explore', 'experimental', 'thinking'], content: `Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes. -**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, investigate the codebase, and run read-only commands or tools without confirmation, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create or update OpenSpec change artifacts (proposals, designs, specs) within a confirmed scope—that's capturing thinking, not implementing. Answering design or clarifying questions is never consent to write. Before the first write-capable action, name the artifacts or files you would change and what you would do, ask a direct yes/no question, and wait for the user's confirmation in a separate message. Confirmation covers only the scope you described; ask again before expanding it. An explicit request to capture the exploration as a new change is itself that confirmation, covering the change and the artifacts the request names; scaffold it first as described below. +**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, investigate the codebase, and run read-only commands or tools without confirmation, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create or update OpenSpec change artifacts (proposals, designs, specs) within a confirmed scope—that's capturing thinking, not implementing. Answering design or clarifying questions is never consent to write. Before the first write-capable action, name the artifacts or files you would change and what you would do, ask a direct yes/no question, and wait for the user's confirmation in a separate message. Confirmation covers only the scope you described; ask again before expanding it. An explicit request from the user to capture the exploration as a new change is itself that confirmation, covering the change and the change artifacts that request names; scaffold it first as described below. **This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore. @@ -477,7 +477,7 @@ Think freely. When insights crystallize, you might offer: - "This feels solid enough to start a change. Want me to create a proposal?" - Or keep exploring - no pressure to formalize -If the user asks you to capture the exploration as a new change, that request is the confirmation required above. It covers scaffolding the change and creating the artifacts the request names, and nothing else. Do not ask for a second confirmation; transition seamlessly into the requested capture: +If the user asks you to capture the exploration as a new change, that request is the confirmation required above. It covers scaffolding that change and creating the change artifacts the request names, and nothing else. This holds only when the request is theirs: a yes to an offer you made confirms only the scope your offer itself named, so name the change and the artifacts in the offer. Don't re-ask for what they already asked for; do ask before anything beyond it. Transition seamlessly into the requested capture: 1. Run \`openspec new change ""\` (with \`--store \` when applicable) before creating any artifacts. Never create a new change directory under \`openspec/changes/\` by hand; the CLI scaffold creates required metadata such as \`.openspec.yaml\`. Keep the selected \`--store \` on every applicable follow-up \`status\` and \`instructions\` command. 2. Run \`openspec status --change "" --json\` (append the confirmed \`--store ""\` only for a registered standalone store), then process the requested artifacts in dependency order. For each requested artifact that is \`ready\`, run \`openspec instructions "" --change "" --json\` (append the confirmed \`--store ""\` only for a registered standalone store). Before creating a requested artifact, evaluate any condition in its own \`instruction\` against the explored change; record a deliberate skip instead when the condition does not apply. If a requested artifact is blocked by a direct prerequisite the user did not request, run \`openspec instructions "" --change "" --json\` (append the confirmed \`--store ""\` only for a registered standalone store) for that prerequisite whether it is \`ready\` or \`blocked\`. If its own \`instruction\` states a condition, evaluate that condition against the explored change and record a deliberate skip only when the condition does not apply. If the condition applies, or the prerequisite is not conditional, treat it as a normal prerequisite and ask before expanding the capture. Do not create an unrequested prerequisite unless the user approves. @@ -551,7 +551,7 @@ When things crystallize, you might offer a summary - but it's optional. Sometime - **Don't fake understanding** - If something is unclear, dig deeper - **Don't rush** - Discovery is thinking time, not task time - **Don't force structure** - Let patterns emerge naturally -- **Don't auto-capture** - Offer to save insights, don't just do it. Read-only commands and tools need no confirmation. Before the first write-capable action—including \`openspec new change\` or another command that writes files—name the artifacts or files and proposed changes, ask a direct yes/no question, and wait for explicit confirmation in a separate user message. That confirmation covers only the described scope; ask again before expanding it. Answers to design or clarifying questions are never consent to write. That rule governs \`openspec new change\` whenever you are the one proposing the capture. When the user explicitly asks you to capture the exploration as a new change, their request is the confirmation for scaffolding it and creating the artifacts the request names, so run the capture transition without asking again and ask before writing anything outside that scope. +- **Don't auto-capture** - Offer to save insights, don't just do it. Read-only commands and tools need no confirmation. Before the first write-capable action—including \`openspec new change\` or another command that writes files—name the artifacts or files and proposed changes, ask a direct yes/no question, and wait for explicit confirmation in a separate user message. That confirmation covers only the described scope; ask again before expanding it. Answers to design or clarifying questions are never consent to write. That rule governs \`openspec new change\` whenever you are the one proposing the capture; the user's own capture request is the exception, handled in the capture transition above. - **Don't manually scaffold changes** - Never create a new change directory under \`openspec/changes/\` by hand. Always use \`openspec new change ""\` (with \`--store \` when applicable) so required metadata such as \`.openspec.yaml\` is created before writing artifacts. - **Do visualize** - A good diagram is worth many paragraphs - **Do explore the codebase** - Ground discussions in reality diff --git a/test/core/templates/explore.test.ts b/test/core/templates/explore.test.ts index 95ca41d56d..df400e62e8 100644 --- a/test/core/templates/explore.test.ts +++ b/test/core/templates/explore.test.ts @@ -202,26 +202,25 @@ describe('explore templates', () => { it('treats an explicit capture request as the write confirmation (#1828)', () => { for (const [label, body] of bodies) { expect(body, label).toContain( - 'An explicit request to capture the exploration as a new change is itself that confirmation, covering the change and the artifacts the request names' + 'An explicit request from the user to capture the exploration as a new change is itself that confirmation' + ); + // Scoped to change artifacts, so the carve-out cannot reach the + // workflow configuration #1715 reported an agent editing. + expect(body, label).toContain( + 'covering the change and the change artifacts that request names' ); } }); - it('resolves the capture carve-out in the guardrail that names `openspec new change` (#1828)', () => { + it('keeps the strict rule for a capture the agent proposed itself (#1828)', () => { for (const [label, body] of bodies) { - // The general rule must survive: an agent proposing the capture on its - // own initiative still owes the user a separate yes/no. - expect(body, label).toContain( - 'including `openspec new change` or another command that writes files' - ); expect(body, label).toContain( 'That rule governs `openspec new change` whenever you are the one proposing the capture' ); + // The guardrail points at the capture transition rather than restating + // the contract a third time, so the three sites cannot drift apart. expect(body, label).toContain( - 'When the user explicitly asks you to capture the exploration as a new change, their request is the confirmation for scaffolding it and creating the artifacts the request names, so run the capture transition without asking again' - ); - expect(body, label).toContain( - 'ask before writing anything outside that scope' + "the user's own capture request is the exception, handled in the capture transition above" ); } }); @@ -237,27 +236,49 @@ describe('explore templates', () => { expect(carveOut, label).toBeGreaterThanOrEqual(0); expect(scaffold, label).toBeGreaterThan(carveOut); expect(transition, label).toContain( - 'It covers scaffolding the change and creating the artifacts the request names, and nothing else' + 'creating the change artifacts the request names, and nothing else' ); - expect(transition, label).toContain('Do not ask for a second confirmation'); } }); - // The carve-out must not become a blanket write permit: #1715's guarantee - // survives only if everything outside the requested scope still stops. - it('keeps the carve-out scoped to what the request named (#1828, #1715)', () => { + // A yes to an offer the agent made looks identical to a user-initiated + // capture request at the point the decision is made, so the discriminator + // has to live in the branch, not only in the guardrail 190 lines below it. + it('carries the agent-proposed discriminator in the branch itself (#1828)', () => { for (const [label, body] of bodies) { const transition = newChangeTransition(body, label); - // String containment alone cannot catch #1828's actual failure mode: a - // second, contradictory instruction added elsewhere. The capture branch - // is resolved only while it carries no confirmation gate of its own. - expect(transition, label).not.toContain('ask a direct yes/no question'); - expect(transition, label).not.toContain( - "wait for the user's confirmation" + expect(transition, label).toContain( + 'This holds only when the request is theirs' + ); + expect(transition, label).toContain( + 'a yes to an offer you made confirms only the scope your offer itself named' + ); + } + }); + + // "Do not ask for a second confirmation" would have contradicted step 2, + // nine lines below it, which requires asking before expanding the capture. + // Narrow the licence to re-asking for what was already asked for. + it('does not license skipping the asks the capture steps still require (#1828)', () => { + for (const [label, body] of bodies) { + const transition = newChangeTransition(body, label); + + expect(transition, label).toContain( + "Don't re-ask for what they already asked for; do ask before anything beyond it" + ); + expect(transition, label).not.toContain('Do not ask for a second confirmation'); + expect(transition, label).toContain('ask before expanding the capture'); + expect(transition, label).toContain( + 'Do not create an unrequested prerequisite unless the user approves' ); - expect(transition, label).not.toContain('wait for explicit confirmation'); + } + }); + // The carve-out must not become a blanket write permit: #1715's guarantee + // survives only if everything outside the requested scope still stops. + it('keeps the carve-out scoped to what the request named (#1828, #1715)', () => { + for (const [label, body] of bodies) { expect(body, label).toContain( 'Confirmation covers only the scope you described; ask again before expanding it' ); @@ -267,10 +288,83 @@ describe('explore templates', () => { expect(body, label).toContain( 'Accepting an answer or a batch of recommendations is not permission to write' ); - expect(transition, label).toContain( - 'Do not create an unrequested prerequisite unless the user approves' + expect(body, label).toContain( + 'creating or editing schemas, templates, or `openspec/config.yaml` is a change' ); - expect(transition, label).toContain('ask before expanding the capture'); + } + }); + + // #1828 was not a missing sentence. It was a second, contradictory sentence + // elsewhere in the same body, and no `toContain` assertion can see one of + // those: every pinned string stays present while the new sentence reverses + // it. So invert the check. Collect EVERY sentence that couples consent + // language to the capture topic and require each to be one the resolution + // sanctions, which surfaces a gate added anywhere in the body - Guardrails, + // "Planning a Change", either capture branch. + // + // Limits worth knowing: this is lexical. A sentence that reverses the + // resolution without using any consent word - redefining what counts as + // "requested", or suspending the carve-out on a condition - is invisible + // here and stays a review responsibility. + const CONSENT_WORDS = + /\b(confirm(?:ation|s|ed)?|yes\/no|approv(?:al|es|ed)|permission|consent)\b/i; + const CAPTURE_WORDS = /(openspec new change|scaffold|captur|write-capable|first write)/i; + + const SANCTIONED_CONSENT = [ + // The stance paragraph: the rule, then the carve-out. + /You MAY create or update OpenSpec change artifacts .* within a confirmed scope/, + /Before the first write-capable action, name the artifacts or files you would change/, + /An explicit request from the user to capture the exploration as a new change is itself that confirmation/, + // The capture branch: the carve-out and both of its fences. + /that request is the confirmation required above/, + /This holds only when the request is theirs/, + /a yes to an offer you made confirms only the scope your offer itself named/, + /Don't re-ask for what they already asked for/, + /Do not create an unrequested prerequisite unless the user approves/, + // The guardrail: the rule, and a pointer back to the branch. + /Before the first write-capable action—including `openspec new change`/, + /That rule governs `openspec new change` whenever you are the one proposing the capture/, + ]; + + // The `--store` reminder repeats on five steps and says "confirmed" only to + // mean "the store id you already resolved", which is not a consent rule. + const STORE_REMINDER = + /\(append the confirmed `--store ""` only for a registered standalone store\)/g; + + function consentSentences(body: string, requireCaptureTopic: boolean): string[] { + return body + .replace(STORE_REMINDER, '') + .split(/(?<=[.:;])\s+/) + .map((sentence) => sentence.replace(/\s+/g, ' ').trim()) + .filter( + (sentence) => + CONSENT_WORDS.test(sentence) && + (!requireCaptureTopic || CAPTURE_WORDS.test(sentence)) + ); + } + + it('couples consent to capture only where the resolution sanctions it (#1828)', () => { + for (const [label, body] of bodies) { + const unsanctioned = consentSentences(body, true).filter( + (sentence) => !SANCTIONED_CONSENT.some((allowed) => allowed.test(sentence)) + ); + + expect(unsanctioned, `${label} must add no unsanctioned consent rule`).toEqual([]); + } + }); + + // Inside the capture branch, drop the topic filter entirely: a gate written + // there is about the capture whether or not it says so. Without this, a bare + // "get a fresh yes/no before running anything" inserted above step 1 reads as + // off-topic and reinstates #1828 with the suite green. + it('adds no confirmation gate of its own inside the capture branch (#1828)', () => { + for (const [label, body] of bodies) { + const unsanctioned = consentSentences( + newChangeTransition(body, label), + false + ).filter((sentence) => !SANCTIONED_CONSENT.some((allowed) => allowed.test(sentence))); + + expect(unsanctioned, `${label} capture branch must carry no gate`).toEqual([]); } }); diff --git a/test/core/templates/skill-templates-parity.test.ts b/test/core/templates/skill-templates-parity.test.ts index 46156e5703..55be69a190 100644 --- a/test/core/templates/skill-templates-parity.test.ts +++ b/test/core/templates/skill-templates-parity.test.ts @@ -38,14 +38,14 @@ import { import { STORE_SELECTION_GUIDANCE } from '../../../src/core/templates/workflows/store-selection.js'; const EXPECTED_FUNCTION_HASHES: Record = { - getExploreSkillTemplate: 'ecc0a0dbc55e8e5058529622302bda0f26058377e149a8f370b854d79f586a61', + getExploreSkillTemplate: 'b2fee699aad9cbdea3f0ea1b8b3c5573c64bdb9261bab0ec42a35f75ddeeb452', getNewChangeSkillTemplate: 'eabd1e895c5881dcb17dcbaa3fb26098dd59e8eacb318e400820b4dc811ef781', getContinueChangeSkillTemplate: '012136f6411a99c8fa228e2f9444cb64b0a89e0f56fdeac2fe03b2f5bee0c5d7', getApplyChangeSkillTemplate: 'd1e7d5ceb85193c0964057dbb88e9651526754bd33f84020e2440ff0621d5dbb', getFfChangeSkillTemplate: 'efa6a70c111b18b61a7720250b9622afa9a212fb64edf609cf80e2182a9bdf8c', getSyncSpecsSkillTemplate: 'b099e2ff31859c9b10d928066e662524f9aad9ecf2be12fceacb732d718c4146', getOnboardSkillTemplate: '3a836faae463d88c289a1c129cb7ee556a563b7e53e1a52a4711ff152a3b51f7', - getOpsxExploreCommandTemplate: '519ea6a8d6801d0f7d54595ce548a9bb3e776066bbf718895b4225658dee78f0', + getOpsxExploreCommandTemplate: '930f56f440e0a56d801995bfe5d6b8b7ae3eedcef95c01d62922d05999541e83', getOpsxNewCommandTemplate: 'f2d30e569798a4c92ba932859d6ba4e0ad10e18feccbade1cfee0957597b3463', getOpsxContinueCommandTemplate: 'e50e50266efa1b8e64ff9b6274ee8254f0a240d6adc1b862d126e2f1c9d3a559', getOpsxApplyCommandTemplate: 'e3579ac78f2e2c75fa3d3a7ac7dc3e49c395e96f7323398f0f041d94f8de9bb0', @@ -66,7 +66,7 @@ const EXPECTED_FUNCTION_HASHES: Record = { }; const EXPECTED_GENERATED_SKILL_CONTENT_HASHES: Record = { - 'openspec-explore': '974cd6c923d1ceaa50fb7d335a0373979d8c5c06d58a29dede6c48bda59327fc', + 'openspec-explore': '411409fcf1f9a42ad732e2cbc932979fcdf54f92ea7796c4c752b4192026dd15', 'openspec-new-change': 'ec4529beef978e34634a6f7286fab55d68fad8fb374dceb45691d52caab33fbb', 'openspec-continue-change': 'bb6194a16c54891cdb253678e8f70ce53b2af86735243980f366ce551d37e42e', 'openspec-apply-change': '81ea96d9fa6ec8536cd23c1fe561ed28e1cc1cad0a8ceb700588e08974cc0e49', From 09078001466bd6a186fa710926ec5ea2a6781c8f Mon Sep 17 00:00:00 2001 From: Clay Good Date: Thu, 10 Sep 2026 12:34:59 -0500 Subject: [PATCH 4/5] fix(explore): keep the contact clause in the scope fence The previous commit reintroduced "the change artifacts that request names", the garden-path parse that 55eeac6 removed: read as a relative clause it says the artifacts name a request. Restore "the request names", matching both restatements. Caught by CodeRabbit. Co-Authored-By: Claude Opus 5 --- skills/openspec-explore/SKILL.md | 2 +- src/core/templates/workflows/explore.ts | 4 ++-- test/core/templates/explore.test.ts | 2 +- test/core/templates/skill-templates-parity.test.ts | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/skills/openspec-explore/SKILL.md b/skills/openspec-explore/SKILL.md index 4384a9a739..d4504311d4 100644 --- a/skills/openspec-explore/SKILL.md +++ b/skills/openspec-explore/SKILL.md @@ -11,7 +11,7 @@ metadata: Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes. -**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, investigate the codebase, and run read-only commands or tools without confirmation, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create or update OpenSpec change artifacts (proposals, designs, specs) within a confirmed scope—that's capturing thinking, not implementing. Answering design or clarifying questions is never consent to write. Before the first write-capable action, name the artifacts or files you would change and what you would do, ask a direct yes/no question, and wait for the user's confirmation in a separate message. Confirmation covers only the scope you described; ask again before expanding it. An explicit request from the user to capture the exploration as a new change is itself that confirmation, covering the change and the change artifacts that request names; scaffold it first as described below. +**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, investigate the codebase, and run read-only commands or tools without confirmation, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create or update OpenSpec change artifacts (proposals, designs, specs) within a confirmed scope—that's capturing thinking, not implementing. Answering design or clarifying questions is never consent to write. Before the first write-capable action, name the artifacts or files you would change and what you would do, ask a direct yes/no question, and wait for the user's confirmation in a separate message. Confirmation covers only the scope you described; ask again before expanding it. An explicit request from the user to capture the exploration as a new change is itself that confirmation, covering the change and the change artifacts the request names; scaffold it first as described below. **This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore. diff --git a/src/core/templates/workflows/explore.ts b/src/core/templates/workflows/explore.ts index d5ab78c36b..f9f9516636 100644 --- a/src/core/templates/workflows/explore.ts +++ b/src/core/templates/workflows/explore.ts @@ -35,7 +35,7 @@ export function getExploreSkillTemplate(): SkillTemplate { description: 'Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements. Use when the user wants to think through something before or during a change.', instructions: `Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes. -**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, investigate the codebase, and run read-only commands or tools without confirmation, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create or update OpenSpec change artifacts (proposals, designs, specs) within a confirmed scope—that's capturing thinking, not implementing. Answering design or clarifying questions is never consent to write. Before the first write-capable action, name the artifacts or files you would change and what you would do, ask a direct yes/no question, and wait for the user's confirmation in a separate message. Confirmation covers only the scope you described; ask again before expanding it. An explicit request from the user to capture the exploration as a new change is itself that confirmation, covering the change and the change artifacts that request names; scaffold it first as described below. +**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, investigate the codebase, and run read-only commands or tools without confirmation, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create or update OpenSpec change artifacts (proposals, designs, specs) within a confirmed scope—that's capturing thinking, not implementing. Answering design or clarifying questions is never consent to write. Before the first write-capable action, name the artifacts or files you would change and what you would do, ask a direct yes/no question, and wait for the user's confirmation in a separate message. Confirmation covers only the scope you described; ask again before expanding it. An explicit request from the user to capture the exploration as a new change is itself that confirmation, covering the change and the change artifacts the request names; scaffold it first as described below. **This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore. @@ -358,7 +358,7 @@ export function getOpsxExploreCommandTemplate(): CommandTemplate { tags: ['workflow', 'explore', 'experimental', 'thinking'], content: `Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes. -**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, investigate the codebase, and run read-only commands or tools without confirmation, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create or update OpenSpec change artifacts (proposals, designs, specs) within a confirmed scope—that's capturing thinking, not implementing. Answering design or clarifying questions is never consent to write. Before the first write-capable action, name the artifacts or files you would change and what you would do, ask a direct yes/no question, and wait for the user's confirmation in a separate message. Confirmation covers only the scope you described; ask again before expanding it. An explicit request from the user to capture the exploration as a new change is itself that confirmation, covering the change and the change artifacts that request names; scaffold it first as described below. +**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, investigate the codebase, and run read-only commands or tools without confirmation, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create or update OpenSpec change artifacts (proposals, designs, specs) within a confirmed scope—that's capturing thinking, not implementing. Answering design or clarifying questions is never consent to write. Before the first write-capable action, name the artifacts or files you would change and what you would do, ask a direct yes/no question, and wait for the user's confirmation in a separate message. Confirmation covers only the scope you described; ask again before expanding it. An explicit request from the user to capture the exploration as a new change is itself that confirmation, covering the change and the change artifacts the request names; scaffold it first as described below. **This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore. diff --git a/test/core/templates/explore.test.ts b/test/core/templates/explore.test.ts index df400e62e8..20bd429530 100644 --- a/test/core/templates/explore.test.ts +++ b/test/core/templates/explore.test.ts @@ -207,7 +207,7 @@ describe('explore templates', () => { // Scoped to change artifacts, so the carve-out cannot reach the // workflow configuration #1715 reported an agent editing. expect(body, label).toContain( - 'covering the change and the change artifacts that request names' + 'covering the change and the change artifacts the request names' ); } }); diff --git a/test/core/templates/skill-templates-parity.test.ts b/test/core/templates/skill-templates-parity.test.ts index 55be69a190..3a14024c8d 100644 --- a/test/core/templates/skill-templates-parity.test.ts +++ b/test/core/templates/skill-templates-parity.test.ts @@ -38,14 +38,14 @@ import { import { STORE_SELECTION_GUIDANCE } from '../../../src/core/templates/workflows/store-selection.js'; const EXPECTED_FUNCTION_HASHES: Record = { - getExploreSkillTemplate: 'b2fee699aad9cbdea3f0ea1b8b3c5573c64bdb9261bab0ec42a35f75ddeeb452', + getExploreSkillTemplate: '3ffe63a08c09164e9fab4747bcb5b193f52ded40d250403c727d186152aeb7e5', getNewChangeSkillTemplate: 'eabd1e895c5881dcb17dcbaa3fb26098dd59e8eacb318e400820b4dc811ef781', getContinueChangeSkillTemplate: '012136f6411a99c8fa228e2f9444cb64b0a89e0f56fdeac2fe03b2f5bee0c5d7', getApplyChangeSkillTemplate: 'd1e7d5ceb85193c0964057dbb88e9651526754bd33f84020e2440ff0621d5dbb', getFfChangeSkillTemplate: 'efa6a70c111b18b61a7720250b9622afa9a212fb64edf609cf80e2182a9bdf8c', getSyncSpecsSkillTemplate: 'b099e2ff31859c9b10d928066e662524f9aad9ecf2be12fceacb732d718c4146', getOnboardSkillTemplate: '3a836faae463d88c289a1c129cb7ee556a563b7e53e1a52a4711ff152a3b51f7', - getOpsxExploreCommandTemplate: '930f56f440e0a56d801995bfe5d6b8b7ae3eedcef95c01d62922d05999541e83', + getOpsxExploreCommandTemplate: '206a7d54b07122137e975cedceedda7e8397a9d7949eab8bb8f664bffc47b11b', getOpsxNewCommandTemplate: 'f2d30e569798a4c92ba932859d6ba4e0ad10e18feccbade1cfee0957597b3463', getOpsxContinueCommandTemplate: 'e50e50266efa1b8e64ff9b6274ee8254f0a240d6adc1b862d126e2f1c9d3a559', getOpsxApplyCommandTemplate: 'e3579ac78f2e2c75fa3d3a7ac7dc3e49c395e96f7323398f0f041d94f8de9bb0', @@ -66,7 +66,7 @@ const EXPECTED_FUNCTION_HASHES: Record = { }; const EXPECTED_GENERATED_SKILL_CONTENT_HASHES: Record = { - 'openspec-explore': '411409fcf1f9a42ad732e2cbc932979fcdf54f92ea7796c4c752b4192026dd15', + 'openspec-explore': '561602f8578779f25f5c43e7beb47f414e7fe961bcca427a16c2d09923f0e587', 'openspec-new-change': 'ec4529beef978e34634a6f7286fab55d68fad8fb374dceb45691d52caab33fbb', 'openspec-continue-change': 'bb6194a16c54891cdb253678e8f70ce53b2af86735243980f366ce551d37e42e', 'openspec-apply-change': '81ea96d9fa6ec8536cd23c1fe561ed28e1cc1cad0a8ceb700588e08974cc0e49', From 989670555fdb902d2eb6d5893cd93f44c24ee85b Mon Sep 17 00:00:00 2001 From: Clay Good Date: Tue, 15 Sep 2026 08:22:15 -0500 Subject: [PATCH 5/5] docs(changeset): drop em dashes from the release note Co-Authored-By: Claude Opus 5 --- .changeset/explore-capture-request-is-confirmation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/explore-capture-request-is-confirmation.md b/.changeset/explore-capture-request-is-confirmation.md index 649bdc0181..da5cfb2674 100644 --- a/.changeset/explore-capture-request-is-confirmation.md +++ b/.changeset/explore-capture-request-is-confirmation.md @@ -2,4 +2,4 @@ '@fission-ai/openspec': patch --- -Resolve the contradiction that left explore mode's capture branch without a governing rule. Explore states twice that the agent must ask a direct yes/no question and wait for confirmation in a separate user message before its first write-capable action, naming `openspec new change` as an example, while the capture branch tells the agent to transition "seamlessly" into running `openspec new change` and creating artifacts with no confirmation step. Both readings were defensible from the text, so the same "capture this as a change" request either wrote `.openspec.yaml` plus several artifacts immediately or stopped and asked, depending on which passage the agent weighed — which made the #1715 guarantee unenforceable in the one explore path that writes files. An explicit capture request is now stated to be that confirmation, covering the change and the artifacts the request names and nothing else. The guardrail keeps its teeth for the case #1715 actually reported: when the agent is the one proposing the capture, or when the work would go beyond the requested scope, it still asks first, and answers to design or clarifying questions are still never consent to write. Both explore delivery surfaces and the committed skill carry the same wording. Fixes #1828. +Resolve the contradiction that left explore mode's capture branch without a governing rule. Explore states twice that the agent must ask a direct yes/no question and wait for confirmation in a separate user message before its first write-capable action, naming `openspec new change` as an example, while the capture branch tells the agent to transition "seamlessly" into running `openspec new change` and creating artifacts with no confirmation step. Both readings were defensible from the text, so the same "capture this as a change" request either wrote `.openspec.yaml` plus several artifacts immediately or stopped and asked, depending on which passage the agent weighed, which made the #1715 guarantee unenforceable in the one explore path that writes files. An explicit capture request is now stated to be that confirmation, covering the change and the artifacts the request names and nothing else. The guardrail keeps its teeth for the case #1715 actually reported: when the agent is the one proposing the capture, or when the work would go beyond the requested scope, it still asks first, and answers to design or clarifying questions are still never consent to write. Both explore delivery surfaces and the committed skill carry the same wording. Fixes #1828.