github gastownhall/gascity v1.5.0
Gas City v1.5.0

latest release: edge
4 hours ago

Gas City v1.5.0

v1.5.0 is the new stable release and supersedes v1.4.2. It is tagged from
release/v1.5.0 at f66474617d, which differs from v1.5.0-rc1 only by the
CHANGELOG release commit. Install it with brew install gascity /
brew upgrade gascity or from the release assets (see Install). The
biggest change is underneath the work ledger: Beads (bd), not Gas City,
now owns the Dolt process of every new city.
Every maintenance order reaches
the ledger through gc bd, and v1.5.0 requires bd v1.3.1. Existing cities
keep running on gc-managed Dolt until you migrate them.

The Dolt ownership change

New cities run on a bd-owned proxied Dolt store, and Gas City no longer
starts a Dolt server of its own for them (#6273).
Existing cities are not
converted automatically. They keep their gc-managed Dolt server until you run
gc beads city migrate-proxied (see
Migrating an existing city).

What changed

  • bd owns the Dolt process lifecycle. A fresh gc init or gc rig add
    runs bd's own proxied-server init. bd starts a bd db-proxy-child, which
    runs a dolt sql-server rooted at <scope>/.beads/dolt. gc stop asks bd to
    stop each scope (bd dolt stop, last, after agents and sessions), and stop
    is re-runnable. gc writes no Dolt runtime state, allocates no Dolt port and
    starts no Dolt server for these scopes.
  • bd is the topology authority. .beads/metadata.json dolt_mode: proxied-server is the persisted truth. gc keeps only
    .gc/scope-ownership.json, a crash-safe journal of its init intent.
  • Each scope has its own store. The city and every rig created by
    gc rig add get their own proxy and their own Dolt server. A legacy city
    ran one gc-owned server for the city and all its rigs.
  • The proxy stays resident. gc initialises its scopes with bd's idle
    timeout set to never (idle_timeout: -1 in
    .beads/proxied_server_client_info.json), so commands do not pay a proxy
    and Dolt cold start after a quiet period.
  • Maintenance goes through gc bd on every topology (#6751). reaper,
    jsonl-export and mol-dog-backup reach each scope with gc bd and use
    bd's own export, backup and purge, so proxied, gc-managed and mixed cities
    are all maintained. The dolt pack's managed-server orders are typed no-ops on
    a bd-owned scope. Details are in the Upgrading Notes.
  • Do not run bd dolt start or bd dolt status on a proxied scope.
    Neither is proxied-aware, and start launches a second, unmanaged
    sql-server over the same data directory. Any ordinary bd command restarts
    a stopped proxy by itself.
  • Escape hatch: gc init --beads-transport direct --beads-target local
    still gives a bd-owned server-mode store (one bd-started dolt sql-server
    per scope, with .beads/dolt-server.pid/.port). It is not the legacy
    gc-managed server, which only an existing city reaches.

Why

Two programs managing one Dolt process was a steady source of lifecycle bugs.
For example, bd cannot see a server Gas City started, so bd's own commands
could start a second server over the same data, or migrate a store under a
running server (#1374, and the hazard migrate-proxied fences below). With
bd owning the process, there is one owner for start, stop, backup and
migration, and gc reaches the store only through bd.

Migrating an existing city

Existing cities are not migrated automatically. A city initialised before
v1.5.0 keeps its gc-managed Dolt server and keeps working on v1.5.0.
gc beads city migrate-proxied moves it to bd's proxied topology. It runs
bd's own bd migrate from-server-to-proxied-server on each scope, city first,
and handles the gc-side residue bd cannot see. There is no reverse
migration
, so take a backup first.

Before you start:

  • Upgrade bd to v1.3.1 (bd version), and upgrade gc to v1.5.0.
  • Stop the city: gc stop <city-dir>. This is required. bd cannot see a
    server gc started, and migrating under a live one commits the mode flip and
    leaves the scope unusable until that server dies. The command refuses while
    gc's server state, Dolt store lock or Dolt port is still live, and
    --dry-run refuses too, so stop first. gc stop also unregisters the city
    from the supervisor, so name the city by its directory until
    gc start <city-dir> registers it again.
  • Back up the stopped city's stores: copy <city-dir>/.beads (it holds
    the shared Dolt data dir, <city-dir>/.beads/dolt) and every rig's .beads
    directory, for example with tar.

Run it from the city directory (or add --city <city-dir> to the
gc beads and gc doctor commands):

gc stop <city-dir>
gc beads city migrate-proxied --dry-run   # per-scope plan, nothing written
gc beads city migrate-proxied             # add --json for scripts
gc start <city-dir>
gc doctor --fix
gc doctor

--rig NAME (repeatable) migrates only the named rigs. The city is always
included, because rigs that share its data dir cannot move while it is still
gc-managed. The command prints a per-scope table and exits non-zero if any
scope failed.

Verify it:

pgrep -af 'db-proxy-child|dolt sql-server'   # bd's proxy and its Dolt child
cat <city-dir>/.beads/metadata.json          # "dolt_mode": "proxied-server"
cat <rig>/.beads/metadata.json               # "dolt_data_dir": "../../.beads/dolt" (relative)
gc bd list
gc status

gc start must not start a dolt sql-server of its own or recreate
<city-dir>/.gc/runtime/packs/dolt/dolt-state.json. A migrated city keeps one
proxy for the city and its rigs, so it runs one proxy plus one Dolt server,
where it used to run one Dolt server. Running bd dolt stop inside a migrated
rig stops the city's proxy too.

What it refuses. Every scope it will not touch is refused by name:

  • a scope that tracks an external Dolt endpoint (gc.endpoint_origin external
    or a pinned dolt.host): leave it external, or move it with
    gc beads city use-managed first;
  • an embedded Dolt scope or a DoltLite scope, which have no server to migrate;
  • a scope already handed to bd by the ownership handoff, or one recorded in
    .gc/scope-ownership.json (a store that was created proxied);
  • a rig with both its own Dolt store and a database in the city's data dir,
    a rig gc cannot place, and a city data dir that holds other databases but
    not the city's own;
  • a city whose beads provider is not bd.

An already-proxied scope reports already-migrated and is skipped, so the
command is safe to rerun.

If it fails:

  • One scope failed and the rest succeeded. Completed scopes stay migrated.
    Fix what the message names and rerun the command. Rigs that share the city's
    data dir are skipped, not attempted, when the city's own turn fails.
  • bd died mid-migration. Rerun the command. It resumes bd's interrupted
    migration from bd's journal (.beads/dolt-mode-migration.json).
  • The migration committed but bd cannot open the store. A legacy server
    is almost always still running. Run gc stop <city-dir>, then bd ping in
    the scope.
  • gc doctor reports dolt runtime state unavailable. A stale
    dolt.mode: server in config.yaml is shadowing metadata.json. Rerun
    gc beads city migrate-proxied to rewrite it.
  • Going back. There is no reverse migration. Restore the backup.

The full procedure is in the
migration runbook.

Highlights

  • New cities hand their Dolt process to bd (#6273). Gas City no longer
    starts a Dolt server for a new city or rig. Existing cities keep gc-managed
    Dolt until you run gc beads city migrate-proxied. See
    The Dolt ownership change.
  • Preview: an opt-in SQLite ledger for infrastructure beads, with
    130–250× faster infrastructure reads (#5070, #5071).
    New in v1.5.0. A
    city can move its execution graph, sessions, messaging, orders and nudge
    queue off the work ledger onto a dedicated SQLite bead engine. On a
    production-scale city that measured 130–250× lower latency for
    infrastructure-class reads. It is off by default and experimental in this
    release; see
    Preview: the SQLite infrastructure ledger.
  • Maintenance works on every topology (#6751). reaper, jsonl-export
    and mol-dog-backup now reach the city and every rig through gc bd, so
    bd-owned proxied, gc-managed and mixed cities are all reaped, archived and
    backed up. A new typed order.skipped event reports any work an order could
    not do.
  • gc doctor no longer starts a stopped store, and it checks backup
    coverage (#6817, #6814).
    Running doctor on a stopped city leaves it
    stopped. The new proxied-backup-coverage check asks bd whether each live
    proxied scope has a recent backup.
  • Sessions and pools are harder to break. Claim and close now use the
    same identity, and pool workers are named <template>-<beadID> again. The
    orphan sweep, the corpse cleaner and gc session kill no longer kill or
    close live or resumable sessions. Claude's workspace-trust dialog is
    confirmed in a closed loop.
  • The Dolt compactor protects adopted and shared history (#5958, #6554).
    It never flattens a database that has a remote, and it only squashes
    commits after a first-sight gc-compact-base tag.
  • The Gas City pack binds as gc (#4508, #6683). gc init writes
    [imports.gc], and gc doctor --fix offers to rename an existing
    [imports.gascity].

Preview: the SQLite infrastructure ledger

A pre-release feature, opt-in only, and new in v1.5.0. A city with no
[storage] section in city.toml is untouched by it: nothing is resolved,
opened or migrated.

Every city stores six classes of state. The work ledger holds what people
and agents reason about: tasks, epics, bugs and convoys. The other five are
infrastructure: the execution graph, session lifecycle, messaging, orders
and the nudge queue. By default all six share the work ledger. A [storage]
split keeps work where it is and moves the five infrastructure classes to a
dedicated SQLite bead engine under .gc/store.

On a production-scale city, infrastructure-class reads measured 130–250×
lower latency
from the SQLite ledger than from the work ledger, because the
controller's constant session, graph and nudge traffic stops competing with
the work ledger's own load (#5071). Closed session beads in the SQLite ledger
are pruned after GC_INFRA_SESSION_PURGE_AGE (default 72h) once wisp_ttl is
set (#6689), so the ledger stays small.

It is experimental in this release. Try it on a new or throwaway
city, not on a production city. The
split storage runbook
describes the configuration.

Upgrading Notes

  • Upgrade Beads (bd) to v1.3.1 before you upgrade a city. v1.5.0 pins
    and tests against bd v1.3.1, in both deps.env BD_VERSION and the go.mod
    library. v1.3.1 is a stable release and keeps bd v1.3.0's schema. To get
    it, run brew install beads (or brew upgrade beads), download it from the
    v1.3.1 release assets,
    or run go install github.com/steveyegge/beads/cmd/bd@v1.3.1. A city that
    stays on bd v1.3.0 keeps working through the bd CLI, with two losses:

    • gc refuses that bd for its native store, because it is older than the
      linked library;
    • mol-dog-backup's proxied bd backup and the reaper's closed-wisp
      bd purge are reported as skipped every run.

    Read beads'
    v1.3.1 upgrade notes
    before you upgrade scripts that call bd directly. Do not move a city to a
    newer bd until a gc release pins it.

  • New cities default to a bd-owned proxied Dolt store; existing cities are
    not converted (#6273).
    Only a scope with no persisted Beads identity gets
    the new default, from gc init or gc rig add. A scope that already has a
    server, embedded, DoltLite or external binding keeps its lifecycle. See
    The Dolt ownership change for the escape hatch
    and for migrating an existing city.

  • New cities import the Gas City pack as [imports.gc]; rename the key on
    existing cities before moving past pack 0.1.6 (#4508, #6683).

    gc init (the default, or --template gascity) used to write
    [imports.gascity]. The pack's skill is now gc.mayor instead of
    gascity.mayor. Existing cities are not rewritten and keep working at the
    pinned 0.1.6. Newer Gas City pack role prompts run gc gc claim, which
    fails under the old key. gc doctor --fix renames the key in place and
    leaves packs.lock unchanged. Do not add a second gc import next to the
    old key, because that imports the pack twice.

  • The reaper, jsonl-export and dolt mol-dog-backup orders now run
    through gc bd on every topology (#6751).
    Each bead scope (the city and
    every rig) is reached with gc bd, and the orders no longer dial Dolt
    themselves. Both orders run with a 900s timeout.

    • Unbound databases are no longer maintained. A database on a
      gc-managed Dolt server that no city or rig binds is no longer reaped,
      exported or backed up. Drop orphaned databases, or bind them to a rig.
    • The JSONL archive switches to bd export format. Each line holds one
      issue, with its labels, dependencies and comments, and the file restores
      with bd import <file>. On its first run, the order moves each database's
      old {"rows":[...]} snapshot files into <db>/legacy/ with git mv.
      Nothing is deleted.
    • Backups use bd backup. When a scope has no backup destination,
      mol-dog-backup registers <city>/.dolt-backup/<db> as its destination,
      then runs bd backup sync. The reaper's session-prune backup gate reads
      bd backup status. A bd older than v1.3.1 refuses backup on proxied
      scopes. Those scopes are reported as skipped, and their session-bead
      prune waits for a backup it can see.
    • Closed-wisp purge uses bd purge --wisps-plane in bounded batches.
      A backlog is cleared across runs within GC_REAPER_PURGE_BUDGET_SECS
      (default 300s). A run that uses up GC_REAPER_RUN_BUDGET_SECS (default
      780s) reports a partial outcome, and the next run continues.
  • Treat gc storage migrate as experimental. The command is new in
    v1.5.0 and still has open correctness issues on split cities (#5987,
    #6015, #5974, #6129, #6242, #6348). Run gc storage preflight first, back
    up every store involved, and do not migrate a production city you cannot
    restore.

  • Run gc doctor after upgrading a city whose beads store gc does not own
    (#6495).
    On start, gc registers its required bead types (including the
    new startup-health-episode) for gc-managed and provider-owned stores. It
    never writes to external Dolt servers, complete storage bindings, hosted
    gateways or legacy proxied opt-in scopes. On those, run gc doctor, and if
    custom-types fails, run gc doctor --fix.

  • Unaliased pool and ephemeral sessions now act as their session bead id
    (#5716).
    GC_AGENT and BEADS_ACTOR (and the event actor and telemetry
    gc.agent) are now the session bead id instead of the session name.
    Restart running sessions to pick this up. When gc hook --claim adopts a
    bead still held under the old spelling, it re-stamps it. If that re-stamp
    fails, it prints the exact bd update <id> --if-assignee <old> --if-status in_progress --assignee <new> recovery command.

  • A failed pool start can now stall its slot instead of leaking a runtime
    (#6549).
    When a pool worker fails to start, gc stops its runtime before
    the slot retries. If that stop fails, the slot stalls and logs
    holding pool session <id> open instead of starting another box. Open
    pool rows created by an RC or main build with the names claude or
    claude-N-pool keep those names until they close.

  • The Dolt compactor never flattens a database that has a Dolt remote
    (#5958).
    Such databases (including bd refs/dolt/data and backup-only
    file:// remotes) get a bare CALL DOLT_GC(), so their history grows. A
    leftover compact-pending-push marker is held for review instead of being
    pushed.

    • First-sight tag. The first time the compactor sees a database, it
      tags it gc-compact-base. The tag goes at the root if the database was
      already flattened or is marked .compact-full-history, and at HEAD
      otherwise. Only commits after the tag are squashed.
    • Flattening on purpose. Announce a window to every clone, then run
      GC_DOLT_COMPACT_ALLOW_FEDERATED=1 gc dolt compact --only-db <db>.
    • After a rollback. If you roll a database back to before its tag,
      compaction fails with instructions until you delete the tag:
      CALL DOLT_TAG('-d', 'gc-compact-base').
  • secrets.env keys must be shell identifiers (#5982, #6022). Any other
    key (optionally after export ) is rejected as line N: invalid key. An
    unclosed quote skips through its closing line as one block, reported by key
    and line range. The rest of the file still applies. Check the supervisor's
    stderr after gc start for skipped lines.

  • Child processes now see BD_BIN= (empty) when no bd pin is configured
    (#6550).
    Previously they inherited the ambient value. A relative or
    non-executable ambient BD_BIN is ignored with a warning. A BD_BIN pinned
    in workspace.env still fails closed.

  • Formula steps and the agent-script notes: key now append to work-bead
    notes (#6412).
    They run bd update --append-notes instead of replacing
    the notes. A retried step can append the same text twice.

  • A retry step's own counter moved to gc.retry_attempt (#6548). Inside
    a ralph or review loop, gc.attempt again names the loop iteration on
    every bead of the iteration body, as in v1.4.2. gc.retry_attempt counts a
    step's retries from 1 in each iteration. Packs that read gc.attempt as a
    retry counter should read gc.retry_attempt and fall back to gc.attempt.
    Beads created before the upgrade have no gc.retry_attempt.

  • Publishing a pack under a bare (unscoped) name now requires
    --allow-unscoped-name (#5394).
    Registry names are <github-owner>/<pack>.
    New packs must set [pack].name = "<github-owner>/<pack>" in pack.toml.
    --name can only restate the name that pack.toml already declares.

  • gc bd new --metadata @file.json now exits 1. gc bd checks
    rig-qualified metadata before it writes, and it cannot read that metadata
    from a file. Pass the JSON inline, for example
    --metadata '{"routed_to": "rig/agent"}'.

  • Main and nightly builds only: the seat-claim and named-seat execution
    backstops were removed (#6524).
    Named always-on seats again have no
    controller claim or execution backstop. An agent must claim its work at
    startup or be nudged. The pool-slot backstop stays, without #6287's
    changes. The execution.claim_stalled event type is gone.

Added

  • An order.skipped event reports work an exec order could not do
    (#6751).
    An order can exit 0 and still miss part of its work, for example
    when a bead scope is unreachable or a safety gate holds a step back. The
    controller now records a typed order.skipped event next to
    order.completed. The event carries the outcome (skipped or partial)
    and the scopes that did not run, and gc order run prints the same
    declaration.
  • gc doctor checks proxied backup coverage (#6814). For each live
    proxied scope, proxied-backup-coverage runs bd backup status. It warns,
    at advisory severity, when a scope has no backup destination, has never
    synced, or has not synced in 24 hours. The fix hint is
    gc order run mol-dog-backup. It never asks bd about a stopped scope.
  • gc doctor catches proxied stores pulled into bd's host-wide shared
    server (#6697).
    The proxied-shared-server check reports and fixes
    gc-owned scopes that are unpinned or bound to bd's shared server.
  • gc doctor offers to rebind the Gas City pack import as gc (#6683).
    The gascity-pack-binding check warns when a city imports the public Gas
    City pack under another key, and gc doctor --fix renames the key to
    [imports.gc]. The fix refuses, and explains why, in three cases: gc is
    already bound to a different pack, the pack is imported twice with
    different settings, or pack.toml or city.toml still reference
    gascity.-qualified names.
  • gc beads city migrate-proxied moves a legacy gc-managed city onto
    bd's proxied-server topology (#6273).
    --dry-run prints the per-scope
    plan, and --json output has a schema. See
    Migrating an existing city.
  • gc storage preflight reports everything the infra-class cutover would
    refuse, from outside the migration window (#5597).
    It runs the same
    checks as gc storage migrate --from-work against a live city, and it
    copies nothing, takes no guard, and publishes no event.
  • Storage-binding events say more (#5597). storage.binding.not_configured
    makes "this city has no split" a visible verdict. Every
    storage.binding.* event now carries proven_beads, the size of the
    proven-copy manifest behind a serving verdict.

Changed

  • Closed session beads in a SQLite infra ledger are purged after
    GC_INFRA_SESSION_PURGE_AGE (default 72h) (#6689).
    This applies only
    when a city relocates sessions to a SQLite infra ledger and wisp_ttl is
    greater than zero. An invalid value logs one warning and falls back to 72h.
    A named session closed for more than 3 days loses its bead and reopens
    fresh. The Dolt reaper keeps GC_REAPER_SESSION_PURGE_AGE (default 720h).
  • gc pack registry publish checks the pack name locally before it sends
    anything (#5394).
    An unscoped name, or a scope that is not the lowercased
    GitHub owner of the source repository, is refused with the exact
    [pack].name edit that fixes it. Before, such a request was parked in the
    review queue as a pending row that could never be approved.
  • gc bd on a split city decides ownership by where a bead lives, not by
    its id prefix (#5634).
    A reserved-prefix id that the class binding does
    not hold now falls through to the work ledger instead of being refused.
    That includes create --deps and --parent. A binding that cannot answer
    is still a hard error and is never read as an absence.
  • gc bd new gets the same --metadata validation as create and
    update.
  • model and effort pins are open options for every provider (#5658).
    Before, a model id the builtin catalog did not know produced no --model
    flag at all, so the agent silently ran on the CLI's default. Any value is
    now rendered through the option's flag template, and the provider CLI
    accepts or rejects it. grok gained grok-4.6 and grok-4.7. codex accepts
    effort = "max", and antigravity accepts "xhigh" and "max". cursor
    gained a model option. A pin that still cannot be honored prints a loud
    startup warning naming the agent, the value, and the valid set.
  • Due condition-triggered orders now fire on the tick that sees them,
    outside the per-tick dispatch budget (#5990).
    Before, cooldown sweeps
    could use up orders.max_dispatches_per_tick on every tick, and one merge
    queue went 11 hours without a dispatch. Cooldown, cron and event orders
    keep the budget. A tick that spends its budget logs one line naming the
    orders it did not reach.
  • gc status no longer scans the whole event log to show Last GC:
    (#4418).
    The lookup reads back a bounded 8 MiB of the active
    events.jsonl only, so on a busy city the field may be absent even though
    maintenance has run. For durable history, query the event log for
    store.maintenance.*.

Fixed

Beads, Dolt and maintenance

  • gc doctor never starts a stopped proxied store (#6817). On a stopped
    bd-owned proxied city, a full gc doctor or gc doctor --fix used to
    start a proxy and a dolt sql-server that never exited. Doctor now asks
    gc's own endpoint inspection whether a scope is running, without calling
    bd or dialing. If a scope is stopped, its store-reading checks keep their
    names and report not checked: store not running. That result is OK on a
    stopped city, and a warning with a fix hint if the controller is up.
    Checks that do not read the store run unchanged.
  • gc init works again below a directory that holds a bd workspace
    (#6701).
    Creating a city, or a gc rig add rig that is not a git repo,
    under a beads repo or a home with ~/.beads used to fail with
    workspace already initialized as database "ws00". gc now pins bd to the
    new scope's own .beads before bd init.
  • gc-owned proxied stores no longer move into bd's host-wide shared server
    (#6697).
    When the user-level bd config set dolt.shared-server: true,
    gc-owned proxied stores were silently moved into
    ~/.beads/shared-server, which could merge two cities' hq databases. gc
    now pins the mode off for every bd it spawns, for agent sessions, and in
    each gc-owned scope's .beads/config.yaml. Beads that a scope wrote while
    bound to the shared server stay there and are not migrated, and
    gc doctor warns while that database exists.
  • gc bd show --watch works on proxied cities (#6681). bd v1.3.0 refuses
    watch mode under the proxied transport. gc now serves
    gc bd show <id> --watch (also view and --current) itself: it polls
    the bead every 2 seconds and redraws it when it changes.
  • gc reads bd 1.3.1's nested config keys (#6814). bd 1.3.1 writes
    dolt.host, dolt.port and dolt.mode in nested YAML form. gc read only
    the flat form, so its repair paths on direct and external scopes failed
    closed. gc's readers, the bd and dolt pack scripts, and the dashboard
    sampler now read both forms, flat first, and the canonical config writer
    leaves one value per key.
  • The reaper's stale-issue auto-close works again when an open bead
    depends on a wisp or external bead (#6751).
    A NULL in that dependency
    emptied the exclusion list, so no stale issue in the store was ever closed.
  • The Dolt compactor no longer rewrites adopted or shared history
    (#5958, #6554).
    The default-on mol-dog-compactor flattened any managed
    database over 2000 commits back to its root, and if the database had a
    remote, it force-pushed the result. See the Upgrading Notes and "History
    protection" in docs/troubleshooting/dolt-bloat-recovery.md.
  • Native Dolt metadata writes no longer silently undo concurrent updates
    (#6222, #6221, #6233).
    SetMetadata and SetMetadataBatch now
    compare-and-swap on the row version. Under sustained contention they
    return an error wrapping beads.ErrVersionMismatch instead of overwriting
    the other writer. Route recovery now skips fenced beads and sets
    gc.routed_to with a key-level compare-and-set.
  • Ready on a remote native Dolt store is fast again (#6491, #6497). A
    pre-release regression made controller ticks take minutes. The blocker
    check is now one batched read.
  • Upgraded cities register startup-health-episode again, so crash-loop
    protection works (#6495, #6557).
    gc now merges its required types into
    both bd's config row and its custom_types table without removing custom
    types. gc doctor --fix keeps types that exist only in the table.
  • Managed Dolt is no longer killed about 15 seconds after the session that
    restarted it closes (#6316, #6546).
    Managed Dolt and the detached
    supervisor no longer inherit session identity, and the orphan sweep skips
    city infrastructure.
  • A nudge no longer closes the shared store on a city that relocates the
    nudges class (#5979, #5980).
    Before, every later class read failed with
    sqlite store: bead store closed until restart. One outage lasted 7.6
    hours.
  • The stale-issue reaper no longer closes active Slack room groups (#6380,
    #6385).
  • Wisp GC now reaps closed, rootless, leaf plain-task wisps (#3780,
    #4927).
    Before, they piled up in the wisp tier forever.
  • The dolt pack's run_bounded python3 fallback sends SIGTERM before
    SIGKILL (#4823, #4875).
    On stock macOS, a SIGKILL during
    dolt backup sync could leave a backup archive permanently unreferenced.
  • gc bd adds a corrective hint when bd suggests bd dolt start against
    a gc-managed endpoint (#1374, #4898).
    The hint points at gc start or
    gc dolt restart. Following bd's suggestion would start a second,
    unmanaged Dolt server.

Sessions, pools and runtimes

  • Claude sessions in untrusted directories start again (#6531, #5796,
    #6121, #6547).
    Current Claude Code builds put the cursor on "No, exit",
    and v1.4.2 confirmed that choice with a bare Enter. gc now moves the
    cursor and presses Enter only after two consecutive frames show
    "Yes, I trust this folder". If it cannot get there, it leaves the dialog
    up and warns.
  • Unaliased pool workers no longer loop on claim and close (#5716, #6324,
    #6597, #6632, #6640).
    Claim, close and the runtime actor now agree on the
    session bead id.
  • Unaliased pool workers are named <template>-<beadID> again (#6549).
    The Kubernetes pod-leak fix is kept: a failed create stops its runtime
    before its bead closes.
  • Session-bead cleanup no longer stops a running session (#6596).
  • The orphan sweep no longer kills a live worker after one transient read
    (#6649).
    A kill now needs the store and the tick's session snapshot to
    agree. BdStore.Get reports not-found only for real bead-level misses.
  • A new pool worker that holds a live claim is no longer drained as
    orphaned (#6665, #6666).
  • The controller no longer crashes on a tmux state-cache race (#6735).
  • A pending create whose command drifted no longer holds its alias forever
    (#6744).
    The row is rolled back as failed-create and recreated.
    gc session reset rolls back a stuck create, and --json reports
    mode: restart|rollback.
  • A codex prompt swallowed during a large paste is submitted again
    (#6739).
    gc re-sends the submit at most 8 times, only while the staged
    draft is still visible and the pane is idle. It never sends into an
    attached session.
  • gc session kill no longer races the reconciler (#6749). While a fresh
    kill-pending marker exists, the reconciler and the corpse cleaner leave the
    row alone, and attach and send return HTTP 409.
  • Killed and dormant sessions are no longer closed as dead-runtime
    (#6752).
  • A lifecycle=one_shot pool session that exits cleanly no longer blocks
    its own runtime name (#5594).
    Rig-scoped one_shot retry attempts also
    keep their rig qualifier and drop stale session pinning (#5609).
  • A named session no longer replays a trigger stamp for a work bead that
    was parked (#4373, #5033).
  • Secret session metadata no longer appears in process argv (#6537). On
    tmux, secret values go through a private 0600 command file. On ssh, they
    are staged over stdin. If staging fails, the write is refused.
  • A single bad line in secrets.env no longer drops every credential
    (#5982, #6022, #6043).
  • A stale or relative ambient BD_BIN no longer takes every gc bd
    offline (#6550).
  • ACP activity is visible across process boundaries (#4612). This
    enables timed idle policies and the opt-in progress_stall_timeout for
    ACP sessions.
  • gc init provider readiness finds Nix-installed CLIs (#3962, #4926).

Formulas, orders and workflows

  • Retries inside ralph and review loops get the right attempt numbers and
    a full retry budget in every iteration (#6548).
    The new
    {retry_attempt} placeholder names a step's own retry. In-flight
    molecules keep working across the upgrade.
  • Retry attempts that pass no longer abort their scope or fail their
    workflow (#6534).
  • Formula steps no longer erase notes on the work bead (#6412).
  • GET /runs/{id}/steps returns steps in pipeline order (#4699, #4876).
  • The dashboard's dependency graph keeps the relation type on inverse
    edges (#4365, #5032).
  • gc doctor's order-firing-current check reports a lookup timeout as
    advisory, not blocking (#4895, #4946).

Split storage (class bindings)

  • The one-live-workflow-per-source-bead guard reads the graph binding
    (#5993).
    Before, a split city admitted a second live workflow for the
    same source bead. A closed binding row also supersedes its retained frozen
    twin, so a converged city no longer refuses a valid sling (#6312).
  • The work-record close gate asks the repository that the bead's owner
    points at (#6138).
    It also resolves gc.work_branch against its
    remote-tracking ref (#5037, #5055).
  • The infra-class cutover carries dependency-edge payloads (#5597). This
    does not repair a city that already cut over. See Known Issues.
  • gc workflow delete-source and reopen-source write the copy that the
    residency contract owns (#5992).
  • A control bead served by a class binding is routed to the dispatcher its
    gc.root_store_ref names (#5918).
    This fixes #5547 and #5587.

CLI, packs and docs

  • gc import add of a local in-git pack locks to HEAD, not the repo's
    latest tag (#3659, #4925).
  • Mail archive and delete expand whitespace-joined message IDs (#4923).
  • gc stop --help and gc stop --json say that stopping a
    supervisor-managed city unregisters it (#4366, #4899).
  • gc formula cook --attach help describes the blocks relationship it
    actually creates (#2392, #4900).
  • The workspace-identity deprecation warning cautions that following it
    can break packs pinned to an older revision (#3887, #4947).
  • The tutorials and quickstart show [imports.gc] (#6676).
  • check-core-boundary.sh scans tracked files only (#4479, #4877).

Known Issues

  • gc import add --version <semver> fails for registry packs. gc
    resolves a semver against git tags, and gascity-packs has no per-pack tags.
    So the "Exactly this version" command printed by gc pack registry show
    fails, for example for Gas City pack 0.1.6 and the new 0.1.1 releases. Until
    a v1.5.x patch release fixes it, pin by commit: run gc pack registry show <pack>, then
    gc import add <source> --version sha:<commit> with that release's commit.
  • gc storage migrate has open correctness issues on split cities
    (#5987, #6015, #5974, #6129, #6242, #6348). See the Upgrading Notes.
  • A split city that cut over before this release still has payloadless
    dependency edges in its binding.
    No command detects or repairs them yet.
    See "If this city cut over before edge payloads were carried" in
    docs/runbooks/split-storage-classes.md.
  • A hot session bead can briefly be marked awake or draining with no
    runtime.
    Two reconciler writes ignore their error after stopping a
    runtime. With the metadata compare-and-swap (#6233), this happens more
    often, and a later tick repairs it.
  • gc start can time out probing Dolt on a heavily loaded host. The
    Dolt identity probe at startup allows 2 seconds and stops gc start with
    "dolt config probe timed out after 2s" if the host cannot answer in time.
    It was seen once during RC validation, at load around 130. Re-run
    gc start once the host is less busy.

Troubleshooting (upgrading to v1.5.0)

Upgrade bd first (bd version should report 1.3.1). Then run this once per
existing city:

gc doctor --fix
Symptom Cause Fix
Every reaper / mol-dog-backup run fires order.skipped (bd-purge-unsupported, or backup refused on a proxied scope) bd is older than v1.3.1 brew upgrade beads, or install bd v1.3.1 from its release assets
gc doctor reports store checks as not checked: store not running The city or rig is stopped. Doctor no longer wakes it (#6817) Expected on a stopped city. gc start, then rerun gc doctor
proxied-backup-coverage warns on a fresh proxied city No mol-dog-backup run has registered a destination and synced yet gc order run mol-dog-backup, or wait for the order
Role workers fail with gc: unknown command "gc" after bumping the Gas City pack past 0.1.6 The city still imports the pack as [imports.gascity] gc doctor --fix (the gascity-pack-binding check)
gc import add ... --version 0.1.1 fails for a registry pack Known issue: no per-pack git tags --version sha:<commit>, using the commit from gc pack registry show <pack>
invalid issue type: startup-health-episode, or custom-types fails on an external or hosted store gc does not write stores it does not own gc doctor --fix
proxied-shared-server warns A gc-owned scope is unpinned or was bound to ~/.beads/shared-server gc doctor --fix. Beads written to the shared server stay there
Old JSONL archive files are gone from <db>/ First run of the new bd export format They were moved, not deleted: look in <db>/legacy/
A database on the gc-managed server is no longer reaped, exported or backed up No city or rig binds it Bind it to a rig, or drop it
gc dolt compact fails after a database rollback The database is behind its gc-compact-base tag CALL DOLT_TAG('-d', 'gc-compact-base')
A pool slot logs holding pool session <id> open gc could not stop a failed start's runtime Stop the runtime by hand; the slot then retries
Lines are skipped from secrets.env The key is not a shell identifier, or a quote is unclosed Fix the reported line or key range

Validation

  • RC Gate 8 passed on the v1.5.0-rc1 commit with no reruns. Run
    36817987389 on release/v1.5.0 at 5cc547ede3: 151 jobs succeeded and
    0 failed. 3 jobs were skipped, as in every RC Gate, because the gate runs
    its own integration tier. The run covered:
    • CI parity, including Beads topology acceptance on bd 1.3.1
      (TestBeadsProxiedDefault 14/14, the shared-server test, and the init
      topology matrix);
    • proxied-native acceptance, contracts, and the Windows process tree;
    • Ubuntu acceptance A, B and C 1–5 with real inference;
    • all 29 Ubuntu integration shards and tutorial goldens 1–6;
    • every macOS regression job;
    • the goreleaser snapshot.
  • gascity-packs inference suites ran against the RC. These runs used RC
    head e0cb0c5e4f with bd v1.3.1-rc.2 and gascity-packs 390cac1, on a
    Claude pool at medium effort. The tagged head adds only a test-fixture fix
    and the promotion of bd rc.2 to v1.3.1, which has no code change, so the
    suites were not rerun.
    • Passed on the first try: superpowers-all, compound-all and bmad-all.
    • Also passed: gastown-all and all 5 pack smokes.
    • gstack-all did not pass, and gstack was accepted on evidence. The
      first attempt failed at gc start on a heavily loaded host, before any
      inference. The retry hit its 9000s build deadline while the model was
      still iterating in release readiness. Every closed step had passed, and
      every artifact it produced validated. The release owner accepted gstack
      on that evidence.
    • Maintenance orders and doctor were checked on a proxied fixture.
      reaper, jsonl-export and mol-dog-backup exited 0 on a proxied city
      plus a rig, with no direct Dolt access. gc doctor left the stopped
      city stopped.
  • Pack releases are locked in. gstack, compound-engineering, bmad and
    superpowers 0.1.1 are published in the gascity-packs registry at 390cac1.
  • Beads v1.3.1 is a stable release. It is tag v1.3.1 on
    c1c4b642ac1c. Its code is identical to v1.3.1-rc.2, and Homebrew core
    beads is 1.3.1.
  • The stable tag differs from v1.5.0-rc1 only by the CHANGELOG commit.
    v1.5.0 (f66474617d) is v1.5.0-rc1 (5cc547ede3) plus
    chore: release v1.5.0 (#7043), which moves [Unreleased] to [1.5.0] in
    CHANGELOG.md and touches no other file.

Install

Upgrade bd to v1.3.1 first (see the Upgrading Notes), then install or upgrade
Gas City with Homebrew:

brew update
brew install gascity    # new install
brew upgrade gascity    # existing install
gc version              # 1.5.0

brew install gascity resolves to the homebrew-core formula, which picks up
v1.5.0 through Homebrew's automatic version bump, usually within a few hours of
the release. Until brew info gascity shows 1.5.0, use the
gastownhall/gascity tap formula (brew install gastownhall/gascity/gascity),
which already installs v1.5.0, or download the archive below.

Or download the archive for your platform (linux or darwin, amd64 or
arm64) from this release and verify it:

gh release download v1.5.0 -R gastownhall/gascity \
  -p 'gascity_1.5.0_linux_amd64.tar.gz' \
  -p 'gascity_1.5.0_checksums.txt'
sha256sum --ignore-missing -c gascity_1.5.0_checksums.txt   # macOS: shasum -a 256 --ignore-missing -c
tar -xzf gascity_1.5.0_linux_amd64.tar.gz gc
./gc version

Then let gc start restart the supervisor on the new binary. The release
archives carry GitHub artifact attestations and an SPDX SBOM
(gh attestation verify <archive> -R gastownhall/gascity). To build from
source, check out the v1.5.0 tag and run make install.

Contributors

Thanks to everyone who contributed to v1.5.0 (between v1.4.2 and
v1.5.0):

@24601, @A3Ackerman, @AJBcoding, @AlexBelous, @allenday, @amir-rezaei,
@aphexcx, @atbrace, @austinborn, @aytheo-bit, @boshu2, @bourgois,
@brandonmartin, @caseymatt, @chris-sanders, @ckumar1, @cmc-veup,
@csauer02-personal-user, @csells, @dlarsen5, @donnabox, @duncan4123,
@edwarddavison, @elmpp, @eudaimos, @figgeous, @GEMISIS, @graham-a11y,
@haddad-daniel, @hexsprite, @iwata-1116, @jacobhausler, @jamelt, @jeffora,
@jm2, @johnzook, @julianknutsen, @justakeyboardbetweenus, @kaushikNaarayan,
@loucmane, @m-u-xyz, @masonjames, @McKean, @mike-reese, @mk-imagine,
@pranaypratyush, @pzxy, @quad341, @rbriski, @remuscazacu, @rjgeng, @Rome-1,
@sarendipitee, @shahshrey, @sjarmak, @swedeinasia-flow, @tdupu,
@TheChrisOneil, @Toady00, @tobasummandal, @tudorsaitoc, @vbtcl,
@vishnujayvel, @wbern, @wonkothesanest, @wynged

New Contributors

Recommended Reading

Full Changelog: v1.4.2...v1.5.0

Don't miss a new gascity release

NewReleases is sending notifications on new releases.