Wave Summary

Field Value
purpose let multi-note epics accumulate safely away from main; bound LLM maintenance cost; close the last reverse-proxy and stall-detection gaps
integration window July 26, 14:53 to 22:54 PDT—8 hours 1 minute
baseline 31938769
final commit 4505cfbc
Git commits landed 15 total: 12 non-merge commits and 3 merge commits
contributors 4 Git author identities
change surface 37 files, 2,101 insertions, 285 deletions
principal feature branch:<name> note tags with queue-snapshotted target branches and target-aware gate, push, and rearm behavior
exit state main remains the default target; tagged notes can build and gate on a long-lived integration branch; distillation is bounded and usage-accounted; stall escalation and prefixed live-viz access are complete

Wave 3 made Yesod a recoverable multi-project factory. Wave 4 addressed the next scaling pressure: not every coherent unit of work should merge directly to main.

The immediate consumer was the AWS-hosted Yesod roadmap, whose features need to accumulate across several notes before they are ready to graduate. The factory needed a way to preserve the full arm, claim, gate, and merge discipline while directing those changes to an integration branch. At the same time, LLM distillation and factory monitoring needed boundaries of their own: bounded prompts, measured provider usage, and a human notification when the factory truly stalls.

The wave’s theme is therefore controlled separation. Work can separate from main without escaping the refinery, and intelligence can remain useful without becoming an unbounded background cost.

A Note Can Choose Its Integration Branch

The main feature uses the existing JSONB note-tag system. An untagged note behaves exactly as before. A note tagged branch:<name> opts into a separate integration target:

  1. The runner resolves the tag and bases the worktree on origin/<name>. If the branch does not exist, it creates it from the current origin/main and pushes the new branch.
  2. When the note enters the merge queue, the resolved target is copied into a new merge_queue.target_branch column. NULL means main.
  3. The worker merges the note branch into that snapshotted target, runs the gate against the resulting tree, and pushes the target branch on green.
  4. A tagged note never pushes its result to main. Graduating the integration branch to main remains a deliberate human action.

This snapshot is the critical safety boundary. The note’s tags can change while work is queued; the gate destination cannot. The worker gates the branch that the queue row committed to, rather than re-reading mutable policy at the most consequential moment.

The change spans the full lifecycle rather than adding a routing hint at one layer:

Lifecycle point Change Evidence
tag resolution validate one branch:<name> target and reject conflicting branch tags 615305c7, note_tags.py
worktree creation create or fetch the integration branch and use it as the base 615305c7
queue admission persist target_branch at enqueue time with migration 0038 23636825
gate and push merge, verify, gate, and push to the snapshotted target bc02b5b1
repair rearm a tagged note against its target instead of assuming main e714b843
compatibility tolerate old claim rows that have no tag data 0cff2f10

The default path stays boring on purpose. Existing untagged work still targets main, the single merge lock remains in place, and branch-targeted merging is not parallelized in v1. The system does not automatically merge an integration branch back to main, and it does not yet reconcile main forward into every active integration branch. Those omissions are explicit safety boundaries, not missing documentation.

The result is a new unit of factory composition. A roadmap can be decomposed into independently tracked notes, each note can receive normal verification, and the epic can remain isolated until a human decides that its accumulated state is ready for release.

main Stayed Releasable

Branch targeting changes the meaning of “merged.” A note can be fully gated and integrated without changing the product branch. That is valuable for large epics, but only if the queue retains an immutable account of where the work went.

The new queue column provides that account in the same durable record that already carries the note branch, gate state, and retry history. The worker’s merge and push paths now report the target explicitly, and refinery rearm checks merge cleanliness against that target. An operator inspecting a queue row can answer “which branch is this work trying to change?” without replaying tag resolution or reading a prompt.

This is the branch-level counterpart to Wave 3’s crash-only leases. Wave 3 made ownership recoverable after a process dies. Wave 4 makes destination ownership recoverable after policy changes. In both cases, the system takes a decision at a safe boundary and persists it before doing irreversible work.

Distillation Became a Bounded Maintenance Job

