Skip to content

feat(parametric): agentic verify ↔ refine loop with screenshot self-review#141

Open
zachdive wants to merge 18 commits intomasterfrom
claude/cadam-agentic-agent-SAqQl
Open

feat(parametric): agentic verify ↔ refine loop with screenshot self-review#141
zachdive wants to merge 18 commits intomasterfrom
claude/cadam-agentic-agent-SAqQl

Conversation

@zachdive
Copy link
Copy Markdown
Contributor

@zachdive zachdive commented May 3, 2026

Summary

Makes parametric mode actually agentic — same shape as the Fusion extension. After generating code, Adam now calls a new view_model tool to request rendered screenshots from chosen angles (named views like iso/front/top or arbitrary custom az/el), the browser fulfills the call by rendering the compiled STL and replays the screenshots as a hidden follow-up user message, and Adam reviews them and either calls build_parametric_model again with a fix description or confirms completion. The whole loop runs without user intervention.

How it works

user: "make a chair"
 → assistant: build_parametric_model → artifact lands
 → [client] hidden nudge: "verify your work"
 → assistant: view_model({views: ['iso','front','top']})  ← agent picks angles
 → [client] renders 3 angles from STL, uploads, replies with images
 → assistant: looks at screenshots, either:
       (a) build_parametric_model("fix: legs detached from seat")  → loops
       (b) "Done — chair has 4 legs, sits flat."                    → terminates

Capped at 3 verification rounds via a shared MAX_AGENTIC_ITERATIONS constant; once hit, the server strips view_model from the toolset and appends a system note telling the agent to finalize.

Files

  • supabase/functions/parametric-chat/index.ts — new view_model tool definition + handler that records the requested views as a pending_verification tool call; iteration counting on the branch with cap-driven tool/prompt swap.
  • shared/types.ts — extends ToolCall with views/reasoning/screenshots/two new statuses, and Content with isVerification/agenticIteration.
  • src/utils/agenticRenderer.ts — re-parses the OpenSCAD STL and renders it from any named or custom (az/el) viewpoint.
  • src/hooks/useAgenticVerification.ts — orchestrator. Path A fulfills pending_verification with screenshots; Path B nudges the agent after a fresh artifact lands. Uses inFlightRef + a process-wide ACTED_ON set so it never double-fires for the same turn.
  • src/views/ParametricEditorView.tsx — wires the hook in.
  • src/components/chat/AssistantMessage.tsxViewModelToolCallCard chip showing inspecting/verified/error states with screenshot thumbnails.
  • src/components/chat/UserMessage.tsx — verification user messages render as a compact VerificationChip instead of a normal bubble.

Test plan

  • Send "make a coffee mug" — confirm agent calls view_model, renders show up in chat, agent either confirms or refines.
  • Send something the agent will likely get wrong (e.g. "a chair that floats above its base") — confirm refinement loop kicks in and terminates.
  • Verify the cap: after 3 rounds, view_model should disappear from the toolset and the agent must finalize.
  • Verify branching: switch to an older message branch mid-stream — no spurious verification fires.
  • Verify cancellation: hit stop while agent is verifying — pending_verification flips to error, no zombie spinners.
  • Confirm tsc -p tsconfig.app.json --noEmit is clean (only pre-existing baseUrl deprecation).

https://claude.ai/code/session_01N82YWFtjHn2Y2cSVBvnWKQ


Generated by Claude Code


Summary by cubic

