Skip to content

0.57.0 - 2026-08-06

Choose a tag to compare

@timofriedlberlin timofriedlberlin released this 06 Aug 23:34
· 415 commits to main since this release

Release Notes

Added

  • A native workspace Board is now the complete cross-repository program authority (C-588).
    A default .flux/board.toml binds member repositories and canonical refs, active milestone,
    ordered program lanes, cross-repository dependencies, configured waves and planning documents.
    Plain flux board auto-selects it; board next and fleet schedule consume the same validated
    projection and never admit an unrelated ready story when a program catalogue exists. Board
    planning config, Fleet execution config and mutable Fleet state now have disjoint schemas and
    ownership, accepted decisions no longer surface as open attention, and workspace metrics report
    real program stories, milestone lanes, waves and members instead of legacy placeholders.

  • flux tui --fleet[=ROOT] is a native Board/Fleet operations surface (C-556, C-557, C-582).
    Explicit attachment validates one Fleet root, opens the reserved main coordinator's isolated
    store and exact recorded session, and journals conversation intake through accepted, delivered,
    and completed/failed acknowledgements. A typed in-process projection supplies a responsive
    attention rail plus bounded Overview, Board, Workers, Decisions, and Stats views from the same
    durable readers and exact flux.board-stats/v1 cube as the CLI; it does not capture ANSI, scrape
    tmux, or spawn CLI subprocesses. Refresh keeps the last-good snapshot on failure. Only explicit
    intake and a twice-confirmed open-decision choice may mutate state; push, release, deploy,
    Fleet-apply, cleanup, and capacity changes remain outside the TUI. Desired/draining capacity is
    shown as unavailable until canonical Fleet state carries it.

  • The attached Fleet main now has a closed coordinator runtime instead of an ordinary coding
    agent catalog
    (C-556). Fleet config binds an explicit operator-authored Flux-Lang loop; each
    turn supplies only the current request and a bounded native Board/Fleet catalog, bypassing the
    generic intent/explore and retained-history budget path. The parent can manage Board and Fleet
    state or start one task; that child uses a separately configured host-authored loop and an
    independently closed read-only research catalog with no generic create_plan, shell, edit,
    git-write, Board/Fleet mutation or nested delegation authority. Missing or invalid main/research
    loop config refuses at startup instead of silently widening behavior.

  • Every runnable agent now has a resolved, versioned loop binding (C-569). General omission
    resolves to the explicit adaptive@1 preset; CLI/SDK, role, nested-task, app and served starts all
    cross the same validation boundary. Start, status, stream and terminal receipts carry bounded
    profile/revision/source-digest/runner metadata, while resume reconstructs digest-addressed source
    and refuses a live-session switch. Fleet task kinds resolve through operator-authored loop policy,
    validate against the admitted capability ceiling, and snapshot exact source for message, restart,
    resume and rework. fleet.run receipts now also return admitted worker ids and wave linkage.

Changed

  • Board and Fleet public docs now define the complete domain model. Concepts appears before
    Coding, and the guides distinguish epics, stories, milestones, program lanes, configured waves,
    dispatched wave instances, workers, handoffs, review, gates, apply and release. Compact diagrams
    show story states, eligibility, isolated worktrees, review/rework and the separate publication
    boundary.

Added

  • flux fleet reclaim [wave] reclaims a finished wave's build output, and its worktrees when they
    provably hold no commit and no uncommitted change. Reclamation previously ran on acceptance alone, so
    every wave that ended any other way — cancelled, parked, conflicted, gate-red — kept its target
    directories and checkouts for as long as the fleet root existed. Disk is what caps how many workers can
    run, so those are not free: the first run on a real fleet removed 47 stale worktrees and retained the
    two that held work. Cancelling a wave now reclaims it in the same step. A wave that can still advance is
    refused rather than reclaimed, since deleting a build it is about to use would cost work rather than
    space.

