Load on new versions via webhooks, render thumbnails from a per-version workspace, and document the architecture - #60
Merged
Conversation
Only a test that needs D1, R2, KV or a workflow pays for a Workers runtime and a migrated database; it says so with a .worker.test.ts name. The suite drops from about 88s to 53s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
A quantity now stays the expression that was typed, (2 + 3) in, through the url, favorites and the insert, and reaches Onshape that way instead of as a canonical base-unit value. Keys are derived from a selection only to address its thumbnail: stored records carry their enumerated values, build issues the values they blame, and the Onshape endpoints take a configuration map. Also fixes: - a favorite's Open document and Copy link dropped its configuration - Restore after cancelling reopened the configuration the menu opened with - favorites never showed the element's own part number, their records missing the insertable's part data - the insert menu header fell back to the default part number when the matching record had been folded away as a search duplicate Old rows upgrade on read (configurations/legacy.ts). The search index moves to a versioned key and is rebuilt on a miss, and immutable API urls carry a response-shape suffix so cached responses in the old shape are not reused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
The per-user Durable Object queue, with its preemption, poll schedule and invalid-key memo, is gone. On a miss the route resolves Onshape's thumbnail id once and starts a RenderThumbnailWorkflow named by a hash of what it renders, handing it that id and both R2 keys; later polls find the same instance and spend nothing. The workflow only asks for the bytes until they land and stores them. A configuration Onshape cannot resolve answers 422 at once, before any workflow starts. wrangler.jsonc deletes the ThumbnailRenderer class in a v2 migration, which discards its stored queue. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
On a narrow panel a long label squeezed its Select to a sliver, and the dropdown, as wide as that sliver, wrapped every option onto several lines. Labels now share a capped column, so controls line up and a long label wraps instead; a dropdown is at least as wide as its input and at most as wide as the screen. The get-app callout's button wraps under its text rather than running off the edge, and clicking into a quantity keeps the whole expression selected in browsers whose mouseup would drop it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
CSS values that were passed around as inline style objects (borders, the frame background, flex-shrink, the title icon nudge, the modal frame) are classes now: shared ones in lib/styles.module.css, one component's in its own module. Tooltip, HoverCard and Card take their repeated props from the theme, so a call site names only what differs; Menu and Select stop restating Mantine's own portal default. style-constants.ts keeps only values handed to Mantine props. Also drops a wrong comment in AppMenu: Mantine fills in flip and shift itself when middlewares names only size. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
A jsdom project runs *.test.tsx with Testing Library. The first tests drive the configuration panel (a typed expression is kept and shown evaluated, conditions show and hide rows, the record follows the selection) and the document links a favorite's menu builds. They caught a crash: an angle typed into a length box threw out of the range check instead of being reported. The parser now rejects a result whose kind is not the quantity's, with a message saying which kind it wanted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
…e trip The probe tests needed the Workers runtime only because the module they test also held its workflow-step variant; that lives with its one caller now, and the limiter in lib/, so both test files run in Node. resetDb, which runs before nearly every worker test, deletes in one batch instead of sixteen round trips. Adds end-to-end checks that an insert hands Onshape the expression that was typed, in a derived feature and in an assembly's configuration. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
Onshape sometimes never renders an element's thumbnail in a version, and the document's own workspace, which the load fell back to, drifts from the version the library shows. A load now finds or branches a workspace off the version (named by it, so a retried step or forced reload reuses it), reads every thumbnail from there with no fallback, and stores its id on the group beside the version. Configured renders and the on-demand reload read from the same branch; a group no load has branched yet falls back to the version for renders, and a reload branches one. A fresh branch takes minutes to render, so thumbnail steps retry for about a quarter of an hour, under a limiter of their own so the wait never holds a probe's slot. Once a group moves to a new version, the branches of older versions are deleted, best effort. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
Every branch now shares that name, so the version it came from is recorded in its description, which is what finding it again matches. Also tightens this change's comments: the thumbnail limiter's was wrong (a waiting step holds its slot), the branch sharing the version's microversion is marked as an assumption, and comments restating the code are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
A load recomputes every build issue, so an issue that named its offending configuration by key only needs to last until the next one. It still reads and renders; it just has no configuration to link to until then. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
Onshape's exclude-from-properties flag no longer decides what indexing varies. A parameter is indexed unless its name marks it as never worth varying (a derivation variable, a color or its R, G and B channels together, a tessellation setting) or an admin excluded it with the new per-parameter switch on the build card. Exclusions are user-owned, kept across reloads, and part studios only: Onshape lets no assembly exclude parameters from its properties either, so an assembly indexes every one it can. Assemblies now index like part studios: automatically under the threshold, and past it once an admin enables indexing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
Parameters recognized by name (derivation variable, color and its R/G/B channels, tessellation) now carry a ParameterRole, shown with an icon in the build status tooltip. A text derivation variable is read-only in the insert menu and filled with a fresh UUID on every derive, since Onshape refuses to derive the same part and configuration twice. It is left out of thumbnail keys, analytics values and saved favorites. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
The role and the fill-in behavior now come from one check, so a non-text parameter named for derivation is an ordinary parameter everywhere rather than never indexed yet still editable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
Links are built on the launch's server origin, so a company session opens frcdesign.onshape.com rather than cad.onshape.com. Only an https onshape.com origin is accepted, since the launch is a url anyone can write. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
A verbose row's thumbnail and status badge gave up width to its title, so they drew smaller than the next row's, most visibly when zoomed in. Only the text shrinks now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
Each header sticks below the ones above it, so favorites and the library stay in reach however far the list is scrolled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
Its options (skipDocumentD, endRoute, endId, featureId) obscured what each call site asked for. addAssemblyFeature loses the update-by-id parameter nobody passed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
parse.ts exports only what routes use, and its tests go through those. readThumbnailUrls had no callers and is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
Headers now stick within their own section, so the next one pushes the last off rather than stacking beneath it, and a section showing a zero state does not stick at all: a header stuck over one only half-covered it on the way out. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
Mantine's HoverCard opens only on the mouse events a tap emulates, and lets the tap through, so on a phone the same tap opened the build-status card and the row's insert menu. AppHoverCard opens on hover and on a click or tap, which pins it; the click, the card's own clicks and the tap that dismisses it (which lands on an invisible overlay) are kept from the row. It replaces every HoverCard: build status, thumbnails and the insert location. Tooltips now show on touch as well. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
Adds an owner access level above admin, granted to the Onshape user OWNER_USER_ID names; the owner's latest session is kept for work the server starts on its own. The owner registers a company webhook from settings. A new version of a library document reloads just that document's groups under the owner's session, queued behind any reload already running; a member added to or removed from the admin team clears every cached access level. Deliveries are checked against a token minted at registration. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
A LiveUpdates Durable Object holds every open client's WebSocket, hibernating, tagged by the library it shows. Jobs starting and finishing, a background load bumping a library's version, a configuration's render landing and an admin team change are pushed through it. Clients apply them to the query cache: job status is no longer polled, and a render waits for its push instead of asking every two seconds. While the socket is down, both fall back to polling as before, and a reconnect refreshes the library for what it missed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
Whenever the owner's access level is resolved, the registration on record is checked in the background and replaced if Onshape no longer has it; an unregister notification forgets it, so the owner's next visit registers anew. The register button, its route and the owner-only guard it needed are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
Loads: - LoadDocumentWorkflow replaces LoadLibraryWorkflow and AddGroupWorkflow. Adding a document writes its shell group and asks for its load; the owner's "reload everything" asks for one per group. - load_jobs in D1 keeps one load per group running; one asked for meanwhile is marked on the row and started as the running one ends. The last load in a library rebuilds its search index. - Each load registers its document's version webhook (isTransient false), removed with the document's last group. A new version reloads that document's groups under the owner's session. - Thumbnail reconciliation moves to a daily cron. Access: - The owner sets each library's admin team. Its members are stored in admin_team_members and pulled again on the team's webhook, so access is a per-library lookup; ADMIN_TEAM and the KV access cache are gone. - A membership sync bumps the library version, which is how clients learn their access changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
Each load that wrote to its group now rebuilds its library's search index before bumping the version, rather than leaving that to the library's last load, so every load stands alone. Reload everything starts every load at once and returns. OWNER_USER_ID is set in every environment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
- Roles are recognized once as a document is loaded and stored on the parameter; indexing, keys and the UI read parameter.role instead of matching names again. The name folding lives with the one detector. - legacy.ts is gone: a force reload rewrites records and parameters in the current shape. A favorite saved in base units stays valid, shown in meters until saved again. - searchRecordsOf(row) replaces toRecords then toSearchRecords at every call site, and the search index is built from those records. - UnitInfo's fields are required. useUnitInfo resolves the document's units, or a placeholder (inches, degrees) outside a document or when they fail to load, so nothing downstream merges defaults. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
…eir own units - Job status is the set of groups loading, pushed as it changes; a group's row, its parts and its card spin only while it loads. The navbar still shows anything loading in the library. - No polling fallback: job status comes from pushes alone, and a render waits for its push, asking once more at its deadline. A reconnect still refreshes the library for what it missed. - Each quantity box reads the document's units itself, from the one cached query, and shows its own unit until they arrive or outside a document, so the panel no longer waits on them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
Renders store both sizes in parallel, so RenderSource no longer orders anything: insertableId alone asks for a render. The render workflow's instance is named after Onshape's thumbnail id rather than a hash, and a configuration Onshape has no part for throws a handled 422 from requestRender instead of threading an outcome string through the route. AGENTS.md now prefers undefined to null, with null kept at boundaries and where it means "clear"; a pass converts the internal nulls. The settings defaults shrink to DEFAULT_THEME, the only one read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
AGENTS.md now defaults to no comment, one or two lines when there is one, no history or alternatives not taken, and no hedging where the answer can be checked. Comments across the codebase are cut to match, and stale ones corrected. AppHoverCard is Mantine's HoverCard where the device can hover and a Popover the target opens on a tap where it cannot, replacing the hand-rolled timers and pin state. useCloseHoverCard goes with it. Also: - The access-level KV cache nothing wrote is removed. - InsertOut.featureId is optional, and a part-studio derive no longer reports a Fasten mate it didn't make. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
…iguration Onshape answers a thumbnail it hasn't rendered with a stand-in unless told not to, and the by-id endpoint renders used could answer with another configuration's image; either way the default was stored, immutably, under the configuration's key. Renders now fetch the documented configured endpoint on the thumbnail workspace with skipDefaultImage, rejectEmpty and requireConfigMatch, and element thumbnails ask for rendered images only. A forced reload also drops a document's configuration renders, clearing any bad ones. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
… included The hover card's Indexing section holds the enable switch, the indexable configuration count and a labeled switch for each enum or boolean parameter. Assemblies are enumerated through the same probe as part studios, so their parameters can be excluded too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
Resolves the id from the group's thumbnail workspace and fetches it from the by-id thumbnail endpoint, with no query flags; element thumbnails drop them too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
A group without a thumbnail workspace starts no render until its next load branches one, rather than falling back to the version. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
… seconds registered_at, last_delivery_at and last_event were missing from databases that applied 0004 before they were folded in, which broke registration; Onshape's own webhook info and the logs cover what they recorded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
Restores the configured render's 5-second polling. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
…key's shape suffix A local origin registered a webhook Onshape cancelled at once, so every load registered another. A new webhook is pinged, so its delivery in the logs shows Onshape can reach it. The ?v= cache key is the library version alone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
A tunnel gives the dev server a fixed public https hostname, so the Onshape OAuth app's urls are set once and webhooks can reach it. DEV_HOSTNAME lets Vite accept requests for it; npm run tunnel runs it beside npm run dev. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
One dashboard-managed tunnel serves the dev server at a fixed hostname, so developers need its token, not access to the domain. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
…nnel token Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
The post-registration ping, the localhost skip and the per-registration and per-delivery logs were for tracking down unreachable webhooks. Self-healing stays, with a warning when it re-registers and one for unknown tokens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
The access-level override answered before the admin's session was kept, and an overridden admin is on no team, so a load nobody is signed in behind found no session. The override now keeps it under a reserved id that the lookup tries last. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
Titles state the problem alone, in one style; descriptions say why it matters and how to fix it, naming the controls that do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
…keeping them current docs/architecture/ documents thumbnails, configurations, loading, favorites and auth: flows, storage, invariants, failure modes and decisions. AGENTS.md says when a change must update them, and npm run check:docs, run in CI, fails on a doc naming a file that no longer exists. REFERENCE.md now links to them instead of repeating them, and the stale paths the check found in GUIDE.md and REFERENCE.md are fixed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
Element and thumbnail editing routes take the library in the path, so the editor and admin middlewares always check the path's library and each handler scopes its query to it. The owner check always asks about the default library, since the owner's access is the same in all. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
…default inline Onshape takes a repeated derive, so the server no longer refills derivation variables; the menu gives one still at its default a unique value. The one case needing a non-empty configuration, a part studio on its defaults, now names its first parameter directly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
…clude nothing yet Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
…rivation filling Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
Each environment declares the url it is served from. The authorize request names its callback there, webhooks point there rather than at whichever request happened to start the load, so loads no longer carry an origin, and the dev server accepts its host. DEV_HOSTNAME is gone; APP_URL in .env overrides the dev value. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
…thumbnails at 30s-4m The stored thumbnail route only serves; a client that misses a configuration and can wait asks POST /api/render-thumbnail/insertable/:id, which answers rendering or no-part, rather than an optional query parameter on the image url starting the render. A render is keyed by the insertable's current microversion. Load thumbnails retry after 30 s, 1, 2, 4, 4 and 4 minutes. Docs: insertable thumbnails, why stale workspaces are deleted after the save, the session each load uses, and worked examples of how search reads and scores a query. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
…s' sessions serve Any admin team member's session can now carry a load nobody is signed in behind: the owner's first, then team admins', then editors', then the dev override's. Sessions saved under the old admin-session prefix are left to expire; each member's is saved again the next time they open the app. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
…toasts, pushes, caching A new platform.md gathers what every feature shares, and loading and thumbnails gain per-step tables of the Onshape calls made and the retry policy each runs under. The query client's comment now matches its two retries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
A group in another library can stay on an older version, held for approval, and still read renders from that version's workspace; cleanup deleted it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
cert's 0004_configuration_instance_key takes 0004, so this branch's migration is regenerated on top of it as 0005_library_loading, with the same statements. Tracking keeps running in the background and passes the part's parameters to the rollups. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This branch reworks most of the app: how documents load, how thumbnails render, how configurations are stored, auth and access, the stores behind the SPA, and the docs.
docs/architecture/now describes each area as it stands. It is the best place to review against.Loading and webhooks (loading.md)
APP_URL.background-sessions.ts), tried in order: the owner, then team admins, then editors, then the dev access-level override.THUMBNAIL_FAILED.drizzle/0005_library_loading.sql, on top of cert's0004_configuration_instance_key.Thumbnails (thumbnails.md)
FRCDesignApp Thumbnails (DO NOT EDIT).POST /api/render-thumbnail/insertable/:insertableId, and return aRenderStatusofrenderingorno-part.RenderThumbnailWorkflowby predictable thumbnail id, and are pushed to clients when stored.GET /api/thumbnail/...) never starts a render.Configurations (configurations.md)
Selection(values as they were entered) or aConfigurationKey(which only names thumbnails). Both are built inselection.ts.Auth and access (auth.md)
libraryOfis gone.redirect_uribuilt fromAPP_URL.SESSION_SECRETandVERBOSE_LOGGING.Frontend
Dev setup and tooling
dev.frcdesign.orgthrough a shared named Cloudflare tunnel (npm run tunnel, token in.tunnel-token). This replaces mkcert.*.worker.test.ts) run in the Workers pool. The rest run in Node.docs/architecture/has one document per area.AGENTS.mdsets the rules for keeping them current.npm run check:docs, run in CI, catches stale paths.Testing
npm test(Node, jsdom and Workers projects, 934 tests),npx tsc -b, lint andnpm run check:docspass on the merged head. Webhook-triggered loads were exercised against Onshape through the dev tunnel.🤖 Generated with Claude Code
https://claude.ai/code/session_01HDekeqMAagMXf7o9zF2SxJ