Adds an automatic verify ↔ refine loop to parametric mode with realtime screenshot self-review in a single request. Also adds multi-file .scad assemblies and an update_file tool for fast per-file fixes.

  • New Features

    • view_model tool with pending_verification/verified statuses, views/reasoning/screenshots; server caps at 3 rounds and then strips the tool.
    • Off-screen renderer re-parses STL and captures named or custom (az/el) views; useAgenticVerification listens on verify-conv-{conversationId}, uploads PNGs, and resumes the loop; assistant shows an inspecting/verified/error chip with thumbnails.
    • Multi-file artifacts via strict // === FILE: <name>.scad === markers; server streams per-file chunks; OpenSCADPreview accepts files and entryFile, writes aux files to the WASM fs (skips the entry by name) so use <file.scad> resolves; chat pill shows a " files" badge.
    • update_file tool for targeted edits to one .scad file: validates filename, promotes single-file artifacts when first used, re-parses params only if the entry changes, replaces or adds the file, and returns an action summary; chat labels it “Updating file...”.
  • Bug Fixes

    • Screenshots: fix black top/bottom via perpendicular camera.up; drop the no-op PNG quality arg.
    • view_model: throw when getSignedUrls returns none; use signedUrls.length for the attached count.
    • Realtime/edge stability: drop invalid side view; fix a stray bracket in the supabase edge bundle; wait for SUBSCRIBED; guard subscribe failures; add subscribe-status handling and fast-fail realtime_unavailable; clean abort/timeout; avoid leaks; convert inner async fns to const arrows.
    • Chip labels: use the renderer’s default custom-view angles so text matches screenshots.
    • STL export: copy the exporter’s DataView into an ArrayBuffer-backed Uint8Array via set(...); fail fast on non-DataView; large exports are faster and safer.
    • Chat panel: force-open and re-sync collapsed state on first mount to recover from auto-saved zero-width panels.
    • Multi-file stability: skip entry-file writes by name and preserve files/entryFile on parameter changes; use a single baseArtifact source to avoid dropping fields.
    • Verification accuracy: ensure screenshots match the latest build by tagging STL outputs with their source code and polling up to 25s; if stale, return a clear error instead of letting view_model time out.

Written for commit 0f8071a. Summary will update on new commits.

…eview

Adds a true agentic loop to parametric mode that mirrors the Fusion extension:
after generating code, the agent calls a new view_model tool to request
screenshots from chosen angles ('iso', 'front', 'top', custom az/el, etc.).
The browser fulfills the call by rendering the compiled STL from those
angles, uploading them, and replaying them as a hidden follow-up user
message. The agent reviews the screenshots and either calls
build_parametric_model with a fix description or briefly confirms
completion. Capped at 3 verification rounds via shared client/server
constants; view_model is stripped from the toolset once the cap is hit.
@vercel
Copy link
Copy Markdown

vercel Bot commented May 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cadam Ready Ready Preview, Comment May 5, 2026 11:38pm

Request Review

@supabase
Copy link
Copy Markdown

supabase Bot commented May 3, 2026

Updates to Preview Branch (claude/cadam-agentic-agent-SAqQl) ↗︎

Deployments Status Updated
Database Tue, 05 May 2026 23:37:52 UTC
Services Tue, 05 May 2026 23:37:52 UTC
APIs Tue, 05 May 2026 23:37:52 UTC

Tasks are run on every commit but only new migration files are pushed.
Close and reopen this PR if you want to apply changes from existing seed or migration files.

Tasks Status Updated
Configurations Tue, 05 May 2026 23:37:53 UTC
Migrations Tue, 05 May 2026 23:37:53 UTC
Seeding Tue, 05 May 2026 23:37:54 UTC
Edge Functions Tue, 05 May 2026 23:38:10 UTC

View logs for this Workflow Run ↗︎.
Learn more about Supabase for Git ↗︎.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 3, 2026

Greptile Summary

This PR makes parametric mode fully agentic: after generating OpenSCAD code, the assistant calls a new view_model tool to request rendered screenshots from the browser, reviews them, and either refines via build_parametric_model / update_file or confirms completion — all without user intervention, capped at 3 rounds via MAX_AGENTIC_ITERATIONS. It also adds multi-file .scad assembly support with a strict // === FILE: name.scad === marker format.

  • Agentic verify loop: useAgenticVerification subscribes to a per-conversation Supabase Realtime channel, fulfills view_model calls by rendering the live STL from any named/custom angle via agenticRenderer.ts, uploads PNGs, and broadcasts back so the server resumes. The server side caps iterations by stripping view_model from the toolset after MAX_VERIFY_ROUNDS and appending a finalization note.
  • Multi-file artifacts: build_parametric_model can now return multiple .scad files delimited by // === FILE: name.scad === markers; OpenSCADViewer writes supplementary files to the WASM filesystem keyed by filename (not content), and update_file enables surgical single-file patches without a full rebuild.
  • Bug fixes absorbed: top/bottom degenerate camera matrix, billing bypass removal, apply_parameter_changes dropping files/entryFile, byte-by-byte STL copy, subscribe-status swallowing, and entry-file WASM duplicate-write are all resolved in this commit.

