Repository navigation
Vibe mode: prompt-to-pattern on the stage, shared by every page - #45
Merged
Merged
Conversation
/vibe is the stage for a crowd: the sphere on a canvas, a text box, and a coding model. A prompt goes to the model, the module it writes is validated on the real lights, saved as generation #N (numbered from 1, never overwritten — rejected drafts kept beside it under a leading underscore, log.json the whole thread), and the stage hot-cuts to it. Every open page polls the same server-side state. A prompt carries the pattern showing when it was typed (name + source), so "slower and more purple" means that one; "from scratch" drops it. The side menu lists named generations, all of them, and the repo's patterns by folder. Two backends behind one contract (always ship a best try, optional short note): SessionCoder runs one Claude Code session per prompt through the Agent SDK — read-only in the checkout, shipping via a ship_pattern MCP tool that validates in-turn and returns what broke — and Coder makes one direct Messages API call. The page's model select picks from LUMINARY_VIBE_MODELS. Mounted only where upload is allowed or a stage key is set (the upload security model); mutations take the key. serve --no-vibe leaves it off. Engine changes: StageCore.cut (insert-after-current + jump, or start when holding), PatternRegistry origins (origin_of/source_of, list() "file"), stage_key_guard shared by the stage and vibe routes. httpx moves to runtime deps; a [vibe] extra brings the Agent SDK. CI's black and mypy lists gain luminary/vibe/. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N97HVk3sAr5jRGYodXcDoC
CI's mypy runs without the [vibe] extra, so claude_agent_sdk is an unresolved import there and the @tool decorator became untyped. A mypy override treats the SDK as Any when absent, and the ship tool is built by applying tool(...) as a call, so the function stays typed in both environments. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N97HVk3sAr5jRGYodXcDoC
The allow list (read-only tools + ship_pattern) still pre-approves and the deny list (writes, shell, web) still blocks first, so the classifier only ever sees a call outside both and nothing waits on a prompt; unlike bypassPermissions, auto is not refused under root. The mode is a knob (LUMINARY_VIBE_PERMISSION_MODE, default auto; "default" for allowlist-only, needed with Haiku-class models, which auto mode does not support). Verified live: a real session as root shipped a validated pattern onto the stage in auto mode. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N97HVk3sAr5jRGYodXcDoC
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
/vibe— the stage for a crowd. The sphere on a canvas, a text box, and a coding model:LUMINARY_VIBE_MODELS, defaultclaude-sonnet-5,claude-opus-5, first is default).GET /api/vibe); pages are thin adapters polling it every ~2 s, so five phones at the sphere agree on the thread, what's cooking, what's showing, and the menu.(n,3)shape, finite, stateless, frame ≤ 250 ms) gets one repair round; a still-failing generation is shown failed and the stage stays put.var/vibe/vibe-0001.py… (registered asvibe-N, real patterns — queueable from the stage page too), rejected drafts kept beside them as_vibe-NNNN-attempt1.py/-failed.py(the registry skips underscores),var/vibe/log.jsonis the whole thread; queued/cooking entries are marked failed on restart rather than lost.patterns,book-one,book-two,conifer) — click anything to cut to it, for comparison or as the base of the next prompt.Backends
One contract, two implementations (
luminary/vibe/coder.py,session.py;setup.pypicks):claude-agent-sdkimports and aclaudeCLI is on PATH): each prompt is its own Claude Code session in the checkout via the Agent SDK — read-only tools (Read/Glob/Grep), writes/Bash/web denied, and one MCP tool of ours,ship_pattern, which runs the core's validator on the spot and returns exactly what broke, so the session iterates inside its own turn. Its final words are the note.httpx, now a runtime dep) withANTHROPIC_API_KEY.LUMINARY_VIBE_BACKEND=session|apiforces one; the page footer shows which is running.pip install -e '.[vibe]'brings the SDK.Security
A generation is model-written code executed in-process — the upload model (spec §15.5.2). Vibe mounts only where upload is allowed or a stage key is configured, and then
POST /api/vibeandPOST /api/vibe/selecttakeX-Stage-Keythrough the samestage_key_guardas the queue.serve --no-vibeleaves it off;/api/healthreports"vibe". Documented indocs/deploy.md.Engine changes (small, shared)
StageCore.cut(raw): hot-switch — insert after the current entry and jump, or just start it when holding. The vibe surface's one verb; no second render (the page decodes the stage's ownWS /api/stage).PatternRegistry: remembers each pattern's origin (origin_of,source_of;list()rows carry"file"relative to their root) — for the folder-grouped menu and for sending the shown pattern's source to the model.stage_key_guard(key)factored out ofregister_stage, shared by both surfaces.Verified
tests/test_vibe.py): numbering from 1 and context capture at submit, cook → stamp → validate → save → registry → cut, repair round keeping the rejected draft, failed generations kept with numbering moving on, model failure as a failed generation (worker survives), validator error texts, folder-grouped menu, select, restart resilience,assign_identitycases, the session backend driven by a fake runner (ship-tool round trips, spoken-code fallback, repair prompt), HTTP routes + stage-key gating,create_appmount with the live worker, and the upload-style gating matrix. Full suite green; black + mypy on CI's lists (now includingluminary/vibe/).serve --stage-key k: the page connects, decodes the stream, lists 39 repo patterns by folder, cuts on click, and a real prompt ("slow purple tide from the crown") went through a live Claude Code session → validated →vibe-0001.py→ stage cut to#1 Tide, with note, in ~37 s on claude-sonnet-5. No console errors; no horizontal overflow at 640/900/1280 px.Docs
README (Web API rows + "Vibe mode" section),
docs/deploy.md(security model + box setup),patterns/README.md(promoting a keeper),plan/spec/implementation-notes.md(component rows, runtime layout),.gitignorecomment.🤖 Generated with Claude Code
https://claude.ai/code/session_01N97HVk3sAr5jRGYodXcDoC
Generated by Claude Code