Store rich configuration records and auto-index by heuristic - #41
Open
AlexKempen wants to merge 17 commits into
Open
Store rich configuration records and auto-index by heuristic#41AlexKempen wants to merge 17 commits into
AlexKempen wants to merge 17 commits into
Conversation
AlexKempen
force-pushed
the
claude/auto-indexing-rebase-9vw1ca
branch
2 times, most recently
from
August 17, 2026 02:44
d95f01f to
7733bf6
Compare
Replace the part-number map with a ConfigurationRecord per probed
configuration (part number, name, description, material, vendor), so
search, the UI, and future build checks can read what each configuration
produces without re-querying Onshape.
- Unify part-studio (/parts) and assembly (element metadata) probing into
a single record parser; assemblies now read the metadata property bag
in one call instead of the assembly definition.
- Enumeration skips isCosmetic ("exclude from properties") parameters.
- Index automatically when an insertable has a non-custom vendor and fewer
than 100 non-cosmetic configurations; the former search toggle becomes a
force-index override. Vendor parts over the auto line raise a new
MANY_CONFIGURATIONS warning; 512 remains the hard cap.
- Search derives its part-number map from records (first-wins over
enumeration order keeps the latest revision).
- Migration 0004 renames search_part_numbers -> force_index, drops
default_part_number, and replaces configurations.part_numbers with
records.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XT52xco8DfQnh1iMGff1gF
Add a Configuration section to the editor build-status hover card that lists each configuration parameter with its type and whether it is excluded from properties (isCosmetic) — the flag that keeps a parameter from multiplying the indexed configuration count, so editors can see at a glance which parameters to mark cosmetic to bring a part under the auto-index line. Also fix the "Configurable" state row to key on the parameter count rather than on a configuration row existing: an indexed non-configurable insertable now carries a row that holds only records, and would otherwise report as configurable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XT52xco8DfQnh1iMGff1gF
Extend search beyond part numbers to the configuration records' part names, surface the matched part number + name in results and (live) in the insert menu, normalize numbers/fractions, and move the search index out of D1 into R2. - Index part names alongside part numbers. `SearchDocument` now carries a `partNames` field and a stored `records` list (`toSearchRecords` dedupes to distinct (part number, name) pairs). A hit picks its single best-matching record (`findBestRecord`, exact -> prefix -> substring) by part number or name, falling back to the default record for a plain title match, and annotates the hit with that record's number/name/config. - Canonicalize numbers in `tokenize`: fractions, mixed numbers, and decimals collapse to a 2-dp decimal at both index and query time, so `.5`, `1/2`, and `0.50` all match. Thread specs (`10-32`) and part numbers (`217-2600`) are left untouched. `normalizeForMatch` keeps the record lookup in step with what the index matched. - Show the part number + name on result rows (a dimmed secondary line) and live in the insert menu, where `findRecordForConfiguration` maps the selected parameters to their record. `/configuration/:id` now returns the insertable's records for this. - Move the serialized MiniSearch index from the D1 `libraries.search_db` column to R2 (`SEARCH_INDEX` bucket), stored gzipped and streamed with `Content-Encoding: gzip`; removes the D1 per-value size ceiling as the index grows with record count. Migration 0003 drops the column. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATK594PtgVSufvSHa24VVp
Prefix search matches a whole indexed term from a shorter query — typing "mot" matches "motor" — and highlighting underlined that entire term, so a match was all-or-nothing. Use MiniSearch's `queryTerms` (what was typed) alongside `match` (the document terms that matched) to underline just the longest query term each matched term starts with. Searching "maxsp" now underlines "MAXSp" of "MAXSpline" rather than all of it; an exact match still underlines the whole term, and overlapping ranges are merged as before. Also escape terms used to locate matches: they can now carry regex metacharacters (a canonicalized "1.5" would otherwise also underline the "125" in "1.5 x 125 Spacer"). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATK594PtgVSufvSHa24VVp
Thumbnails are the slowest thing we ask Onshape for — a render can need polling and take minutes — and until now only one existed per element, its default configuration. A favorite pinned to a configuration and a search hit that matched one both showed the default image. Configuration thumbnails are now produced at runtime and stored in R2, addressed by a canonical configuration so equivalent selections share one object. - Add canonicalizeConfiguration: drops parameters hidden by a visibility condition and values matching the parameter default (Onshape applies those anyway, so an all-defaults selection reduces to the default thumbnail), normalizes quantity expressions through the evaluator, and emits in declaration order rather than object-key order. input-parser moves to src/shared so both sides can evaluate expressions. - New R2 key scheme splits `thumbnails/default/` (never expires — it is what everything falls back to) from `thumbnails/config/`, which carries a ~90-day lifecycle rule. Keys pin the microversion, so objects are immutable rather than overwritten in place. - The serving route falls back to the default thumbnail for a configuration we haven't rendered, cached briefly so the real one can take over; `warm=1` also starts a ThumbnailWorkflow, whose instance id is derived from the configuration so concurrent requests for the same thumbnail collapse onto one render. The insert menu's live proxy stores what it already proxied, warming the cache at no added latency. - Rows and hovers now render the same configuration. Favorites and the insert menu warm eagerly; search rows only serve what is cached, so a cold search can't start a render per row. - Replace the generic thumbnail_urls JSON map with explicit small/large columns, and trim ThumbnailSize to the two stored sizes (the unused 600x340 is gone; the insert preview moves to the stored 300x300). Migration 0004. - Fix an unguarded items[0] in getThumbnailId (a configuration matching nothing threw a TypeError) and a per-size try/catch that never caught, since it returned the promise without awaiting it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATK594PtgVSufvSHa24VVp
Follow-up on review. Three things. Records now store their canonical configuration, computed at parse time where the parameters are already in hand. Previously each surface encoded its own configuration as-is, so a thumbnail warmed from the insert menu was not the one a search row asked for. `enumerateConfigurations` omits cosmetic, quantity, and string parameters while the insert menu holds the user's whole selection; canonicalizing both drops the values left at their default, and what remains differs only when the render genuinely does. The insert menu and favorite editor get the canonical form from ConfigurationWrapper, which is the only place with both the parameters and the document's units, and a favorite now stores it — Onshape applies defaults for whatever is omitted, so it inserts the same thing. `canonicalizeConfiguration` takes `unitInfo` optionally, since it is only needed to evaluate quantity expressions and enumerated records never carry them. Image fetches no longer mint object URLs. `handleImageResponse` created one per fetch and nothing ever revoked it, so every thumbnail — and every refetch of one — held a Blob until the page unloaded. The query now validates the URL and returns it, which both keeps the existing loading, error, and retry behavior and leaves the response in the browser cache for the `<img>` that follows. Revoking was not an option: a cached query can share a URL across rows, so freeing it on one unmount could blank another. Also revert the REFERENCE.md edit; the R2 lifecycle note belongs in the PR. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATK594PtgVSufvSHa24VVp
The card listed each parameter's type but not the thing an admin is actually looking for: which parameters drive the configuration count, and so which ones to exclude to bring an insertable under the auto-index threshold. - Each parameter now carries an Indexed / Not indexed badge whose tooltip gives the reason: it is varied, it is excluded from properties, or it is a quantity or text parameter, which are never varied. - Extract `isIndexedParameter` and use it inside `enumerateConfigurations` rather than duplicating the rule, so what the card reports cannot drift from what enumeration varies. A test pins the two together. - An enum's type badge shows its option count and lists the options on hover, which is where its share of that count comes from. - Rename the section to Configurations and give both badges the same size/variant; the type badge was the odd one out. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATK594PtgVSufvSHa24VVp
**Insert menu header.** The part number and name the selected configuration produces moved out from under the preview image and into the modal's header, under the element's name — all three are shown, since the element name is how the part was found and the part number and name are what gets inserted. The header is updated rather than rendered, so it follows the selection as the configuration changes. ConfigurationWrapper reports the matched record upward instead of rendering it, and the favorite editor gets the same header rather than losing the display. **Insert target.** Both insert routes took the tab being inserted into as URL path segments, reassembled with non-null assertions and an unchecked cast to the instance type. A missing or unrecognized piece became a nonsense Onshape URL and an opaque failure rather than a complaint. They now take the target as one typed object in the body, validated at the boundary, and the path guards check the instance type against its literals rather than trusting whatever arrived. **Stale version ids.** A tab whose microversion hasn't changed is skipped by a reload, so it never reaches saveInsertable and kept the version id it was last loaded at, while the group row advanced to the new one. The geometry is identical, but that id is what insertion and every document link are built from, so a skipped insertable now moves forward with its group. Also give the stored search index its content type back: it is served straight from R2 via writeHttpMetadata, so dropping it from the put left the response untyped. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATK594PtgVSufvSHa24VVp
Replaces the hand-rolled target-path guard with a zod schema on each route, matching how the group and thumbnail routes already validate. The whole body is checked, not just the path: the flags get explicit defaults rather than arriving as undefined, and a bad request is rejected before any of the handler runs. `InstanceType` and the runtime list validators check against are now one `as const` definition, so the schema's enum can't drift from the type. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATK594PtgVSufvSHa24VVp
The row shows the matched configuration's part number and name under the title, but only the title was underlined — a query that hit a part number gave no indication of where. `generateHighlightPositions` now takes the text and field to search rather than assuming the document's name, so a hit carries positions for the part number and name alongside the title's. `HighlightedText` is split out of `SearchHitTitle` so the detail line can render each part with its own matches, and the two are joined as elements rather than a string. A title-only match underlines nothing in the detail line: the record shown there is the default one, which the query never matched. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATK594PtgVSufvSHa24VVp
The two buckets held disjoint key prefixes and were never addressed together, so a single BLOB binding costs nothing and halves the provisioning. Lifecycle rules are per-prefix, so thumbnails/config/ can still expire while thumbnails/default/ and search-index/ do not. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATK594PtgVSufvSHa24VVp
The hardcoded urls predated the microversion query param, so the fixture no longer looked like anything uploadThumbnails returns. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATK594PtgVSufvSHa24VVp
The thumbnail workflow authenticated with an empty session id, so every warm render 401'd before reaching Onshape; it now carries the requesting session like the other two workflows do. Boolean and string inputs read the raw configuration value, which canonicalization now omits whenever it equals the default, so a default-true checkbox rendered unchecked. Both fall back to the parameter default, as the enum input already did. Also gates the favorite save on a reported canonical configuration, rebuilds the search index before bumping the version so an immutable url cannot pin a stale index, and restores includeComputedProperties on the metadata probe indexing runs per configuration. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATK594PtgVSufvSHa24VVp
Instance ids are unique for the lifetime of the account, so deriving one from the configuration meant a single failed render blocked that configuration forever. Runs now take a generated id and skip a render that is already stored — checked before calling Onshape and again before writing, since a render takes minutes. Two concurrent runs for one configuration are possible and cost a duplicate render at worst. Element resolution now matches on the microversion as well, so it stays correct if a tab ever backs more than one insertable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATK594PtgVSufvSHa24VVp
Matching the microversion as well was wrong: the stored value is the current one and the url carries whatever the client last loaded, so any reload made them diverge and resolution found nothing. The microversion is a reload marker, not part of the element's identity. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ATK594PtgVSufvSHa24VVp
AlexKempen
force-pushed
the
claude/auto-indexing-rebase-9vw1ca
branch
from
August 18, 2026 23:59
b51f684 to
f572cea
Compare
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.
Replace the part-number map with a ConfigurationRecord per probed
configuration (part number, name, description, material, vendor), so
search, the UI, and future build checks can read what each configuration
produces without re-querying Onshape.
a single record parser; assemblies now read the metadata property bag
in one call instead of the assembly definition.
than 100 non-cosmetic configurations; the former search toggle becomes a
force-index override. Vendor parts over the auto line raise a new
MANY_CONFIGURATIONS warning; 512 remains the hard cap.
enumeration order keeps the latest revision).
default_part_number, and replaces configurations.part_numbers with
records.
Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01XT52xco8DfQnh1iMGff1gF