Confidence Score: 5/5

The agentic loop, multi-file support, and all previously flagged correctness issues appear properly resolved; only style-level rule violations remain in the new code.

All substantive issues from the prior review round — billing bypass, degenerate top/bottom camera matrix, files/entryFile being dropped by apply_parameter_changes, subscribe failures silently swallowed, byte-by-byte STL copy — are addressed in this commit. The only new finding is two as type casts introduced in the supabase edge function that violate the team's no-cast rule, which carry no runtime risk given the surrounding guards. The core verify loop logic, Realtime bridge, WASM multi-file write path, and STL export fix all look mechanically correct.

supabase/functions/parametric-chat/index.ts is the most complex change (2040 line insertions) and warrants manual smoke-testing of the full verify loop, the iteration cap, and the update_file → apply_parameter_changes ordering for multi-file artifacts.

Important Files Changed

Filename Overview
supabase/functions/parametric-chat/index.ts Largest change: adds view_model tool, update_file tool, multi-file OpenSCAD parsing, agentic loop with iteration cap, and executeViewModelTool Realtime bridge. Two new as type casts (view and payload) violate team rule. Previously flagged issues (billing bypass, files/entryFile drop in apply_parameter_changes, unhandled subscribe rejection) all appear resolved.
src/hooks/useAgenticVerification.ts New orchestrator hook for the browser side of the verify loop. Addresses several issues from prior review iteration (subscribe status callback, realtimeBroken flag, waitForFreshStl for STL freshness). The waitForFreshStl snapshot-timing race (messages cache may be empty when polling starts) was flagged in a previous thread.
src/utils/agenticRenderer.ts New off-screen WebGL renderer for agentic verification screenshots. Correctly handles top/bottom degenerate view matrix via perpendicular camera.up, properly disposes GL resources in finally block.
src/components/viewer/OpenSCADViewer.tsx Adds files/entryFile props; correctly skips writing the entry file to WASM fs by name instead of content equality, fixing the fragile identity-by-content approach flagged in a prior review.
src/utils/meshPrintProcessUtils.ts Replaces byte-by-byte DataView copy with Uint8Array.set() memcpy, adds DataView instanceof guard to fail fast on unexpected STLExporter return types.
src/components/chat/AssistantMessage.tsx Adds ViewModelToolCallCard chip with inspecting/verified/error states and screenshot thumbnails; adds file count badge for multi-file artifacts. Uses viewLabel from agenticRenderer to get correct default angles.
src/views/ParametricEditorView.tsx Wires useAgenticVerification; introduces handleOutputChange to keep currentOutput and currentOutputCode in sync as a single atomic update.
shared/types.ts Adds ViewName/ViewRequest types, extends ToolCall with views/reasoning/screenshots/new statuses, and ParametricArtifact with files/entryFile for multi-file support. Clean, well-documented additions.

Sequence Diagram

sequenceDiagram
    participant U as User
    participant C as Client (Browser)
    participant S as Server (Edge Fn)
    participant LLM as OpenRouter LLM

    U->>S: "make a chair"
    S->>LLM: agent turn (tools: build_parametric_model, view_model, update_file)
    LLM-->>S: build_parametric_model(text)
    S->>LLM: generateOpenSCADCode()
    LLM-->>S: streamed .scad code
    S-->>C: stream artifact (code/files/entryFile)
    C->>C: OpenSCAD WASM compile → STL
    S->>LLM: agent turn (iteration 1)
    LLM-->>S: view_model({views:[iso,front,top]})
    S->>C: Realtime broadcast verify_request
    C->>C: renderArtifactFromViews(STL, views)
    C->>C: upload PNGs to Supabase Storage
    C->>S: Realtime broadcast verify_response({imageIds})
    S->>LLM: agent turn with screenshot images
    alt screenshots look wrong
        LLM-->>S: build_parametric_model(fix: legs detached)
        S-->>C: stream updated artifact
        Note over S,C: loop up to MAX_AGENTIC_ITERATIONS (3)
    else screenshots look correct
        LLM-->>S: text Done — chair looks good.
        S-->>C: stream final message
    end
Loading

Reviews (17): Last reviewed commit: "fix(parametric): three P1s on the agenti..." | Re-trigger Greptile

