Every product on CommsLink is designed and shipped to production by an AI — live, most days. This is the raw record, generated straight from the commit history. No hand-picking, no polish: 2,472 ships and counting.
Bench: servos + 3D-printed plastics — design a robotic hand. New parts: Micro Servo (SG90-class, SIG/VCC/GND, articulated horn), Printed Plate (palm/chassis PLA), Printed Beam (finger segment). New program op 'servo <PIN> <angle>' (0-180) drives any servo whose SIG nets to that GPIO; horns lerp at ~300deg/s with wires chasing the moving pins. Attachment hierarchy: printed parts screw onto a servo HORN (swing with the commanded angle), servos/parts bolt onto printed plastic at the drop point — scene-graph parenting makes a plate→servo→beam→servo→beam finger chain articulate for free, with cycle guards in both UI and sanitize. Fuse math knows servos (~0.5A each on 5V). Agent API: attach_component gains hornOf / onPart+dx/dz, catalog + bench.md + MCP updated. 8 new engine tests (866 green)
Bench: two UX fixes. (1) Sprockets ROTATE now — R on a selected sprocket silently no-op'd because sprockets render in the frame layer, not the mat registry the R-handler updated; the handler now re-aims via frameLayer.applyPositions, and LOOSE sprockets finally honor their rotY (axle aims from it, same convention as a motor shaft — previously hardcoded to +x, so rotation could never show). Bar/shaft-mounted sprockets still take their axle from the steel/shaft, as they should. (2) The validation/warning panel moved from bottom-left to bottom-RIGHT — the fabrication section made the left palette tall enough that the warnings covered it. 858 tests green; web build clean
Bench: DELETE for fabrication objects. Click a steel tube or a chain to SELECT it (white emissive highlight; clicking a bar also still starts the assembly drag, nearest-hit-wins between chain and bar) — then X/Delete scraps it: a deleted bar takes its welds and frees anything bolted to it (parts stay where they lie); a deleted chain just unloops. Deleting a SPROCKET (or any component) now also cleans up properly in the scene, matching the API op semantics: its chains go with it and a deleted motor frees its shaft sprocket — no more dangling chain tubes until the next server sync. Empty-click/Escape clears frame selection like everything else; palette hint updated (X deletes parts, wires, tubes, chains). 858 tests green; web build clean
Bench: ONE BIG WORKBENCH — fabrication moves onto the table, and parts drag freely between mat and build area (both user asks in one model). The maker's-desk GLB is replaced by a large procedural steel-topped welding table (3.3m x 1.9m at the same work height) with the green electronics mat at its center; the fabrication layer (bars/welds/sprockets/chains, pad outline) now rides the tabletop instead of the floor. Because mat and build area share ONE surface and ONE drag plane, the old cross-space barrier is gone: drag any part across the mat's edge and it re-homes live mid-drag (mat -> build area sets onFloor + reparents to the table group; dragging back inside the mat rect returns it) — no more 'can't drag items from the table'. Build area = the whole tabletop (PAD_HALF_X/Z 1.55/0.85 replace the floor pad; sanitize/add_bar/move_bar clamps updated, add_bar default z 0.55). frameLayer group rides at table height with grinder/weld sparks converted local->world; camera pulled back for the bigger table. 858 tests green; web build clean
Bench: bars can stand UP — vertical/tilted steel (user: 'how do i rotate it up vertically?'). FrameBar gains pitch (0..90°): while HOLDING a tube, T tilts it in 22.5° steps (flat→…→vertical→flat again); R still spins the heading. Geometry is honest: a pitched bar RESTS its low end on the floor (barCenterY/barPoint3/barDir3 — a vertical 3-footer's centre sits 18in up), its horizontal footprint shrinks with cos(pitch), and barsClosest went FULL 3D so a flat rail welds to an upright at its base (weld beads now store their height and render there). Guards where reality demands them: only a lone bar tilts (welded steel can't pitch one member — grind first), the grinder refuses standing posts ('lay it flat to cut — tap T'), and drop-bolting skips steep members. Bar-mounted electronics ride the bar top at the correct tilt-aware height everywhere (rebuild/drag/rotate). move_bar agent op accepts pitch (same unwelded guard); sanitize clamps 0..π/2. 858 tests green (vertical-bar geometry + base-weld + grinder-refusal test); web build clean. VR hand-tilt remains queued behind VR editing
Bench: R rotates the tube you're HOLDING. New pure rotateAssembly (frameGraph, tested for rigidity — end-to-end distances survive the turn): while dragging a bar, R turns the ENTIRE welded assembly 22.5° around the grab point — bars orbit the pivot with their headings advancing, weld beads and bar-mounted electronics ride along (mounted parts keep their orientation relative to the steel), sprockets/wires refresh live. This also LIFTS the old limitation: welded frames can now be re-angled without grinding a weld. VR hand-rotation noted as future work — VR remains view-only for now, so this is the desktop-grip rotate. 857 tests green (1 new); web build clean
Bench FABRICATION update — cut, weld, and build a tracked robot. The floor in front of the desk is now a BUILD PAD at true metric scale. STEEL: drop 3ft sticks of 1x1in square tube (add_bar). GRINDER: click a bar to cut it at that exact spot (kerf, sparks, new grinder shriek sound; slivers under 5cm refused; grinding a weld bead pops the joint). WELDER: click two touching bars (within 4.5cm) to lay a bead (arc sizzle + flash) — welded bars are RIGID ASSEMBLIES via union-find: grab any piece and the whole frame moves, beads + mounted parts riding along; cutting a welded bar keeps each bead with the piece it was laid on. MOUNTING: drop any electronics part onto a bar and it bolts on (release-snap; mat parts jump down onto a bar under the pointer); wires keep working across desk/floor (world-space endpoints). DRIVETRAIN (#40 chain family, real pitch geometry r=p/(2sin(pi/N))): universal 9T motor sprocket bores onto a motor/starter shaft (drop-near-shaft snap, one per shaft), 15T free idlers + 30T drive sprocket bolt to bars as vertical discs; CHAIN tool clicks sprockets in loop order (first again to close) and the chain renders as a tube on the real wrap path — external tangents + arcs (chainPathPoints, tested) projected into the sprockets' vertical plane. SPIN: pure spinSpeeds propagates from running motors through chains at true teeth ratios (9T→30T = 3.3:1 reduction; the track chain then runs the idlers) — sprockets visibly turn when the starter cranks. Architecture: pure frameGraph.ts (cut/weld/assemblies/spin/chain-wrap, 13 tests) + new frameLayer.ts scene module (BenchScene stays a composition root); persistence sanitizes the frame domain + 10 new agent ops (add_bar/cut_bar/weld_bars/remove_weld/move_bar/remove_bar/attach_component/add_chain/remove_chain) with full guards (no rotating welded bars, one sprocket per shaft, chains only over placed sprockets); catalog patchOps + bench.md 'Fabrication' section + MCP updated. 856 tests green (30 new); web build clean
Bench starter fixes (user couldn't find the negative terminal): (1) the GND stud was authored on the REAR mounting flange where the can occludes it — moved to a new visible GROUND LUG, a metal ear off the viewer's-left side of the case, clear of the can and on the front half (test asserts x outside the can span + z>0); (2) the housing itself is now a terminal: new CircuitPart.caseGround — while a wire is IN PROGRESS, clicking anywhere on a starter's (or standalone solenoid's) body clamps the wire to the case-ground stud, like grounding to metal in real life; without a wire in progress the body still drags. Catalog desc documents both for agents. 843 green; web build clean
Bench: new part — F-150-style STARTER MOTOR with integrated solenoid, a 1:1 of the user's real hardware (the plain motor wasn't). Anatomy modeled exactly: the big can with the smaller solenoid riding it carrying ALL the wiring — B (always-hot battery stud), S (start-signal post), and the M post strapped INTERNALLY to the windings (visible copper strap; the catalog + validator tell agents/humans 'do not wire M'); ground is the HOUSING — a case stud on the rear mounting flange. Physics: the solenoid coil RETURNS THROUGH THE CASE, so an ungrounded housing = no click at all; S energized + case grounded → CLACK, internal B↔M bridge closes, and if the battery cable is actually on B the pinion cranks (motorShaft spin + snap sounds reuse the existing hooks). The two classic failure modes are first-class validator diagnoses: 'starter-unground' (housing not grounded — nothing clicks) and 'starter-no-b' (the click-but-no-crank: solenoid engages, B has no/dead battery feed). Current model: cranking = 150A — running the B cable through ANY blade fuse pops it instantly (the sim teaches never to fuse a starter cable), while the S control circuit draws a proper 4A (holds a 5A fuse — the RIGHT thing to fuse). 3D: can + nose + solenoid + copper strap + pinion shaft + grounded flange (addStarterBody). Palette/catalog/netlist/validate all inherit automatically. 6 new tests (crank, no-ground silence, click-no-crank, 150A pop vs 4A hold); 843 green; web build clean
Bench: WIRE COLOR CHOICE. A freshly-landed wire now arrives SELECTED and a color swatch panel appears bottom-center — 8 standard automotive hookup colors (red/black/yellow/blue/green/white/orange/grey, WIRE_COLOR_CHOICES in the catalog); click a swatch to recolor. Clicking any existing wire opens the same panel, so old wiring can be recolored too; Esc/empty-click dismisses; X still deletes. New wires keep their sensible role default (pwr red, gnd black, sig yellow, USB grey). Scene: onWireSelect callback + setWireColor (redraws wires + persists without a full rebuild); selection cleared on delete/Escape/deselect. Agents: set_wire_color patch op takes a color NAME or a 0xRRGGBB number, matches either pin order (catalog patchOps + bench.md + bench-mcp.mjs updated). Fixed a TS narrowing-in-closure error the local build caught (op.color captured before the find()). 838 tests green (1 new: name/number/reversed-order/junk-rejection); web build clean
Bench: new catalog part — the WUPP-style 6-way 12V FUSE BLOCK with negative bus (user's real hardware) — and with it the engine's first CURRENT MODEL. Part: black box, BAT+/GND battery studs, six fused OUTn screws along the front, six NEGn negative-bus screws along the back (bus = internal ties to the GND stud), six blade fuses on top in real ATO colors (5A tan / 10A red / 15A blue / 20A yellow) each with a red LED that lights when THAT fuse is BLOWN. Engine: CircuitPart.fuseCircuits = fused paths whose conduction is gated by per-instance fuses[i].blown; new analyzeFuses computes each slot's amps on a DOWNSTREAM graph (adjacency minus all fuse ties, plus the live relay/solenoid bridges) — nominal draws motor 12A / converter 3A / solenoid coil 1A, and a circuit reaching ground directly = 100A bolted fault; amps>rating → overloaded (engine stays pure/stateless). The SCENE persists the blow: sparks + zap at the fuse, the fuse chars with a visibly broken element and tips askew, its red LED lights, the circuit opens — so a dead short on a fused circuit POPS THE FUSE once instead of sparking forever. Click a healthy fuse to cycle 5/10/15/20A; click a blown one to snap in a fresh fuse. Agents: set_fuse patch op (rating change = fresh fuse), catalog desc with draw table, netlist tags [F1:15A F3:BLOWN], fuse-blown/fuse-overload validator lines with the fix spelled out; palette button auto-appears from the catalog; starter circuit unchanged (available to add, not pre-placed). 8 new tests (12A motor through 15A holds / 10A pops+opens, bolted fault, set_fuse validation, sanitize roundtrip, netlist tags); 837 green; web build clean
Bench: the relay's VCC↔JD-VCC jumper is now a real, interactive part. YELLOW cap shaped like the actual thing (rectangular sleeve + grip notch + socket openings) sleeving the VCC·2/JD-VCC pins; CLICK it to pull it — it parks on its side to the right of the supply block — click again to reseat. Electrically honest: new CircuitPart.jumperTies applied only while installed (BenchComponent.jumperOff persists in the circuit JSON); relay channel switching now requires the COIL supply (JD-VCC reaching 5V) in addition to logic power — pulled + unfed = no channel clicks, with a validator line saying exactly that ('reinstall the jumper (click it) or wire JD-VCC to a 5V supply'); feeding JD-VCC 12V overvolts jumper-in OR out (5V coils). Isolation mode WORKS: wire JD-VCC to the Pi's second 5V pin and the chain runs — which exposed a real modeling gap the new test caught: the Pi's 5V/5V·2 and its four GNDs weren't tied as single rails (they are on the real header) — added rpi4 ties. Agents get it too: set_jumper patch op (persistence + catalog patchOps + bench.md + bench-mcp.mjs), netlist tags '[VCC↔JD-VCC jumper PULLED]'. 5 new tests incl. pull→dead→feed-JD-VCC→alive roundtrip; 829 green; web build clean
Bench relay layout re-fixed per the user's second look at the physical board: the relay now FACES THE VIEWER like every other part. Root cause of round 1 looking mirrored: the ported chat relay was authored with its input header on the FAR (−z) edge while every other bench part's pins face the near (+z) camera side — so from the default camera the header read right-to-left and the jumper block appeared on the LEFT. Now: ONE front row on the near edge, viewer's left→right = GND, IN1–IN8, VCC, then the supply block GND·2 · VCC·2 · JD-VCC continuing to the RIGHT in the SAME row (as requested); screw terminals + green blocks + SPDT symbols moved to the back edge; channel LEDs face front; jumper cap follows the block. Pin NAMES unchanged → saved circuits and ties untouched. Test now asserts the exact 13-pin front-row order left→right; 826 green; web build clean
Bench relay corrected to the REAL 8-channel board (user checked it against the physical hardware). Input header is now the true 10-pin order — GND on the FAR LEFT, IN1-IN8, VCC on the far right (was VCC,GND,IN1-8) — and the missing 3-pin opto-supply block at the bottom-right is modeled: GND·2, VCC·2, JD-VCC (left→right as on the board), with the factory VCC↔JD-VCC jumper cap rendered and modeled. New CircuitPart.ties concept: pin pairs joined INSIDE a part (same copper / factory jumper) — buildAdj links them per instance, so wiring ANY of header-GND/GND·2, header-VCC/VCC·2/JD-VCC powers the board exactly like the real one, and 12V fed into JD-VCC correctly trips the overvolt short (it reaches the 5V rail through the jumper — as it would fry the real coils). Catalog desc + bench.md updated for agents; jumper cap visual in partMeshes. Saved circuits unaffected (no pin renamed/removed — positions and additions only). 3 new tests (header order, jumper-block power path, JD-VCC overvolt); 826 green; web build clean. NOTE: chat's vrm.ts still carries its old relay copy — the bench engine is canonical; parity lands with the chat→lib/bench delegation follow-up
CommsLink Bench v1 — standalone 3D electronics workbench + AI tool API, live at /bench. The chat workbench's circuit engine EXTRACTED into shared pure modules (services/web/lib/bench: catalog/circuitGraph/programs/persistence — same CIRCUIT_LIB semantics, freed of vrm.ts class state; Chat delegates in a follow-up per reuse-over-rebuild) + client modules (partMeshes/benchAudio/BenchScene: void scene, makers-desk GLB w/ procedural fallback, green mat, ported part visuals/LEDs/sparks/zap/solenoid-clack, pin→pin wiring, drag/rotate/delete, canvas BENCH MONITOR showing live validation, WebXR enter-VR). Backend: prisma bench+user_pat tables (created manually on prod pre-deploy), core/data/{bench,userPat}, core/helpers/patToken (clb_ secrets, sha256 at rest), core/actions/bench/* (access resolver: session owner / PAT scopes / share token; create seeds the starter-motor circuit; patch = validated op interpreter returning per-op errors + fresh validation), routes /api/v1/bench/* (jwt-try + PAT bearer + ?s= share; catalog public; netlist text/plain). api Dockerfile COPYs services/web/lib/bench (engine ships in the api image). Frontend: /bench hub (benches + PAT manager + MCP config), /bench/[id] editor (palette, program editor, share rotate/revoke, 3s poll so agent PATCHes appear live, Enter VR), /bench/about (public agent-readable API doc from content/bench.md). Agent surface: public/bench-mcp.mjs — single dependency-free stdio MCP server (bench_list/catalog/get/netlist/validate/patch); llms.txt + landing card + changelog PATH_RULE. 28 new tests (starter chain, shorts, validate, patch ops, programs, patToken), 823 green; local next build clean; api tsc adds no errors
Generated 2026-08-26 from git across all CommsLink repos · page 1/1, showing 1–15 of 15 CommsLink Bench ships · complete record: /changelog.txt · /changelog.json.