Changed

  • A Fleet agent no longer pays disk for incremental compilation it can never reuse. Each story gets
    a fresh worktree and a fresh target directory that is discarded when the wave is reclaimed, so
    incremental artifacts were written and then deleted without ever being read — roughly half of a
    checkout's build output. Disk, not model concurrency, is what caps how many workers can run, so this
    is directly a width change. CARGO_INCREMENTAL also joins the forwarded environment allow-list,
    without which the variable was dropped between the agent process and the cargo it runs and setting
    it had no effect at all. CARGO_TARGET_DIR is deliberately not forwarded: it is a path, and one
    shared target directory is locked exclusively by cargo, which would serialize the parallelism the
    fleet exists to provide. An operator's own rebuilds keep incremental compilation.
  • flux board create commits the document it creates, path-scoped, with --no-commit to opt out.
    Items are resolved at a git ref wherever a board is federated — a workspace member's stories are read
    with ls-tree/show at its canonical_ref — so an uncommitted document is invisible to every read
    that matters. Twenty-seven stories were filed, reported as created, and could not be scheduled: the
    command had succeeded and nothing had happened. The commit names exactly the new path and never sweeps
    in unrelated dirt, it goes to the current branch rather than a side branch (a side branch would
    reproduce the same invisibility), it does nothing outside a git repository, and it refuses rather than
    committing into an in-progress merge or rebase. The reported envelope carries the commit sha.

Fixed

Added

  • A repository may declare a prepare step that regenerates its candidate's derived artifacts before
    the gate.
    Some checked-in artifacts are derived from a whole wave rather than from one story — a
    documentation mirror, a generated index — and they belong to the candidate, not to any story. Two
    stories regenerating one artifact collide, and regenerating it on either branch alone produces an
    artifact missing the other story's contribution. Observed on a real wave whose gate refused the
    candidate with embedded docs are stale while both stories were correct in isolation. The step runs
    once, after every cherry-pick for that repository and before its gate, and whatever it regenerates is
    committed into the candidate so it survives into the accepted tag. A preparation failure is recorded as
    that repository's failure rather than surfacing later as the stories being wrong.

