Skip to content
All releases

Release

v1.2.13

What shipped, in numbers

36
PRs merged
3,187,603
Lines added
867,000
Code lines
Code
867,000
Tests
950,145
Docs
1,370,458
Sum · code + tests + docs
3,187,603

of 36 merged

  • bug 15 42%
  • feature 11 31%
  • polish 0 0%
  • docs 2 5%
  • groundwork 6 17%
  • unclassified 2 5%

since v1.2.12 · tagged

What shipped

The release that renames the hierarchy and stops assuming you only work in one place. An aggregator is now an Initiative and an umbrella is now an Epic — so a Jira Epic finally imports into something called an Epic, and the words on your board mean what they mean everywhere else. Every FlowForge surface used to be bound to one repository, chosen once and never revisited — so a developer with work in three projects kept three cockpits open, and a company admin who registered a teammate on the console screen watched the board never learn about them. This cut closes that: a workspace is something you switch, with Ctrl+K, from a screen that names exactly what it can and cannot reach — and the switch probes the target before it commits, so a workspace that cannot serve you refuses out loud instead of quietly showing you another company's data. Riding with it: Buckets, a second lens you draw across your tickets without touching the repository they live in; a cross-project company board and a "my work across repos" view; and the tenancy boundary closed at the database. ForgePlay 4 — go-to-market — ships as the fifth play. Alongside the new surfaces, the review pipeline stops forgetting its own history, the release lane's 42-run red streak is broken, and two half-second-per-keystroke stalls are gone from the cockpit.

