The node world: ffrwd:av 0.19.1 - #3
Merged
Merged
Conversation
…ts SQL One module interface: typed input ports with a clock and a pairing each (lockstep, hold, interval, arrival), typed outputs with a format, a time base and a latency, describe without params and shape(params, bound), one tick per process call with frames fetched on demand, progress from the end of each tick's interval. types, values, encoder and decoder carry on; every other 0.18.0 interface is frozen in worlds/0.18.0 for the host to adapt. Cookbook recipes 145 to 154 are the SQL surface, red until the compiler and sidecar land: rows-only returns, structural row matching, one call evaluated once, mixed kinds, optional inputs, many-ports, several outputs, ffrwd.merge_spans, explain's delays, and a node with no inputs as a source. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… read their frozen copy Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ructural rows A LANGUAGE wasm function over a node module reads describe for its params schema and asks the sidecar for its shape per distinct (module, params, bound) call, cached. Arguments bind ports by position and name: DEFAULT NULL on any stream or rows parameter, arrays on many-ports, mixed kinds in any order, typed rows parameters as data ports with a schema, a hold port given as a port number. A rows-only return is a run-time data stream; a STRUCT return names the outputs the node makes; RETURNS source on a node with no streams is a relation in FROM. A SQL function may return a stream and its rows as one record. Row matching is structural: a reader's fields must be in the producer's port schema with the same type, and extra fields pass. One call with the same module, arguments and params is one node wherever it is written. A signature only a node can carry keeps the older world's refusal on the declaration and raises it at lowering once describe says the module is no node, so the seven tests that pinned that refusal now read it there. LIVE_LEAD is declared, with its errors.md entry; timing.py holds the delay sums and is not yet wired into compile or explain. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
spot, ring, dim, hear, burn, inset, tile, matte and ticker, written with ffrwd-node 0.1.0 (pinned by tag) and a shared stand-ins crate for the mark finder, drawing and a bitmap font. Each exports ffrwd:av/node at 0.19.0 and is the fixture its recipe compiles against. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ith delays explained A region holding a node is a node network: pads bind ports by name, unlatched outputs are left off, every stream between the region and another process rides one NUT, a region takes several inputs and outputs, and the wire format of each edge comes from the ports it feeds, through a split where one stream feeds two nodes. Nodes with no inputs are sources, ended by their reader's -to; packets outputs and inputs are wired; ffrwd.merge_spans is the host's rowmerge with max_span; params past 4 KB go by -params-from; a hold port fed by a port number is listed under the command. explain's JSON gains "timing" and explain --delays prints each node's window in streaming words and each output's delay behind the source; compile sizes edge buffers from the same sums, warns past 512 MiB held (HELD_STREAM) and refuses a live lead that cannot be met (LIVE_LEAD). dialect.md carries the node world's rules. Recipes 145 to 154 are pinned from real compile output against the nine stand-ins and a node-aware sidecar; 151 no longer writes its rows as a track, and 153's prose names where the sound waits. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
mod world_0190 binds node-module, values-module and codec-module from wit/; node_world.rs is the node binding, the tick resource with frames fetched on demand, same resolved to the input's own buffer, and set-params refused when the shape changes. --describe reports a node module's world, and --shape prints the WIT's node-shape as JSON for a module's params and bound inputs, the adapted shape for a module of an older world (NODE-SHAPE.md). node.rs holds the shape, tick and emission types with the host's refusals and the progress rule; tick.rs assembles ticks: window and stride, lockstep with audio re-cut to a video clock, interval with ahead and latency, arrival, data and packets clocks, earlier rows. adapters.rs gives every older world a node shape and node_loop.rs runs them all through one loop; the five single-world run loops leave main.rs, each world's command-line setup kept word for word in legacy.rs. Frame modules run as nodes inside the existing lanes. The sidecar's suite and the cli's pass as before; NODE-CLI.md is the spelling for node calls that the compiler lowers to and the host reads next. The six things the adapters could not say in a shape are B1 to B6 in the plan's gap log. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
# Conflicts: # sidecar/modules/Cargo.toml
… as one NUT, and accepts is checked between nodes The one-output rules that a module process writes one stream now apply to old-world modules only: a node network hands several streams to one process as one NUT with a -map per stream, and a group holding a node may take in a split. A node input fed by another node's output is checked against the port's accepts, through splits and through like, and refused naming the node, the port and both formats, with the ffmpeg filter that converts as the hint. Recipe 151 dims the picture by the matte's rows and writes the matte beside it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…pelling, shape modules A network binding any node module runs on the node graph: every -i is one NUT read through PushDemuxer, one reader thread per input and one writer per output, several -map before one output interleaved into one multi-stream NUT (ffrwd-nut 0.1.5, patched to its branch until tagged), data labels as NUT with heartbeats or as NDJSON, webvtt, srt or null, and the run ending with exit 0 once every reader has closed. The parser takes [port=pad], [port=label], [N:v|a|d[:k]], [@rows=label], source chains with no input and -params-from name=file. rowfilter runs on data edges and rowmerge=max_span is the span reducer. Each port's accepts is checked against its bound stream at init. Any pure node runs frame-parallel in lanes.rs: ticks are cut centrally before dispatch, a state input opens every instance up front and hands each the rows of the ticks it did not process, credit backpressure covers lanes and output queues, and the last call goes to the instance that said finished. A video or packets clock waits for the next frame before it ticks, so progress is the same at every -jobs. Nine shape test modules on raw bindings, 13 host tests asserting identical bytes at -jobs 1, 2 and 4 and the first, a middle and the last tick. The spike's benchmarks match a 0.27.4 build within 2.5%. Gray on the raw wire (Y800). B7 to B12 in the plan's gap log. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The starter crate implements Node for one video input as the clock and one output like it, hands the picture back, and names the one place the work goes. ffrwd-node and ffrwd-frame come by git tag; there is no build.rs and no wit copy, and the README's build line has no install step. The scaffold tests build the crate, describe it as node-module, compile its recipe and run it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…reach the sink, a link wins on install NUT carries no colorimetry, so a module reading yuv420p was told "unknown" and converted as tv BT.601 whatever the source. The compiler now writes each raw picture's probed colour, or what leaves a filter it placed, as the -pad of the -i that carries it, and a network of node modules reads -pad into the bound stream's colour, row and rendition. shape-canvas reports the colour it was handed on its first tick, and the shape tests prove it arrives. A node's rows under unnest(f.video) v WHERE v.index = n are one stream's and reach a media sink as a track or a rows file. A bare install drops a pin that a link has since answered for, so the lock comes out as a fresh install with the link standing would write it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ne source crosses a region once Row matching reads a producer's schema through the rows nodes over it: a row filter passes it through, a span merge adds end_t and types id, so a reader wanting end_t accepts ffrwd.merge_spans over per-tick rows. Ports of one region reading one source stream in one wire format bind to one stream of the edge, split inside the network, instead of a copy per node. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
hold.rs pairs a source's frames onto a node's clock: one group per hold input or per hold.group, the newest frame at or before the tick with repeat and skip counted, audio re-cut to the tick and none while behind, shared-clock held until the clock reaches the first frame, first-frame primed on lead seconds and scheduled on the clock's grid, tagged choosing by the source's tags, linger and timeout, a jump a new feed, ends foretold on the grid, every feed's start and end reported as a row. feeds.rs is the host-owned loopback listener per group: one connection at a time, 16 MiB received only while there is room, NUT demuxed with ffrwd-nut, headers matched to the group's members, pictures conformed between rgba and yuv420p at any size and sound between s16 and f32. A hold input with a port_param the call left unbound is served at init; a bound one gets a free port written into its param. Interval inputs are bounded by the clock's arrival and late messages reported. shape-switch does what the switch did as one node with none of its code, and tests/live.rs feeds it over TCP through fifteen cases: the switch's holes, timed, late, gone, sync, layer and throughput, with the feeder throttle its README measured gone at 720p and 1080p. ffrwd-frame is patched to its yuv branch beside ffrwd-nut until both are tagged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…added Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…t, one picture per wire format A node called over an input's stream in FROM is read at compile time as a packet sink was: its shape must read packets there and make no output, the copy follows the port's wants, and its rows are the relation (index's records and spaces). A column handed to a port declared DEFAULT NULL may come back NULL from an outer join's gap, and that instance leaves the port unbound. A region no longer takes in a split whose node readers want different wire formats; the feeding ffmpeg converts each copy and the copies that agree still merge. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… reach the node, .* reads what the shape makes A run-time lateral's streams on a node's hold ports with a port_param take a feeder connection, one per hold group, the picked port written into the param and the lateral's writer placed against it, conformed to what the ports accept. Tags a query writes on a stream a node reads go to the region's -i as "tags" in its -pad beside the colour. (<call>).* in a WITH body writes the fields the call's shape makes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…rce, laterals on a many hold port A RETURNS sink node is lowered as the 0.17 packet sink was: the SELECT's rows bind its ports by kind, the WITH options shape the encoder fronting each media cell, one -pad per input. A node whose describe has a rows schema gets an @rows output written as the run's report. A node network's packets keep their inputs' clock and a source's are its own. A lateral's data argument may be a one-element array. A lateral-fed hold port counts as bound when the shape is asked, and several laterals feed one many port, each its own connection, their ports a list where the param takes one. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…, a scalar stays its first type Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…he Go modules as nodes merge_spans keys a span by start_t and id. Rows in an ndjson file carry pts and time. Heartbeats go only to a pipe. On the packets path: a format-less packets output takes its clock's coded stream and delay, order is checked on a present dts, the header keeps its frame rate, a dts going backwards is dropped, and the first tick reports nothing late. -pad carries tags into a stream's info, a feed group's refused sound member is marked absent, an audio member gets its exact at, feed ports listen before the run's inputs open, an rgba feed into an rgba port is accepted, a rate source is not run ahead of a hold beside it, one stream reaches two ports of one node, and a port param may be a list, one listener per entry. The last writer keeps its output open until exit, so a consumer's EOF arrives at exit and the cli's cascade window stops misreading a slow exit. scheduler.rs is gone: the older worlds run through the node lanes by older.rs. invert-go and window3-go are nodes on both Go roads. Benchmarks at or above 0.27.4. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…tap is an ndjson pipe, a node's stream into a sink is encoded A node network's data output read by several processes is written by the network, one output per reader; the once-handed rule no longer applies to a node's data labels, so a label read by a lateral and by a publisher compiles. A lateral fed by a node reads its messages from an ndjson pipe the region writes and the relay joins, not from an ffmpeg copying NUT to tcp; an older module's tap keeps the tcp path. An edge into a packet sink is encoded by the WITH's encoder when a node produced it, and a node's data reaches the sink sidecar to sidecar. The demo's head compiles as written with 9 processes where the 0.28 plan had 14. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… leaky is a host node of a network A connection brings any subset or superset of a feed group's members; the members it does not carry are absent from that feed, and the offset comes from each source's own lead, the picture if it brings one, else its first member. leaky runs inside a node network beside rowfilter and rowmerge, so a picture from a node source keeps near the wall. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Each packet is judged in decode order by its dts, a keyframe on its own lateness, and a late packet drops the rest of its group up to the next keyframe that is in time; a packet with no dts is never behind. The 0.28 leaky only ran on decoded frames; a leaf keeping a subscribed broadcast near the wall runs it on the packets now. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Nothing renamed. wants.timing for a frame input never fetched; shape's bound list carries a binding per input with a stream hint (the rate) per stream; interval gains an anchor and a hold group; feed-start says when the start became known; the tick says its ordinal and which feeds ended since the instance's previous call. 0.19.0 was never published, so this replaces it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Nothing renamed. wants.timing for a frame input never fetched; shape's bound list carries a binding per input with a stream hint (the rate) per stream; interval gains an anchor and a hold group; feed-start says when the start became known; the tick says its ordinal and which feeds ended since the instance's previous call. 0.19.0 was never published, so this replaces it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…o a node's grouped data input A run-time lateral may return a data_stream column beside its picture and sound. Handed to a node's data input whose interval names a hold group the lateral feeds, it takes that group's port and connection: the instance writes picture, sound and rows into the one NUT, and the input binds no pad. A stream of the query's own bound to such an input while a port serves the group is refused, since its rows would never arrive. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sked with The host now runs the compiler's list as it stands, so the compiler writes it: -bound <name>=<json> per node call, in chain order, the same JSON --shape --bound took, port-served inputs and labels included. The -pad rate goes, and so does mimicking the host's order and label rules: the shape is asked with the compiler's own list (again only where an audio port conforms a sound to another rate), and a node's output is hinted at its clock's rate once more. A node read in FROM carries its list on its reader, a node sink its second list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… timing inputs carry no bytes -bound <name>=<json> per node call, the k-th for the k-th chain calling a name, is the list the shape is asked with and the hints each bound stream gets at init, checked against the pads; an input it names that no pad binds has to be one a port serves. -pad's rate is gone. A stream only timing inputs read is handed its frames' times and no bytes, an audio run its length in samples, and a port feed conforms nothing for such a member; fetch or same on one is a fault. The run counts the bytes it carried, which the timing test measures. A failed run stays stopped while a port feed's listener is joined, which hung before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hain order Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd does A name several chains call takes the k-th -params-from for its k-th chain, and more files than chains are refused. Two refusals whose messages ran their halves together read as one sentence again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An interval input anchored first-frame, or tagged without its tag set, holds no tick until its first message, which stands at the tick being made when it is first handed; that fixes the stream's offset, and every message and progress mark after is rescaled onto the clock's time base and moved by it. The node is told the clock's time base for the stream at init. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nnection An interval input naming a hold group is a data member of that group: a port feed's connection hands it the first JSON streams in shape order, progress marks dropped, and the group's offset places each message on the clock, handed on the tick whose interval holds it; a connection's messages go with its feed. Bound to a stream beside its group's, it waits on its progress as any data input. Bound by a pad when its group is served by a port, it is refused. shape-probe reports its feed and cues, and a live test sends picture, sound and rows on one connection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A sighting is a block of every frames of the run, named by the tick's ordinal and starting at its first frame's time, which the bound rate measures; any worker names a frame's sighting alike. Recipe 145 at -jobs 1, 2 and 4 writes the same picture and the same rows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both roads' bindings regenerated from the 0.19.1 world (wit-bindgen-go for TinyGo, componentize-go for mainline Go), and the four shape exports take the binding list. Every Go test runs and passes with both toolchains on hand. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only a grouped data input's marks go to its group; a held picture's moved its group's progress past its frames and cut one tick more. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…wd-frame v0.1.1 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ound Run by compile against the 0.19.1 sidecar and the rebuilt stand-ins: every node call now carries the bound list its shape was asked with, after -filter_complex, one per call in chain order; a node source carries []. Nothing else in the pinned commands moved. 153 prints the same delays. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… at its end The writer parsed each row into a serde_json Value to add the two fields and wrote it again, which sorted its keys and, without float_roundtrip, moved some numbers by one ulp (7.9005e-34 written 7.900500000000001e-34). The row is now read only for its keys, and the fields it lacks are appended to its own text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ts with its stage A process writing a run-time lateral's rows is held until the feeder port of the calls its instances feed accepts. Since a node region writes the lateral's tap itself, that region can also host one of those calls, or feed the programme its readers need before they listen: held, it waited for itself, and playout's lane-a-file, channel-file and the one-reader lane B started every member but it. A writer that reads its own connection, or whose readers read what it writes, now starts with the rest of the stage, and its lateral's first instance waits for the ports instead. A stage that ends without starting a held writer, or whose port was never heard while it waited for itself, fails with the writer named; before, both ran to exit 0 having written nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A port read for its frames' times alone is handed the stream as it is at that point of the plan. The format was the input's own even past ffmpeg.format, so a yuv420p source formatted to yuv444p crossed as yuv420p, a conversion made for a port that reads no pixels; the stream now carries what the nearest format names, else the input's own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…faster than its turns A rate clock with inputs cut ticks while every input was ready, and an arrival input always is, so a node like moq's publish, whose outputs nobody reads, was cut ticks as fast as a worker ran them: 2965 and 4841 ticks of 1/50 (59.3 s and 96.8 s) for an 8 s source in tests/live.py. Until its inputs end, such a clock now cuts a tick when something has arrived that no tick has taken, or one period of its rate after its last tick, and an idle worker waits for that turn; the same run ends at 399 ticks (7.98 s). Every other clock is cut as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…me tick every run The end of a hold input bound to a stream was told on whichever tick saw its source close, which hangs on how far the reader had got, itself held back by the lane's queue: blitz's presence.sql told an ad's end on 6.933 s in some -jobs 1 runs where 6.750 s was due, and its lead-out frames differed. Such a source is now held `lead` ahead of the clock, as its priming already was, and its end is told only once the last frame's turn is within `lead` of the tick, which is what that tick waited for; a first-frame start also waits for a frame past its first. A port feed is told as before. NODE-CLI.md says when `ends` is known for each kind of feed. A test runs a first-frame feed at -jobs 1, 2 and 4, three times each, and checks the rows are identical and the end is told on its tick less the lead (it was told 4 ticks later before). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…'s offset A data input on a hold group whose hold inputs are bound to streams, not a port, was implemented and documented but never run. The test binds an untimed picture and a JSON stream on a millisecond time base of its own from one file, and checks at -jobs 1, 2 and 4 that each message lands on the tick the group's one offset puts it on, and that the feed's end is told the lead ahead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ce they end Once every input had ended, a rate clock with inputs ran on until its tick count passed the newest pts any input had reached. A publisher's arrival inputs carry whatever origin the feed has, so with a live feed at the epoch (1.79e9 s) and a clock counting from 0, moq's publish ran on at about 400 s of clock a wall second and never took its last call. An arrival input's pts now never hold the clock: once every input has ended, a tick takes what arrived untaken, if anything, and the last call follows at once. Paired inputs (lockstep, hold, interval) still have their last frame's time passed first, and a rate clock with no inputs is cut as before. shape_sink takes a `rate` for a publisher's shape. A file source stamped at the epoch through it at 50/s ended 21 ms after its source with 45 ticks; it ran on past a 10 s deadline before. moq's live suite: packets identical (240 in 8 groups), 244 ticks where the run-on made 399; the same run with its source at the epoch never ended before (stopped at 30 s, its last group unsent) and now ends with the feed, 245 ticks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
--jobs N still sets the count, never above the machine's cores; --jobs 1 is still the serial escape hatch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It is the one object in charge of cancelling an end's blocking calls, and the old name was one of the two words this repo keeps out. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ath patches gone Both are tagged now, so the workspace builds where the owner's checkouts are not beside it, which is where CI builds it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A needless borrow of a Copy closure in tick.rs, and the argument count of listen_for allowed as the file already allows it elsewhere. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Recipe 111's shown command is pinned as compile prints it, wrapped the way the exec test wraps it. The host's timing unit test builds shape-probe for wasm32-wasip2, so where that target is not installed (the module-free CI job) it stands down instead of failing the build. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…en the last Since the lanes replaced the scheduler (a71598f), what a lane hands an output waits in the writer's queue, up to twice the worker count and two. leaky judges a picture by the wall clock as it hands it on, so the pictures it passed sat in that queue and grew later than it judged them: behind a slow stage it passed runs of ten in a row and then dropped half a second or more, and the picture's lag swung by a second where it used to hold still. The older scheduler wrote an output from the worker itself, so a reader that had not taken the last picture held leaky back. A lane may now be unbuffered: it runs only once every lane it feeds has nothing queued and every output it feeds has written all it was handed. leaky's lane is, in a network of older modules and in a node network. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
The node world:
ffrwd:av@0.19.1, onenodeinterface in place of every older stream interface, with every module declaring its own ports, pairing and clock.What changes for a query: nothing that compiled before stops compiling; node modules declare
DEFAULT NULLports, rows-only returns andSTRUCTreturns only where a node makes several outputs (recipes 145 to 154 indocs/examples.md, rules indocs/dialect.md).What is new underneath:
nodeand runs every older world through adapters; one scheduler (lanes.rs) for every network, frame-parallel for pure nodes;ffrwd init --rustscaffolds a node on ffrwd-node 0.2.0;ffrwd/wasm0.19.1 ships fromsidecar/wasm. Every ffrwd package is migrated on its ownnodebranch and releases after this.Suites: sidecar workspace green (three wall-clock tests are load-flaky and pass alone); cli offline 5012; exec 499 passed with the five known environment failures.
🤖 Generated with Claude Code