Multi-topic batch. The big-ticket item is the skills audit; the rest are smaller patches that compounded during the audit work. ## Skills audit (rules→recipes split) Vendored all 26 skills from /home/samkintop/opt/skills/ into data/skills/ (the boocode-repo-local skill library — see docker-compose change below). Audited via 5 parallel Claude Code agent-teams running the mgechev/skills-best-practices 4-step protocol (Discovery → Logic → Edge Case → self-Architecture-Refinement) per skill, ~2 min wall-clock vs the ~3.7-hour serial estimate. Result: 14 skills surviving (renamed to gerund form, frontmatter matched), 11 deleted (duplicates, BooCode-irrelevant patterns, Claude-already-does- natively), 1 migrated to BOOCHAT.md/BOOCODER.md as an always-true rule (verification-before-completion). Each surviving skill had its description refined to fix specific trigger gaps surfaced by the protocol — 4 real-bug findings landed (dead refs, stale tags, broken sub-file references in the original vendored content). Audit decisions documented in openspec/changes/v1.13.12-skills-audit/ audit-notes.md. Convention codified in BOOCHAT.md/BOOCODER.md "rules vs recipes" sections — future workflow rules go to those files (100% present), recipes stay in data/skills/ (~6% invoke rate in multi-turn per the Codeminer42 measurement). ## Token tracking + stale-stream banner fix (same root cause) ws-frames.ts IsoTimestamp was z.string().min(1) but postgres returns timestamp columns as JS Date objects. Every message_complete / session_updated / chat_updated frame was failing the v1.13.11 Zod gate and being silently dropped. Symptoms: token tracking blank in the UI (no usage frames landed); the 60s no-token-activity timer tripped the stale-stream banner because the frontend's local message state never saw status='streaming' flip to 'complete'. Fix: z.preprocess(v => v instanceof Date ? v.toISOString() : v, z.string().min(1)) applied to the IsoTimestamp primitive. Centralized, no publisher changes, works identically server + web (the parity test still passes). ## Codecontext .codecontextignore auto-install services/codecontext_client.ts now copies the codecontext/.codecontextignore.template into any project's root on the first call to that project if no .codecontextignore exists. One file written per project, idempotent (in-memory Set guard + access-check), silent fallback on read-only project. Stops the upstream empty-source- file parser crash on foreign projects' node_modules — previously required manually copying the template per project. ## Tool-call budget cap 30 → 50 services/inference/budget.ts: BUDGET_READ_ONLY and BUDGET_NO_AGENT bumped to 50 (from 30). BUDGET_NON_READ_ONLY stays at 10 (no write tools landed yet). Real recon sessions were hitting 30 with ~3 turns wasted on codecontext parse failures; legitimate need was ~27, and Architect-class system overviews want deeper recon. Headroom of 20 absorbs failure-retry turns without changing the safety floor — the doom-loop guard (3 identical calls → abort) catches the actual failure mode this cap was guarding against. v1.14 (Phase C outer agent loop) will supersede this via per-agent agent.steps. Throwaway-ish patch but unblocks deeper recon today. ## UI cleanups - ChatPane queued-message dropdown removed. Each queued message now has three buttons: edit (pop back into ChatInput via sendToChat event), force-send (was the dropdown's only useful action), and cancel. Default behavior (send when streaming completes) needs no UI — it's the implicit do-nothing path. - ChatThroughput removed from desktop tab strip (ChatTabBar.tsx). Mobile tab switcher still shows it. ## Plumbing - .gitignore: data/* + !data/AGENTS.md + !data/skills/ negation patterns so the vendored skill library + agent registry become git-tracked while session DB state stays out. - docker-compose.yml: removed /opt/skills:/data/skills override mount. Skills now live in the boocode repo at data/skills/, auditable per-batch. The host-level /opt/skills/ is preserved untouched for any other tools that read from it. - .codecontextignore at repo root: auto-installed when codecontext was first called against /opt/boocode itself; matches the template. - CLAUDE.md: updated to document the v1.13.11 publishFrame wrapper + message_parts table + tool_cost_stats view + DB-integration test pattern + host-side smoke endpoint quirk. (Pre-existing in working tree before this batch; shipped here for completeness.) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
12 KiB
Visual Companion Guide
Browser-based visual brainstorming companion for showing mockups, diagrams, and options.
When to Use
Decide per-question, not per-session. The test: would the user understand this better by seeing it than reading it?
Use the browser when the content itself is visual:
- UI mockups — wireframes, layouts, navigation structures, component designs
- Architecture diagrams — system components, data flow, relationship maps
- Side-by-side visual comparisons — comparing two layouts, two color schemes, two design directions
- Design polish — when the question is about look and feel, spacing, visual hierarchy
- Spatial relationships — state machines, flowcharts, entity relationships rendered as diagrams
Use the terminal when the content is text or tabular:
- Requirements and scope questions — "what does X mean?", "which features are in scope?"
- Conceptual A/B/C choices — picking between approaches described in words
- Tradeoff lists — pros/cons, comparison tables
- Technical decisions — API design, data modeling, architectural approach selection
- Clarifying questions — anything where the answer is words, not a visual preference
A question about a UI topic is not automatically a visual question. "What kind of wizard do you want?" is conceptual — use the terminal. "Which of these wizard layouts feels right?" is visual — use the browser.
How It Works
The server watches a directory for HTML files and serves the newest one to the browser. You write HTML content to screen_dir, the user sees it in their browser and can click to select options. Selections are recorded to state_dir/events that you read on your next turn.
Content fragments vs full documents: If your HTML file starts with <!DOCTYPE or <html, the server serves it as-is (just injects the helper script). Otherwise, the server automatically wraps your content in the frame template — adding the header, CSS theme, selection indicator, and all interactive infrastructure. Write content fragments by default. Only write full documents when you need complete control over the page.
Starting a Session
# Start server with persistence (mockups saved to project)
scripts/start-server.sh --project-dir /path/to/project
# Returns: {"type":"server-started","port":52341,"url":"http://localhost:52341",
# "screen_dir":"/path/to/project/.superpowers/brainstorm/12345-1706000000/content",
# "state_dir":"/path/to/project/.superpowers/brainstorm/12345-1706000000/state"}
Save screen_dir and state_dir from the response. Tell user to open the URL.
Finding connection info: The server writes its startup JSON to $STATE_DIR/server-info. If you launched the server in the background and didn't capture stdout, read that file to get the URL and port. When using --project-dir, check <project>/.superpowers/brainstorm/ for the session directory.
Note: Pass the project root as --project-dir so mockups persist in .superpowers/brainstorm/ and survive server restarts. Without it, files go to /tmp and get cleaned up. Remind the user to add .superpowers/ to .gitignore if it's not already there.
Launching the server by platform:
Claude Code (macOS / Linux):
# Default mode works — the script backgrounds the server itself
scripts/start-server.sh --project-dir /path/to/project
Claude Code (Windows):
# Windows auto-detects and uses foreground mode, which blocks the tool call.
# Use run_in_background: true on the Bash tool call so the server survives
# across conversation turns.
scripts/start-server.sh --project-dir /path/to/project
When calling this via the Bash tool, set run_in_background: true. Then read $STATE_DIR/server-info on the next turn to get the URL and port.
Codex:
# Codex reaps background processes. The script auto-detects CODEX_CI and
# switches to foreground mode. Run it normally — no extra flags needed.
scripts/start-server.sh --project-dir /path/to/project
Gemini CLI:
# Use --foreground and set is_background: true on your shell tool call
# so the process survives across turns
scripts/start-server.sh --project-dir /path/to/project --foreground
Other environments: The server must keep running in the background across conversation turns. If your environment reaps detached processes, use --foreground and launch the command with your platform's background execution mechanism.
If the URL is unreachable from your browser (common in remote/containerized setups), bind a non-loopback host:
scripts/start-server.sh \
--project-dir /path/to/project \
--host 0.0.0.0 \
--url-host localhost
Use --url-host to control what hostname is printed in the returned URL JSON.
The Loop
-
Check server is alive, then write HTML to a new file in
screen_dir:- Before each write, check that
$STATE_DIR/server-infoexists. If it doesn't (or$STATE_DIR/server-stoppedexists), the server has shut down — restart it withstart-server.shbefore continuing. The server auto-exits after 30 minutes of inactivity. - Use semantic filenames:
platform.html,visual-style.html,layout.html - Never reuse filenames — each screen gets a fresh file
- Use Write tool — never use cat/heredoc (dumps noise into terminal)
- Server automatically serves the newest file
- Before each write, check that
-
Tell user what to expect and end your turn:
- Remind them of the URL (every step, not just first)
- Give a brief text summary of what's on screen (e.g., "Showing 3 layout options for the homepage")
- Ask them to respond in the terminal: "Take a look and let me know what you think. Click to select an option if you'd like."
-
On your next turn — after the user responds in the terminal:
- Read
$STATE_DIR/eventsif it exists — this contains the user's browser interactions (clicks, selections) as JSON lines - Merge with the user's terminal text to get the full picture
- The terminal message is the primary feedback;
state_dir/eventsprovides structured interaction data
- Read
-
Iterate or advance — if feedback changes current screen, write a new file (e.g.,
layout-v2.html). Only move to the next question when the current step is validated. -
Unload when returning to terminal — when the next step doesn't need the browser (e.g., a clarifying question, a tradeoff discussion), push a waiting screen to clear the stale content:
<!-- filename: waiting.html (or waiting-2.html, etc.) --> <div style="display:flex;align-items:center;justify-content:center;min-height:60vh"> <p class="subtitle">Continuing in terminal...</p> </div>This prevents the user from staring at a resolved choice while the conversation has moved on. When the next visual question comes up, push a new content file as usual.
-
Repeat until done.
Writing Content Fragments
Write just the content that goes inside the page. The server wraps it in the frame template automatically (header, theme CSS, selection indicator, and all interactive infrastructure).
Minimal example:
<h2>Which layout works better?</h2>
<p class="subtitle">Consider readability and visual hierarchy</p>
<div class="options">
<div class="option" data-choice="a" onclick="toggleSelect(this)">
<div class="letter">A</div>
<div class="content">
<h3>Single Column</h3>
<p>Clean, focused reading experience</p>
</div>
</div>
<div class="option" data-choice="b" onclick="toggleSelect(this)">
<div class="letter">B</div>
<div class="content">
<h3>Two Column</h3>
<p>Sidebar navigation with main content</p>
</div>
</div>
</div>
That's it. No <html>, no CSS, no <script> tags needed. The server provides all of that.
CSS Classes Available
The frame template provides these CSS classes for your content:
Options (A/B/C choices)
<div class="options">
<div class="option" data-choice="a" onclick="toggleSelect(this)">
<div class="letter">A</div>
<div class="content">
<h3>Title</h3>
<p>Description</p>
</div>
</div>
</div>
Multi-select: Add data-multiselect to the container to let users select multiple options. Each click toggles the item. The indicator bar shows the count.
<div class="options" data-multiselect>
<!-- same option markup — users can select/deselect multiple -->
</div>
Cards (visual designs)
<div class="cards">
<div class="card" data-choice="design1" onclick="toggleSelect(this)">
<div class="card-image"><!-- mockup content --></div>
<div class="card-body">
<h3>Name</h3>
<p>Description</p>
</div>
</div>
</div>
Mockup container
<div class="mockup">
<div class="mockup-header">Preview: Dashboard Layout</div>
<div class="mockup-body"><!-- your mockup HTML --></div>
</div>
Split view (side-by-side)
<div class="split">
<div class="mockup"><!-- left --></div>
<div class="mockup"><!-- right --></div>
</div>
Pros/Cons
<div class="pros-cons">
<div class="pros"><h4>Pros</h4><ul><li>Benefit</li></ul></div>
<div class="cons"><h4>Cons</h4><ul><li>Drawback</li></ul></div>
</div>
Mock elements (wireframe building blocks)
<div class="mock-nav">Logo | Home | About | Contact</div>
<div style="display: flex;">
<div class="mock-sidebar">Navigation</div>
<div class="mock-content">Main content area</div>
</div>
<button class="mock-button">Action Button</button>
<input class="mock-input" placeholder="Input field">
<div class="placeholder">Placeholder area</div>
Typography and sections
h2— page titleh3— section heading.subtitle— secondary text below title.section— content block with bottom margin.label— small uppercase label text
Browser Events Format
When the user clicks options in the browser, their interactions are recorded to $STATE_DIR/events (one JSON object per line). The file is cleared automatically when you push a new screen.
{"type":"click","choice":"a","text":"Option A - Simple Layout","timestamp":1706000101}
{"type":"click","choice":"c","text":"Option C - Complex Grid","timestamp":1706000108}
{"type":"click","choice":"b","text":"Option B - Hybrid","timestamp":1706000115}
The full event stream shows the user's exploration path — they may click multiple options before settling. The last choice event is typically the final selection, but the pattern of clicks can reveal hesitation or preferences worth asking about.
If $STATE_DIR/events doesn't exist, the user didn't interact with the browser — use only their terminal text.
Design Tips
- Scale fidelity to the question — wireframes for layout, polish for polish questions
- Explain the question on each page — "Which layout feels more professional?" not just "Pick one"
- Iterate before advancing — if feedback changes current screen, write a new version
- 2-4 options max per screen
- Use real content when it matters — for a photography portfolio, use actual images (Unsplash). Placeholder content obscures design issues.
- Keep mockups simple — focus on layout and structure, not pixel-perfect design
File Naming
- Use semantic names:
platform.html,visual-style.html,layout.html - Never reuse filenames — each screen must be a new file
- For iterations: append version suffix like
layout-v2.html,layout-v3.html - Server serves newest file by modification time
Cleaning Up
scripts/stop-server.sh $SESSION_DIR
If the session used --project-dir, mockup files persist in .superpowers/brainstorm/ for later reference. Only /tmp sessions get deleted on stop.
Reference
- Frame template (CSS reference):
scripts/frame-template.html - Helper script (client-side):
scripts/helper.js