One person, many workspaces

  • Ctrl+K opens the workspace switcher. A six-state screen that never conflates its facts: loading, ready, empty, unreadable, unanchored, and refused each render differently — "the roster came back empty" and "I could not read the roster" are different things to know, and you are told which one you are looking at. When nothing is anchored, no row wears the active marker rather than the screen inventing an anchor. The chord itself is validated at the wire — the raw byte through the real terminal decoder on legacy, kitty CSI-u and modifyOtherKeys, and through tmux — so it cannot ship unreachable (#4993, #5050).
  • The switch is a transaction: probe before commit. The old order wrote the target to disk and then rebound, and an unreachable target silently degraded to GitHub with nothing said — in a company workspace that is not a lesser answer, it is the wrong company's answer. The switch now resolves, then proves the target actually serves on a throwaway connection, and only then writes the first byte; a failure aborts before anything is rebound, so disk and memory can never disagree (#4991, #5026).
  • Your current workspace is always on screen. The indicator lives in the footer row every framed screen already reserved and rendered blank, so it cannot be dropped at a narrow width — verified down to a 32-column render. Four states, each with its own glyph and its own words: bound, unbound, undetermined, and degraded (#4992, #5041).
  • A company probe that fails now refuses instead of falling back. Scope is threaded through the provider seam end to end, and a rebind that cannot serve the requested scope surfaces the error rather than serving a GitHub stand-in (#4990, #5043).
  • Workspace membership is read through a real door. workspaces_for_user() plus a REST-backed directory means the roster you see is the roster you belong to — with no database connection string anywhere near the client (#4989, #5027).
  • The TUI knows who you are. Current-user identity resolves on the TUI path, which is what makes every membership-scoped surface above possible (#4988, #5020).
  • One resolution path, structurally enforced. The provider bundle grew the five resolver faces that legitimate callers were missing, every environment-reading bypass was migrated onto it, the exported bypass was deleted outright, and an architecture test walks the syntax tree to keep provider construction from leaking back out (#4985 · #5003, #4986 · #5019, #4987 · #5017). The design that governs all nine slices ships in-tree (#4966).

Epic and Initiative — the two words swap

  • An aggregator is now an Initiative; an umbrella is now an Epic. The old names had drifted from every other tool's meaning, which made an import from Jira lossy at the vocabulary level before a single field moved. The swap is complete end to end — the data model, the boards, the screens, the command line and the docs (#4784).
  • A Jira Epic now lands on our Epic, losslessly. The importer previously requested no parent field at all, so every child of a Jira Epic imported orphaned and the hierarchy silently flattened. It now reads the unified parent (not the deprecating Epic Link, which Atlassian is retiring) and fails closed on hierarchy level: a subtask's Story parent and a Premium Epic's Initiative parent both resolve to no edge rather than fabricating a wrong one, because a ticket grouped under something that groups nothing is worse than an honest gap. Server and Data Center instances that omit the level fall back to the issue type, so on-prem does not import orphans. Proven against a real Jira response body served verbatim over the wire, not a hand-built fixture (#5086, #5109).
  • --epic is the canonical flag, and --umbrella keeps working. Both shims are covered by tests for the first time — including the case where a half-migrated script passes both, where the canonical flag wins, because resolving to the legacy value there would write the wrong parent edge in silence. backfill-umbrella becomes backfill-epic with the old spelling still dispatching (#5086, #5109).
  • The boards say the new words. The War Room renders ▣ INITVE and ◉ EPIC (#5084, #5093); the Initiatives screen and the initiative gantt carry the vocabulary through (#5085, #5100); the data layer, the kind values and the shared ancestry predicate make it real underneath (#5083, #5088); and the long tail across the cockpit, the remaining packages and the docs closes it out (#5087, #5114).
  • Shipped as five independently reviewed slices, each earning its own GOLDEN, after the single 418-file change proved too large for any reviewer to finish.

Buckets — a second lens over your tickets

  • A bucket is a label you draw across tickets, in any number, without moving anything. The repository a ticket belongs to stays untouchable: buckets are a many-to-many lens layered over your work, not a re-filing of it. Fail-closed access rules from the first migration (#4980, #4999).
  • flowforge bucket creates, lists, renames, attaches and detaches from the command line (#4982, #5000).
  • Tag work as you create it — --bucket on flowforge ticket new and on feature emission, with a guard that refuses to let a lens masquerade as the repository anchor (#4983, #5021).
  • See them on the board. Buckets become a fifth composable filter axis, ANDed and peeled like every other, plus a per-card chip that picks the widest label that genuinely fits — never a bare glyph, never a sliced word, and a "+N" that names how many buckets are not shown so a many-to-many lens is never misread as a category (#4984, #5025).
  • Reads are batched behind the provider seam with no per-ticket query fan-out (#4981, #4998). The v1.0 design ships in-tree (#4967).

Work that spans repositories

  • The company board. One cross-project roster — open and closed — with a project chip on every card, a project filter, and an honest coverage summary. It refreshes as a company board instead of silently falling back to a single project's fetch (#4516, #4948).
  • "My work across repos" (c). One flat list of your tickets across every project in scope, filterable to the current company sprint — which is derived from real signals (the nearest begin date among sprints with open work) and degrades to "all" when nothing qualifies, rather than guessing. Opening a row peeks it in its own project, not the screen's ambient one (#4518, #4947).
  • The console is the source of truth for who is in a company. The console's Seats & members screen — the one a paying customer's admin actually uses — is now linked to the ticket store, and board membership derives from it. Registering a member no longer leaves the board unaware, which is how "why can't my developer see anything" used to reach a support call. Ships with the ordered repository importer that brings an existing organisation's projects in (#4953, #5009).
  • The tenancy boundary is closed at the database. Project-connection rows are scoped to their tenant, anonymous access is revoked, and every read policy is gated on membership (migration 0048) (#4915, #4936). A company activation runbook covers both the single-company default and real multi-project grouping (#4519, #4935).

The review pipeline stops forgetting

  • The shipped review-round budget is now 8, calibrated against measured history. DefaultRoundBudget was 3 — below the measured mean — so the convergence seal, built to catch the exceptional runaway treadmill, fired on the majority of PRs instead. Each stall needed a hand-issued grant, and because stalled PRs never drain while healthy ones do, a busy board converged to all-stalled within hours. Re-measured over the convergence store (n=87 PRs with round data): mean 4.21, median 3, p75 5, p90 8, p95 12, max 20 — and 46% (40/87) needed a grant at a budget of 3. A budget of 3 fits 57% of PRs; 8 fits 91% and covers p90 exactly. The remaining ~9% are the genuine treadmills the escalation exists to surface. The [rules] review_round_budget setting now documents that trade-off in operator terms, with the calibration data at the constant's definition so the next person to change it re-measures instead of guessing. A budget escalation addresses the controller first and the founder second, so a sleeping human is never the required unblocker (#4917, #4942).
  • A review round is now a durable record. Rounds are written to a real store behind an interface, with nullable counts that stay honest when a lane reported nothing, and a retention policy — so the history that explains a verdict outlives the process that produced it (#4857, #5032).
  • Every round is captured at the lease seam. All three review engines pass through one session API, and the per-lane counts stop being discarded on the way out (#4860, #5040).
  • Budget stalls are legible as a class. --stalled enumerates every PR stopped awaiting a disposition, and the cockpit carries a count that never goes silent — closing the gap v1.2.13's first draft named, and the reason the 2026-08-02 incident stayed invisible for hours (#4946, #4997).
  • The round result is stamped on the ACE rail once per round, score included, as a sibling of the GOLDEN stamp (#4863, #5073).
  • The reviewer rail says what the round actually found. Real lane counts, the last round's score, and the R6/3 round ordinal, carried on the beat the rail already had (#4862, #5076).
  • A Live Reviews screen. The screen shell with on-screen PR switching and the founder-ratified r shortcut (#4864, #5077); per-round history — the round ledger, the verbatim-review reader, and an honest-state matrix where a running round renders OPEN rather than a fabricated 0/0/0/0, an errored one says the reviewer never spoke, and a missing round keeps its ordinal as a gap row (#4865, #5091); and a legible trend — the shape ramp, the delta column and the pinned verdict in words (#4866, #5101).
  • A dependency edge is now an enforced output of ticket creation. A batch could be emitted with its ordering written only in prose, which meant waves collapsed to one and the board drew no dependencies — silently. There is now a gate at the batch door and an outcome detector behind every door (#5102, #5107).
  • flowforge review rounds import <pr> recovers round history from before this feature existed — and is explicit about what it cannot reconstruct rather than inventing it (#5035, #5074).

The War Room

  • Search finds a ticket by its number or title, not only by sprint name (#4921, #5045).
  • An older milestone hydrates when you expand it. The newest-200 window was hiding the rows themselves, not just the edges between them, so older work looked empty rather than collapsed (#4974, #5053).
  • Batch assign reports n/N as it runs and settles the selection to exactly the work that did not land, so a partial run leaves you holding the remainder (#4919, #5055).
  • Creating a container above milestones works. The emit gate never read the ticket's kind, which made the operation impossible for every user, not just some (#4767, #5047).
  • Epics gain a completion bar for epics and umbrellas alike (#4951, #4970), an ahead-only filter on S as the inverse of B (#4920, #4962), and the reading keys o and v on the one screen built for reading (#5056, #5071).
  • The Gantt gets the War Room's craft. One bar geometry instead of three weights for three states of the same object, zebra striping on by default, and a second cycleable vision so the chart can answer more than "when is each ticket scheduled" (#5038, #5072).
  • The pause glyph means one thing again. ⏸ was doing double duty — the pause state and "awaiting your m" — on the same screen, so the decoder had to teach two readings for one symbol. The consent-owed state moves to ☐, an unchecked box, and the legend teaches exactly one meaning per glyph (#5061, #5075).
  • o gains a ticket-body rung — the item's own body, the one document its three artifact rungs structurally cannot supply (#4765, #5060).
  • v and Enter open the ticket under the fold cursor and resolve the peek against the right project (#4950, #4954); an umbrella whose subtickets are all closed still nests (#4958); the top hierarchy level is never mistaken for company chrome (#5049, #5065); a failed board read stops impersonating an empty one (#5058, #5064); the Waivers tab's load result reaches the tab (#5022, #5067); and the ledger's pinned footer no longer hides the last data row (#4858, #5033). Scores render through one width-aware renderer (#4861, #5036).

Speed

  • Half a second per keystroke, twice, is gone. A sub-model command was being executed inline inside the update loop — 533ms measured per keypress — and the epic name prompt had the same defect on its own path. Both are fixed, and a guard now catches the pattern rather than the next person measuring it (#5057 · #5070, #5046).
  • The cockpit stops hammering the ticket provider. A request governor, one shared pull-request snapshot instead of many, a circuit breaker, and an honest degrade when the provider is rate-limited — so a busy board slows down and says so instead of failing in pieces (#4716, #5048).
  • Cockpit state is rehoused into feature-scoped types, taking the main application file from 1658 to 1458 lines (#4834, #4944).

The release lane

  • The lane is green again. A 42-run red streak on the release line is broken — three separate causes behind three guards, with no guard weakened to get there (#5042, #5052).
  • A lane that has been red for N runs now says so where the merge decision is made, and the detector reaches the frame it previously never rendered into (#4938 · #5001, #5044 · #5051).
  • The site deploy gate runs on release lines, split so the gating half needs no credentials (#4939, #4979).
  • Auto-fire honours lanes-only, closing the third door into the release line (#4940, #4995).
  • A pre-release candidate channel lets you run a staged build side by side with your installed one, with PATH provably untouched (#4975, #5006); the stale-binary banner now distinguishes "behind" from "broken" instead of advising a change that would break parity with what customers run (#4977, #5008).
  • The cut authorises its own single, commit-bound push (#4929, #4996), and it is measured before it is optimised — 78% of the previous release's 17 hours was dark time, not build time (#4976, #5005).

Desktop

  • The Linux AppImage actually contains the application. It was shipping 14 KB stubs while every gate in front of it reported green (#4963, #4968) — and could not launch outside Debian, because the WebKit exec-path hook was compiled out and the gate only ran where the bug is invisible (#4959, #4961).

ForgePlay 4 — Go-To-Market

  • A fifth ForgePlay: go-to-market. The play exists as a runnable artifact — the playbook and skill, its manifest entry, and the staged install assets — reusing the existing ratification record and chokepoint rather than forking a second audit trail for GTM work (#4796, #5110). A spike answered that question first, at source, before a line was built (#4795, #5104).
  • Sixteen phases behind two gates — fourteen of them runnable today. The plan leg clears Gate 1 before anything is produced; the buyer pre-flight is a read-only paper build that must come back clean before the founder ever sees Gate 2, and it is structurally able to come back dirty (#4800, #5112). The funnel and campaign-architecture charts are enforced artifacts, not optional extras (#4799, #5115), and the variants-per-placement edge asserts that the creative spec set actually meets the number the measurement plan declared (#4801, #5116).
  • The site renders it. The ForgePlay catalog now iterates instead of hardcoding four entries, and both catalogs derive identity from one source so they cannot drift apart again (#4804, #5108).
  • Its artifacts are decodable — the FP4 artifact kinds carry their own glyphs on the DONE screen, keyed to the stems the play actually emits (#4798, #5111).
  • The last two phases do not run yet, and the play says so rather than failing quietly. emit and pm-milestone need a GTM composition root behind the existing ratification chokepoint — the #4795 spike scoped it (E-1…E-4) and it is not built. The play reports the phase blocked and stops, instead of pretending. Everything up to and including Gate 2 — research, offer, funnel, campaign architecture, the buyer pre-flight and the full creative spec set — runs (#4796).
  • The dogfood run — executing the play end to end against FlowForge itself — is deliberately deferred; it produces marketing artifacts, not product code, so nothing in this binary depends on it (#4802).

Trust, hygiene and waves

  • A flag-parse error is never silent again. Thirty-one verbs — including merge — exited non-zero with zero output when a flag failed to parse, so a malformed command was indistinguishable from a gate refusing you. One root error handler, guarded by a test that walks the whole command tree (#5081, #5082).
  • A QUEUED relay is a success, not a failure. Messages delivered into a busy agent's queue were reported as delivery failures, and following that advice re-sent a duplicate brief into a working agent (#4764, #5094).
  • A background-agent row's liveness is proven, not inherited — a dead vehicle no longer renders as running (#4927, #5095).
  • An unknown duration stops impersonating a measurement — formatDuration guards zero and negative inputs instead of rendering 2,562,047 hours (#4904, #5096).
  • The selected row is one continuous highlight with one foreground, fixed at the shared seam so every ledger screen gained it, not just the merge queue (#5089, #5090).
  • Workers and Merge Queue headers carry their count, as Background Agents already did (#4846, #5097).
  • The War Room footer finally advertises v, paid for in columns reclaimed elsewhere so nothing already on screen was evicted (#5092, #5099).
  • The unit sweep is sharded into three jobs. It had outgrown its 18-minute wall and died mid-run, so every package after it went untested while the failure read as ordinary red — a union assertion now makes a truncated run impossible to mistake for a passing one (#5054, #5078).
  • Every ForgePlay 4 artifact stem resolves — the FP-GTM-* drawing series registered in the resolver, including two stems previously assumed known that were not (#4797, #5105).
  • A retired or closed worker can no longer relay a message — the lifecycle check now runs on the calling side (#4910, #4955).
  • A guard that stops enforcing becomes visible, through uniform firing telemetry across every pre-tool guard (#4905, #4956).
  • A closing keyword only counts as a trailer, never as a word inside a sentence, so a ticket cannot be closed by a passing mention (#4937, #4978).
  • The merge-failed dialog is split by severity — the blocking reason first, warnings apart from it, violations as a list (#5002, #5004).
  • The model each worker is running shows on its row and on the fleet swim-lane, sourced honestly and visible from spawn (#4913 · #4969, #3142 · #5010).
  • A routine profile call stops erasing a stored email address (#4957, #4994); leaked terminal sockets are reaped at the source and by a probe-gated sweep (#4841, #4960); a partial out-of-band seal reports itself as partial and keeps its self-heal record (#4902, #4952).
  • The waves screen reads the same data as the waves widget, and PAUSE becomes a state distinct from stop (#5059).

v1.2.13 — 36 PRs, 3.2M lines, made with FlowForge