diff --git a/.changeset/plain-ascii-explore-diagrams.md b/.changeset/plain-ascii-explore-diagrams.md new file mode 100644 index 0000000000..4b6638f960 --- /dev/null +++ b/.changeset/plain-ascii-explore-diagrams.md @@ -0,0 +1,5 @@ +--- +'@fission-ai/openspec': patch +--- + +Draw explore-mode diagrams with plain ASCII. The worked examples in the explore skill and `/opsx:explore` command used Unicode box-drawing, arrow, and marker glyphs, whose display width varies across terminals, fonts, and locales. Agents copied the style, causing padded boxes and aligned tables to drift. diff --git a/skills/openspec-explore/SKILL.md b/skills/openspec-explore/SKILL.md index 706552004e..1e651af4a3 100644 --- a/skills/openspec-explore/SKILL.md +++ b/skills/openspec-explore/SKILL.md @@ -54,22 +54,25 @@ Depending on what the user brings, you might: **Visualize** ``` -┌─────────────────────────────────────────┐ -│ Use ASCII diagrams liberally │ -├─────────────────────────────────────────┤ -│ │ -│ ┌────────┐ ┌────────┐ │ -│ │ State │────────▶│ State │ │ -│ │ A │ │ B │ │ -│ └────────┘ └────────┘ │ -│ │ -│ System diagrams, state machines, │ -│ data flows, architecture sketches, │ -│ dependency graphs, comparison tables │ -│ │ -└─────────────────────────────────────────┘ ++------------------------------------------+ +| Use ASCII diagrams liberally | ++------------------------------------------+ +| | +| [State A] -------> [State B] | +| | | +| v | +| [State C] | +| | +| System diagrams, state machines, | +| data flows, architecture sketches, | +| dependency graphs, comparison tables | +| | ++------------------------------------------+ ``` +**Draw with plain ASCII only** — borders `+` `-` `|`, arrows `-->` `<--` `^` `v`, markers `*` `x`. +Unicode diagram glyphs can render at different widths across terminals, fonts, and locales, so padded boxes and aligned tables can drift. Keep every diagram character ASCII. + **Surface risks and unknowns** - Identify what could go wrong - Find gaps in understanding @@ -170,17 +173,17 @@ User: I'm thinking about adding real-time collaboration You: Real-time collab is a big space. Let me think about this... COLLABORATION SPECTRUM - ════════════════════════════════════════════ + ============================================ Awareness Coordination Sync - │ │ │ - ▼ ▼ ▼ - ┌────────┐ ┌────────┐ ┌────────┐ - │Presence│ │Cursors │ │ CRDT │ - │ "3 │ │ Multi │ │Conflict│ - │online" │ │ select │ │ free │ - └────────┘ └────────┘ └────────┘ - │ │ │ + | | | + v v v + +--------+ +--------+ +--------+ + |Presence| |Cursors | | CRDT | + | "3 | | Multi | |Conflict| + |online" | | select | | free | + +--------+ +--------+ +--------+ + | | | trivial moderate complex Where's your head at? @@ -192,27 +195,27 @@ User: The auth system is a mess You: [reads codebase] - ┌─────────────────────────────────────────────┐ - │ CURRENT AUTH FLOW │ - └─────────────────────────────────────────────┘ - │ - ┌─────────────┼─────────────┐ - ▼ ▼ ▼ - ┌─────────┐ ┌─────────┐ ┌─────────┐ - │ Google │ │ GitHub │ │ Email │ - │ OAuth │ │ OAuth │ │ Magic │ - └────┬────┘ └────┬────┘ └────┬────┘ - │ │ │ - └─────────────┼─────────────┘ - ▼ - ┌───────────┐ - │ Session │ - └─────┬─────┘ - │ - ▼ - ┌───────────┐ - │ Perms │ - └───────────┘ + +---------------------------------------------+ + | CURRENT AUTH FLOW | + +---------------------------------------------+ + | + +-------------+-------------+ + v v v + +---------+ +---------+ +---------+ + | Google | | GitHub | | Email | + | OAuth | | OAuth | | Magic | + +----+----+ +----+----+ +----+----+ + | | | + +-------------+-------------+ + v + +-----------+ + | Session | + +-----+-----+ + | + v + +-----------+ + | Perms | + +-----------+ I see three tangles. Which one's burning? ``` @@ -244,19 +247,19 @@ User: A CLI tool that tracks local dev environments You: That changes everything. - ┌─────────────────────────────────────────────────┐ - │ CLI TOOL DATA STORAGE │ - └─────────────────────────────────────────────────┘ + +-------------------------------------------------+ + | CLI TOOL DATA STORAGE | + +-------------------------------------------------+ Key constraints: - • No daemon running - • Must work offline - • Single user + - No daemon running + - Must work offline + - Single user SQLite Postgres - Deployment embedded ✓ needs server ✗ - Offline yes ✓ no ✗ - Single file yes ✓ no ✗ + Deployment embedded needs server + Offline yes no + Single file yes no SQLite. Not even close. diff --git a/src/core/templates/workflows/explore.ts b/src/core/templates/workflows/explore.ts index e9c72e5744..0810ce38fb 100644 --- a/src/core/templates/workflows/explore.ts +++ b/src/core/templates/workflows/explore.ts @@ -56,22 +56,25 @@ Depending on what the user brings, you might: **Visualize** \`\`\` -┌─────────────────────────────────────────┐ -│ Use ASCII diagrams liberally │ -├─────────────────────────────────────────┤ -│ │ -│ ┌────────┐ ┌────────┐ │ -│ │ State │────────▶│ State │ │ -│ │ A │ │ B │ │ -│ └────────┘ └────────┘ │ -│ │ -│ System diagrams, state machines, │ -│ data flows, architecture sketches, │ -│ dependency graphs, comparison tables │ -│ │ -└─────────────────────────────────────────┘ ++------------------------------------------+ +| Use ASCII diagrams liberally | ++------------------------------------------+ +| | +| [State A] -------> [State B] | +| | | +| v | +| [State C] | +| | +| System diagrams, state machines, | +| data flows, architecture sketches, | +| dependency graphs, comparison tables | +| | ++------------------------------------------+ \`\`\` +**Draw with plain ASCII only** — borders \`+\` \`-\` \`|\`, arrows \`-->\` \`<--\` \`^\` \`v\`, markers \`*\` \`x\`. +Unicode diagram glyphs can render at different widths across terminals, fonts, and locales, so padded boxes and aligned tables can drift. Keep every diagram character ASCII. + **Surface risks and unknowns** - Identify what could go wrong - Find gaps in understanding @@ -172,17 +175,17 @@ User: I'm thinking about adding real-time collaboration You: Real-time collab is a big space. Let me think about this... COLLABORATION SPECTRUM - ════════════════════════════════════════════ + ============================================ Awareness Coordination Sync - │ │ │ - ▼ ▼ ▼ - ┌────────┐ ┌────────┐ ┌────────┐ - │Presence│ │Cursors │ │ CRDT │ - │ "3 │ │ Multi │ │Conflict│ - │online" │ │ select │ │ free │ - └────────┘ └────────┘ └────────┘ - │ │ │ + | | | + v v v + +--------+ +--------+ +--------+ + |Presence| |Cursors | | CRDT | + | "3 | | Multi | |Conflict| + |online" | | select | | free | + +--------+ +--------+ +--------+ + | | | trivial moderate complex Where's your head at? @@ -194,27 +197,27 @@ User: The auth system is a mess You: [reads codebase] - ┌─────────────────────────────────────────────┐ - │ CURRENT AUTH FLOW │ - └─────────────────────────────────────────────┘ - │ - ┌─────────────┼─────────────┐ - ▼ ▼ ▼ - ┌─────────┐ ┌─────────┐ ┌─────────┐ - │ Google │ │ GitHub │ │ Email │ - │ OAuth │ │ OAuth │ │ Magic │ - └────┬────┘ └────┬────┘ └────┬────┘ - │ │ │ - └─────────────┼─────────────┘ - ▼ - ┌───────────┐ - │ Session │ - └─────┬─────┘ - │ - ▼ - ┌───────────┐ - │ Perms │ - └───────────┘ + +---------------------------------------------+ + | CURRENT AUTH FLOW | + +---------------------------------------------+ + | + +-------------+-------------+ + v v v + +---------+ +---------+ +---------+ + | Google | | GitHub | | Email | + | OAuth | | OAuth | | Magic | + +----+----+ +----+----+ +----+----+ + | | | + +-------------+-------------+ + v + +-----------+ + | Session | + +-----+-----+ + | + v + +-----------+ + | Perms | + +-----------+ I see three tangles. Which one's burning? \`\`\` @@ -246,19 +249,19 @@ User: A CLI tool that tracks local dev environments You: That changes everything. - ┌─────────────────────────────────────────────────┐ - │ CLI TOOL DATA STORAGE │ - └─────────────────────────────────────────────────┘ + +-------------------------------------------------+ + | CLI TOOL DATA STORAGE | + +-------------------------------------------------+ Key constraints: - • No daemon running - • Must work offline - • Single user + - No daemon running + - Must work offline + - Single user SQLite Postgres - Deployment embedded ✓ needs server ✗ - Offline yes ✓ no ✗ - Single file yes ✓ no ✗ + Deployment embedded needs server + Offline yes no + Single file yes no SQLite. Not even close. @@ -371,22 +374,25 @@ Depending on what the user brings, you might: **Visualize** \`\`\` -┌─────────────────────────────────────────┐ -│ Use ASCII diagrams liberally │ -├─────────────────────────────────────────┤ -│ │ -│ ┌────────┐ ┌────────┐ │ -│ │ State │────────▶│ State │ │ -│ │ A │ │ B │ │ -│ └────────┘ └────────┘ │ -│ │ -│ System diagrams, state machines, │ -│ data flows, architecture sketches, │ -│ dependency graphs, comparison tables │ -│ │ -└─────────────────────────────────────────┘ ++------------------------------------------+ +| Use ASCII diagrams liberally | ++------------------------------------------+ +| | +| [State A] -------> [State B] | +| | | +| v | +| [State C] | +| | +| System diagrams, state machines, | +| data flows, architecture sketches, | +| dependency graphs, comparison tables | +| | ++------------------------------------------+ \`\`\` +**Draw with plain ASCII only** — borders \`+\` \`-\` \`|\`, arrows \`-->\` \`<--\` \`^\` \`v\`, markers \`*\` \`x\`. +Unicode diagram glyphs can render at different widths across terminals, fonts, and locales, so padded boxes and aligned tables can drift. Keep every diagram character ASCII. + **Surface risks and unknowns** - Identify what could go wrong - Find gaps in understanding diff --git a/test/core/templates/explore.test.ts b/test/core/templates/explore.test.ts index 71f6a1b9dc..feacf81d7e 100644 --- a/test/core/templates/explore.test.ts +++ b/test/core/templates/explore.test.ts @@ -29,6 +29,25 @@ function occurrenceCount(body: string, value: string): number { return body.split(value).length - 1; } +const NON_ASCII = /[^\x00-\x7F]/; + +function fencedBlockLines(body: string): Array<[number, string]> { + const lines: Array<[number, string]> = []; + let inFence = false; + + body.split('\n').forEach((line, index) => { + if (line.trimStart().startsWith('```')) { + inFence = !inFence; + return; + } + if (inFence) { + lines.push([index + 1, line]); + } + }); + + return lines; +} + describe('explore templates', () => { // Regression for #696: explore never loaded the project's declared // context, so it reasoned without the tech stack, conventions, and @@ -223,6 +242,28 @@ describe('explore templates', () => { } }); + // Regression for #983: the worked examples drew boxes and tables with + // Unicode box-drawing, arrow, and marker glyphs. Agents copy those + // examples verbatim, and on terminals that render the glyphs + // double-width the right border of every padded box drifted loose. + it('draws every fenced example with plain ASCII only (#983)', () => { + for (const [label, body] of bodies) { + const offenders = fencedBlockLines(body) + .filter(([, line]) => NON_ASCII.test(line)) + .map(([lineNumber, line]) => `${lineNumber}: ${line}`); + + expect(offenders, `${label} fenced examples must be pure ASCII`).toEqual([]); + } + }); + + it('tells the agent to draw with ASCII and says why (#983)', () => { + for (const [label, body] of bodies) { + expect(body, label).toContain('**Draw with plain ASCII only**'); + expect(body, label).toContain('render at different widths'); + expect(body, label).toContain('Keep every diagram character ASCII'); + } + }); + it('stops after scaffolding when the user requests only a new change (#668)', () => { 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 e7bee01f34..c7a8b44b5c 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: 'cf59415faf8fe340b02a83e6d1267b8fabe78a9ee6d3856353daa5cb09c54279', + getExploreSkillTemplate: 'ecaa0bea4c1cd14eee9dbfcfe4b5808fff4ff808cba0a46789b37c1df3048d9a', getNewChangeSkillTemplate: 'eabd1e895c5881dcb17dcbaa3fb26098dd59e8eacb318e400820b4dc811ef781', getContinueChangeSkillTemplate: '012136f6411a99c8fa228e2f9444cb64b0a89e0f56fdeac2fe03b2f5bee0c5d7', getApplyChangeSkillTemplate: 'd1e7d5ceb85193c0964057dbb88e9651526754bd33f84020e2440ff0621d5dbb', getFfChangeSkillTemplate: '5501740e7ec36ab23ab8c3a0d6dd0655a5e2f35433c7b90e82904fef5e7a326a', getSyncSpecsSkillTemplate: 'b099e2ff31859c9b10d928066e662524f9aad9ecf2be12fceacb732d718c4146', getOnboardSkillTemplate: '3a836faae463d88c289a1c129cb7ee556a563b7e53e1a52a4711ff152a3b51f7', - getOpsxExploreCommandTemplate: '6d7031b7801b25eb4698831b9f07d1d0ace394f56e9197a8deea3f4cb7194b22', + getOpsxExploreCommandTemplate: '1460fcb4fbdf22244e9e76608102e611db598cd4cca8c5dbd001292854bcba6e', getOpsxNewCommandTemplate: 'f2d30e569798a4c92ba932859d6ba4e0ad10e18feccbade1cfee0957597b3463', getOpsxContinueCommandTemplate: 'e50e50266efa1b8e64ff9b6274ee8254f0a240d6adc1b862d126e2f1c9d3a559', getOpsxApplyCommandTemplate: 'e3579ac78f2e2c75fa3d3a7ac7dc3e49c395e96f7323398f0f041d94f8de9bb0', @@ -66,7 +66,7 @@ const EXPECTED_FUNCTION_HASHES: Record = { }; const EXPECTED_GENERATED_SKILL_CONTENT_HASHES: Record = { - 'openspec-explore': '39d8f9052d752f2618d5c7c11010435309775e310d2f245b4eb0f3f4830e594f', + 'openspec-explore': '886680e71f2900378bd12bb9ff25c888a41a8f851e0bb3ec056affcc18d07ca8', 'openspec-new-change': 'ec4529beef978e34634a6f7286fab55d68fad8fb374dceb45691d52caab33fbb', 'openspec-continue-change': 'bb6194a16c54891cdb253678e8f70ce53b2af86735243980f366ce551d37e42e', 'openspec-apply-change': '81ea96d9fa6ec8536cd23c1fe561ed28e1cc1cad0a8ceb700588e08974cc0e49',