diff --git a/.changeset/explore-capture-request-is-confirmation.md b/.changeset/explore-capture-request-is-confirmation.md new file mode 100644 index 0000000000..da5cfb2674 --- /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..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. 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 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. @@ -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 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. +- **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 20db318293..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. 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 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. @@ -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 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. +- **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. 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 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. @@ -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 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. +- **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 b6ec059403..20bd429530 100644 --- a/test/core/templates/explore.test.ts +++ b/test/core/templates/explore.test.ts @@ -192,6 +192,182 @@ 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 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 the request names' + ); + } + }); + + it('keeps the strict rule for a capture the agent proposed itself (#1828)', () => { + for (const [label, body] of bodies) { + 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( + "the user's own capture request is the exception, handled in the capture transition above" + ); + } + }); + + 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( + 'creating the change artifacts the request names, and nothing else' + ); + } + }); + + // 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); + + 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' + ); + } + }); + + // 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' + ); + 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(body, label).toContain( + 'creating or editing schemas, templates, or `openspec/config.yaml` is a change' + ); + } + }); + + // #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([]); + } + }); + 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..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: '06aba775c621e61f00995a9ebc3a02fe873ddcc9bf024e416c4adaf91ccce115', + getExploreSkillTemplate: '3ffe63a08c09164e9fab4747bcb5b193f52ded40d250403c727d186152aeb7e5', getNewChangeSkillTemplate: 'eabd1e895c5881dcb17dcbaa3fb26098dd59e8eacb318e400820b4dc811ef781', getContinueChangeSkillTemplate: '012136f6411a99c8fa228e2f9444cb64b0a89e0f56fdeac2fe03b2f5bee0c5d7', getApplyChangeSkillTemplate: 'd1e7d5ceb85193c0964057dbb88e9651526754bd33f84020e2440ff0621d5dbb', getFfChangeSkillTemplate: 'efa6a70c111b18b61a7720250b9622afa9a212fb64edf609cf80e2182a9bdf8c', getSyncSpecsSkillTemplate: 'b099e2ff31859c9b10d928066e662524f9aad9ecf2be12fceacb732d718c4146', getOnboardSkillTemplate: '3a836faae463d88c289a1c129cb7ee556a563b7e53e1a52a4711ff152a3b51f7', - getOpsxExploreCommandTemplate: '8046003e97d885a86ed392d4fb522bb78544a02872b042e51347a5021cc10523', + 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': '32b20cfbcc7d51ff526bb19571ff3dc3d0c616a5911b8de74cf6d9b15650cf3e', + 'openspec-explore': '561602f8578779f25f5c43e7beb47f414e7fe961bcca427a16c2d09923f0e587', 'openspec-new-change': 'ec4529beef978e34634a6f7286fab55d68fad8fb374dceb45691d52caab33fbb', 'openspec-continue-change': 'bb6194a16c54891cdb253678e8f70ce53b2af86735243980f366ce551d37e42e', 'openspec-apply-change': '81ea96d9fa6ec8536cd23c1fe561ed28e1cc1cad0a8ceb700588e08974cc0e49',