Comment thread src/utils/agenticRenderer.ts
Comment thread src/hooks/useAgenticVerification.ts Outdated
Comment thread src/utils/agenticRenderer.ts
Comment thread src/hooks/useAgenticVerification.ts Outdated
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 7 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/hooks/useAgenticVerification.ts">

<violation number="1" location="src/hooks/useAgenticVerification.ts:177">
P2: The Supabase `.update()` result is not checked for errors. Unlike `fetch`, the Supabase client returns `{ data, error }` without throwing. If this write fails, the local cache is optimistically set to `'verified'` while the DB retains `'pending_verification'`, causing state drift and potential re-triggering on refresh.</violation>
</file>

<file name="src/utils/agenticRenderer.ts">

<violation number="1" location="src/utils/agenticRenderer.ts:37">
P3: Custom view labels use different default angles than rendering, so displayed metadata can be wrong for omitted azimuth/elevation.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread src/hooks/useAgenticVerification.ts Outdated
Comment thread src/utils/agenticRenderer.ts Outdated
Replaces the client-driven nudge loop with a true intra-request agent loop
in parametric-chat:

- The agent loops `streamAgentTurn` → execute tool → append tool_result →
  loop until it emits text without tool_calls (or hits the cap). All in
  one HTTP request.
- `view_model` is bridged to the browser via Supabase Realtime: server
  broadcasts `verify_request` on `verify-conv-{conversationId}` and
  awaits a matching `verify_response`. The browser-side hook is mounted
  with the editor and stays subscribed for the conversation lifetime, so
  there's no race between fetch start and listener wireup.
- Multi-tool-call streaming is now keyed by SSE index, so a single agent
  response can cleanly emit multiple tool calls.
- Added request-scoped abort/cancel (parametric-chat didn't have one
  before) so clicking Stop tears down OpenRouter fetches + the realtime
  wait + the code-gen fetch in one go.

Removed: the client-side nudge user messages (`isVerification` /
`agenticIteration` fields, `VerificationChip`) and the `useAgenticVerification`
orchestrator paths. The hook now only carries the realtime listener.

Caps: at most 3 verification rounds per request (`MAX_VERIFY_ROUNDS`)
strips view_model from the toolset on the cap; total agent iterations
capped at 8 (`MAX_AGENT_ITERATIONS`) as a belt-and-braces backstop.
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 5 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="supabase/functions/parametric-chat/index.ts">

<violation number="1" location="supabase/functions/parametric-chat/index.ts:1243">
P2: `getSignedUrls` silently filters out failed entries, so `signedUrls` may be shorter than `imageIds` (or empty). The agent is told N screenshots are attached but could receive fewer or none, making verification silently ineffective. Add a check for `signedUrls.length === 0` (treat as error) or at minimum use `signedUrls.length` in the tool result text.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread supabase/functions/parametric-chat/index.ts
- Top/bottom views were rendering black: camera direction was anti-parallel
  to camera.up, producing a degenerate view matrix. Pick a perpendicular
  up axis (world +Z) when the view direction is within ~1° of vertical.
- Drop the ignored `quality` arg from canvas.toBlob — it's a no-op for
  PNG and reads like compression tuning that doesn't happen.
- Sync default custom-view azimuth/elevation between viewToDirection
  (was 30/25) and viewLabel (was 0/0) via shared constants, so the chip
  text matches the rendered angle when the agent omits both.
- view_model: throw if getSignedUrls returns nothing (it silently filters
  on per-path failures); use signedUrls.length for the agent's tool
  result so the count matches what's actually attached.

Also: fix a pre-existing TS error in meshPrintProcessUtils.ts that the
pre-commit hook surfaced — Blob constructor's tightened BlobPart type
rejected the STLExporter's DataView; copy into a fresh ArrayBuffer.
The supabase edge-fn preview was failing with 'failed to bundle function:
exit status 1' because of a stray duplicated `},` at the end of the
ReadableStream initializer (deno parse failed at index.ts:1894). Fixing
that unblocks the bundle.

While in there, address the rest of the new Greptile findings:
- P1: drop the invalid 'side' view-name reference in the system prompt;
  use the actual allowed names ('left'/'right') and explicitly tell the
  model NOT to request 'side', which would otherwise be silently filtered
  and break verification.
