Skip to content

Generated fasteners for FRC and FTC, part sets, and the Onshape workflow - #24

Merged
AlexKempen merged 87 commits into
mainfrom
claude/zealous-shannon-e0iklk
Nov 8, 2026
Merged

AlexKempen merged 87 commits into
mainfrom
claude/zealous-shannon-e0iklk

Conversation

@AlexKempen

@AlexKempen AlexKempen commented Nov 8, 2026 •

Copy link
Copy Markdown
Member

What this adds

  • Part sets ([tool.fs.parts.*], fs parts SET --test|--release|--diagnose): FeatureScript-backed Part Studios, each configured by a definition. A release builds them in FRCDesignLib's documents, with a manifest (<set>.parts.json) that FRCDesignApp reads. See docs/parts.md.
  • Fasteners (featurescripts/fasteners/): screw, nut and washer features, with tables generated from fastenerTables.py.
    • FRC's imperial set, fasteners, goes into FRCDesignLib.
    • FTC's metric set, ftcFasteners, goes into ConfigLib. It covers Generic, McMaster-Carr, goBILDA, REV and AndyMark, in M3 and M4.
    • Both sets share one Feature Studio. A hidden configuration input resolves within its own set (resolveChoices).
  • Branch workspaces and fs sync-main: each branch pushes to its own Onshape workspace, and main is landed in the main workspace by merge.
  • fs push: files are now pushed after the files they import, so their import versions stay in sync. fs parts refuses to build while Onshape holds import versions the local files lack.
  • Workflows:
    • checks.yml runs on pull requests.
    • onshape.yml runs on each push to main: fs sync-main, then fs run-releases, then a push of what they commit.
  • Release files:
    • releases/fasteners.toml releases v1.0.0 of fasteners.
    • releases/ftc-fasteners.toml releases v1.0.0 of ftcFasteners.

Merging this runs Onshape

This PR brings onshape.yml to main, so merging it starts the workflow. The run spends the API allocation of the account in ONSHAPE_ACCESS_KEY/ONSHAPE_SECRET_KEY.

Each release:

  • Creates a version in the backend document.
  • Builds the Screw, Nut and Washer Part Studios, plus the manifest, in its library document's main workspace.
  • Does not version the library document. Its owner does that by hand after checking the Part Studios.

Before merging:

  • The two secrets must be set.
  • github-actions[bot] must be allowed to push past main's branch protection.

Tests

uv run pytest -q: 942 passed. fs check and fs releases are clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS

claude and others added 30 commits October 21, 2026 04:02
…for FRCDesign

