feat(parametric): agentic verify ↔ refine loop with screenshot self-review#141
feat(parametric): agentic verify ↔ refine loop with screenshot self-review#141
Conversation
…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.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Updates to Preview Branch (claude/cadam-agentic-agent-SAqQl) ↗︎
Tasks are run on every commit but only new migration files are pushed.
View logs for this Workflow Run ↗︎. |
Greptile SummaryThis PR makes parametric mode fully agentic: after generating OpenSCAD code, the assistant calls a new
Confidence Score: 5/5The 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 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
Sequence DiagramsequenceDiagram
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
Reviews (17): Last reviewed commit: "fix(parametric): three P1s on the agenti..." | Re-trigger Greptile |
There was a problem hiding this comment.
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.
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.
There was a problem hiding this comment.
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.
- 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.
There was a problem hiding this comment.
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.
- 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.
There was a problem hiding this comment.
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.
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.
There was a problem hiding this comment.
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.
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).
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.
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.
There was a problem hiding this comment.
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.
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.
|
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 |
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.
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.
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.
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`.
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.
Summary
Makes parametric mode actually agentic — same shape as the Fusion extension. After generating code, Adam now calls a new
view_modeltool to request rendered screenshots from chosen angles (named views likeiso/front/topor arbitrarycustomaz/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 callsbuild_parametric_modelagain with a fix description or confirms completion. The whole loop runs without user intervention.How it works
Capped at 3 verification rounds via a shared
MAX_AGENTIC_ITERATIONSconstant; once hit, the server stripsview_modelfrom the toolset and appends a system note telling the agent to finalize.Files
supabase/functions/parametric-chat/index.ts— newview_modeltool definition + handler that records the requested views as apending_verificationtool call; iteration counting on the branch with cap-driven tool/prompt swap.shared/types.ts— extendsToolCallwithviews/reasoning/screenshots/two new statuses, andContentwithisVerification/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 fulfillspending_verificationwith screenshots; Path B nudges the agent after a fresh artifact lands. UsesinFlightRef+ a process-wideACTED_ONset so it never double-fires for the same turn.src/views/ParametricEditorView.tsx— wires the hook in.src/components/chat/AssistantMessage.tsx—ViewModelToolCallCardchip showing inspecting/verified/error states with screenshot thumbnails.src/components/chat/UserMessage.tsx— verification user messages render as a compactVerificationChipinstead of a normal bubble.Test plan
view_model, renders show up in chat, agent either confirms or refines.view_modelshould disappear from the toolset and the agent must finalize.pending_verificationflips toerror, no zombie spinners.tsc -p tsconfig.app.json --noEmitis 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
.scadassemblies and anupdate_filetool for fast per-file fixes.New Features
view_modeltool withpending_verification/verifiedstatuses,views/reasoning/screenshots; server caps at 3 rounds and then strips the tool.useAgenticVerificationlistens onverify-conv-{conversationId}, uploads PNGs, and resumes the loop; assistant shows an inspecting/verified/error chip with thumbnails.// === FILE: <name>.scad ===markers; server streams per-file chunks;OpenSCADPreviewacceptsfilesandentryFile, writes aux files to the WASM fs (skips the entry by name) souse <file.scad>resolves; chat pill shows a " files" badge.update_filetool for targeted edits to one.scadfile: 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
camera.up; drop the no-op PNG quality arg.view_model: throw whengetSignedUrlsreturns none; usesignedUrls.lengthfor the attached count.sideview; fix a stray bracket in thesupabaseedge bundle; wait forSUBSCRIBED; guard subscribe failures; add subscribe-status handling and fast-failrealtime_unavailable; clean abort/timeout; avoid leaks; convert inner async fns to const arrows.DataViewinto anArrayBuffer-backedUint8Arrayviaset(...); fail fast on non-DataView; large exports are faster and safer.files/entryFileon parameter changes; use a singlebaseArtifactsource to avoid dropping fields.view_modeltime out.Written for commit 0f8071a. Summary will update on new commits.