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:
- The runner resolves the tag and bases the worktree on
origin/<name>. If the branch does not exist, it creates it from the currentorigin/mainand pushes the new branch. - When the note enters the merge queue, the resolved target is copied into a
new
merge_queue.target_branchcolumn.NULLmeansmain. - The worker merges the note branch into that snapshotted target, runs the gate against the resulting tree, and pushes the target branch on green.
- A tagged note never pushes its result to
main. Graduating the integration branch tomainremains 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.