Skip to content
All releases

Release

v1.2.21

What shipped, in numbers

15
PRs merged
33,005
Lines added
5,839
Code lines
Code
5,839
Tests
8,226
Docs
18,940
Sum · code + tests + docs
33,005

of 15 merged

  • bug 8 54%
  • feature 2 13%
  • polish 0 0%
  • docs 5 33%
  • groundwork 0 0%
  • unclassified 0 0%

since v1.2.20 · tagged

What shipped

The release that stops the cockpit shouting, and gives the Gantt something worth drawing. Two defects made a whole evening unworkable: a notification that paged the operator dozens of times for one event, and a timeline that drew every ticket in a sprint as the same bar. Both are closed here, and the second one closes with the tooling that stops it coming back.

The rail stops paging you for nothing

  • 🔴 One undeliverable verdict could page the operator forty times. Every line said "needs your attention", for a message no human could act on. Three defects stacked: a retry with no backoff over a failure that never resolves on its own, no dedupe on the notice, and — the big one — queued was treated as a failure. queued is a documented SUCCESS: the target was mid-turn and the message is processed when its turn ends. A worker being mid-turn is the NORMAL case for a relay, which is exactly why one event produced forty lines. The retry now asks the queue before paging, terminates instead of looping, and a clean GOLDEN is no longer pushed at a worker that has nothing to do with it (#5681).

The timeline draws the plan, not the milestone

  • 🔴 Every ticket in a sprint rendered the same bar. The Gantt discarded each ticket's own dates and regenerated them from Created and the inheritance-resolved Due. On GitHub there is no per-issue start or due column, so Due came from the MILESTONE — identical for every ticket in it — and Created is the emission timestamp, identical for a batch. Identical inputs, identical bars, for every customer on every sprint, no matter what dates the tickets carried. The ticket's own dates block now wins, and the synthesised one remains the honest fallback for a ticket that has no plan of its own (#5705).
  • A sprint's dates now derive from its dependency graph instead of being typed. Every ticket states its OWN duration, +20% breathing room is applied per ticket rather than pooled at the end, and start/end fall out of the Depends-on: edges — so two tickets with no edge between them overlap, which is the entire reason the chart is worth opening. flowforge sprint-audit is the detector: it reads the BOARD, so it catches the flat rectangle no matter which door created it, including the per-card date editor no lexical guard can reach (#5666).
  • The founder's own words for the defect this ends: "setting the tickets to begin date the begin of the sprint, and end date the end of the sprint turns a Gantt in something completely unreliable and with no point." A chart where every bar spans the sprint has no critical path, no float, no parallelism and no slip — while looking exactly like a plan.

The delivery mix tells the truth

  • 🔴 Fleet's release view read unclassified 49 · 100% on a release whose every PR title states its kind. The classifier was right; it was never CALLED on that path. Period mode classified, the paged/release path did not, and Class's zero value IS unclassified — so the omission was silent, a confident wrong answer rather than a blank. The classify call now lives in the ONE shared projection both paged consumers use, so a future third consumer inherits it for free (#5660).
  • polish is a first-class ticket kind — lapidação, the gem-cutting word: the stone already exists and you are finishing it properly. Not a defect, not a new capability, not upkeep. It carries its own delivery-mix slice rather than folding into another (#5508).

The merge lane says what it decided

  • A bound GOLDEN on a green, mergeable PR could sit as a draft and never merge, while the row read "worker's turn" — pointing at a worker that owed nothing. There is now ONE owner for the classification, and the lane RECORDS every PR it considers, including the ones it declines. Silence was what made the failure invisible: it is indistinguishable from never having looked (#5676).

Rendering

  • A terminal RESIZE could panic the cockpit. The Live Reviews history rebuilt its columns at a new responsive tier while its row memo, keyed on a different width, handed back rows built for the old one. The memo now keys on the tier (#5657).
  • The ACE rail's status strip carries a live CLOCK, and it sat inside the memoized region — so the frame diverged and the static box rebuilt on every keystroke, because time had passed. Moved out of the memo (#5706).
  • The background-agents band stops printing a count of finished transcripts on disk. It is an accurate number a customer cannot act on, on a screen whose purpose is who needs me right now (#5668).

Under the hood

  • The release lane's own guards are honest again: two ungoverned gh seams routed, and the new create-sprint skill catalogued so the discipline index can advertise it (#5686).

Known, and stated plainly

  • A worker waiting on its own background job still reads with ACE · blocked, and attaching to it flips the state — observing changes the answer. The fix is written and reviewed; it is held back because it also changes BILLING on its primary path, turning a waiting worker from not-billable to billable for the whole wait. That is a customer-invoice decision and it is not one to take in a hurry. It lands in 1.2.22, with a bound (#5667).
  • The relay guard still refuses on a machine-generated hint. It reads the composer's bytes, where Claude Code's own suggested next message and typed text are identical, so it can refuse a relay and name a human who typed nothing. The styling-channel fix is written and its reviewer found two real defects in it; both land in 1.2.22 rather than at the end of a long night (#5671).

v1.2.21 — 15 PRs, 33k lines, made with FlowForge