feat(studio): tunnel Intent Form to in-process 3D pulse (C1) - #18
Conversation
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… tests - invalidate() while kick decays so the pop survives a frameloop='demand' switch - document the client-side-singleton/SSR assumption - test snapshot reference stability (the useSyncExternalStore invariant) - test multiple independent subscribers
|
Wondering what really moved? Review this PR in Change Stack to inspect semantic changes, definitions, and references. 📝 WalkthroughWalkthroughAdds an in-memory intent pulse store and types, a client hook, Vitest test setup and tests, package scripts/devDependency for Vitest, replaces full-page reload with intent emission + router.refresh on form submit, and drives MatrixScene pop animation from emitted pulses. ChangesIntent Pulse System
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Restores the Active Intent Vectors list update that the old window.location.reload() provided, via router.refresh() — a soft App Router refresh that re-runs the /status server fetch while preserving the persistent WebGL Canvas. Completes the end-to-end intent loop alongside the in-process 3D pulse. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
ui/studio/src/app/ClientViews.tsx (1)
12-23:⚠️ Potential issue | 🟠 MajorGuard post-submit actions behind HTTP success.
In
ui/studio/src/app/ClientViews.tsx(lines 12–22),await fetch(...)is not checked for HTTP failure (noresponse.ok/ status handling). The code then unconditionally clears inputs (setName,setMetric), emitsemitIntentPulse, and callsrouter.refresh(), which can reflect a successful “pop” even when the POST returns 4xx/5xx—gate those actions on a successful HTTP response before updating UI/state.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@ui/studio/src/app/ClientViews.tsx` around lines 12 - 23, In the submit handler (function submit) ensure you inspect the fetch response before proceeding: capture the result of await fetch('/intent', ...) into a variable, check response.ok (or status) and only then call setName(''), setMetric(''), emitIntentPulse(name, metric, Date.now()) and router.refresh(); on non-ok responses handle the error path (log or set an error state and avoid clearing inputs/emitting/refreshing) and optionally await response.json() to surface server error details; keep the request method/headers/body as-is but gate all post-submit side effects on a successful HTTP response.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@ui/studio/src/app/ClientViews.tsx`:
- Around line 12-23: In the submit handler (function submit) ensure you inspect
the fetch response before proceeding: capture the result of await
fetch('/intent', ...) into a variable, check response.ok (or status) and only
then call setName(''), setMetric(''), emitIntentPulse(name, metric, Date.now())
and router.refresh(); on non-ok responses handle the error path (log or set an
error state and avoid clearing inputs/emitting/refreshing) and optionally await
response.json() to surface server error details; keep the request
method/headers/body as-is but gate all post-submit side effects on a successful
HTTP response.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 17ea6026-4c4e-48e3-b6fb-c528da0b9635
📒 Files selected for processing (1)
ui/studio/src/app/ClientViews.tsx
C1: Tunnel Intent Form submissions to an in-process 3D pulse
Who is submitting this PR? (required)
claude-opus-4-8, 1M context). Implementation/review subagents: dispatched at theopustier; the harness does not surface the exact minor version and the tier selector cannot pin 4.7, so workers most likely ran on the session default Opus 4.8, not 4.7. Disclosed honestly.What problem are you trying to solve?
The Feature Studio's Intent Form (
ui/studio/src/app/ClientViews.tsx) responded to a submission by callingwindow.location.reload()— a full-page reload "for MVP". That destroys all client state, tears down and re-initializes the persistent React-Three-Fiber<Canvas>(causing WebGL context churn and a visible flash), and means a user "injecting an intent" gets a jarring reload instead of feedback in the 3D scene. The R3F implementation plan's Phase 1 Step 3 ("event tunnel / global state manager so DOM inputs trigger 3D animations") was the unchecked task addressing this.What does this PR change?
Replaces the full-page reload with an in-process "intent pulse": a tiny framework-agnostic external store (
src/lib/intentPulseStore.ts) plus auseSyncExternalStorehook (src/lib/useIntentPulse.ts). The Intent Form emits a pulse on submit;MatrixScenereacts by "popping" (scaling) the central core. Adds Vitest infrastructure and unit tests for the store. No new runtime dependencies (the store is hand-rolled, not zustand).Is this change appropriate for the core library?
No. This is fork-specific UI behavior for the RotarySlider Feature Studio, not a general-purpose Superpowers skill. Internal fork PR only.
What alternatives did you consider?
zustand(already present transitively via drei/fiber) as the store — rejected: adding it as a direct runtime dependency is unnecessary weight for a single pulse value; React's built-inuseSyncExternalStoreover a 40-line pure store does the job with zero new deps and is trivially unit-testable in plain node.<Canvas>boundary; a module-level external store crosses that boundary without prop-drilling or nesting providers, and keeps the emit path callable from a plain event handler.tunnel-rat/ drei<View>(named in the plan as options) — rejected: those tunnel rendered DOM into 3D space, which is a different concern (already used for telemetry); here we need a state/event signal, not portalled markup.Does this PR contain multiple unrelated changes?
No. Every file serves one goal: route an Intent Form submission to a 3D reaction. The Vitest config + tests exist to verify the store that does it.
Existing PRs
Environment tested
npm test→ 5/5 passing (intentPulseStore.test.ts): starts-empty, monotonic id + exact shape, subscribe/unsubscribe, snapshot reference stability, multiple independent subscribers.npm run build→ Compiled successfully (Next 14.2.35, 4/4 static pages).npx tsc --noEmit→ exit 0.Failed to load url ../intentPulseStore) before the store was implemented.New harness support (required if this PR adds a new harness)
N/A — no new harness.
Evaluation
N/A for skill evals — this is an application feature, not a behavior-shaping skill. Functional evaluation: red→green TDD on the store (failing test → implementation → 5 passing), then two independent reviewer passes — a spec-compliance review (re-ran the suite, confirmed
window.location.reloadis gone andVisualGalleryis byte-identical, confirmed no new runtime deps) and a code-quality review (verifieduseSyncExternalStoresnapshot/subscribe semantics, flagged aframeloop="demand"robustness gap which was then hardened withinvalidate()).Rigor
Human review
Summary by CodeRabbit
New Features
Tests
Chores