Fixed

  • Accepting a candidate no longer requires the repository to have stood still. Two refusals were left
    over from when applying MERGED the candidate: a merge has to land on the base it was tested against and
    it touches the working tree, so a moved canonical ref and a dirty checkout were both real hazards.
    Acceptance now writes an annotated tag on the candidate and nothing else — it does not read the working
    tree, moves no branch, and cannot be invalidated by unrelated commits arriving on the canonical ref.
    Keeping the checks cost a green wave: one passed both repository gates and was then refused with "moved
    from its pinned base" because ordinary work had continued during the hours the wave took, which means the
    longer a wave is worth accepting the more certain it becomes that it cannot be. The invariant that does
    matter is still enforced — the candidate branch must point at the commit that was gated — and the base it
    was gated against is now recorded in the acceptance entry, because the later step that writes the
    canonical branch has to re-gate against whatever that branch has become. Acceptance is not landing.
  • A concurrent write no longer throws away an entire integration. Integration is the longest operation
    the fleet performs — cherry-picks, candidate preparation and a full repository gate, tens of minutes from
    cold — and it writes state several times along the way. Any coordinator write inside that window lost the
    compare-and-set and discarded the whole run: one real integration died on stale fleet revision 428; current revision is 429 after both gates had already produced their verdicts. Each of those writes now
    rebases onto current state, which is safe for the same reason it is safe for dispatch: the call replaces
    the record of exactly one wave, and that wave is owned by the integration for its duration, with the
    ownership recorded.
  • A retry no longer re-judges a repository whose candidate is already accepted. Acceptance is the end of
    that repository's road — its candidate is pinned by a tag and nothing later in the wave can improve it —
    so re-gating it spends the pipeline's longest operation to re-derive a known answer, and a re-gate that
    came out red would make accepted work read as failed. Stale preparation evidence is cleared too, so a
    retry cannot report work it did not do.
  • Reclamation no longer removes a worktree an unfinished wave still needs. Build output is regenerable
    and always goes; a worktree is structure, and worktree_holds_work answers "does this contain work?"
    rather than "is this still needed?". Reclaiming a wave whose worker turn had failed therefore removed its
    integration worktree — legitimately empty and at its pinned base — while a story worktree in the same
    wave held 940 uncommitted lines, leaving the wave with deliverable work, nowhere to assemble a candidate,
    and no operation able to rebuild the missing structure. Worktrees are now removed only for a wave that is
    applied or cancelled.
  • A failed integration can be retried once its cause is fixed. conflict and red were terminal, so
    a wave that failed kept a memory of failing and no later fleet integrate would touch it — which made
    fixing the cause pointless. Three delivered stories stayed unreachable after the defect that stranded
    them was found, fixed and installed, refused with "not ready for integration". Retrying loses nothing:
    the handoffs are still accepted and each integration worktree is reset to its pinned base first, which
    the retry path must do or it refuses itself for a different reason than the original failure. The
    guard worth keeping — never spend a second gate run on unchanged inputs — belongs to the candidate, not
    the wave, and is now enforced by comparing the recomputed candidate with the one already gated. The
    wave's verdict is also decided once, from the collected failures, rather than in each branch on its way
    out: deciding it per branch is exactly how a path slipped through without setting one, leaving a wave
    integrating with its owner released and permanently wedged.
  • Clipped output keeps the end, where a failure explains itself. Captured gate output was clipped
    from the head, and a repository gate emits thousands of Compiling … and test … ok lines before
    failing at the very end — so a failed gate's record filled its 16 KiB budget with progress and cut the
    error off. On one real wave both streams were exactly 16385 bytes and the reason for the failure was
    not recoverable from Fleet state at all. The budget is now split, two thirds to the tail, with an
    explicit marker naming how many bytes were elided; completeness is never claimed silently.
  • Integration applies the worker's whole commit range, not just the commit its handoff cited. A
    handoff names one commit and a story worker legitimately makes several — implementation, then its
    record, which is the shape the contract asks for. Cherry-picking the cited commit therefore applied
    only the LAST one and silently dropped everything before it: measured on one real wave, a two-commit
    story contributed only its documentation commit and a five-commit story likewise. It surfaced as a
    conflict, which was luck — a clean apply would have produced a candidate documenting code that was not
    in it, and a gate that never compiles the missing part could even have passed it. The evidence already
    assumed the range, since handoff verification computes the write set with diff <base> <commit>, so
    the record described a range the integration never applied. A handoff citing its own base is now
    refused outright rather than producing an unchanged candidate.
  • A conflicted integration leaves its worktree reusable. The half-applied cherry-pick was kept on
    disk, which bought nothing (the conflict evidence is recorded in state) and cost the retry: a wedged
    worktree fails the next attempt's "clean at its pinned base" check, so the wave reported a different
    reason the second time and could never recover on its own.
  • An interrupted integration can be retried. integrating is transient and recorded no owner, so a
    process dying mid-gate — the longest operation the fleet runs — left the wave integrating forever
    with every retry refused as "not ready for integration" and no escape but hand-editing state, which the
    operating rules forbid. Integration now records its supervisor and releases it on every exit, and a
    wave held by a process that is gone may be retried.
  • One repository's integration failure no longer discards another's candidate (C-630). Integration
    assembles and gates one candidate per repository, but the first repository to conflict, gate red, miss
    a gate argv, or hold a dirty integration worktree aborted the run for all of them — so a
    flux-internal collision threw away an exchange candidate whose stories were independent and perfectly
    integrable. Each repository's outcome is now recorded on its own, the error names both the repositories
    that failed and the ones that produced a green candidate, and fleet apply <wave> --only <repository>
    accepts a named candidate from a wave that is not wholly green. The per-repository green-gate
    requirement is unchanged: this relaxes which waves may be asked, not what counts as accepted. A named
    apply reports what it left behind, does not change the wave's own verdict, and does not reclaim storage
    the remaining repositories still need.
  • A dispatch is no longer discarded by a concurrent write. Creating a wave already happened on disk
    — a worktree and a branch per story — before the state write. If the coordinator or an operator wrote
    Fleet state in that window, the compare-and-set lost and the whole dispatch was thrown away with its
    worktrees already created and nothing recording them; observed as a dispatch failing the moment the
    coordinator accepted an intake. The write now rebases onto current state, which is safe here precisely
    because the call only ever adds keys nothing else could have written: a fresh wave id and its fresh
    worker ids. Re-running the action instead would be wrong, since worktree creation refuses an existing
    wave root and integration would spend a second gate.
  • Fleet state can now tell a live worker from a dead one. A turn runs synchronously in the process
    that recorded working, so that process dying is the one case where no terminal status is ever
    written: a worker read working for hours with nothing behind it, inflating the active count, keeping
    its wave out of reaping, and forcing the driver to reimplement liveness by scanning /proc because the
    answer was not recoverable from state at all. The supervising process is recorded when a turn starts
    and cleared on every terminal transition — on the wave path as well as the single-turn path, the wave
    path being the one that produced the stuck record — and an active record whose supervisor is gone projects as
    interrupted — which also routes it to the attention list, where it belongs. Records written before
    this keep their recorded status rather than being downgraded. Pid reuse can still fool the check, which
    is exactly the previous behaviour, so this is a strict improvement rather than a guarantee.
  • A handoff is no longer refused because a sibling test target filtered to nothing. cargo test -p <pkg> <filter> runs the package's lib unittests and each of its integration-test binaries, so the
    filter matches in one target and reports 0 passed; 0 failed; N filtered out for every other. The
    "did this run any tests" check was a substring search over the combined output, so it saw the
    non-matching target and concluded no test had run — silently refusing a handoff whose cited test
    genuinely ran and passed, and parking a wave that held delivered, committed work. Every summary is now
    counted, in both libtest and nextest vocabulary, and the run counts as executed if any target ran a
    test.
  • A lost update no longer reports itself as a contradiction. Fleet's compare-and-set printed stale fleet revision 392; current revision is 392 when a concurrent writer had already produced that
    revision with different content. Same message for two unlike failures, and unactionable for the one it
    described least. The two cases now say what happened and what to do.
  • Two stories in one repository may now write the same file. Integration refused any wave whose
    stories' write sets intersected — a proxy for "these commits will not combine", and within one
    repository wrong far more often than right. A wave of two delivered stories was parked because each
    had appended an entry to one changelog, exactly as the worker contract asked; and since nearly every
    story in a subsystem edits the same module, the proxy made more than one story per repository per
    wave impossible. The real test already followed: each accepted commit is cherry-picked into the
    integration worktree, where a genuine conflict fails with the conflicting files, git's own stderr and
    the preserved candidate. Disjoint edits to one file now integrate; overlapping edits are refused by
    git rather than guessed at. One writer per story remains enforced.
  • A running wave is now visible while it runs (C-599, C-602). A wave wrote durable state exactly
    twice — once before its first worker started, once after the last joined — and the surface's refresh
    token hashed only those files, so flux tui --fleet took zero snapshots for a wave's entire
    duration and a working fleet looked frozen. The supervisor now projects each worker's stream to a
    bounded activity sidecar as it arrives, the refresh token includes it, and a worker's rail entry
    shows live progress instead of its previous turn's events. The projection is structural only —
    event type, operation name, coarse outcome — so tool inputs, result bodies and the prompt never
    reach it.
  • The transcript no longer silently drops characters (C-341 revised). The vertical scrollbar is
    drawn as an overlay into the transcript's own rect, so any character wrapped into the last column
    was overwritten with nothing on screen to indicate the loss — a path, SHA or command output read off
    the screen could simply be wrong. That column is now reserved. This reverses C-341's "the overlaid
    scrollbar must not consume transcript width": one column of width is a smaller price than corrupted
    text.
  • Board and Fleet results summarize their shape instead of their envelope (C-599). Every
    board.*/fleet.* card previewed the first bytes of a bounded envelope — the identical
    {"data":{"bounded":true,"byte_limit":262144,… prefix for every call, and the widest possible line.
    Cards now read 31 workers · 16 waves · r276, with the full envelope one expand away.
  • The Fleet header names the wave that is actually running (C-599). Active-wave selection tested
    the inverse of a four-item terminal list, so any unrecognised status — including
    agent-turn-failed — counted as active, and BTreeMap iteration returned the oldest match. A
    long-dead failed wave held the header while another ran, with the same panel's worker list
    disagreeing. Selection is now a closed allow-list of in-flight statuses, newest first by wave
    number. Worker rows also prefer the current wave within a status class, so finished workers stop
    crowding out the running one.
  • flux fleet run --dry-run validates without writing (C-594). It promised "validate and return
    the proposed result without writing" and skipped worktree creation accordingly — then wrote a loop
    binding snapshot into a story worktree that deliberately did not exist, so every dry run failed with
    workspace root …/stories/<ID>: No such file or directory before returning a topology.
  • An operator-authored segment is no longer told its actions are only captured (C-597). Every
    ai_segment inherited the adaptive planner's system prompt, which states that Flux may capture an
    action instead of executing it and directs the model to hand a plan back through finalize_plan.
    That is correct for the plan-then-approve CLI path and wrong for an authored segment, where effects
    really do execute through the approve/execute batch path — and Fleet story workers complied with
    the prompt: one made 35 read-only calls, attempted no effect, and reported it could not run
    anything, while holding write, edit, bash and git_commit in its admitted ceiling. Authored
    segments now carry an execution contract instead; the evidence and grounding discipline is
    unchanged, and the adaptive planner's prompt is untouched.
  • An authored segment that reaches its history ceiling no longer destroys the turn (C-595).
    A Fleet story worker committed its complete deliverable and then had the whole turn discarded by a
    retained-history budget it crossed by 0.43%, so Fleet reported the one story that actually
    delivered as the wave's only failure. Crossing the ceiling in an authored ai_segment now sheds
    the oldest tool-result payloads into tool_result_omitted digest receipts — the same shape an
    oversized single result already used — and keeps running with the most recent exchange verbatim.
    When elision cannot free enough, the segment returns its evidence ledger as a result instead of an
    error. ai_segment accepts max_history_bytes to raise the ceiling for a long implementation
    loop. Ordinary adaptive turns are unchanged.
  • A failed worker turn now records the session and receipt it already proved (C-595). The turn's
    parsed session id and event stream — including any commit the loop made before it failed — were
    read successfully and then discarded, leaving a failed worker with no discoverable deliverable
    and no fleet rework re-dispatch (that path requires a recorded runtime session). Both are now
    persisted on the failure path alongside the error.
  • An operator-authored ai_segment now runs with the exact tool ceiling it declares (C-593).
    The adaptive intent router's four-family cap bounds what the model may select; it was also
    applied to the deterministic family union an authored tools: ceiling expands to, so any authored
    loop spanning five or more families failed before its first tool call with adaptive capability declaration selected N distinct families; the maximum is 4. A Fleet story-implementation loop
    naming read, write, git, shell, datasource and system operations could never start a worker. The
    cap now applies only to a model-declared selection; the independent operation-count and
    schema-character budgets still bound both paths, and an adaptive state serialized by an older
    runtime still resumes capped.
  • Workspace-root errors now name the offending path. Workspace::new and Workspace::with_root
    reported a bare workspace root: No such file or directory while Workspace::new_optional
    already named its path, leaving failures reached through System::rerooted with nothing to go on.
  • flux fleet status and dashboard now return the bounded flux.fleet-status/v1 operational
    projection instead of embedding durable last_turn receipts, tool events, answers and intake
    bodies. Current lifecycle state—not stale receipt text—drives active and attention counts, and
    terminal cancelled/completed workers no longer keep old errors in the attention rail. The compact
    response keeps exact worker, wave, Board ref, session and repository identity plus targeted
    inspect commands for deeper evidence. Targeted Fleet inspection is bounded too: a 256 KiB
    structural response ceiling preserves terminal facts and replaces oversized evidence with
    indexed omission metadata referencing Fleet state or the event journal, including digests for
    omitted strings.
  • Structured JSON tool results are now redacted as JSON values before reserialization. A
    credential-shaped substring inside an escaped model-stage state can no longer consume an escape,
    invalidate the object, and make an authored loop see its typed result as opaque text; results that
    need no redaction remain byte-for-byte unchanged.
  • An agent attached through flux tui --fleet can now enumerate durable native Fleet workers with
    the bounded fleet.agents operation instead of asking the operator to supply guessed worker ids.
    The attachment-only read uses the same state as flux fleet agents and the Workers view and stays
    distinct from the pointwise transient-process fleet.worker_status operation. Both census paths
    return role, task kind and resolved loop identity without embedding instructions or historical
    turn receipts. The validated attachment pre-authorizes this bounded census, while an
    operator-authored deny still wins.
  • Fleet-main Board/Fleet reads now use bounded native projections and require no approval. In
    particular, fleet.status reads the compact inspect snapshot rather than serializing historical
    worker turns and waves into the model context. Typed mutations retain revision, idempotency and
    acknowledgement guards, and the installed catalog cannot refresh back into general coding,
    plugin, pane, eval or transient-process operations between turns.
  • Durable loop bindings now canonicalize the set-valued required-operation and runtime-feature
    fields at every reconstruction and live-session comparison. Sessions admitted by an older build
    resume when only insertion order differs, while source, digest, profile, revision, runner and
    entry-point drift still require an explicit new session.
  • Fleet-attached TUI startup and refresh now build Board/Fleet projections away from the terminal
    event loop. A cheap durable-state token avoids redundant rebuilds, explicit loading/unavailable
    states replace a frozen screen, and a failed later refresh keeps the last-good view marked stale.
  • Concurrent fleet.isolate calls now serialize Git's shared worktree-administration mutation
    while retaining disjoint checkout allocation, preventing partially written .git/worktrees
    metadata from breaking a parallel Board/Fleet wave.
  • Release checksum verification accepts cargo-dist sidecars with trailing blank lines while still
    requiring one exact lowercase digest/filename record and at least one newline terminator.
  • Candidate artifact preparation now verifies and reuses the controller's successful exact cut-CI
    run instead of rebuilding, testing, linting and formatting the same release a third time.