Agent ensembles arrive — scheduler-driven fan-out, Channel.await fan-in, and a clock for agents (RFC S, v0.25) Loomcycle released v0.25 "agentic-ensemble" with three new primitives—Context op=time, Channel.await, and schedule max_fires—enabling scheduler-driven fan-out and fan-in for agent ensembles. Experiment 5 validated the release with five parallel RSS collectors, a consolidator using Channel.await, and Telegram output, running both static and fully-dynamic variants. Three follow-up fixes (F37, F38, F39) addressed scope resolution, agent registry, and env interpolation issues surfaced by the experiment. Experiment 5 ran end-to-end as a real agent ensemble: five RSS collectors Hacker News, Wired, Engadget, Ars Technica, TechCrunch fired in parallel by loomcycle's scheduler every five minutes, each on success pinging a fan-in channel via the scheduler's on complete hook, a consolidator scheduled one minute after each crossing calling the new Channel.await {channels, mode: at least, n: 5, wait ms: 120s} combinator and waking when all five collectors had reported in or the budget elapsed , URL-deduplication across 25 items, one Telegram digest as output. Three cycles then max fires=3 auto-retired every collector schedule. Both static and fully-dynamic variants every entity created at runtime via REST validated on v0.25.3 with zero workarounds. The v0.25 "agentic-ensemble" release ships RFC S — three primitives the substrate had been missing for ensemble-shaped pipelines. P0: Context op=time gives every agent a wall-clock closes F34 — pre-RFC-S Context had op=tools/doc/help/self but no op=time, so cycle bucketing required shelling to Bash date which dragged Bash into the collector's allowed tools surface for no other reason . P1: Channel.await {channels, mode: any|all|at least, n, wait ms} is the fan-in combinator across N channels with race / rendezvous / count semantics and a hard deadline closes F35 — pre-RFC-S Channel.subscribe was single-channel with no AT LEAST N / OR / AND combinator; a consolidator had to poll one channel in a loop and count distinct ids, burning max iterations with no clean timeout; the only native AND-barrier Agent.parallel spawn bypasses the scheduler by coupling N children to one parent run, exactly the coupling an ensemble is supposed to avoid . Symmetric Channel.broadcast publishes one payload to N channels in one call. P2: schedule max fires retires a def after N successful fires closes F36 — pre-RFC-S a ScheduledRun fired indefinitely until manually retired; a test ensemble had no way to express "this is bounded"; a forked def can lift the cap with explicit max fires:0 . Three follow-up fixes each surfaced by exp5 itself, all shipped within 24 hours. F37 422, v0.25.1, RFC T : the scheduler's on complete: channel.publish hook was publishing under the run's user scope, ignoring a channel's declared scope: global, so a global fan-in channel's pings landed at user/exp5 and a global Channel.await reader saw 0 messages; the v0.25.0 workaround was scope: user, sort-of-worked because all five collectors shared user id but a fan-in channel SHOULD be global. The fix is a ResolveChannelScope resolver wired into the scheduler so the publish hook uses the channel's declared scope, not the run's. F38 424, v0.25.2, RFC U : the fully-dynamic exp5 variant every entity at runtime failed with "unknown agent: exp5-collector" — the scheduler fire path was resolving agents from the yaml-static registry only, not the AgentDef substrate store; same shape as the F30 webhook-spawn fix from exp4. Fix: scheduled runs now stamp the def's tenant on spawn, agent resolves against both registries. F39 426, v0.25.3, RFC V : the fully-dynamic Telegram leg got HTTP 404 because the bot received the literal string ${LOOMCYCLE TELEGRAM BOT TOKEN} as its API token — dynamic stdio MCPServerDef env values weren't being interpolated against the runtime's env at spawn time, only at YAML-load which the dynamic path bypasses by design . Same envelope-of-references pattern hit in MCPServerDef header expansion back at v0.18. Fix: expand ${ENV} references in the dynamic stdio MCP def's env map at spawn time, using the same prefix-allowlisted resolver the rest of the substrate uses. The agent-ensemble term: a multi-agent system is not an ensemble. An ensemble needs primitives that survive the loss of any single agent's run. Each agent has its own run independent run id, independent lifecycle, billed and OTEL-spanned independently . Coordination flows through the substrate channels, memory, scheduling, Channel.await , not through call stacks. The ensemble outlives any single agent. The ensemble can be authored declaratively data, not control flow . Companion change: every prior experiment exp1-exp4 now ships as a self-contained directory under loomcycle/examples — each carries its own loomcycle.yaml, run.sh launcher, .env.local.example template, and reproducible README, routing Anthropic-OAuth-primary with a DeepSeek fallback so an operator can clone and exercise the substrate in minutes. exp5 joins next. The substrate is now ensemble-shaped, not just agent-shaped.