quickFastener/quickFastenerTables.py is the one source of FRCDesign's Quick
fastener catalog: FRC (#6-32 to 1/4-20) and FTC (M3, M4) screws in socket,
button and flat heads, flat and wide washers, and nylock and hex nuts. fs gen
writes the features' enums and dimensions, and quickFastener.gen.json, the
manifest FRCDesign pins to a tag and reads its catalog, configurations and hole
scan from.

Each part is a Part Studio exposing its feature through a List input per
parameter, Length showing only the lengths the chosen size comes in.
fs configure-studio writes that configuration and configures the feature by it
(fs_cli/configured_studios.py), so the FeatureScript and the configuration
can't drift. A screw's and nut's Washer input moves only its one mate connector
onto the washer it sits on, so FRCDesign fastens every part to the hole's own
edge without mate offsets.

quickFastenerHoleScan.fs is the lambda FRCDesign evaluates in a Part Studio to
find its parts' holes and the selected circles; its body is copied into the
manifest, so it keeps to std's common.fs.

Definitions can now set EXPORTS, files fs gen writes beside them.

Untried in Onshape: the features' geometry, the configuration payloads, and the
hole scan's evaluation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
FRCDesign fastens a nut, or a screw into a blind hole, to the edge at that end
of the hole, so it needs the edges' ids.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
The screws, nuts and washers are now the ones FRCDesign's FRC library already
offers (vendor/frcdesign-fasteners.json, a snapshot of its configurations), with
their part numbers: McMaster-Carr, REV and WCP black oxide screws from #4-40 to
1/4-20, McMaster-Carr's nuts, and black washers, plus Generic, which offers
every length anyone makes. Each studio has a Vendor input.

Length is an enum of the 49 standard lengths, offered only where the chosen
vendor makes that size and head; the feature errors on anything else. Washer
is a checkbox, which moves the mate connector onto the vendor's seat washer.

Configured studios can now have checkbox inputs, and show a List's options by
combinations of several earlier inputs.

FTC comes out until its list is decided.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
…est, library releases

The screw and nut studios lose their Washer input and the seat washers
behind it: FRCDesign fastens a screw or nut to the top edge of the washer
it inserts under it, so any washer fits and the mate connector stays at
the origin.

The manifest carries each studio's configuration as Onshape answers it,
so FRCDesign parses it with the parser it uses on any configuration. List
visibility conditions are factored by input, which takes the screw
studio's from 192 KB to 61 KB.

`[tool.fs.libraries]` names library documents: the FeatureScripts in
their folders are released to the library's default workspace instead of
the frontend document, may define several features, have no betas, and
are left out of `fs sync-versions`. Quick fastener releases to FRCDesign's
Fasteners library.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
Run in Onshape, the hole scan failed whenever a face or circle was
selected: `transientQueriesToStrings` names only transient queries, and
`qOwnerBody(edge)` isn't one, so it came back as a map where a string was
declared. The scan now evaluates the owner body first.

Also tried in Onshape: every kind of screw, washer and nut regenerates
with its mate connector at the origin, and the scan reports through and
blind holes as the planner expects.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
FRCDesign's Fasteners group has only #10-32 screws from WCP, one of them
under a part number WCP no longer lists (its 3/4 in. button head is now
WCP-0135). WCP's and AndyMark's screws now come from their Shopify
stores' product lists, snapshotted in vendor/stores.json and refreshed by
`python quickFastenerTables.py fetch`, replacing the group's for those
vendors: WCP's #8-32, #6-32 and #4-40 screws, and AndyMark's #4-40 to
1/4-20 socket, button and flat heads, each with its part number and page.
A store screw without a thread lock patch is preferred where both are
sold. AndyMark is a new vendor; 2-1/8 in. a new length.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
…eners

They're the library's new fastener, which Quick fastener inserts too,
not Quick fastener's own: the folder is `fasteners/` (`fasteners.fs`,
`fastenerTables.py`, `fasteners.gen.json`), the features `fastenerScrew`,
`fastenerWasher` and `fastenerNut`, named Screw, Washer and Nut. The hole
scan keeps its Quick fastener name, since only Quick fastener evaluates it.

The writeup now says what the dialogs already are: flat enums with no
conditions, wired one for one to configuration inputs whose visibility
conditions show only what's made.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
The fasteners are generated for FRCDesignApp, which inserts them from the
library and into assemblies; nothing here belongs to Quick fastener. The
hole scan is `fastenerHoleScan`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
…xing

`fastenerTables.py` now names each screw, washer and nut, with its vendor,
part number and link, as a `Record` per configuration. The tables carry
them, so the features set exactly these properties (now with Vendor too),
and the manifest lists them per studio, so FRCDesignApp indexes the
generated records rather than asking Onshape about each configuration.
`ConfiguredStudio.combinations()` enumerates what a configuration offers,
and each studio's records are tested against it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
…dor sells them