The factory’s LLM summaries had grown from a convenient lookup into a catalog- wide maintenance surface. The distillation work put an explicit budget around that surface:

  • select current usage notes, open work, and recent completed work rather than sending the entire historical note log;
  • cap the context and model output sizes;
  • include the model and exact context in cache identity, so a summary is not reused after its inputs or model change;
  • record provider input/output usage in PostgreSQL through migration 0032;
  • render cached summaries through the maintenance command and run the broad workflow nightly or manually, rather than triggering an LLM call on every push.

This makes “current understanding of the catalog” an observable, budgeted product feature. It also improves the quality of the summary: superseded and terminal history remain available to the database, but stale history does not crowd out the live work in every prompt (c8308820, ys-yes-a2xn).

The rollout itself supplied a useful warning. The first refinery attempt for the distillation branch encountered a migration-number conflict and unrelated baseline failures, producing a gate red even though the change was not yet on main. The branch was repaired, rechecked against the current base, and its retry budget reset before integration. A bounded maintenance job still needs the same merge discipline as product code.

A Stall Now Pages a Human

The propulsion daemon previously nudged agents when it found idle or stalled work. Wave 4 adds a separate human escalation: when the factory meets its stall criteria, eSPS invokes the email-stephen-devfactory process through fmail (a41930cc, ys-yes-apq1). This is intentionally not another agent nudge. Agent mail coordinates work; the human-facing channel reports that the factory itself needs attention.

The wave also narrows the runner’s claim fast path to the intersection of armed and ready Beads (48face33). That avoids ranking work that cannot be claimed and makes the scheduler’s hot path reflect actual eligibility rather than catalog volume.

Dispatch received a small but explicit capability update as well: the codex-gpt-5.6-sol-max lane exposes Sol at maximum reasoning effort (e4ad6d1e). Together these changes make resource choice and escalation visible policies instead of accidental behavior hidden in a daemon loop.

The Proxied Dashboard Became Complete

Wave 3 made the dashboard’s links and WebSocket path reverse-proxy-aware. Wave 4 fixed the remaining custom API choke point. refineries.html was still calling six /api/... endpoints through an absolute-path helper, so the page worked directly but showed an endless “connecting” state under /yesod/viz/refineries.html. The fix derives the API base from the current pathname inside the page-level helper, with tests covering the prefixed and direct forms (06a6e5c8, ys-yes-iq28).

This is a small patch with a large operational consequence. A dashboard that loads HTML but cannot reach its data source is more misleading than a page that is visibly unavailable. URL construction now belongs to the layer that knows how the URL is being assembled, while the server-side prefix rewriter handles ordinary HTML and redirect surfaces.

The final watchdog test fix belongs to the same “red means broken” standard. The supervisor overrun test had crashed an xdist worker during an unrelated front-end gate. The test now joins its watchdog thread inside the os._exit patch, keeping the test’s process-control seam contained (deda1f07, ys-yes-2ch8). A front-end change should not be rejected because a timing- sensitive test can kill its worker.

What Shipped, and What Stayed Out

Area Shipped result Boundary at wave exit
integration work tagged notes, auto-created branch bases, queue target snapshots, target-aware gate/push/rearm branch graduation to main is human-controlled
safety immutable merge destination and legacy-row compatibility no parallel branch merging or automatic main-to-epic synchronization yet
LLM maintenance status-aware bounded context, output caps, model-aware cache, usage metrics, scheduled rendering distillation still depends on the provider and its recorded usage
propulsion runner claim fast-path narrowing and human stall email paging is escalation, not automatic diagnosis or repair
dashboard prefixed Refineries API requests work behind the deployed path direct and proxied surfaces still need parity tests as new pages appear
dispatch Sol max-effort lane and explicit lane cleanup lane economics still require observation in real work

The New Exit Criterion

Wave 3 asked whether the factory could recover from process, host, repository, credential, and network failure. Wave 4 asks whether it can grow a coherent feature without forcing every intermediate state onto main, while keeping its own intelligence and monitoring within explicit budgets.

The next meaningful proof is an end-to-end integration-branch epic: several tagged notes should build on one another, each should gate against the snapshotted target, and the final branch should graduate to main only through an explicit human review. In parallel, distillation usage and stall paging should be observed over a maintenance cycle rather than judged only by unit tests.

The durable lesson is that a factory needs more than a merge queue. It needs a safe place for work that is not ready for release, a durable record of which place was chosen, and a bounded amount of intelligence watching the whole system. Integration branches provide the first capability; budgets and human escalation keep it governable.