- P2: convert the three inner async function declarations
  (streamAgentTurn / generateOpenSCADCode / executeViewModelTool) to
  const arrow expressions so deno lint's no-inner-declarations rule no
  longer flags them.
- P2: replace the `as ArrayBuffer` / `as unknown as BlobPart` casts in
  meshPrintProcessUtils.ts with an explicit ArrayBuffer-backed Uint8Array
  so the BlobPart narrowing succeeds without any casts.
- P2: relocate the verify_response listener wiring to outside the
  responsePromise constructor and detach abort/timeout in the finally
  block, so a timeout / abort path no longer leaks the listener.
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/utils/meshPrintProcessUtils.ts">

<violation number="1" location="src/utils/meshPrintProcessUtils.ts:973">
P1: The non-`DataView` fallback silently creates an empty STL payload. Fail fast (or correctly handle alternate result types) instead of defaulting to `new ArrayBuffer(0)` to avoid generating corrupt 0-byte exports.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread src/utils/meshPrintProcessUtils.ts Outdated
Comment thread supabase/functions/parametric-chat/index.ts
Comment thread src/utils/meshPrintProcessUtils.ts Outdated
- executeViewModelTool: wrap the channel.subscribe() promise in the
  same try/finally as send + responsePromise. CHANNEL_ERROR / TIMED_OUT
  on subscribe was throwing past the finally, leaving the verify-response
  setTimeout armed and producing an unhandled rejection in Deno Deploy.
  Also defer arming the response promise + abort listener until AFTER
  SUBSCRIBED, and slap a no-op .catch() on the response promise so any
  early throw between subscribe and the first await still drains it.

- meshPrintProcessUtils: replace the byte-by-byte getUint8 loop with
  a single Uint8Array.set(...) — native memcpy is thousands of times
  faster on multi-MB print STLs (~200-500ms saved on a 20MB export).
  Also fail fast if STLExporter ever returns a non-DataView in binary
  mode, instead of silently emitting a 0-byte STL.
… OAuth

Adds a preview-only escape hatch for testing the agentic loop on Vercel
preview deployments where the per-branch Supabase project doesn't have
Google OAuth (or any other provider) configured.

Two paired flags, both default off (production unaffected):

- VITE_PREVIEW_DEMO_MODE: when "true", AuthGuard skips the
  redirect-to-/signin path and instead asks Supabase for an anonymous
  session via supabase.auth.signInAnonymously(). If anonymous sign-in
  is also disabled on the project, surfaces a clear inline message
  ("enable anonymous sign-in or unset VITE_PREVIEW_DEMO_MODE")
  instead of bouncing forever between guard and /signin.

- PREVIEW_BYPASS_BILLING: edge-function env var. When "true",
  parametric-chat skips both the chat-token and parametric-token
  billing.consume() calls. Anonymous Supabase users have no email
  and no adam-billing record, so without this flag the chat would
  400 on the email check or 502 on billing. Pair with
  VITE_PREVIEW_DEMO_MODE on the client.

Setup for testing on a preview:
  1. Supabase dashboard → Authentication → Settings → enable
     "Allow anonymous sign-ins"
  2. Vercel preview env: VITE_PREVIEW_DEMO_MODE=true
  3. Supabase preview edge-function env: PREVIEW_BYPASS_BILLING=true

Production keeps both unset and behaves identically.
Adds a standalone /cadam/demo route that walks through the agentic
verify ↔ refine UI without any auth, Supabase calls, or LLM requests.
Lets the PR preview be poked end-to-end with zero dashboard config.

What's REAL:
  - OpenSCAD WASM compile of a canned coffee-mug SCAD
  - renderArtifactFromViews capturing actual screenshots from the live
    STL at iso/front/top angles
  - The same ViewModelToolCallCard chip the production assistant
    message uses (inlined into the demo so we don't drag in chat
    plumbing)

What's CANNED:
  - Each "agent" text reply (preamble + verdict)
  - The fixed iso/front/top viewing angles
  - The "looks good" judgement (no LLM is asked)

Route is mounted outside AuthGuard and outside the Layout shell so it
needs no session, no conversation, and no MeshFiles seed data — just
hit /cadam/demo on any deploy and click Run.
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 2 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/views/AgenticDemoView.tsx">