A screw's Variant chooses among what its vendor sells beyond the standard:
zinc plated (McMaster-Carr, REV), aluminum (WCP), or fully threaded
(McMaster-Carr's long socket heads, whose standard is partly threaded). A
washer's Finish chooses plain stainless steel over black where McMaster-Carr
sells both. Each input is hidden wherever only the standard is made, Generic
included, and a hidden input is left out of the records, as FRCDesignApp
enumerates it. Parts take their material and their finish's appearance from
the records.

ConfiguredStudio inputs can now be shown for some combinations of earlier
inputs (Onshape's visibilityCondition), as their options already could.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
`fs build-studios <definition>` builds each of a definition's configured
Part Studios in the backend document, to try them: it finds the tab by name
or creates it, writes its configuration, and inserts its feature from the
pushed Feature Studio beside the definition (or points the one it has at it),
configured by it. A feature failing to regenerate in its default
configuration stops the build.

A library release builds them the same way in the library document, from
the studio it just pushed there, then always versions the library and
records where each Part Studio is in `<definition>.studios.json`, writing
the definition's exports again. The fasteners' manifest takes its locations
from there, in place of LOCATIONS.

`fs configure-studio` is gone: nothing is made by hand.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
…g Onshape

The fasteners go straight to their library document, so the backend-document
`fs build-studios` is gone; `fs release` builds them there. AGENTS.md now
says to ask before every call to Onshape: the FeatureScript MCP's tools and
every fs command that talks to it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
The screw's Variant is split in two. Finish is an enum between Head and
Length, always shown and offering what's made in the size and head
(Generic's only black), so Length shows the lengths made in it. Fully
threaded is a checkbox at the end, shown only where a length is sold both
ways. A screw's table entry holds its standard part and, there, its
fullThread one.

Length's conditions are factored by Finish first (ConfiguredStudio
factor_first), which keeps the screw's configuration at 170 KB rather than
221 KB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
The stores snapshot now holds only products that are catalog screws, which
fetch keeps by the same parse the catalog uses: no listings shipped from
Sydney, metric or self-piercing screws, or WCP's Kraken spares, whose IP8
drive isn't a hex socket. AndyMark's am-1754, zinc plated by its url, is
now the zinc plated 1/4-20 x 3/4 in. button head.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
FeatureScript-backed parts are now part sets ([tool.fs.parts.<set>]: a
title, its folder, and the FRCDesignLib document it's released into), in
place of library documents. Their FeatureScript stays in the backend
document, so pushing, checking and core imports work as before, but they
have their own commands and versions:

- `fs parts test <set>` builds each Part Studio in the backend document as
  `<Part> (TEST)`, from the FeatureScript as pushed, to try a change.
- `fs parts release <set>` versions the backend (`Fasteners - v1.2.0`),
  points the main document's Feature Studio at it, builds the Part Studios
  there, versions the main document, records where they are, writes the
  definition's exports again, and tags the release.

`fs release` refuses a part set's FeatureScript, and `fs sync-versions`
leaves its versions out. docs/parts.md describes the flow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
Pushed fasteners/fastenerHoleScan.fs
Pushed fasteners/fastenerTables.gen.fs
Pushed fasteners/fasteners.fs
Pushed gear/gearCommon.fs
Pushed gear/robotGear.fs
…ape refused

Inserting a fastener's feature answered 409 ("the feature list becoming
invalid"). Its enum values (FastenerVendor, ScrewHead, ...) are defined in
its FeatureScript, but were sent with no namespace; they now name the
feature's. A build that Onshape refuses now says which step it refused
(creating the tab, writing the configuration, or inserting or updating the
feature) and in which Part Studio.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
…ape kept the inputs' ids

A refusal now names which of the two feature writes Onshape refused, and a
configuration whose input ids or enum names Onshape changed fails before
anything names them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
…cosmetic threads with hole attributes

Screws get a 45 degree tip chamfer, socket heads a top chamfer, flat heads
their band, and button heads a short side, a dome and a flat top, in
McMaster-Carr's proportions. Each screw's shank carries std's external
thread attribute and a cosmetic thread, 2D + 1/2 in on a standard socket
head and to the head otherwise; each nut's bore is the tap drill, tapped
through as std's Hole does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
Writes each studio's configuration in stages to a scratch (DIAGNOSE) Part
Studio, stopping at the first Onshape refuses, and lists the features in its
(TEST) tab. Onshape refuses the screw's whole configuration with a 409, and
its message doesn't say why.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
…-length variables

FRCDesign's bolt Part Studios give each screw's threaded length as configured
variables (BO_ThreadedLength and so on), which its group snapshot doesn't
carry. This saves them, from the versions the snapshot names, for the tables
to read offline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
… threaded is a switch

Onshape refuses any visibility condition but an AND of inputs, each one of
some values, with a 409 ("The change would result in the feature list
becoming invalid"), which fs parts diagnose found on the screw's first option
rule. Each option's combinations are now factored into such conditions, one
rule per condition, several of which may control an option; a List's own
visibility is folded into its options', since a List with none shown is
hidden. A checkbox shows by one condition, which the studio checks, so the
screw's Fully threaded, which needs five, is a switch: a List of false and
true setting the same boolean parameter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
FRCDesign's threaded lengths will be copied in from its tables instead of
read from Onshape.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
…e stores it

Onshape still refuses the screw's first option rule, now in the shape its UI
makes; one made in its UI shows what differs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
… rest always

A configuration made in Onshape's UI ends its option rules with one showing
every option always: Onshape hides an option no rule controls, so the
screw's first rule, which left its default #10-32 to none, made the feature
list invalid. Each List's options no condition controls now get that rule,
and each equality says inArray, as Onshape writes them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
Alone, an input's first rule can hide its default in the default
configuration, which Onshape refuses: finish's did, with Black shown only
for AndyMark. Size and head show the rules themselves are accepted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
Names and options spell inches "in", as Onshape does. The screw and the
washer took one FastenerFinish, so the screw's own dialog offered Stainless
steel, which no screw comes in; ScrewFinish and WasherFinish keep the same
option ids.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
Onshape refused the screw's configured parameters: "Parameter with
parameterId vendor ... has configured values using parameters that are
assigned parameterId values". Only the configured parameter names it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
claude and others added 28 commits November 6, 2026 15:22
It's on main (ca8ee75), against main's reworked custom frames; this
branch's version was written against the frames before them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
goBILDA's M4 screws, nuts, and washers and REV's M3 ones, with each
part's dimensions read from the vendor's STEP model, what the fastener
features need to offer them, and the few models still missing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
FRCDesign renamed ftc-design-lib to config-lib, so `-l ftc` was refused
as an invalid library. It's `-l config` now. The FTC fastener catalog
gains what ConfigLib's teams insert, which sets its defaults.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
AndyMark's metric screws, nuts, and lock washers from its store's product
list, and McMaster-Carr's series, whose part numbers have to come from its
pages by hand.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
Where usage doesn't pick, a part's vendor and finish default to the lowest
price per piece: goBILDA for M4, AndyMark for M3 so far. Records the prices
behind that, how McMaster-Carr's supplies are chosen between, and that its
metric tables and REV's per-length prices are still needed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
…f 250 or fewer

McMaster-Carr's zinc-plated Class 8 nylocks are the cheapest M3 and M4
ones per piece, so they're the defaults. Packs over 250 don't count toward
the cheapest, since a team doesn't buy 2,000 nuts to save a fraction of a
cent each.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
Price orders only the finishes within a vendor, by the piece in its
smallest pack. Vendors go Generic, McMaster-Carr, goBILDA, REV, AndyMark;
goBILDA stays behind McMaster-Carr, whose nuts cost less. Generic covers
screws and washers, not nuts, as for FRC. Adds McMaster-Carr's hex nuts
and stainless M4 nylocks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
Its socket heads in zinc-plated and black-oxide alloy steel and 18-8
stainless, every length and supply, and its nylocks and hex nuts in the
three finishes, go in vendor/mcmaster-metric.json for the generator, in
place of the doc's tables. 18-8 is McMaster-Carr's cheapest socket head,
or within two cents, and sold in the most lengths, so it's the screws'
default finish.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
Its button heads in the three finishes join the snapshot, with the head
dimensions measured from the models it publishes, which agree with its
tables. Stainless goes first for them, as for its socket heads. Low
profile socket heads are left out: under 1% of ConfigLib's M4 inserts,
and a button head is lower on the same hex key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
Its hex-drive flat heads in the three finishes join the snapshot, with the
M4's head measured from its model. Black oxide is the cheapest flat head
at every common length, so it goes first. A metric flat head sits in a
90 degree countersink, where the FRC features assume 82.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
Its general purpose M4 washers in the three finishes join the snapshot,
and its models of an M4 hex nut, nylock and washer give their shapes: the
nylock's hex height and insert bore replace the proportions FRC's nylocks
are drawn from. McMaster-Carr's washers cost less than goBILDA's too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
Lock washers are left out, a nylock or thread locker doing the job.
REV's M3 washer, which it publishes no dimensions for, is taken as a
standard ISO 7089 one. Prices only order finishes and pick McMaster-Carr's
supply, and go into no part's records, so REV's per-length prices aren't
needed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
Its general purpose M3 washers in the three finishes join the snapshot,
taken as ISO 7089's 0.5 mm thick. Stainless is the cheapest M3 washer, and
a black one black-oxide 18-8, which costs less than its black-oxide steel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
Both are DIN 934 M3 nuts of the same size, so nothing is still needed
from a vendor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
… own

FTC's M3 and M4 parts from McMaster-Carr, goBILDA, REV and AndyMark are
FRC's Screw, Nut and Washer features and tables, offered by Part Studios
of their own: ftcFastenerTables.py configures them from fastenerTables.py's
FTC catalog and writes their manifest, ftcFasteners.gen.json. Each set's
studios offer its own sizes, so FRC's manifest is unchanged.

The catalog takes metric sizes and lengths, hex heads and REV's T-slot, a
90 degree countersink for metric flat heads, ISO 4762's 2D + 12 mm socket
head thread, and a vendor's word that a part is threaded its whole length.
Nuts come in finishes: FRC's in zinc plating alone, which their
configuration doesn't ask. FTC's studios list finishes cheapest first, and
vendors Generic, McMaster-Carr, goBILDA, REV, AndyMark.

fs: a part set may name its definition, so two can share a folder, and a
definition configuring studios from another's tables needs no CONTENTS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
ConfigLib has no T-slot screw, and nobody inserts one, so REV's
REV-41-1907 and the T-slot head shape it alone needed are gone; its hex
cap screws stay.

FRCDesign's analytics count a part's uses now (`useCount`), which
`fs cots` failed on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
Pushed fasteners/fastenerTables.gen.fs
Pushed fasteners/fasteners.fs
Pushed gear/gearCommon.fs
Pushed gear/robotGear.fs
# Conflicts:
#	featurescripts/core/linearStock.fs
#	featurescripts/released/frame/frameTables.gen.fs
#	featurescripts/released/frame/robotFrame.fs
#	featurescripts/released/nutStrip/robotNutStrip.fs
#	featurescripts/released/shaft/robotShaft.fs
#	featurescripts/released/shaft/robotShaftTables.gen.fs
#	featurescripts/released/spacer/robotSpacer.fs
The screw was every solid the feature made, which took in the socket's
cutter once it was extruded, and Onshape refused to subtract a part from
itself. It's the revolve, or a hex head's joined body, again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
Pushed core/cutStock.fs
Pushed core/linearStock.fs
Pushed fasteners/fasteners.fs
Pushed gear/gearCommon.fs
Pushed gear/robotGear.fs
Pushed released/frame/frameTables.gen.fs
Pushed released/frame/robotFrame.fs
Pushed released/nutStrip/robotNutStrip.fs
Pushed released/shaft/robotShaft.fs
Pushed released/shaft/robotShaftTables.gen.fs
… from them

The feature's tables hold both sets' parts, so a vendor in both, such as REV
(#10-32 for FRC, M3 for FTC), had two sizes: a hidden Size holding the
studio's default resolved to neither, and the screw failed with "does not make
this screw in this finish". A hidden size now resolves among its own set's
sizes (FASTENER_SIZES' new `metric`), the only ones its studio offers.

The features also built from the inputs' raw values, so a hidden size, head
or length that held something else made the wrong geometry. resolveChoices
replaces made: it resolves each choice to the option it stands for, and the
screw and nut build from those.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
…y in sync

Pushed together, a file could land before a tab it imports, and Onshape then
rewrote its import to the tab's new version, leaving the local file behind
until `fs pull`. fs parts built from that state, and Onshape refused to
configure the screw ("Feature not found"). Files are now pushed in waves, each
after the tabs it imports among those pushed, naming their new versions.

fs parts also refuses to build while Onshape has import versions the local
files lack, and says to run fs pull.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
M3 and M4, as McMaster-Carr's are, in one finish with no vendor or part
number, as Generic's screws and washers are. Generic comes first among the
vendors, so it's FTC's default nut, as it is its default screw and washer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
Each a major release, their first: v1.0.0 into FRCDesignLib's fasteners
document and ConfigLib's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
A release builds the Part Studios and writes the manifest in the main
document, and leaves versioning it to its owner, who checks the studios
first; FRCDesignApp then loads that version or holds it for approval. A
release is numbered by the backend's versions, and its recorded locations
name the document and tabs, with no version. A target is still versioned, for
a local FRCDesignApp to load.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
Rather than refusing until `fs pull`, fs parts writes them into the local
files itself, as fs pull does, with no API calls. After fs sync-main merges
into the main workspace, Onshape can change them there, and refusing would
stop fs run-releases in the Onshape workflow; it commits them with the
release instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4daFKz4qvNwCEYcg38VKS
@AlexKempen
AlexKempen merged commit 37b65e4 into main Nov 8, 2026
1 check passed
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