Repository navigation
Consolidate Frame into this repo, with its history from Studio - #26
Merged
Merged
Conversation
- scene_collect: build_scene_primitives_and_effects returns an EffectsPlan
(emissive draws recoloured in world space, strength = intensity × effective
opacity; shader regions = primitive range + node-local→world box) next to
unchanged primitives. build_scene_primitives draws emission as a new
EffectKind::Glow (vello: blurred rounded box; SVG: feGaussianBlur filter).
- render::fx (anyrender): Mode {Gpu, Fallback, Off}, Stage: vello → texture,
shader subtrees rendered with their enclosing groups/clips/layers/masks,
run through a WGSL effect and composited in paint order; emission bucketed
by radius, half-res, downsample + separable Gaussian (tight + wide), added
mostly around the emitters. Built-ins glass, crt, lighting, flicker;
asset:<id> custom WGSL (CONTRACT.md); failures logged once, drawn plainly;
pipelines cached.
- Offscreen::render_with_effects; render_surface/kit_sheet --fx; desktop
window + headless use the stage (--no-fx / --fx), redraw continuously for
time-dependent effects.
- examples/surfaces/08-glow-demo.fm; plan + GPU pixel tests.
Parity corpus: 26/26 actual.png + actual.svg byte-identical to the baseline.
Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
A backlit VU needle's bloom leaked out below the meter window: the emissive draw painted the node's own geometry without the clips that cut the node itself. Each EmissiveDraw now carries its ancestors' open ClipStarts with their world transforms, and bloom applies them. Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
Reverb faces 01-ir … 08-special, one per reverb-ui SpaceDesign, each drawn as its machine: a convolution processor with the impulse on a glass display; a digital hall rack with a phosphor display (program, decay drawn as reflections + tail, live RT60/pre-delay/size/mix) and a fader bank; a steel plate seen through its window (frame, springs, driver, pickups, damper) with the remote's DECAY dial printed in seconds and a VU; a room box with the room in perspective and its early-reflection tap diagram; an open spring tank on a green chassis; an ambient halo; a scope of drifting delay lines; and a black effect box with a lamp ladder on the tail level. Modulation faces 01-chorus … 05-wah: four stompboxes (enclosure, lid art, LFO rate lamp on a <face>/lfo scalar stream, bypass footswitch with its LED) and a side-on wah whose treadle rocks on its axle as a 16° knob. Params namespaced per face with real units (decay s, pre-delay ms on a printed 0–250 scale, rate Hz log 0.05–20, output dB, width to 200 %); choice params for programs/engines/circuits; <face>/in,out level streams. Shared helpers (copied from the VCA faces, plus displays, glow via emission, ribbons, lamp rows, LFO lamp, footswitch, enclosure, any-ink knobs) live in reverb/ui.rs. Test: every face lints 0 errors against its merged catalog and every binding resolves. Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
…ive circuits Delay (plugin "delay"): 01-digital (rack DDL with a VFD reading both times in ms and as note values, TAP/FREE-SYNC/LINK keys, echo train on the glass), 02-tape (tape echo: reels, record + three switchable playback heads, VU, time window), 03-analog (BBD stompbox with the chip in a window, bypass and tap footswitches, red LED readout), 04-pitch (staircase display, interval in semitones), 05-rhythmic (16 step keys are the taps), 06-special (reverse/spectral/diffuse with the smear on a scope). Saturation (plugin "saturate"): 01-tube (valves glowing behind a window, stage selector, VU), 02-tape (meter bridge, speed and formulation selectors), 03-transformer (core and winding in an open chassis), 04- transistor (hammertone fuzz pedal), 05-digital (LCD staircase transfer curve, bits/rate/ceiling readouts). Shared parts (delay::parts, delay::art): placing by shaft, tighten, printed scales at real positions, Label/Value slots for printed legends and values, note-division Selector pickers, emitting readouts/lamps, LED columns, reels, heads, enclosure, footswitch, valve, transformer, chip. Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
…c knob, LED meters, footswitch, lamps, GR ballistics, meter switch, readout formats Kit (surface-kit): - Place gains ink (Light/Dark/#hex), keep_case, sweep, touch (tight hit box padded to TOUCH_MIN), reverse, inner (concentric cap), select (meter source switch), format; helpers ink_color/case/opt/flag/control. - Printed scales, numerals, detents, ticks and panel legends honour ink on knobs, switches, levers, push-in and LED banks; legends keep case on ask. - Knobs with options are stepped (detent sweep, options as scale); every rotary takes Place::sweep. lever.pultec's paddle now turns about its pivot (square pivot frame), so no unfold_needle is needed. - Opt-in tight boxes (Place::touch) for knobs, rotary switch, lever; place_in_cell/drawn_bounds for laying them out; kit_sheet uses them. - New: knob.concentric (collar + nested cap, two bindings), meter.led_bar, meter.led_column, meter.led_ladder (thresholds, gr, reverse), meter.lamp (Glow on a scalar stream), switch.footswitch (LED, momentary). - Banks reverse; cap legends shrink to fit the cap. Runtime/proto: - Gain reduction rests at 0 dB and attacks downward (Ballistics::reduction); meters rest correctly with no data; Meter bars honour the binding curve; reversed spans; gauge fill/thumb follow the lane's long axis. - Gauge/Meter Led slots light as a threshold ladder; unlit Leds drop their emission; new SlotRole::Glow (opacity = value). - Control::select (SourceSelect/MeterTap) switches a meter's stream from a choice param; Control::format (ValueFormat: decimals, unit, note, labels); stepped ranges print the decimals their step needs. - design-lint: nested concentric controls are not an overlap; gauges and meters may show their value with Fill/Thumb/Led/Glow; readouts with Glow. - render_surface: --stream id=v[,v2] (repeatable); --level feeds every level stream not named by --stream. - design-session add_component takes ink, keep_case, sweep, touch, reverse. Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
…n strokes, text glow, exact aligned strokes
Nodes not imported from Figma (source.tool != "figma") are now drawn as
authored; Figma imports keep the exporter-compatible path, so the parity
corpus is unchanged (26/26 actual.png + actual.svg byte-identical to a
baseline rendered on this GPU before the change).
- PaintPrimitive::{Path, Rect} carry `authored`; Text carries `effects`.
- Shadows follow the outline: DropShadow / InnerShadow / Glow (and a solid
LayerBlur) on authored vectors, and on authored rects with per-corner
radii, are rasterised as an antialiased coverage mask of the real fill +
stroke geometry, Gaussian-blurred on the CPU and drawn as a cached image
(new anyrender_renderer/soft.rs). vello only blurs uniform rounded rects.
- Geometry as written: no compute_path_fit_transform for authored paths —
path data is node-local and may reach outside the box. A clipping frame
now clips authored geometry that reaches past its child's box, and an
authored frame's clip opens after its own paint, so its own shadow,
glow and outside/centre stroke aren't cut by it (children still are).
- Open strokes: a stroke without stroke geometry strokes the fill geometry
(kurbo stroke, caps/joins/dashes; inside/outside via clip/DestOut on
closed subpaths); open subpaths are stroked lines, never filled, and
nothing falls back to stroking the box. SVG export matches (fills only
the closed subpaths of such nodes).
- Top-edge band: an inside/outside stroke on a rounded rect was stroked
along an inset outline; vello's GPU stroker put an edge a hair above
the first tile row and filled that whole 16 px row with the stroke
colour (the "dark band" on the 1176 faceplate at x=0,y=0, w=960, weight
1.0667, radius 3.2 — not the angled gradient). Authored aligned strokes
are now the exact even-odd ring between the two edges.
- Text: glyph outlines (skrifa) carry drop shadows, inner shadows and the
fallback emission Glow; Figma text keeps only the glow.
- Golden primitive dumps regenerated: only the new fields changed.
Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
…derer - Drop underpaint (the band was the inside stroke); a unit's own colour is now the panel's own fill (repaint). - Lines are open stroked paths: ribbons, header rules/grooves, scale ticks, graticules, VFD mesh, laminations, windings, leads, board traces, tape, reel dogs, screw slots, the transistor symbol; over()/path_in gone. - Kit instead of workarounds: Place::touch for knob boxes, Place::ink for toggle captions, stepped chicken-head selectors via Place::options, meter.led_column for every wet/output column, switch.footswitch (tap is momentary) for the stompboxes; bank legends auto-fit (fit_legends gone). - Readouts carry ValueFormat: note pickers print ValueFormat::labels(DIVISIONS), the crusher's rate one decimal. - Lit things emit instead of wearing drop-shadow glows (lenses, readouts, annunciators, display bars, traces, valve cathodes); crt on the pitch display and the special scope, flicker on the valves' heater light, glass on every VU window. - Removed dead helpers: tighten, unfold_needle, set_sweep, detent_sweep, detent_angle, reink, fit_legends, add_value, detents, print_scale, section, sheen, led_row, hand-built led_column, art::footswitch, jack, ribbon, over, path_in. Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
The emissive recolour dropped a path's fill geometry when it had no fills, but an authored stroke with no stroke geometry strokes the fill geometry; with both gone the bloom fell back to the node's box. Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
…t/renderer Faces (1176 ×3, LA-2A, CL 1B, 670, Vari-Mu, SSL bus, dbx 160, Distressor, Pultec, SSL channel, API 550A, Neve 1073): - Place::ink / keep_case instead of reink/repaint and legend restyling; Place::touch for tight boxes; Place::options stepped sweeps instead of blank scales + strip_scale + set_sweep; Place::reversed for the 1176 ratio bank; unfold_needle removed (lever.pultec, switch.rotary). - 1176 METER bank and LA-2A meter selector really switch the VU (Place::select + SourceSelect: GR / output ranges / off). - Neve mid/low: knob.concentric instead of the hand-built collar + cap. - Distressor GR ladder -> meter.led_ladder (gr, reversed); dbx 160 BELOW/ABOVE -> a two-lamp GR ladder (dark at rest); SSL channel output -> meter.led_column ×2. - Lamps and lit readout figures use emission, not drop-shadow glows; readout formats (ValueFormat) instead of whole-number guesses. - Renderer workarounds removed: comp_fet's Center-stroke switch, filled ribbon glyphs (now open stroked paths). - Dead helpers removed (tighten, unfold_needle, set_sweep, detents, print_scale, gr_ladder, threshold_lamps, led_column, reink, …). Kit: - knobs: contact() is one vector + zero-offset drop shadow along the outline (KIT.md "What turns" updated). - meter.vu_amber: lamp bloom toned down (wash 0.05, hotspot tint only) so the card glows warmly but the scale stays crisp on the GPU. Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
…renderer - ui: glowing() is emission only; stroked artwork is open stroked paths (strokes/line/bars) boxed to their points — ribbon, line_node quads, shape() padding, glyph and the haze-under-a-line glows are gone. - Knobs via Place::ink/touch/scale/options/sweep: the kit prints each scale in the face's ink round a tight box; no strip_scale, tighten, set_sweep or face-printed scales. Stepped selectors are switch.rotary (chicken-heads) or a stepped knob, legends pushed clear of their dots. - Kit meters and lamps in the face's colours (tint_lamps): meter.led_column for the IR/hall outputs, meter.led_bar for the ambient/random/special lamp rows, meter.lamp on <face>/lfo for every rate lamp, switch.footswitch for every stomp. lfo_lamp, lamp_row, led_column, footswitch, reink and the other dead helpers removed. - ValueFormat per param unit on every readout (whole %, ms; 0.1 dB). - IR file keys carry their names; glass shader on the IR and random displays and the plate window, crt stays on the hall; the wah treadle turns as one body. Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
…omentary/colour/glass options Kit: - Scale labels (knob numerals, stepped option words, concentric collar, switch.rotary) are placed so their box clears the tick/dot: pushed out along the ray by the box's half-extent, anchored left/right on the sides (draw::radial_label / radial_extent / text_width). Type floor draw::SCALE_TYPE_MIN = 9 px whatever the knob's size; the frame and the legend grow to hold them. Option words take the place's case. - An empty scale (`scale(&[])`) prints no ring at all. - Button banks fit one legend size for every cap (the smallest any needs), with a margin inside the cap; led_bank likewise. - Single buttons (button.illuminated etc.) take the `momentary` flag. - knob.concentric: `grey` variant (the 1073's all-grey pair) and stepped `options`. - switch.rotary is the chicken-head (knob.chicken_head's moulding) over detent dots. - Lamps, LED bars/columns/ladders and footswitch LEDs take `color=#hex` or per-lamp `colors=#a,#b,…` (Place::lamp_color). - VU meters take a `glass` flag (glass shader on the card, GPU only). - kit_sheet variations show each of these. Faces: - delay/parts.rs: clear_ticks and the hand-built momentary() removed; TAP keys use `momentary`. - reverb/ui.rs: clear_dots and tint_lamps (+ HSL retint) removed; lamps, bars and stomps use color=/colors=. - comp_tube selectors and API frequency/gain knobs use the kit's printed scale instead of re-engraving it; Pultec output likewise. - Neve 1073 mid/low pairs: knob.concentric `grey`, stepped by options. - Pultec levers back at their on-panel pivot (tight boxes kept). - SSL channel trim/drive readouts: 0.1 dB / whole %; filter caps print every other mark. - glass on every compressor VU; tight boxes on the tube-comp knobs and SSL caps (the larger print no longer overlaps boxes). Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
Every needle VU now carries Control::reference_dbfs = VU_REFERENCE_DBFS (−18): a level stream reads against it, gain reduction does not, and a meter switch's +4/+8 taps offset on top. The Fairchild's hand calibration is gone. The LA-2A face prints no model name. Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
…neric names FET limiter: three plates laid out from front-on photos — black (blackface LN), silver (early blue-band), late (late silverface) — as fet/ with files 01-fet-black / 02-fet-silver / 03-fet-late. 2U proportions, plate-as-ears with corner slots and Phillips screws, dial discs, key columns, dual-scale meter card; no brand or model printed. Power jewel only on the early plate. Tape echo: RE-201-style front (tolex case, name strip, black and green fields, chrome-skirted knobs, jack strip); drops the EC-3 model line. New kit parts: knob.knurled_cap, knob.chrome_skirt, button.push_in_key_bank, meter.vu_limiter. Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
Rebuild the four hardware EQ faces from reference photographs, with generic names only: - 01-program-eq (progeq/): 3U blue-grey panel, black scalloped phenolic knobs with bare 0-10 numerals, bar levers under CPS/KCS, IN bat toggle, pilot jewel, power switch (decoration); invented output trim and dB readout windows removed. - 02-console-eq (consoleeq/): the channel strip's sections as columns (FILTERS, HF, HMF, LMF, LF) keeping the strip's stagger, collet caps with coloured tops in numbered dot scales, grey keys, warm dark grey paint; invented drive removed; LF gets its seventh position (400 Hz). - 03-proportional-eq (propeq/): portrait black module, HF/MF/LF winged concentric pairs (cream gain wing, blue lobed frequency ring), seven frequencies a band and 2/4/6/9/12 dB gain clicks, FLTR toggle, LF/HF shelf-peak and IN push caps with the IN lamp; drive/trim removed. - 04-classa-eq (classaeq/): blue-grey rack row, red and blue wing selectors and grey turrets in clear skirts, knurled aluminium collars for the stepped mid/low frequencies, cream phase/EQ keys; trim removed. New kit components (new files, registered with one line each): knob.scalloped, knob.console_cap, knob.winged_pair, knob.turret, knob.turret_pair, switch.toggle_nut, button.round_cap, button.key. Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
Rebuild the four tube faces from reference photos of the real units and strip every maker/model name: - 04-opto-leveler (opto/): grey 3U panel, red slanted name + swoosh, glossy black tab-pointer knobs with numerals turned round them, VU in a chamfered charcoal frame, meter selector over bare POWER toggle. - 05-opto-comp (optocomp/): royal-blue panel, seven-sided knobs read against dots at the unevenly printed angles (param ranges follow them), bar selectors for meter / timing / sidechain bus, tan VU, red jewel, OFF/ON power knob. Adds meter select, sidechain bus and IN params and an input level stream; drops the invented readouts. - 06-twin-valve (twinvalve/): both channels on a black plate in a grey cabinet front: bakelite meters, paddle metering/time-constant knobs, chunky scalloped input gain (20..0 dB) and threshold (0-10), shared AGC, ON toggle, pilot and fuse. Drops the invented output knob and plate. - 07-vari-mu (varimu/): stereo layout on brushed gunmetal: right-to-left white reduction meters in black cases, black sub-panel of fin knobs in tick rings, toggle cluster, bypass/limit toggles, jacks and trims. New kit components: knob.tab_pointer, knob.heptagon, knob.bar_pointer, knob.fluted_stack, knob.paddle, knob.fin (knobs/valve.rs), switch.toggle_bare (controls/bare_toggle.rs), meter.reduction (meters/reduction.rs). Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
…aracter compressors Rebuild faces 08-10 from photographs of the real units and give them generic names (08-bus-comp / buscomp, 09-vca-comp / vcacomp, 10-character-comp / charcomp). No brand or model text anywhere visible. - Bus compressor: 1U pale silver-grey rack front; tall black housing with an amber-lit compression meter (needle rests left, swings right); blue capped-skirt knobs in dot rings; square backlit keys in grey bezels; adds the unit's external side-chain, auto-fade and fade-rate controls; drops the invented mix knob. - VCA compressor: gloss-black desktop front between walnut cheeks with aluminium trim; knurled black knobs with aluminium inserts; white piano keys (power, interlocked meter select); big ivory card printed in blue in a deep bevelled frame. - Character compressor: black 1U with white hairline frames; colour-coded 16-lamp GR ladder numbered above; per-colour ratio/detector/audio lamp selects; four big cream numbered dials; power rocker; drops mix. New kit components (new files, one-line registrations): knob.knurled_insert, knob.capped_skirt, knob.numbered_cream (knobs/outboard.rs); meter.vu_decibel_white, meter.gr_compression (meters/movements.rs); button.piano, button.piano_bank (controls/piano.rs). Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
…logo styles Heavy tight lowercase mark with a dark dot on the VCA compressor, the stacked maker's-corner name and boxed series badge on the bus compressor, and a big heavy slanted name on the character compressor — generic words only. comp_vca/logo.rs sets a mark's weight (overprinted past 600, as the renderer carries one face), slant, tracking and colour. Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
… and EQ units Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
The unit's top half only: one meter, metering switch, input gain, threshold and time constant driving both sides, with the AGC switch (stereo or mid/side) under the time constant. The cabinet front keeps its frame at 410 px tall instead of 590. Per-channel params and the second GR stream are gone. Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
- feed-proto: FeedKind (Level, GainReduction, Spectrum{bins,f_min,f_max,
floor_db}, Tuner, Scalar, + History{of,len}, Waveform{len},
Envelope{stages,voices}), FeedInfo, FeedOptions/FeedRequest, FeedFrame,
VoiceState. Owns the shared value types (EntryId, Range, Unit, Curve…),
moved from surface-proto which re-exports them; StreamKind/StreamInfo/
StreamFrame are aliases, old catalog JSON reads unchanged.
- SurfaceEngine::request_feeds(Vec<FeedRequest>) -> Vec<FeedInfo>, with a
default body so existing engines compile; SurfaceError::UnknownFeed.
- feed-dsp: realtime-safe taps (spectrum, history, level, scalar, scope,
envelope) over a lock-free SPSC ring, own radix-2 FFT, Publisher.
Allocation-counting test proves audio-thread pushes don't allocate.
- feed-memory: demo generators for every kind; Memory and the desktop
mock use them and honour requests; render_surface --demo-feeds.
- docs: visualizer-framework section 1 describes what was built.
Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
- features/library: library-proto (SurfaceStore rpc: list/get/publish/ catalogs, #[subscribe] updates(id) and changes; Signature "fits"; whole-snapshot updates), library-fs (a watched folder of .frame/.fm, sidecar meta, previews, versions), library-link (ws serve/connect, Blocking client, Follow thread with backoff). - apps/frame/library-server: vox over WebSocket + HTTP index/downloads/ GPU-rendered previews. - surface-runtime: Runtime::swap_surface (new runtime/swap.rs; one `mod` line in runtime.rs) keeps values, coalescing edits, meters and a gesture in flight across a design swap. - desktop: --library/--surface/--browse/--install-dir; follows a surface live, gates updates by parse + new lint errors with a toast; a frame- drawn browser grid (fits filter, preview, install, fork); headless --updates N for before/after shots. Player::swap preserves state. - frame-mcp: --library [--surface] publishes after every edit. Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
A knob over a choice (3-way meter switch, time constant, ratio) sent the raw drag position, so it glided between its positions and only snapped when the engine rounded it. Values sent to a stepped param (choice, toggle or Range::steps) are now quantized, and the wheel and arrow keys move one whole detent per notch or press (their small nudges would round straight back). Continuous params are unchanged. Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
- surface-proto: ControlKind::Plot { style, x, y }, ::Curve { provider,
inputs, series, style, x, y }, ::Handle { x, y, wheel, x_map, y_map };
PlotStyle, Axis (Linear / Log / Time), PlotRange, CurveInput +
InputSource, HandleAxis (after/shift offsets), BUILTIN_CURVES; slot roles
Trace / Area / Marker; validation of curve inputs and handle axes.
- surface-runtime: plot geometry (decimated to pixel width, NaN gaps,
steps/bars/dots, areas to a baseline); spectrum bins placed by their
feed's band; client-side history rings for readings, History feeds as
sent; CurveRegistry with builtins eq.response (RBJ bell/shelves/cuts
with slopes/notch/band-pass, sum + per band), comp.transfer (soft knee),
adsr.envelope (curvature, voice playheads), filter.response — memoised
and shared across controls, overridable by engines; handles drag their
params through their axes (log/linear, offsets), wheel, Shift fine,
double-click reset, through transformed containers; hit_box,
feed_requests + sync::Engine::request_feeds on connect.
- design-lint: curve-input, curve-provider, handle-area; plots bound to
params / missing feeds are binding errors; handles skip overlap.
- surface-kit: Family::Viz — viz.spectrum, viz.gr_history, viz.scope,
viz.comp, viz.eq_graph, viz.adsr; viz::catalog builds their params/feeds.
- examples: 09-comp-viz, 10-eq-graph, 11-adsr (+ catalogs), generated by
make_viz_examples; kit_sheet --family viz; frame --script
drag-first-handle.
Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
An EQ graph binds its bands through curve inputs and handles, so the signature missed them and the surface claimed to fit any engine. Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
…, saved derivations, one pedal height Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
…tors, LFOs, noise, spin A `Motion` on `Node::motion` drives one target property (translate, rotate, scale, stretch, skew, opacity, fill/stroke colour, emission, dash offset, path morph, shape-provider geometry) from an input expression (params, feeds, time, gestures, other motions) through physical dynamics (spring, oscillator excited by a rising source, lfo, noise, ballistic, keyframes, spin). Plain data: round-trips in markup, live-reloads, deterministic offline. - features/motion/motion-proto: data types + the expression language. - features/motion/motion-runtime: Animator — fixed 1 ms step, stall cap, state kept across swaps, is_animating, shape providers (coil, reverb.room, wave). 200 motions: 0.016 ms/frame (release). - surface-runtime: owns an Animator, steps it in frame(t), keeps it across swap_surface, hover/press sources, is_animating(). - document-proto/-text/-loro: Node.motion, Prop::Motion, motion= attr. - surface-kit: motion presets; meter.lamp warms through a spring, `flicker` option on lamps and VU faces. - design-lint rule `motion`; design-session `animate` tool. - render_surface: --frames/--fps/--gif/--frames-dir, --set-at, --stream-at. - plugin faces: spring tank rings with the input level and decays by dwell/damping; room geometry follows size/decay/diffusion; tape machine reels spin; tape echo loop runs; valve heaters warm and flicker with drive; pedal rate lamps pulse; wah treadle springs. - docs: visualizer-framework §9 Motion, KIT.md, REFERENCE.md. Claude-Session: https://claude.ai/code/session_01Nwusq16bN6CJteQKkaEwzo
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…en from the side The volume pedal, wah and dive bomb drew a toe half and a heel half that squashed together. Now: a low casting with its nameplate let in and a chrome trim, rubber feet, and one rubber-topped treadle tilting on its axle (toe on the right down at the top of the range), rocked by a sideways drag; the toe switch a nub under the toe. The wah gets an iPad FX-row face: the pedal and its sweep across the row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
render listed tokio from the workspace — `full`, net and all — and used none of it. As a path dependency of session's page build, that put tokio's net (mio) onto wasm32, where it does not build: the browser remote had not compiled since. Nothing in render's code, tests or examples names tokio. Claude-Session: https://claude.ai/code/session_01WYrYRZB6AGrkJoofKycdaZ
theme-runtime gave every Time machine its own phone face and back, tall faces for a lane column, and the faces' heads with their own bypass; fleet-rc7 gave them the iPad pane tier, the Digital delay, routing-grid block faces, live streams and the touch EQ panel. The merge keeps both: - a Time entry carries its tall pair and its pane faces; every delay and reverb machine's small face is its own (the placeholders are gone); - the analog / tape / plate / pre-mod nameplates sit in the heads with the pane's sizes; tall faces and block faces both stay; - phaser, flanger and rotary are theme-runtime's machines (desktop, back, phone) with an iPad pane too, laid out on fleet-rc7's knob row; its inline lane-only versions go; - the EQ draws the touch panel on a touch surface, the fixed / tinted band panel otherwise; - one Hall small face (theme-runtime's), the 480 with its pane. faces.json, the catalog and every face are regenerated from the merged code, not hand-merged: only rig-faces changed, every other plugin's faces came out byte for byte. The 480 binding test compares against the algorithms' faces, not the routing grid's block cells.
frame is only used here, so it lives here now: frame/, a workspace member like the others (37 crates). It comes from frame's merge of fleet-rc7 into theme-runtime (2dae10b, local branch merge-fleet-rc7): signal's iPad faces and live streams and session's theme runtime and tall faces, one frame. - The workspace's frame deps are path deps on frame/, not on a checkout beside the repo; signal-guitar-ui takes frame-live from the workspace again. - frame's crates keep the versions they were built with where the workspace's differ (vello 0.10, anyrender from git, chrono with serde, image's defaults), written into their own manifests. - The faces (frame/examples/plugins, 130 MB generated) are not in git: `just faces` writes them, byte for byte; session-daw and signal read them there. - The 35 MB 1176 render-parity scenario is in studio-assets (just assets). - library-fs takes an architect EventSink (the subscribe API it was already behind on). - imported.txt follows ../frame merge-fleet-rc7; catch-up never carries the generated faces. Claude-Session: https://claude.ai/code/session_0147CDF47Ra4UTu52kXSjuaB Studio-commit: FastTrackStudios/studio@5052b17
Phase 1 of the reorganisation: crates move, none merge. The five folded
repos' directories are gone.
- apps/ session/{desktop,daw,daw-web,web,cli,player,vault-sync},
signal/{desktop,web,mobile,cli,convert}, patchbay, frame,
plugins/ (every plugin shell and the CLAP host)
- crates/ the shared core: daw (with the standalone engine), audio,
sync, audiocore, midicore, input, dsp, ui, util, keyflow
(until it goes home), frame
- features/ one directory per feature — dawfile, reaper, the FX (eq,
comp, delay, reverb, saturate, ...), the rigs (guitar,
drums, keys, synth, ...), signal, session, expression-editor,
daw-ui, faces, patchbay, ...
- vendor/ every patched upstream, in one place
- xtask/ the REAPER test runners, the plugin bundler, frame's xtask
- docs/<repo>/ and legacy/<repo>/ hold what the old repos kept outside
their crates, to fold into the root's (legacy/README.md).
Every manifest path, workspace member and patch, the code paths that
reach across crates, assets.txt (and studio-assets, re-laid to match),
.gitignore, the Justfile, nix and CI follow. features/faces/generated is
where `just faces` writes now.
So the whole workspace's tests build and run in one go (they never had):
- plugins export their CLAP/VST3 entry points only for `just
plugins-bundle` (--features export); in one workspace build cargo
unified the export on into builtin-fx and its tests, three
`clap_entry`s in one binary;
- the vendored wgpu-hal names no examples it does not carry;
- signal's tests call their compat set_ext_state explicitly, fx-blocks'
tests use their own crate's name, rig_dump fills Compositions::tones,
daw-standalone's plugin_render example defines pump_run_loop once;
- the ableton round trip reads the fixtures it links to.
neural-amp-modeler comes from its v0.1.1 tag rather than a checkout
beside the repo: nothing outside studio is read any more.
Tests: 11,385 run, 11,324 passed. The failures traced are older than this
(processor's delay / saturate references, frame's viz count, a stale
.session-is-a-directory check) or need data this machine's checkout lacks
(keyflow's MusicXML corpus, sample libraries, the user's rig library).
Claude-Session: https://claude.ai/code/session_0147CDF47Ra4UTu52kXSjuaB
Studio-commit: FastTrackStudios/studio@5c27a4f
…ature
- crates/ daw (with its audio graph, allocator, utils and test macro),
frame (its desktop / CLI / MCP / library server as tools/),
keyflow (until it goes home)
- features/audio audio IO, sample, audiocore core, the web worklets
midi · input · sync · formats (was dawfile)
adapters/reaper · control-surfaces (its own feature)
dsp/{core, eq, comp, delay, reverb, ..., builtin}
rigs/{core, guitar, drums, keys, synth, sampler, nam, ...}
ui/{daw-ui, themer, widgets, theme, meters, ...}
- apps/ session · signal · patchbay · plugins
Crates move, none merge; manifests, cross-crate code paths, assets.txt
(studio-assets re-laid to match), the Justfile, nix and docs follow.
Tests: 11,385 run, 11,326 passed; the same failures as before the move,
less the three it fixed, plus one that is flaky under load and passes
alone.
Claude-Session: https://claude.ai/code/session_0147CDF47Ra4UTu52kXSjuaB
Studio-commit: FastTrackStudios/studio@df6b35e
A folded crate's tests and examples need the features their modules sit behind. A crate-level self dev-dependency turns those features on for its own test builds, so a plain workspace test run builds them all again (1,471 tests had silently stopped running). formats' fixtures now live at tests/fixtures/<format>, the same layout in studio-assets. String literals that named a module path (tracing targets, probe names, a checklist doc) name the crate again rather than `crate::`; the webview seam check looks for the feature by its folded name. Claude-Session: https://claude.ai/code/session_0147CDF47Ra4UTu52kXSjuaB Studio-commit: FastTrackStudios/studio@5612283
…one crate Claude-Session: https://claude.ai/code/session_0147CDF47Ra4UTu52kXSjuaB Studio-commit: FastTrackStudios/studio@d0a97fa
frame's libraries are one crate at crates/frame, by layer: the document, surface, motion and feed protocols (and the theme expression language) always; the runtime, render, theme, io, library, figma, design, live and embed layers each behind a feature, as nested modules (frame::document::proto, frame::surface::runtime, frame::render::canvas, ...). The tools (cli, desktop, mcp, library-server) stay binaries at crates/frame/tools. REFERENCE.md sits beside document::text and KIT.md beside surface::kit. frame-live painted into a host's anyrender 0.11 scene while render uses anyrender's git main: the two shared a dependency key, so the 0.11 pair now has its own (anyrender_0_11, anyrender_svg_0_12), and the three per-crate anyrender-0-11 features are one. Claude-Session: https://claude.ai/code/session_0147CDF47Ra4UTu52kXSjuaB Studio-commit: FastTrackStudios/studio@b15eb7d
…workspace
dx (krates) panics on a renamed dependency whose source a [patch]
replaces — `anyrender_0_11 = { package = "anyrender", version = "0.11" }`
resolved to the blitz fork the workspace patches crates.io anyrender
to — and with it every dx build in the workspace failed at the metadata
step. The dependency now names that git source itself.
Claude-Session: https://claude.ai/code/session_0147CDF47Ra4UTu52kXSjuaB
Studio-commit: FastTrackStudios/studio@dad7add
Guitar rig: builds into a Rig like the others — open / open_with keep their signatures, open_in joins a given engine, open_offline runs on an engine with no device; its output meter is read by track guid, its output trim addresses the rig's project. Rigs on a shared engine remove their tracks when dropped (GuitarRig, SamplerRig's last handle, KeysRig). One engine per process: signal_rig_host::engine::install_shared makes an engine the process's, and every rig opened afterwards joins it — the guitar rig (re)opens the device with its own audio settings (restart_audio), the others open it only if nothing has (ensure_audio). signal-desktop installs one at start-up, in --engine and in-process alike, and serves that engine's daw over /vox beside the rig services, so a daw client sees the rigs as the tracks they are. A rig on its own engine still rolls its transport (Engine::play); a shared engine's transport is the app's. MIDI: the process's one hardware input (rig-host's midi_hub) and the attach wide event move to features/midi as midi::hub and midi::telemetry. daw-standalone's midi-input feature routes it into the tracks armed with a MIDI record input (Standalone::route_midi_input), and Session's engine keeps that route with whichever song it has attached — Session plays instruments from a keyboard. Control surfaces keep their own device pair: the hub merges devices and does not say which an event came from, which a surface needs. Claude-Session: https://claude.ai/code/session_0147CDF47Ra4UTu52kXSjuaB Studio-commit: FastTrackStudios/studio@0ba6dc1
legacy/ is folded into the root Justfile, CI, nix and docs and deleted;
CLAUDE.md and README are rewritten for studio. Paths that pointed into it
or at the old sibling layout are fixed: the REAPER config, both REAPER
test runners, frame's parity scripts, the dev opt-level allowlist (11
names were stale, so those audio crates built unoptimised), FTS Mic in the
bundler.
Duplication removed:
- app-shell: Session's and Signal's log ring, crash log, flight log and
update check are one crate; prefs go straight to utils::prefs. The
update check reads the studio repo's GitHub releases.
- Loudness: dsp-core's BS.1770 K-weighting and gated integrated LUFS are
the one implementation; the sampler and the analyzer use them.
- The sampler's DrumMixer is gone: a kit's mixing is the daw's tracks.
- Tempo: the rigs' tempo is the engine's transport tempo (lock-free read
on the audio thread).
Also: the realtime clippy rules apply under features/{dsp,rigs,audio}
only; inferno-control and nih-plug come from GitHub; every Tailwind input
points at the real crates (Session's sheet did not build) and scans
architect-ui through a resolved symlink; the golden media writer replaces
files atomically, so parallel tests no longer SIGBUS on a mapping.
Claude-Session: https://claude.ai/code/session_0147CDF47Ra4UTu52kXSjuaB
Studio-commit: FastTrackStudios/studio@6f0a34c
Step 1 of the agreed reorganisation (docs/design/session-daw-split,
docs/design/timeline.md for the next steps):
- features/signal is Signal's domain (was rigs/core/*, plus macromod);
features/rigs holds the instruments, with rigs/host and rigs/proto for
the shared rig plumbing. sampler, plugin-host and dsp/nam move out of
rigs/; dsp/analyzer, dsp/separator, dsp/align are flattened; plugin
faces live under ui/; app/{shell,launcher}; kit-sheet goes to xtask.
- adapters/<host>: formats is split into adapters/protools, ableton,
logic and interchange (AAF, DAWproject), with their tests, examples and
fixtures; the daw's own `.session` file and its store become
crates/daw/session-format. dawfile-reaper and the dynamic-template
extension join adapters/reaper; the web AudioWorklet is adapters/web.
- adapters/reaper: the forked overlay crates are one again — reaper-dioxus
and reaper-embed are the daw-* forks (Retina sizing, dock host,
snapshots) with the other fork's shared GPU device + prewarm and the
overlay's start_hidden ported in.
- expression-editor is `editor` (task's unused `editor` entry dropped),
grouped into model/, pitch/, drums/, harmony/, interact/; the chords
crate folds into harmony/chords (it duplicated harmony's ChordGun).
Paths that pointed at moved crates are rewritten (manifests, include
paths, fixtures, asset links with their repo paths kept, .gitignore, CSS
sources, the generated faces, a tracked fixture symlink).
Claude-Session: https://claude.ai/code/session_0147CDF47Ra4UTu52kXSjuaB
Studio-commit: FastTrackStudios/studio@11e12c7
…een (FastTrackStudios/studio#174) ## Summary FastTrackStudio pivots to one app, **Studio**. Signal's app crate moves to `apps/studio/desktop` (`studio-desktop`, bundle `app.fasttrackstudio.studio`, the existing "Studio - by FastTrackStudio" App Store record) and links Session's app as a library (`session-desktop` now has a lib; feature `session-app`). The window opens on a front screen (`src/front.rs`) that enters Signal or Session; a launch runs one side, so one engine (`FTS_STUDIO_OPEN=signal|session` skips it). The iOS scene delegate moves into `app_shell::ios_scene` (`StudioSceneDelegate`). New app icon (desktop + iOS), generated with Codex. Shipping: `studio-ios.yml` is the one TestFlight upload; `signal-ios.yml` and `session-ios.yml` are gone, and `release.sh` (+ its sandbox) ships the one app. CLAUDE.md, `docs/agents/release-workflow.md`, the airlock skill and the dev-shell banner follow. The fleet skill was updated to match (FastTrackStudios/fleet-skills#25). ## Issue Refs: none ## How tested - `cargo check --workspace`: green. `just web-wasm-check`: green. Lockfile, `cargo fmt --all --check`, `cargo rail change check`: green. - `cargo check -p studio-desktop --target wasm32 --no-default-features --features signal` (the advisory Signal browser-remote check): red on `rig_view.rs`'s `libraries_view` reference, which is untouched by this PR. - iOS: `dx build --platform ios --device --release` with the studio-ios feature set built; `deploy-testflight.sh` signed it with the Studio profile and uploaded it (build 1792716084, marketing 0.1.2, manual test upload). Info.plist carries `StudioSceneDelegate`, display name Studio, the new icon and frame's faces. - Not yet run on a device or simulator. ## Risk and rollback Session in Studio's window on a desk: the window opens with the system title bar (Session's style); Signal's side removes it at runtime and draws its own. On iOS, Studio keeps Signal's launch settings (iPhone landscape only, `BLITZ_SAFE_AREA=0`), which now apply to Session too. Revert the PR to restore the two apps. ## Change files - `.changes/added-studio-the-one-b46a.md` (studio-desktop, session-desktop, app-shell: minor) - `.changes/pointed-comments-and-the-9b71.md` (nam-plugin, signal-mobile: none) - `.changes/sta-266-…` renamed its `signal-desktop` key to `studio-desktop` ## AI disclosure Tool: Claude Code (+ Codex for the icon). Extent: wrote all. Human reviewed: no. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Cody Wright <acodywright@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Studio-commit: FastTrackStudios/studio@729ca0c
…ong them (FastTrackStudios/studio#184) ## Summary The nextest run on main had 94 failures and 34 timeouts (run 39905030943), unnoticed because the tests job was advisory. They fell into four kinds: - **Missing data:** the private assets, the generated plugin faces and the mic models. - **Machine:** no GPU on the CI VM, and timeouts under load. - **Stale tests.** - **Real bugs:** - **sampler:** document mode played no legato transitions, its off-grid shorts spoke 14 ms late and it started documents in the wrong articulation; a note-on also allocated on the audio thread. - **web rig:** a stomped-on pedal played the dry guitar, and wasn't seamless when its worker fell behind. - **eq:** a clippy rewrite broke the spectral bell's math. - **stretch:** the run-up to a hit was smeared early, so quantized hits landed 2–3 ms off. - **Real-time allocations:** the Velvet reverb's tap list and PSOLA's pitch detector allocated on the audio thread, as did a rebuilt plugin's first render on macOS. - **session:** a setlist's later songs drew no waveforms. - **dynamic-template:** its checks looked for Process in the wrong place. - **frame:** the gate visualiser had a self-referencing slot. Golden DSP vectors now compare within a float tolerance instead of bit-exact hashes, which can't hold across x86_64 and aarch64. Three deliberate DSP changes, made months ago without re-recording, now have fresh references; the commits are cited in `8d1f5282`. ## Issue Refs: STA-184. Follow-ups filed: - FastTrackStudios/studio#178: blitz-fork click and hit-testing bugs; 2 tests ignored. - FastTrackStudios/studio#180: on macOS, Cmd+N is bound to both comp-take and window-set; 1 test skipped on macOS only. - FastTrackStudios/studio#182: the Velvet reverb costs a full core per instance. - FastTrackStudios/studio#183: a load-only `HITS_PLACE` race; 1 test runs alone. ## How tested - **Full workspace suite** on macOS aarch64, with the CI feature set (`--workspace --exclude winit-uikit`; that crate is iOS-only and leaves the workspace with the vendor move): **10,957 / 10,959** passing in the last run. The two that failed then were load-sensitive and are handled: kit-sheet's knob shot gets a 60 s timeout, and `edit_restore` runs alone pending FastTrackStudios/studio#183. - **Stress runs** for the timing-sensitive fixes: - worklet stomp: 30/30 alone and 45/45 run three at a time - `media_fetch`: 4/4 - `midi`: 3/3 - `edit_restore`: 5/5 - **Not run here:** Linux and lavapipe. The golden tolerances and the scene-picture rule are built for exactly the differences seen in the CI log, but this PR's CI run is their first test on Linux. - `cargo fmt --all --check` is clean, and the lockfile is unchanged. ## Risk and rollback - **Live behaviour changes:** - Libraries with a legato engine but no vibrato controller, such as Cinematic Brass, now play legato transitions. - Stretched hits now play their run-up as recorded. - EQ's spectral bell now reduces only inside its band. - **Not run here:** the orchestra legato tests, which only run with the CSS library installed. - Everything else is test-only. Revert is the rollback. ## Change files `.changes/`: - **patch:** sampler, web-audio-worklet, stretch, daw-standalone, eq, reverb, pitch, modulation, frame, dynamic-template. - **none:** mic-plugin, dsp-core, signal-browser, signal-live. `cargo rail change check` passes. ## AI disclosure Tool: Claude Code. Extent: wrote all. Human reviewed: no. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Cody Wright <acodywright@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Studio-commit: FastTrackStudios/studio@a18fad5
## Summary The two flaky tests, carried into the stack from FastTrackStudios/studio#192 and FastTrackStudios/studio#191 (cherry-picked with `-x`), so the stack's own Local CI run is stable. - `frame library::link::tests::server_in_process_client_subscribes` accepts the update and the index change in either order. It failed 5 times in 40 runs before the fix and 0 in 40 after. - The EQ editor's typing tests wait for every keystroke to land. Supersedes FastTrackStudios/studio#192 and FastTrackStudios/studio#191. ## Issue Refs: none ## How tested `Local CI` on the stack top: 10,947 tests passed. The frame test was already stress-tested in FastTrackStudios/studio#192. ## Risk and rollback - Test-only. - Revert to roll back. ## Change files .changes/frame-link-test-order-3c1e.md (frame, none), .changes/the-eq-editor-tests-8923.md ## AI disclosure Tool: Claude Code. Extent: wrote all. Human reviewed: no. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Cody Wright <acodywright@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Studio-commit: FastTrackStudios/studio@f0eb1b7
## Summary This cuts the test suite to a core set: **11,317 tests in 815 binaries become 648 in 39.** The suite cost more than it bought, because about 800 test binaries got built on every run, and that's why CI needed big paid runners. **Kept:** - `dsp-core` (184): the shared DSP math. - `session-format` (120): project files, where data safety lives. - `daw-standalone` unit tests (118): the engine core. - `timeline` (99), `daw-proto` (56), `studio`, `daw-transport-sync`, `utils`, `daw-control`. - Every effect's `param_round_trip`, which CLAUDE.md requires. **Removed everywhere else** (not `vendor/`): - Every `#[cfg(test)]` module: 1,295 of them in 1,152 files, plus the file behind each `#[cfg(test)] mod x;`. - Integration tests outside the set, their `[[test]]` tables and orphaned `tests/` helpers: 681 files. Other `#[cfg(test)]` items stay, because they're test-mode variants of production code, not tests. The non-test build is unchanged, and fixture data stays. **Follow-ups in the same PR:** - `just test`, `just ci`, local CI and the churn check build the Justfile's `test_packages` (the crates that still have tests). - `nextest.toml` loses the overrides for removed tests. Recipes that ran removed tests are gone: the fx and editor screenshot shots, `daw-scenes`, `daw-folder-items`, `ee-practice-test` and `drums-bench`. - The flow gate (FastTrackStudios/studio#48) lost the verifying tests for 39 implemented flow rules. They're listed in its exemptions file with that reason, so the gate still holds for every new rule. ## Issue Refs: none ## How tested - `cargo check --workspace --all-targets` with the full feature list passes. - The core set runs 629 tests, all passing, in 17–19 s. - `just daw-flows` passes. - `Local CI` is green: 7m44s. ## Risk and rollback - **Lost coverage:** about 95% of the regression tests are gone. FastTrackStudios/studio#184's suite found 12 real bugs, and this coverage goes with it. The deleted tests stay in git history. - **Rollback:** revert. ## Change files .changes/the-test-suite-was-e642.md (92 crates, none) ## AI disclosure Tool: Claude Code. Extent: wrote all. Human reviewed: no. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Cody Wright <acodywright@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Studio-commit: FastTrackStudios/studio@b79cf34
…ndency once (FastTrackStudios/studio#210) ## Summary Faster clean builds, from the dependency graph: - **No aws-lc.** Its build script, a cmake C build, took 186 s of a 375 s clean `cargo check --workspace`, all of it on the critical path. One crate asked for it: `tone3000` 0.1.0, through reqwest 0.13's `rustls`, whose only crypto backend is aws-lc. `vendor/tone3000` is the published crate on reqwest 0.12's ring-backed `rustls-tls`, which is already in the graph. There are no source changes and its 32 unit tests pass (`FTS-PATCH.md`). Clean check: 6m15s → 5m29s. - **12 fewer duplicated crates** (Cargo.lock: 203 → 191 crates with more than one version): - eq-standalone took manganis / dioxus-signals 0.7 from crates.io, which dragged in the whole dioxus 0.7 core stack. - reaper-launcher moves to icns 0.4. - The workspace skrifa is now 0.42. - ableton moves to quick-xml 0.41. - **Every external dependency declared once** in `[workspace.dependencies]`: 281 declarations in 72 manifests. Left as they were: declarations the workspace entry would give extra features (tokio `full`, uuid, image, …) and the graphics stack's split versions. - **A guard.** `scripts/check-duplicate-deps.py` fails when Cargo.lock gains a crate version beyond `.config/duplicate-deps.toml`. It runs in CI's Gates job, `just ci` and `just deps-dupes`. ## Issue Refs: none (build-time work) ## How tested - `cargo check --workspace` - `--all-targets` with `just test`'s features - all four wasm checks (`session-daw-web`, `session-web`, `signal-web`, `studio-desktop --features signal`) - the duplicates check - vendored tone3000's tests (32 pass) - `just ci-local` on the top of this stack ## Risk and rollback - **tone3000 HTTP:** the client now runs on reqwest 0.12. Its mocked-HTTP tests pass; a real TONE3000 sign-in and download hasn't been exercised by hand. - **Workspace inheritance:** no declaration gained features or changed version. Ones that would have are skipped (the script's report is in the commit message). - **Rollback:** revert. ## Change files `.changes/dedupe-deps-*.md` (`--bump none`: no user-visible change) ## AI disclosure Tool: Claude Code. Extent: wrote all. Human reviewed: no. Studio-commit: FastTrackStudios/studio@f6f3227
Stacked on FastTrackStudios/studio#354. ## Why kache 2.0 reuses incremental state across worktrees only on rustc 1.98 or newer. The user also wants the newest stable compiler. ## What - **The pin moves to `stable."1.99.1"`** (`nix/modules/toolchain.nix`). `rust-overlay` is now its own flake input, `oxalica/rust-overlay` at 6ecf534a: `dioxus-flake`'s copy stops at 1.94.1. No other input moved. - **The nightly is pinned by date** to the same one as before, 2026-04-05. It's only used for phon-jit's stencils and the wasm-threads worklet. - **`rust-overlay-rail` is removed.** cargo-rail now builds with the workspace toolchain; it only needed its own because rail required rustc 1.98.1 or newer. - **CI:** `xtask/workflows` `RUST = "1.99.1"`, and the workflows are regenerated. - **The two warnings 1.99 adds are fixed:** - `AtomicU32::fetch_update` → `try_update` (session-engine `stream_set`, frame `level`); - `ambiguous_import_visibilities` in `patchbay_service`: explicit imports replace `use super::*`. - Docs and comments updated from 1.94 to 1.99.1. ## Testing (on 1.99.1) - Dev build of both bins: pass, 7m 52s cold. - `just check`: pass. - Wasm checks: pass. - `just test`: 766 passed. - `just workflows-check`: pass. ## Note Every cache key changes with the compiler, so the first build after merging is cold. Some `#[must_use]` on constants in plugin-faces will become hard errors in a future compiler, and are worth a follow-up. Co-authored-by: fts-forge[bot] <345159799+fts-forge[bot]@users.noreply.github.com> Studio-commit: FastTrackStudios/studio@0e8dcba
The root Studio gave Frame, now here: the workspace (frame, its tools, plugin-faces, xtask), the dependencies at the versions Studio pinned at the cut (studio@fc6b1954: architect v0.9.31, vox 0.10.0-rc.5, Blitz fts-pointer-2026-10) and the patches of Studio's that reach Frame's graph, with Studio's Cargo.lock as the starting lock. - Rust 1.99.1 in rust-toolchain.toml and the flake's dev/ci shells (same nixpkgs and rust-overlay revisions as Studio), with mold and the Vulkan loader. - .cargo/config.toml carries Studio's rustflags tables in Studio's order, so kache keys shared crates the same in both repos. - The paths that named Studio's layout: plugin-faces' path to frame, xtask/frame/scripts -> xtask/scripts, the xtask's repo root, the faces' output (examples/plugin-faces/generated). - Every crate MIT OR Apache-2.0, version 0.1.0. - Lints: the rust lints as Studio had them and clippy's defaults, enforced. Studio's pedantic/nursery denies were never met by Frame (its clippy ran advisory) and are left out.
Mostly clippy --fix (collapsible ifs, auto-deref, manual impls). By hand: - wgpu_preview's frame-time buckets no longer overlap; - stray #[must_use]/doc pairs left on constants where functions moved; - feed::dsp's doctest names frame:: (Studio ran nextest, which skips doctests, so it never compiled); - Prop/Op keep their large variants (allowed): boxing them is a runtime performance call for frame#18; - three test helpers whose tests Studio cut (studio#203) are allowed dead.
The repo is private, so every job runs on the self-hosted pool, which sets RUSTC_WRAPPER=kache over the host's shared store. No RUSTFLAGS in the workflow: it would replace .cargo/config.toml's tables. `ci` is the one status to require.
Studio kept it in studio-assets; the parity scenarios move with Frame. Its files are byte-identical to 2dae10b's, so this adds no new objects.
Until Studio depends on Frame by tag (FastTrackStudios/studio#373): each Studio commit after the newest Studio-commit trailer that touches Frame's paths is cut to them, renamed into this layout, applied with git am -3 and given its own trailer. Checked by replaying studio@0e8dcba4 onto its parent here: the result's tree equals the imported commit's.
Frame is open source under MIT OR Apache-2.0 (Cody, frame#2). The README describes the one-crate layout, the recipes and where the history came from; CLAUDE.md replaces the old beads AGENTS.md (removed in Studio).
This was referenced Nov 2, 2026
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.
Closes #3. Part of #2.
What
Frame's history from Studio, joined to this repo's own history, so all of Frame lives here and history reads continuously from March 2026 to Studio's tip at the cut (
studio@fc6b1954, 2026-11-01).git filter-repoon a fresh Studio clone, keeping every place Frame ever lived (frame/,legacy/frame/,crates/frame/,apps/frame/,docs/frame/,docs/frame-themes.md,xtask/frame/, plugin-faces underfeatures/faces/andfeatures/ui/,scripts/face_gallery.py). Each path is renamed into this repo's layout:docs/frame/becomesdocs/, andxtask/frame/becomesxtask/.examples/plugin-faces, its original Frame path. It moves into Frame's examples (Cody, Tracking: Frame as a Control-Surface designer on architect and figcraft #2).studio@5052b171) onward are recreated on2dae10bwithgit commit-tree. Trees, authors and dates are unchanged. Studio's#Nrefs becomeFastTrackStudios/studio#N, and each commit ends withStudio-commit: FastTrackStudios/studio@<sha>. Because no existing hash changes,main(7975939) and2dae10bare ancestors and this merges without rewritingmain. The import commit's diff against2dae10bis only what Studio's commit message lists:library-fstaking anEventSink;rust-toolchain.tomland in the flake (same nixpkgs and rust-overlay as Studio, with mold and the Vulkan loader). Studio's rustflags tables are copied in the same order so kache shares builds. The paths that named Studio's layout are fixed.-D warnings, and thefeed::dspdoctest now compiles.-D warnings, check, test, and onecistatus.2dae10b, so no new objects).scripts/frame-catch-up.sh(just catch-up <studio>).LICENSE-MIT+LICENSE-APACHE: Frame is MIT OR Apache-2.0. figcraft's NOTICE comes with Depend on figcraft by tag (fts-v0.14.0-1), with a NOTICE and slim engine features #7, once figcraft is a dependency.Branches: backed up and checked
theme-runtimeandmerge-fleet-rc7(2dae10b) existed only on THEBATTLESHIP. They are now pushed as-is and asarchive/theme-runtimeandarchive/merge-fleet-rc7, and the Marchmainis taggedarchive/main-2026-03.worktree-agent-*branches, plus the named topic branches, against2dae10b:2dae10b: 23 branches, already merged.3b4d9ee536b2c03c87b10515c994e4594324d0e98a4315bb21f99eb1620a07a879a3fac033a6fa2a9738d52651376bdb8a5e2fd0988fd1fc3692520). Most of their blobs are in2dae10b's history.1a73736Blackout Gain suite,1d522dbModern Rhythm suite,e920a2bwip amp-suite kit parts). Every file they add exists in2dae10b, developed further by76db7e4"three amp-suite recreations". So they are superseded.worktree-agent-*branches. All local branches are also in a bundle at/run/media/Development/wt/frame-p0/frame-local-branches-2026-11-02.bundle.worktree-agent-a8950d35e9164cb1b, which is checked out in a live worktree. Its two commits are merged or superseded.final-*,suite-*,font-system, …). All are merged or superseded by the same check, but they are not worktree branches.Checks (local, in
nix develop .#ci, kache)cargo fmt --all -- --check: passcargo clippy --workspace --all-targets -- -D warnings: passjust check: passjust test: pass. Note how few tests there are: Studio'sstudio#203cut Frame's integration tests, which leaves 4 unit tests and 1 doctest.just catch-up: I replayedstudio@0e8dcba4onto its parent here, and the tree equals the imported commit's.git log --followon moved files reaches through2dae10bto their creation in Frame.Notes
--cap-lints warn). Frame had 5,593 of those, so its table keeps the rust lints plus clippy's defaults, and enforces them. Stricter lints can be added one at a time, with their fixes.just parityruns locally.crates/frame,docs/frame*,xtask/frameorfeatures/ui/plugin-facesare replayed here withjust catch-up.