# Collaborative visual decisions This branch is a draft. The current implementation exposes a fixed Chopin specimen, which is not the intended production feature. Visual decisions must concern the user's own app. The corrected [rendering scope](superpowers/plans/2026-10-08-dynamic-design-card-rendering.md) removes that public example and accepts preview/control definitions as inputs; local-agent pickup and MCP publication follow separately. That generalization is not implemented yet. The behavior below describes the current specimen. In **Decisions**, a repository writer can choose **Tune decision card** to create the built-in visual decision. It renders Chopin's real decision options with two controls: vertical padding of 4, 6, or 8 px, and selected-option colour in `#RRGGBB` format. The inspector sits beside the preview when the card has room and below it in a narrow container. Hold **Show current** to see the frozen baseline. An assistive activation toggles that view until another activation or focus loss. Editors share accepted control values live. Different controls converge independently; the last server-accepted edit wins for a control. **Reset** changes the shared draft. Native controls follow input immediately while acknowledgements catch up; the preview shows accepted values. Unfinished colour text stays local and survives unrelated padding updates. Viewers observe updates and can peek, but cannot edit or save. This first specimen's interface is ordinary checked-in React and CSS. Humans create the card and tune its controls; neither the hosted Planner nor a local coding agent publishes a component through MCP in this version. Saving records the agreed values in the document, rather than restyling the app or committing code. A later implementation can apply that recorded decision. Reconnect restores accepted state before replaying unacknowledged edits. Durable client sequence high-water marks prevent a lost acknowledgement from replaying an old value over a collaborator's newer edit. **Save decision** claims one accepted revision, pauses further edits, and stages the values, saver, timestamp, and Questionnaire document projection together. Publication follows the fenced durable commit. A stale Save returns the latest values for review. An explicitly rolled-back PostgreSQL transaction releases the claim for retry; uncertain connection, fencing, and corruption failures retain the existing fatal storage boundary. A saved visual decision is immutable and appears in resolved decision history. The sidecar owns the immutable definition, draft, and saved record. The Questionnaire's `visual="decision-card-v1"` marker selects the renderer; its attributed Answer is a protected projection, not editable authority. Visual draft revisions are independent of document, Yjs, and storage counters. ## Local preview `bun run build` produces the inline preview bundle from the real `QuestionView`, shared theme, and embedded local fonts. It also writes the exact SHA-256 manifest and hash-based CSP. Generated files under `apps/web/preview/dist` are ignored. The experiment at `6e131218` informed the boundary, but none of its temporary jig or Dial Kit is a production dependency. Run the dedicated preview process separately: ```bash VISUAL_PREVIEW_APP_ORIGIN=http://127.0.0.1:8787 bun run start:visual-preview ``` Set `VISUAL_PREVIEW_ORIGIN=http://localhost:8793` on the Chopin process. The local hosts deliberately differ. Without a configured preview, visual decision creation is refused. Existing records remain durable. The authenticated trusted host obtains a fixed descriptor, fetches preview bytes with credentials omitted, checks the manifest, digest and response CSP, then loads an opaque `sandbox="allow-scripts"` iframe. That navigation is a separate request: browsers do not enforce HTML subresource integrity. The trusted preview server holds verified bytes in memory and serves the same bytes for every request at the exact digest URL. The reviewed TLS and ingress boundary must preserve those bytes and CSP, without response rewriting or injection. The preview accepts only versioned messages from its parent. Replies must match the current frame source, opaque origin, session, revision and values; replies never update saved values. Host CSP restricts frame navigation to the configured preview origin. Each unexpected load starts a fresh handshake; failure replaces the frame at most twice, then exposes a host-owned **Retry preview** action. Replacement retains the latest desired values. ## Production deployment gate Do not enable this feature on a production instance until a separate credential-free preview site is provisioned and reviewed. A second port or a sibling subdomain of the Chopin site is insufficient. Non-loopback configuration requires HTTPS for both sites, distinct registrable domains (including private public-suffix rules), and `VISUAL_PREVIEW_CREDENTIAL_FREE=1`. That flag is an operator assertion. The preview site's ingress, cookies, authentication, redirects and response headers must be checked before setting it; runtime domain checks cannot establish those operational facts. The site must serve only the verified immutable bundle and manifest, with the expected CSP, no reusable Chopin credential, no Set-Cookie, no response rewriting or injection, and no bundle-controlled fetch targets. Set `VISUAL_PREVIEW_APP_ORIGIN` to the exact application origin, and the preview host/port to its deployment bindings. The application's CSP and preview frame-ancestor policy bind those origins. This branch does not deploy either site. Historical bundle retention is another delivery requirement before rebuilding a published specimen: local and Linux container builds produced different digests at the same source revision. Retain the exact published bytes rather than regenerating an old bundle. The current server serves one digest, so an older saved decision safely shows an unavailable preview after a bundle change. Its frozen baseline, values and attribution remain in the record; silently substituting the new bundle is refused. ## Verification and limits The focused browser suite uses real authentication, WebSockets, document collaboration and PostgreSQL: ```bash bun run e2e --config e2e/visual-decision.playwright.config.ts ``` Its Save failure and in-flight claim probes use a test-only PostgreSQL trigger and advisory lock, never a production failure endpoint. It checks concurrent edits, stale and rolled-back Saves, short reconnects, retry after a peer edit, reload, write permissions, protected projections, wide/narrow placement, pointer/touch/Space/assistive peek, immutable bytes, cookies/storage, egress/navigation and bridge rejection/recovery. The built-in public specimen contains no private repository code. There is no arbitrary bundle publication or decision editing after Save. A document permits 20 visual decisions; each decision retains up to 512 editing client sessions without evicting deduplication history. The iframe has no hard CPU or memory quota. Schema-valid size claims can be false; the trusted host caps displayed height at 300–640 px. Browser evidence is Chromium-specific and does not certify arbitrary code, private bundles, or other browser engines. After the review fixes, the focused unit run passed 364 tests. All 72 PostgreSQL persistence/lifecycle tests passed, and all 14 Chromium integration scenarios passed. TypeScript, build and local CI validation passed. Before that integration, the container build passed with verbose install logging; a networking-disabled runtime check verified its packaged manifest, bytes, CSP and preview imports. The broader unit run passed 4,187 tests with three skips and four timing failures in existing Git/harness fixtures. Those failures passed isolated reruns; the broad run itself was not wholly green.