<violation number="1" location="src/views/AgenticDemoView.tsx:182">
P2: Capture failures are reported as successful verification because the error path sets `phase` to `done`, and `done` is rendered as verified/finalized.</violation>
</file>

<file name="src/main.tsx">

<violation number="1" location="src/main.tsx:78">
P2: The new `/demo` route is unreachable when Supabase config is missing because the app renders `MissingConfig` instead of the router, so the claimed no-Supabase demo path will not work.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread src/views/AgenticDemoView.tsx Outdated
Comment thread src/main.tsx Outdated
The supabase/seed.sql in this repo already creates a test user
(test@adamcad.com / password) and Supabase Branching applies that seed
to preview branches but never to production. So we can use that as a
zero-config sign-in path on PR previews — but parametric-chat would
otherwise 502 on the billing.consume call because the seed user has no
adam-billing record.

Add the seed user email as a second trigger for BYPASS_BILLING,
alongside the existing PREVIEW_BYPASS_BILLING env var. Sign in with
test@adamcad.com / password on any preview and the agent loop runs
end-to-end against real OpenRouter, with no dashboard knob to flip.

Production: seed.sql doesn't run there, so the test user doesn't exist
and the auto-bypass can't activate.
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="supabase/functions/parametric-chat/index.ts">

<violation number="1" location="supabase/functions/parametric-chat/index.ts:610">
P1: Avoid account-based billing bypass in production path; gating should be environment/config based only.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread supabase/functions/parametric-chat/index.ts Outdated
Removes everything that was added solely to make the Vercel preview
testable without dashboard config. Plan is to vibe-merge against
production where the existing auth + billing already work.

Backed out:
  - VITE_PREVIEW_DEMO_MODE branch in AuthGuard (anonymous sign-in
    fallback + inline error UI)
  - PREVIEW_BYPASS_BILLING env var support in parametric-chat
  - The seed-test-user auto-bypass (test@adamcad.com)
  - The /demo route and AgenticDemoView (canned-response walkthrough)
  - The VITE_PREVIEW_DEMO_MODE entry in .env.local.template

What remains is just the agentic feature: parametric-chat agent loop
with view_model + Realtime bridge, the browser-side fulfillment hook,
the off-screen renderer, the chip UI, and the type extensions in
shared/types.ts. Plus the small TS-narrowing fix in
meshPrintProcessUtils.ts (kept — it's an unrelated fix the pre-commit
hook flagged).
Comment thread src/components/chat/AssistantMessage.tsx
The ViewModelToolCallCard was reimplementing the custom-view label with
its own ?? 0 defaults for azimuth/elevation, but agenticRenderer.ts
renders missing angles at DEFAULT_CUSTOM_AZIMUTH_DEG=30 and
DEFAULT_CUSTOM_ELEVATION_DEG=25. The chip would say 'custom (0°/0°)'
while the screenshot was actually captured at 30°/25°.

Delegate to the renderer's exported viewLabel() so chip text and
captured angle agree.
Comment thread src/hooks/useAgenticVerification.ts Outdated
The agent can now decompose complex models into multiple .scad files
and an entry that 'use's them — the same pattern Adam users would
already write by hand for non-trivial assemblies.

Strict code prompt teaches the format: literal
  // === FILE: <name>.scad ===
