Repository navigation
Verified design loop, Acts I–III: claim basis, trust boundary, predict_physics, macro library - #524
Conversation
Receipt: claims carry an optional basis (predicted|verified|measured; absent = verified for wire back-compat). New basis-aware ReceiptVerdict rollup reads all-pass-but-predicted receipts as provisional — surrogate estimates can steer a design, only verified/measured evidence certifies one. Mirrored in the TS producer (receiptVerdict/effectiveBasis) and regenerated IR types. Commerce: mechanical pre-dispatch trust boundary at the tool-dispatch choke-point. Money-plane tools (quote_manufacturing, authorize_spend, place_order) accept opaque ids only; artifact refs must be store-scoped (vcad hosts, no dot segments); fab-bound free text (ship_to, material, finish) refuses URLs and control characters. Fail-closed, with smuggling tests as CI proof. Contract in docs/trust-boundary.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Choji review — Nothing blocking — a few notes Act III adds the agent macro library (define_loon / call_loon / list_loons / use_loons). The implementation is clean: macros are smoke-tested at definition time, the trust boundary is respected (name validation, RESERVED set, source goes through the existing engine eval path), inline/stateless pass-by-value works correctly, and the disk persistence is appropriately best-effort. No correctness defects found in the changed files. Security
3 minor findings
Verified by ChojiChoji ran your change live — checks failed. 5 checks.
Checks marked environment failed in Choji's sandbox, not against your PR. They never block. No live preview was run — this change isn't exercisable in the running app. All three check failures are attributable to the environment, not the PR's code:
The PR's own stated verification results (Rust tests all green, TS suite 795 passed including 7 smuggling tests and 5 predict_physics tests, tsc --noEmit clean, clippy clean) cannot be independently confirmed here, but none of the check failures point at code the PR changed. The tool-surface fixture update is deliberate and accounted for. No visual or functional regressions can be assessed without a live preview. No issues found in the running app. Was this review useful? Rate the findings → — your thumbs up or down trains Choji on what's worth flagging. Reviewed |
| let url: URL; | ||
| try { | ||
| url = new URL(handle); | ||
| } catch { |
There was a problem hiding this comment.
🟠 Major · Security — HTTP (non-TLS) artifact URLs are accepted from external callers, allowing a downgrade to a plaintext channel
In isAllowedArtifactHandle, the URL check accepts both https: and http: protocols for allowlisted hosts. For localhost and 127.0.0.1 this is reasonable (local dev), but for mcp.vcad.io, vcad.io, and www.vcad.io accepting http: means a caller can supply a plaintext URL that the server will treat as a valid store-scoped reference. Even if the server itself fetches over HTTPS, the boundary check passes the http://vcad.io/artifacts/art_x string through to the handler. Consider restricting production hosts to https: only, or at minimum only allowing http: for loopback addresses.
| } catch { | |
| if (url.protocol !== "https:" && !(url.protocol === "http:" && (url.hostname === "localhost" || url.hostname === "127.0.0.1"))) return false; |
| /** Opaque-id charset: what our own tools mint (uuid/art_/ord_/auth_ …). */ | ||
| const SAFE_ID = /^[A-Za-z0-9._:-]{1,128}$/; | ||
|
|
||
| /** Hosts an artifact_url may name. Everything else is refused. */ |
There was a problem hiding this comment.
🟢 Minor · Correctness — ARTIFACT_HOSTS includes www.vcad.io but the path check only requires /artifacts/ anywhere in the pathname, which could match /not-artifacts/artifacts/evil
url.pathname.includes("/artifacts/") is a substring check, not a prefix check. A URL like https://mcp.vcad.io/evil/artifacts/art_x would pass. Consider anchoring: url.pathname.startsWith("/artifacts/").
| /** Hosts an artifact_url may name. Everything else is refused. */ | |
| return url.pathname.startsWith("/artifacts/"); |
Act II of the verified-design-loop plan: the fast inner loop. Kernel: vcad-kernel-topopt gains a standalone analyze module — static solve on the existing matrix-free voxel-hex FEA (unit-E solve rescaled by the real Young's modulus), returning max displacement, element-centroid von Mises stress, and compliance. Resolution is the fidelity dial; the predict tier floor is 32 because trilinear hexes lock in bending below ~4 elements through the thinnest section (res-20 cantilever reads 2.2x too stiff — documented in code). Validated against Euler-Bernoulli beam theory. WASM: analyzeStaticsBox / analyzeStaticsMesh bindings; engine wrappers + StaticAnalysisSpec/StaticAnalysisResult types. MCP: predict_physics tool. fidelity=predict (res 32, ~100ms) stamps claims basis=predicted -> receipt summary reads PROVISIONAL; fidelity=verify (res 72, same oracle) stamps basis=verified -> clean pass. Optional max_displacement_mm / max_von_mises_mpa limits become physics.static.* claims with predicted/measured quantities. Read-only: box runs touch no session. WASM artifacts intentionally reverted to origin/main (wasm-refresh.yml owns them); PR CI builds WASM from source. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Dismissing prior approval to re-evaluate 934c0e0.
…oons Act III of the verified-design-loop plan: vcad becomes an accumulating library instead of a stateless kernel. A macro is named loon source — [let <name> [fn [params...] ...]] — the exact idiom the stdlib uses, prepended to programs the same way, so no engine or language change is involved. define_loon smoke-tests at definition time (source must compile AND the example-argument call must yield a non-empty scene): only known-good macros enter the library. call_loon instantiates by positional args into a fresh session; create_cad_loon gains use_loons to compose stored macros inside arbitrary programs. Redefinition bumps a version. Storage v1: process-warm registry + best-effort JSON files under VCAD_MCP_STATE_DIR/loon-macros for local/stdio. A per-user Supabase mcp_macros table (hosted durability + receipt-certified macros) is the planned next rung; the MacroStore seam is shaped for it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Act III added: the agent macro library ( A macro is named loon source — Storage v1 is process-warm + local JSON files under Suite: 804 passed (9 new macro tests). WASM artifacts again reverted to origin/main. 🤖 Generated with Claude Code |
Dismissing prior approval to re-evaluate 4b6d9e5.
define_loon now returns the portable macro record ({name, source,
params}); call_loon accepts it as `macro` and create_cad_loon as
`loons` — both win over the warm registry, so macros survive
serverless cold starts with zero server state. Arity checking is
skipped for inline macros that omit params (loon reports mismatches
itself). use_loons and the warm registry remain the convenient path
on long-lived instances.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Dismissing prior approval to re-evaluate b536229.
Migration 036: mcp_macros table — (user_id, name) primary key, params jsonb, source text, RLS mirroring documents, plus a reserved receipt column for the certify_loon rung. WRITTEN BUT NOT DEPLOYED (supabase db push needs explicit confirmation). MacroStore seam (packages/mcp/src/macro-store.ts) mirrors SupabaseSessionStore: PostgREST + service-role auth, user_id always the verified caller. loon-macros hydrates on miss (artifact-store pattern): call_loon/use_loons pull absent names from the cloud, list_loons merges the whole library, define_loon continues the cloud version sequence on cold instances and saves best-effort. Anonymous callers stay warm-only — a macro library is identity-scoped. Fail-soft throughout: no store or a Supabase hiccup never breaks define/call. certify_loon design (verify-tier claims over the parameter range → receipt stored with the macro) documented in docs/loon-macro-library.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
First three moves of the verified-design-loop plan, in dependency order.
1. Receipt claim basis (
vcad-receipt)ClaimBasisenum:predicted | verified | measured. Optional onReceiptClaim; absent = verified so every existing receipt keeps its meaning (wire schema staysvcad.receipt/1).ReceiptVerdictrollup withprovisional: a receipt that would pass but has any claim resting on a surrogate prediction reads provisional, never pass. Fail-closed default (unverifiable).ReceiptSummarygainspredicted_basiscount + basis-awareverdict; TS producer mirrored; IR types regenerated.2. Commerce trust boundary (
@vcad/mcp)Mechanical injection confinement at the single dispatch choke-point, before any handler runs (docs/trust-boundary.md):
order_id/authorization_id/idempotency_key/document_id) — a "part number" from a poisoned datasheet can never be an order argument.art_…ids,/artifacts/…paths (dot-segment traversal refused), or vcad-host URLs only.ship_to/material/finishrefuse URLs, control characters, unbounded fields.TRUST_BOUNDARY:refusals; smuggling tests are the CI proof.3.
predict_physics— two-tier static FEA (the fast inner loop)analyzemodule on the existing topopt voxel-hex FEA (vcad-kernel-topopt/src/analyze.rs) — max displacement, von Mises stress, compliance. Unit-E solve rescaled by the real modulus; validated against Euler–Bernoulli beam theory.fidelity=predict(res 32, ~100 ms) stamps claimsbasis=predicted→ receipt reads provisional;fidelity=verify(res 72) stampsbasis=verified→ certifiable pass. This is the honest two-tier pattern Question: Usage of STEP export and future plans for OCCT dependency #1 exists to support.analyzeStaticsBox/analyzeStaticsMesh+ engine wrappers; MCP tool is read-only (box runs touch no session).Verification
vcad-receipt24,vcad-kernel-sheet131,vcad-kernel-topopt23+1 (release) — clippy-D warnings(own crates), fmt,ir:checkall clean@vcad/mcpsuite 795 passed (incl. 7 smuggling tests + 5 predict_physics tests exercising the WASM path end-to-end); fulltsc --noEmitclean🤖 Generated with Claude Code