markers on their own lines delimit per-file content. The first file is
the entry; the others are written alongside it in the OpenSCAD WASM
filesystem. Top-level user-exposed parameters MUST live in the entry
(modules in `use`d files don't surface their top-level vars). The
agent prompt encourages decomposition for vehicles / multi-part
furniture / 200+ line models, and explicitly tells the build tool when
to split.

Server side:
- parseMultiFileOpenSCAD splits the strict-code-prompt response on the
  FILE markers, dedupes repeated names, returns per-file chunks.
- build_parametric_model populates artifact.files (full set including
  entry) and artifact.entryFile alongside the existing artifact.code
  (entry's content). Single-file output keeps producing the same
  artifact shape as before.
- Streaming preserves multi-file structure: the partials parse
  incrementally so the viewer can write each file to the WASM fs and
  recompile as they grow, instead of accumulating one giant
  marker-laden blob until the final response.

Client side:
- OpenSCADPreview now accepts a `files` prop. Before compileScad runs,
  each supplementary .scad is written to the WASM filesystem so the
  entry's `use <wheel.scad>` resolves. Cached by name+content so the
  worker round-trip is skipped when nothing changed.
- ParametricPreviewSection and ParametricPreviewDialog forward the
  artifact's files to the viewer.
- Object pill in the chat shows a "<N> files" badge on multi-file
  artifacts; single-file pills look unchanged.

Existing flows unchanged for single-file artifacts: parameter parsing,
share view, fix-with-AI, parameter-update tool, DXF export — all read
from artifact.code which remains the entry's content.
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 6 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/components/viewer/OpenSCADViewer.tsx">

<violation number="1" location="src/components/viewer/OpenSCADViewer.tsx:122">
P1: Skipping aux `.scad` files by content equality can incorrectly omit required files. Distinct files can have identical contents, so this should not gate writes.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread src/components/viewer/OpenSCADViewer.tsx Outdated
Comment thread supabase/functions/parametric-chat/index.ts
Adds a third path between full-rebuild and parameter tweaks: the agent
can rewrite (or add) ONE .scad file in the current artifact via
update_file({ filename, content, rationale }). Cheaper and faster than
build_parametric_model — no inner code-gen call runs; the outer agent
authors the new file content directly. Useful when verification flags
a problem in one part ("the wheel is too thin") and re-running the
whole code generator would be wasteful.

Tool dispatch:
- Validates filename matches /^[A-Za-z0-9_.-]+\.scad$/ — bare names
  only, no directories — same constraint as the WASM-fs writer side.
- Promotes single-file artifacts to multi-file shape on first
  update_file call (the existing artifact.code becomes main.scad in
  artifact.files).
- Re-runs parseParameters() only when the entry file is the one being
  edited. Non-entry files don't surface top-level params, so the
  parameter panel stays put for unrelated edits.
- Replaces a matching filename, otherwise appends — letting the agent
  also use update_file to extract a module into its own file.
- Tool result tells the agent whether it replaced or added, the
  rationale (for chat-history readability), and whether the
  verification budget allows another view_model call.

Agent prompt:
- Picks update_file vs build_parametric_model based on verification
  scope: one-part fix → update_file, multi-part / structural → rebuild.

Chat UI:
- The tool-call pill labels update_file as "Updating file..." /
  "Failed to update file" instead of the raw tool name.

Backwards compat: build_parametric_model and apply_parameter_changes
behaviour unchanged. Single-file artifacts that never use update_file
keep the existing single-code-string shape.
@cubic-dev-ai
Copy link
Copy Markdown

cubic-dev-ai Bot commented May 4, 2026

You're iterating quickly on this pull request. To help protect your rate limits, cubic has paused automatic reviews on new pushes for now—when you're ready for another review, comment @cubic-dev-ai review.

Re-adds the seed-user (test@adamcad.com) billing bypass that
c57cb2f originally introduced and 57827b1 cleaned up. The seed user
exists only on the preview Supabase branch (Branching applies seed.sql
to previews, not production), so this trigger only ever fires on PR
previews — the production project doesn't have that user.

Lets you sign in to the Vercel preview with test@adamcad.com / password
and exercise the new agent end-to-end (multi-file build, view_model
verification, update_file, MAX_VERIFY_ROUNDS cap) without provisioning
adam-billing for the seed user.

Strip this commit before merging. Production is unaffected either way
(seed user can't exist there) but the preview-only escape hatch
shouldn't ride along into the merged history.
Comment thread supabase/functions/parametric-chat/index.ts Outdated
Companion to 2837d2b. The server-side BYPASS_BILLING was already in
place, but the chat input was still being disabled because the
client-side `limitReached` check reads billing.tokens.total from the
billing-status edge function — which returns 0 for the seed user
(it has no adam-billing record). The frontend was blocking input
before any request hit the server.

Short-circuit the billing-status query for test@adamcad.com to return
a synthetic Pro billing object with effectively infinite tokens. Same
preview-only scope as 2837d2b: the seed user only ever exists on
Supabase preview branches, never on production. Strip both commits
before merge.
Comment thread src/contexts/AuthProvider.tsx Outdated
The PanelGroup's autoSaveId can restore the chat panel's size to 0 from
a prior session where the user collapsed it, but the React-side
isChatCollapsed flag stays at its default `false`. With those two
layers disagreeing, neither the in-panel collapse overlay nor the
out-of-panel expand-tab renders — the chat panel disappears with no
way to recover short of clearing localStorage. Particularly nasty on
PR-preview origins where the persisted state isn't shared with prod.

Force-expand the chat panel on first mount and reset isChatCollapsed
to false. Manual user collapses afterwards still work because the
chevron handler updates both layers.

Pre-existing bug, not introduced by this PR's agentic loop changes,
but a clean preview-test workflow needs it gone.
Comment thread src/components/viewer/OpenSCADViewer.tsx Outdated
Reverts 2837d2b + 5e747e7. The preview-only escape hatches stopped
being worth their cost: the preview Supabase project's pre-existing
state issues (missing OAuth, missing adam-billing for the seed user,
billing-products 502s, persisted localStorage panel layout) made the
preview unsuitable for testing the agent end-to-end no matter how many
bypasses I added.

Plan: vibe-merge against master and exercise the agent in prod with a
real account, real OAuth, real billing — none of which need bypasses.

The chat-panel autoSaveId desync fix (9014d4d) stays — it's a real
pre-existing bug that any user could hit, not a preview-test scaffold.
…param changes

Two related multi-file edge cases flagged in review:

1. OpenSCADViewer skipped the entry's WASM-fs write by checking
   `f.content === code` (content equality). That's fragile in two
   directions: (a) two distinct files could legitimately share
   identical content, and one would be silently dropped from the fs;
   (b) when update_file streams a patched entry, the `files` prop
   updates one render before `scadCode` does — so for one frame the
   entry's content in `files` no longer equals `code` and the entry
   would get duplicate-written to the WASM fs alongside the
   compileScad call, producing redeclared-top-level-var errors.
   Pass `entryFile` as a prop and skip BY NAME instead.

2. apply_parameter_changes on a multi-file artifact silently dropped
   `files` and `entryFile` from the new artifact. The viewer would
   then receive `files={undefined}`, skip the aux-file writes, and
   the next compile would fail on `use <chassis.scad>` once the
   WASM-fs cache rotated. Forward both fields, and mirror the patched
   entry content back into the corresponding `files[]` entry so the
   array agrees with `code`.
Comment thread src/hooks/useAgenticVerification.ts Outdated
Comment thread supabase/functions/parametric-chat/index.ts
Comment thread src/hooks/useAgenticVerification.ts Outdated
1. Stale STL race in useAgenticVerification (P1)
   The hook used to capture `outputRef.current` the moment a
   verify_request broadcast arrived. But OpenSCAD WASM compilation is
   async, so when an agent has just streamed a fresh artifact and
   immediately fired view_model the browser would screenshot the
   PREVIOUS model and feed it back to the agent — silently failing the
   core "self-review the latest build" promise.

   OpenSCADPreview now tags every emitted output with the entry
   scadCode that produced it. ParametricEditorView keeps both blob
   and source-code in sync. The hook reads the latest assistant
   message's artifact.code from the messages cache and polls
   `outputCodeRef === expectedCode` for up to 25s before screenshotting.
   If the timeout hits, it broadcasts a clean error chip back to the
   server instead of letting view_model time out at 60s.

2. Silent Realtime subscribe failure in useAgenticVerification (P1)
   `channel.subscribe()` was called with no callback, so CHANNEL_ERROR
   / TIMED_OUT outcomes were silently swallowed and the user stared at
   a 60-second "Inspecting model" spinner before the server gave up.

   Pass a status callback that flips a `realtimeBroken` flag and
   reports to Sentry. Subsequent verify_requests on the broken channel
   immediately broadcast `realtime_unavailable` so the server unblocks
   instead of hitting its silent timeout.

3. Closure-mutation hazard in apply_parameter_changes (P1)
   The handler read `content.artifact?.code` to derive `baseCode` (with
   a messages-cache fallback), then later read `content.artifact?.files`
   / `.entryFile` / `.title` / `.version` independently. If
   `content.artifact` was unset and only the messages fallback fired
   for code, every subsequent `content.artifact?.x` read returned
   undefined — silently dropping multi-file structure on the next
   render.

   Capture the source artifact in a single `baseArtifact` local that
   resolves to either `content.artifact` OR the latest artifact-bearing
   message's artifact. All downstream reads use that one stable
   object.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants