Skip to content

Releases

v0.0.15

Choose a tag to compare

@AndrewGrachov AndrewGrachov released this 06 Nov 06:08

Highlights

  • MCP server. The board hosts an MCP endpoint with its own OAuth authorization server, so MCP clients can create, follow up on, wait for and inspect tasks and batches (#1229). Off by default: set mcp.enabled in the chart.
  • Task titles. Tasks carry a short title, editable from the task page (BEL-252, in #1229).
  • Base branch and skill menu. The composer picks the base branch a task starts from, and / opens a skill menu (BEL-162, BEL-202).
  • Task page. The right column now runs full height. Task controls sit on top, published work moves into an Outcome tile, and the other sections are collapsible (#1254). New task and follow-up share one chat input, with attach and send inside the box (#1253). Further redesign passes in #1203 and #1249.
  • Gate skip and reuse. A run that changed nothing skips its gates, or reuses the last result for the thread (BEL-254).

Fixes and internals

  • Recovery keeps the agent's own branch. The interruption budget counts the trailing hour (BEL-244).
  • A lapsed or superseded lease on a running task is treated as pending, not refused, so runner credentials survive a takeover (BEL-243).
  • Gates renew their GitHub token through the gate mailbox (BEL-258).
  • task.ready is sent for a batch PR task once the board is idle (BEL-260).
  • Test harness fixes (BEL-267, BEL-268, #1204).

Upgrade notes

  • Migrations 120–126 apply on boot: job base branch, prior lease, OAuth tables, pending authorize, attachment upload, job title, and the batch task-ready notification.
  • New chart values mcp.enabled (needs an https auth.publicUrl), mcp.trustedOrigins and batches.enabled. All are off by default.
  • Executor image tags. Bump claudeCode and opencode together with the board and driver tags.
  • npm CLI not published for this release: the npm trusted publisher still points at the pre-rename repository.

Validation

  • CI is green on the release commit. Release validate and test-db pass.
  • v0.0.15-rc through v0.0.15-rc-4 were published. The stage board ran rc-4, which has the same code as this release.
  • verify:ui 179/0 on the task page change.

v0.0.14

Choose a tag to compare

@AndrewGrachov AndrewGrachov released this 02 Nov 07:01

Highlights

  • Notifications. Task events (ready for review, blocked on a person, a pending question) reach members through a durable outbox with retry and dedupe (BEL-128..131, BEL-133..135). Discord is the first connector, with the webhook URL kept in the secret store (BEL-132). A settings UI covers org subscriptions, personal subscriptions and a test send (BEL-177).
  • Task attachments. Files uploaded to a task are delivered to the agent on both the docker and kubernetes executors (BEL-113..119). The chat shows image previews and inline tiles, and files can be dropped anywhere on it (BEL-195).
  • Connect block. A task that needs a connection asks for it, then resumes once the member connects (BEL-174).
  • Admin actions. Admins can follow up on, retry or control the wait of another member's task. Every such action is audited and notified (BEL-165).
  • Redesign. Settings, notifications, repositories, executors, environment and the task composer move to a compact layout (#1168). The integrations settings get a connected overview, a catalog and per-connection details (#1156).
  • Env secrets. Env secrets now have one stored format, and no code path reads plaintext (BEL-150).

Fixes and internals

  • Task detail shows live close-phase gate progress at the bottom of the turn (BEL-175).
  • Settings hide planned integrations, and notification subscriptions no longer have a workflow filter (#1159).
  • Both executor images share one docker/executor-common (#1147).
  • Duplicate code was removed across server routes, web shells and driver leased writes (#1144–#1146), and the docs had a maintenance pass (#1143).
  • Lane script reliability: the ask-lane answer match (BEL-176) and no early-exit reader under pipefail (BEL-178).
  • Repository references and package metadata follow the Bellows rename.

Upgrade notes

  • Migrations 101–119 apply on boot: finalized grant claim, notifications, runtime requirements, env secret form, attachments, and the notification workflow-filter drop.
  • Attachments use the existing artifacts store (artifacts.backend, normally S3). The chart adds artifacts.attachmentDraftTtlHours, default 24: an upload not attached to a task within that many hours is deleted.
  • Migration 113 removes the plaintext env format. Run secrets:convert-env on v0.0.13 first, and upgrade only when it reports remaining 0.
  • Executor image tags. Bump claudeCode and opencode together with the board and driver tags.

Validation

  • CI is green on the release commit.
  • v0.0.14-rc through v0.0.14-rc-4 were published, and the stage board runs rc-4, which has the same code as this release.
  • On the redesign branch: unit 11308/0, test:jobs 387/0, kind test:k8s --cluster 646/0, verify:ui 137/0.

Known limits

  • The CLI npm package for 0.0.14 is not published yet: the npm trusted publisher still names the repository from before the rename. latest stays 0.0.13 until the publish job is re-run.

v0.0.13

Choose a tag to compare

@AndrewGrachov AndrewGrachov released this 30 Oct 10:10

Highlights

  • Managed connections. Integrations now work through principal/grant connections, with personal defaults and execution bindings (BEL-59). A shared outbound OAuth lifecycle backs them (BEL-138), and runners fetch credentials from a lease-authenticated broker (BEL-60).
  • Jira Cloud OAuth replaces the Jira proxy (BEL-61). Members connect Jira through an Atlassian 3LO app; API-token connections end.
  • Runner permissions. The claude runner is pinned to auto mode on fresh and resumed sessions (#1107). The git guard is gone: every git and gh operation is allowed in the task checkout (#1109).
  • Env secrets. Writes are encrypted, with a one-shot conversion of existing plaintext (BEL-149).
  • Commerce groundwork (BEL-146): plan push and execution-usage reporting, off by default.

Fixes

  • k8s driver: a leftover pod stuck Terminating hands the claim back as blocked instead of expiring the lease (BEL-160).
  • Job store lock order: the claim try-locks the thread lock, retry never row-locks a live row, and thread writers lock the root row first (BEL-157, BEL-81, BEL-161).
  • Commerce store: enqueue stamps next_attempt_at with the store's clock (BEL-163).
  • Integrations: one clock for the authorization deadline and the claim lease; opencode image checks; a secrets fault flake (BEL-164, BEL-166, BEL-170).
  • Gates skill: drops the stale curl requirement (BEL-122).

Upgrade notes

  • Migrations 094–101 apply on boot (integration connections, commerce, grant lineage, integration authorization, Jira OAuth, operation receipts, legacy Jira tokens, finalized grant claim).
  • Jira API-token connections are disconnected and their tokens are scrubbed (migration 100). A queued thread bound to one waits on a Jira requirement until its member connects Jira through OAuth and binds it. Before upgrading:
    1. Set integrations.jira.clientId and secret.atlassianClientSecret (ATLASSIAN_CLIENT_ID / ATLASSIAN_CLIENT_SECRET).
    2. Add the board's callback URL to the Atlassian app.
    3. Ask Jira users to reconnect after the upgrade.
  • New chart values: integrations.jira.clientId, secret.atlassianClientSecret, dashboard.commerce.{enabled,url}, secret.commerceToken (commerce stays off unless enabled).
  • Run secrets:convert-env once after the deploy to encrypt existing plaintext env values (BEL-149).
  • Bump the executor image tags (claudeCode, opencode) together with the board and driver tags.

Validation

  • CI is green on the release commit. v0.0.13-rc and v0.0.13-rc-2 published, and the stage board runs rc-2.
  • Stage smoke task on the claude-code runner: auto mode active; a Jira read through the OAuth broker; git fetch, git switch, gh pr list.

Known limits

  • A repository's own .claude/settings.json deny rules still apply in the runner and win over auto mode (for example, a repo denying git rebase*).
  • The driver and dashboard can land on spot nodes; a spot reclaim drains the driver and costs the running tasks a lease takeover (recovered, not lost).

v0.0.13-rc

v0.0.13-rc Pre-release
Pre-release

Choose a tag to compare

@AndrewGrachov AndrewGrachov released this 29 Oct 23:14
72ab661

Release candidate for v0.0.13.

Highlights

  • Managed connections. A principal/grant model with personal defaults and per-task execution bindings (BEL-59). A shared outbound OAuth lifecycle with grant lineage and recovery (BEL-138). A lease-authenticated credential broker for runners (BEL-60).
  • Jira Cloud OAuth. Personal Jira connections through Atlassian 3LO, with read and explicit write consent. A runner Jira helper with comment receipts that never post twice. A Workspace Jira panel. The API-token Jira proxy is removed (BEL-61).
  • Env secrets. Secrets are written encrypted through the secret store, and a one-shot conversion handles the plaintext rows that remain (BEL-149).
  • Commerce groundwork. Plan push and execution-usage reporting, off by default (BEL-146, BEL-163).

Fixes

  • Job store lock order:
    • BEL-157 / BEL-81: the claim try-locks the thread lock, and retry never row-locks a live row.
    • BEL-161: thread writers lock the root row first.
  • BEL-160: a leftover pod stuck Terminating hands the claim back as blocked.
  • The claude runner pins auto permission mode (#1107). The runner git guard is lifted (#1109).
  • BEL-122: the gates skill drops a stale curl requirement.

Upgrade notes

  • Migrations 094–100 are expand-only, except 100. It disconnects every legacy API-token Jira connection and scrubs its token. Queued tasks bound to such a connection wait for their user to connect Jira through OAuth and bind it.
  • Jira OAuth needs an Atlassian 3LO app. The steps are in docs/jira-app-runbook.md. Configure it with integrations.jira.clientId and secret.atlassianClientSecret (ATLASSIAN_CLIENT_ID / ATLASSIAN_CLIENT_SECRET). While those are unset, starting a Jira connection answers INTEGRATION_NOT_CONFIGURED.
  • After the rollout completes, run the env-secret conversion once, and rerun it until it exits 0: kubectl exec deploy/<release>-factory -c dashboard -- node server/dist/secrets/convert-env-cli.js. Oversized rows are listed and must be re-entered.
  • New optional chart values: dashboard.commerce.{enabled,url}, secret.commerceToken, integrations.jira.clientId and secret.atlassianClientSecret.

Validation

  • On the merged heads: npm test, test:db, typecheck, lint:changed and verify:ui (106).
  • test:jobs --changed: one known failure, BEL-166.
  • Kind test:k8s --cluster, per PR: BEL-138 632/0 and BEL-60 632/0. BEL-61 637/1: a chart-render check that passed on its own; the check is now instrumented.
  • Migration upgrade path: a v0.0.12 seed with legacy Jira connections, bindings and a secret env var applies 094–100 with every row kept.

Known limits

  • BEL-164: the authorization deadline and the lineage claim lease are not yet unified.
  • BEL-166: the opencode image check still asserts the git denies that #1109 lifted.
  • BEL-140/141: there is no blocking connect interaction yet. A waiting task is unblocked through the bind route.
  • BEL-169: the Atlassian remote MCP investigation.

v0.0.12

Choose a tag to compare

@AndrewGrachov AndrewGrachov released this 28 Oct 21:35

Highlights

  • Shared secret store. The board now has one encrypted store for server-side secrets. Three backends are available, chosen with SECRETS_BACKEND:

    • database: AES-256-GCM envelopes in PostgreSQL, with a key ring, rekey rotation (npm run secrets:rotate) and a collector;
    • aws: AWS Secrets Manager;
    • vault: HashiCorp Vault KV v2.

    Every board boot wires it in. (BEL-64 #1079, BEL-136 #1080 #1081, BEL-137 #1082 #1083 #1086)

  • Environment secrets: groundwork for encryption at rest (step 1 of 3). The env_var table can now hold a secret store reference. Claim-time resolution reads secrets stored either way: plaintext rows as before, or rows that point into the store. A secret that cannot be read fails the claim closed with a 503; the job stays queued and the attempt is not used up. Writes are still plaintext in this release. Encrypted writes and a one-shot conversion come in the next release (BEL-149), so a mixed-version rollout never hands a runner an empty secret. (BEL-54 #1088)

  • Dashboard replicas. The chart value dashboard.replicas can go above 1, but only with an S3 artifacts backend. Above one replica, a stopping pod drains for dashboard.preStopSeconds before SIGTERM. The repo allowlist is read on every request. (BEL-90 #1075, BEL-124, #1078)

  • Older builds tolerate a newer schema. A build whose database is ahead of it skips the repeatable views instead of re-creating them. (BEL-125 #1077)

Fixes

  • BEL-127: the k8s driver read a killed runner pod's null container times as real values, and leases looped until the task died. (#1076)
  • BEL-151: the dashboard e2e seed produced a week with no token data only when it ran Friday to Monday, so CI's e2e job failed Tuesday to Thursday. (#1090)
  • BEL-152: the secret store conformance harness's 200 ms per-call deadline raced cases that hold a call. (#1091)
  • BEL-153: on hosts with uutils coreutils (ubuntu-26.04), the tool broker recreated the shared TMPDIR without setgid. Shipped runner images use GNU coreutils and were not affected. (#1092)
  • BEL-154: server/test/secrets.boot.test.ts read the ambient environment, so the repository's test gate failed inside every runner that had GITHUB_TOKEN or BASE_BRANCH set. (#1095)
  • BEL-156: bellows batch wait could exit 0 ("every stage verified") before the board had written the stage's verification. It now succeeds only once the board marks the batch completed. (#1098)
  • Runner images: npm's bundled undici 6.29.0 had two HIGH findings (a certificate-validation bypass and a TLS-renegotiation DoS). No npm release ships the fix yet, so both images replace it with 6.29.1 in place. (#1099)
  • The takeover gremlin bug proof can no longer pass without catching the planted lapse; the cap on excused seeds drops from 50% to 25%. (#1089)

Upgrade notes

  • The secret store is required at boot. The board refuses to start without it.
    • Helm: set secretStore.backend (database, aws or vault).

      • Every backend needs secretStore.operationKey and secretStore.fingerprintKey (each openssl rand -base64 32), or a secretStore.existingSecret that holds them.
      • database also needs secretStore.dataKeys (k1:<32 random bytes, base64>) and secretStore.activeKeyId.
      • aws needs secretStore.aws.region and secretStore.aws.prefix.
      • vault needs secretStore.vault.addr, mount, prefix, role and audience (default vault).

      The chart's validate.yaml refuses a render that is missing any of these.

    • Compose and local runs: run npm run secrets:init once. It writes a gitignored secrets.env (see secrets.env.example).

    • Local kind clusters: make build only restarts pods and does not re-apply chart values. After secrets:init, re-apply them once: node scripts/k8s-local-values.mjs | helm upgrade dev charts/factory -f charts/factory/values-local.yaml -f -, or run make start.

  • Back up the store's keys apart from the database. A database backup can't be decrypted without them. After a rotation, keep a retired key until secrets:rotate verifies it is unused.
  • Four new migrations, applied automatically at board start:
    • 090_views_<hash> (a view marker)
    • 091_secret_store (new tables)
    • 092_secret_external (new tables)
    • 093_env_var_secret_ref: adds two nullable columns to env_var and drops NOT NULL from env_var.value. Additions only; an older pod keeps working against it.
  • Deploy this release before the next one. The next release (BEL-149) moves environment secrets into the store. Every deployment must run this version on all its pods first. Do not upgrade from v0.0.11 straight past it.
  • New chart values: dashboard.replicas (default 1) and dashboard.preStopSeconds (default 15).

Validation

  • On the release candidate v0.0.12-rc-2 (57ce6d6, the code this release ships; 65153d4 only bumps VERSION): it adds only the runner-image undici swap (#1099) to 82d0a26. Both runner images passed the release scan locally, and the test:jobs phases that build and exercise them passed (73 passed).
  • On 82d0a26: typecheck, lint, npm test (10376 passed) and the full test:jobs lane (379 passed) are green, and CI on main is green, e2e included. On 32e8e4d, which differs only by a test file and the CLI's batch verdict, test:db (1271 passed), the helm assertions (347 passed) and verify:ui (102 passed) were also green.
  • Staging: v0.0.12-rc-2 ran on a two-replica staging deployment with the database secret store; migrations 091–093 applied on boot and a task ran end to end.
  • Upgrade from v0.0.11. That release's schema was seeded with rows, then this release's migrations ran on top. 090–093 applied, every table kept its rows, and the migration ledger reached 093.
  • Live batch on kind, with this release's board, driver and runner images and the chart upgraded in place:
    • It ran one automerge stage of two parallel tasks, both writing the same scratch file on a sandbox branch.
    • The first run found BEL-154: the test gate failed in every runner. The agents reported themselves blocked instead of widening their changes.
    • In the rerun:
      • both tasks passed their gates;
      • the second PR conflicted after the first merged, and the board repaired it with a merge that kept both lines;
      • main was untouched.
    • Both PRs were merged by hand and stage verification stayed pending, because the sandbox branch gets no CI (BEL-155). The probe also found BEL-156.

Known limits

  • Environment secret values are still stored in plaintext; that changes with BEL-149, and BEL-150 then removes the plaintext path.
  • Live probes against a sandbox branch can't exercise automerge or stage verification, because CI runs only for main, so a sandbox PR reports no checks and the board waits on a person (BEL-155).
  • driver/src/review/review-step.ts also relies on mkdir -m for special bits. It runs only inside the bookworm runner image (GNU coreutils), so it is noted in BEL-153 rather than changed.

v0.0.12-rc-2

v0.0.12-rc-2 Pre-release
Pre-release

Choose a tag to compare

@AndrewGrachov AndrewGrachov released this 28 Oct 19:15
57ce6d6

Release candidate for v0.0.12. It supersedes v0.0.12-rc, whose runner images failed the release scan (see #1099), so nothing was published under that tag.

Highlights

  • Shared secret store. The board now has one encrypted store for server-side secrets. Three backends are available, chosen with SECRETS_BACKEND:

    • database: AES-256-GCM envelopes in PostgreSQL, with a key ring, rekey rotation (npm run secrets:rotate) and a collector;
    • aws: AWS Secrets Manager;
    • vault: HashiCorp Vault KV v2.

    Every board boot wires it in. (BEL-64 #1079, BEL-136 #1080 #1081, BEL-137 #1082 #1083 #1086)

  • Environment secrets: groundwork for encryption at rest (step 1 of 3). The env_var table can now hold a secret store reference. Claim-time resolution reads secrets stored either way: plaintext rows as before, or rows that point into the store. A secret that cannot be read fails the claim closed with a 503; the job stays queued and the attempt is not used up. Writes are still plaintext in this release. Encrypted writes and a one-shot conversion come in the next release (BEL-149), so a mixed-version rollout never hands a runner an empty secret. (BEL-54 #1088)

  • Dashboard replicas. The chart value dashboard.replicas can go above 1, but only with an S3 artifacts backend. Above one replica, a stopping pod drains for dashboard.preStopSeconds before SIGTERM. The repo allowlist is read on every request. (BEL-90 #1075, BEL-124, #1078)

  • Older builds tolerate a newer schema. A build whose database is ahead of it skips the repeatable views instead of re-creating them. (BEL-125 #1077)

Fixes

  • BEL-127: the k8s driver read a killed runner pod's null container times as real values, and leases looped until the task died. (#1076)
  • BEL-151: the dashboard e2e seed produced a week with no token data only when it ran Friday to Monday, so CI's e2e job failed Tuesday to Thursday. (#1090)
  • BEL-152: the secret store conformance harness's 200 ms per-call deadline raced cases that hold a call. (#1091)
  • BEL-153: on hosts with uutils coreutils (ubuntu-26.04), the tool broker recreated the shared TMPDIR without setgid. Shipped runner images use GNU coreutils and were not affected. (#1092)
  • BEL-154: server/test/secrets.boot.test.ts read the ambient environment, so the repository's test gate failed inside every runner that had GITHUB_TOKEN or BASE_BRANCH set. (#1095)
  • BEL-156: bellows batch wait could exit 0 ("every stage verified") before the board had written the stage's verification. It now succeeds only once the board marks the batch completed. (#1098)
  • Runner images: npm's bundled undici 6.29.0 had two HIGH findings (a certificate-validation bypass and a TLS-renegotiation DoS). No npm release ships the fix yet, so both images replace it with 6.29.1 in place. (#1099)
  • The takeover gremlin bug proof can no longer pass without catching the planted lapse; the cap on excused seeds drops from 50% to 25%. (#1089)

Upgrade notes

  • The secret store is required at boot. The board refuses to start without it.
    • Helm: set secretStore.backend (database, aws or vault).

      • Every backend needs secretStore.operationKey and secretStore.fingerprintKey (each openssl rand -base64 32), or a secretStore.existingSecret that holds them.
      • database also needs secretStore.dataKeys (k1:<32 random bytes, base64>) and secretStore.activeKeyId.
      • aws needs secretStore.aws.region and secretStore.aws.prefix.
      • vault needs secretStore.vault.addr, mount, prefix, role and audience (default vault).

      The chart's validate.yaml refuses a render that is missing any of these.

    • Compose and local runs: run npm run secrets:init once. It writes a gitignored secrets.env (see secrets.env.example).

    • Local kind clusters: make build only restarts pods and does not re-apply chart values. After secrets:init, re-apply them once: node scripts/k8s-local-values.mjs | helm upgrade dev charts/factory -f charts/factory/values-local.yaml -f -, or run make start.

  • Back up the store's keys apart from the database. A database backup can't be decrypted without them. After a rotation, keep a retired key until secrets:rotate verifies it is unused.
  • Four new migrations, applied automatically at board start:
    • 090_views_<hash> (a view marker)
    • 091_secret_store (new tables)
    • 092_secret_external (new tables)
    • 093_env_var_secret_ref: adds two nullable columns to env_var and drops NOT NULL from env_var.value. Additions only; an older pod keeps working against it.
  • Deploy this release before the next one. The next release (BEL-149) moves environment secrets into the store. Every deployment must run this version on all its pods first. Do not upgrade from v0.0.11 straight past it.
  • New chart values: dashboard.replicas (default 1) and dashboard.preStopSeconds (default 15).

Validation

  • On the release commit (57ce6d6): it adds only the runner-image undici swap (#1099) to 82d0a26. Both runner images passed the release scan locally, and the test:jobs phases that build and exercise them passed (73 passed).
  • On 82d0a26: typecheck, lint, npm test (10376 passed) and the full test:jobs lane (379 passed) are green, and CI on main is green, e2e included. On 32e8e4d, which differs only by a test file and the CLI's batch verdict, test:db (1271 passed), the helm assertions (347 passed) and verify:ui (102 passed) were also green.
  • Upgrade from v0.0.11. That release's schema was seeded with rows, then this release's migrations ran on top. 090–093 applied, every table kept its rows, and the migration ledger reached 093.
  • Live batch on kind, with this release's board, driver and runner images and the chart upgraded in place:
    • It ran one automerge stage of two parallel tasks, both writing the same scratch file on a sandbox branch.
    • The first run found BEL-154: the test gate failed in every runner. The agents reported themselves blocked instead of widening their changes.
    • In the rerun:
      • both tasks passed their gates;
      • the second PR conflicted after the first merged, and the board repaired it with a merge that kept both lines;
      • main was untouched.
    • Both PRs were merged by hand and stage verification stayed pending, because the sandbox branch gets no CI (BEL-155). The probe also found BEL-156.

Known limits

  • Environment secret values are still stored in plaintext; that changes with BEL-149, and BEL-150 then removes the plaintext path.
  • Live probes against a sandbox branch can't exercise automerge or stage verification, because CI runs only for main, so a sandbox PR reports no checks and the board waits on a person (BEL-155).
  • driver/src/review/review-step.ts also relies on mkdir -m for special bits. It runs only inside the bookworm runner image (GNU coreutils), so it is noted in BEL-153 rather than changed.

v0.0.11

Choose a tag to compare

@AndrewGrachov AndrewGrachov released this 27 Oct 10:46

Highlights

  • Gate runs no longer run twice. When an agent has already passed a gate during its run, task completion reuses that pass instead of running the gate again. It does so only when all of these still hold:

    • the gate is the same: same command, image and env values;
    • the run claim is the same;
    • the files are the same. A publish commit no longer counts as a change.
    • nothing touched the tree while the gate ran.

    Any read that can't be confirmed runs the gate as before. (#983)

  • Gate and PR activity is recorded and shown. The board records:

    • every gate run, with a running row first and then exactly one outcome;
    • every PR operation, as created or reused;
    • every gate and PR call the agent made, read from its transcript.

    The task stats dialog and the activity API show them. Coverage reads complete, partial or unknown, never a false zero. (#983)

  • Claude-code can run gates and publish itself. The factory-gate and factory-control wrappers are built into both runner images and allowlisted for claude-code. Before this, the allowlist refused the agent's gate and publish commands. (#1061)

  • Transcripts survive the volume reaper. Before deleting a task volume whose thread isn't complete, the reaper re-uploads any transcript records the exporter never had acknowledged. (#938)

Fixes

  • #1061: claude-code's command allowlist refused the gate and publish commands.
  • #1045: a runner adopted after a driver takeover kept the old lease, so Jira, GitHub and the branch reporter refused it. (#1049)
  • #1064: the gate image ships no lsof, so a cleanup.sh test hung and the repo's test gate failed on every task. (#1065)
  • #938: unacknowledged transcript records were lost when a task volume was reaped. (#1042)

Upgrade notes

  • Three new migrations, applied automatically at board start:

    • 087_job_gate_run
    • 088_job_pr_operation
    • 089_transcript_tool_event

    They create new tables and do not alter existing ones.

  • New driver setting: TASK_VOLUME_EXPORT_ATTEMPTS, chart value taskVolume.exportAttempts, default 3. It is how many failed re-export rounds the reaper tolerates before it deletes a volume anyway and logs the loss.

  • Runner images changed. Both include the factory-gate and factory-control wrappers and the lease-pair helper. Claude-code's managed settings allowlist Bash(factory-gate *) and Bash(factory-control *). Deploy the matching images with the board and driver.

Validation

  • On the release commit: typecheck, lint, npm test, test:db and the helm assertions (test:k8s) are green.
  • Upgrade from v0.0.10. That release's schema was seeded with rows, then this release's migrations ran on top. 087–089 applied, every table kept its rows, and the migration ledger grew by exactly three.
  • verify:ui: 101 of 102 specs pass. The one failure, #1073, is a date-dependent token-usage spec; it fails on v0.0.10 too.
  • Kind lane (test:k8s --cluster): 544/0 on the #983 epic.
  • test:jobs full lane: 379/0 on the #983 epic before its last two slices; not rerun for this release.
  • Live test on a local kind board with these images, claude-code executor, using throwaway PRs that were closed:
    • Agent runs its gates, then publishes. The lint pass was reused at completion after the publish commit. The PR was recorded once as created and once as reused, and no second PR was opened.
    • Agent edits after its gates. The change was detected, the gates re-ran, and the PR was recorded as created.

Known limits

  • Cases where a gate pass is never reused. The gates then simply run, so this is slower but safe:
    • git rewrites content: autocrlf, a text/eol/encoding attribute, or a filter driver that a path's filter attribute names;
    • the checkout doesn't record modes or symlinks, or git folds case;
    • the index has assume-unchanged or skip-worktree entries, or is split.
  • A gate pass does not cover git-ignored files the gate reads.
  • A repository whose own test suite writes inside the work tree, as this one's does, never gets its test pass reused: the change guard sees the writes.
  • Deferred:
    • #909: a taken-over run's ad-hoc gate requests keep the lost driver's endpoint;
    • #1059: the opencode free-tier model returns empty replies;
    • #1060: a kind lane flake.

v0.0.10

Choose a tag to compare

@AndrewGrachov AndrewGrachov released this 26 Oct 14:42

Highlights

  • Gate filter and pre-push gates. A gate that sets filter: true in .bellows.yaml accepts an ad-hoc {"gate","filter"} call; the filter reaches the command only as env GATE_FILTER, for that one run, and never counts as evidence. The master prompt now requires the covering gates before every push (#1041).
  • Token accounting by type, task, run, user, day, model and source (#842).
  • No-change tasks skip close-time gates (#1037).
  • Runner uid seam fixed: files shared between the CLI uid and the tool uid within bounds, skill helpers installed outside the private config dir, gate pair minted before the group and a commit identity for the agent (#1007, #1016–#1018, #1021, #1023, #1024).
  • Executor images ship python3 and procps; opencode ships ripgrep instead of downloading it at runtime (#1022).

Fixes

  • #1010 runner token requests actions:read; a 422 names the missing App permissions.
  • #1019 a job whose executor its scope does not hold is refused at create.
  • #1020 a jest per-test timeout gate failure is classified as environment.
  • #1039 claude-executor image checks match the entrypoint and run as a test:jobs phase.
  • Driver git helpers hardened against agent-written checkout config.

Upgrade notes

  • Migrations 085_job_execution.sql, 086_metric_point_identity.sql and the repeatable views apply on boot.
  • The GitHub App should grant Actions: read; without it the board names the missing permission on a 422.
  • No chart value or env var changes.

Validation

  • Full suite on a93502d: npm test 9221 passed, test:db, helm, test:jobs 379/0, kind --cluster 544/0, verify:ui 101/101.
  • v0.0.10-rc-2 (same code) ran on a staging deployment, migrating from v0.0.9-rc-4 data.

Known limits

  • No lane drives a filtered ad-hoc gate call end to end; integration tests cover it.

v0.0.9

Choose a tag to compare

@AndrewGrachov AndrewGrachov released this 24 Oct 21:35

Highlights

  • Task-owned runners. Setup, gates and declared services run in the task pod (#772, #773). Each root thread gets its own task volume and checkout. Each attempt's tree and session are checkpointed at finalize, a lost volume is restored from its checkpoint, and the reaper retires volumes using board thread facts (#775). The internal reviewer runs in the task pod and claims its review in-band (#774).
  • Infrastructure loss interrupts a task instead of losing it. Driver takeover works on both executors (#829). An expired lease is recovered as an interruption and keeps its session (#822). Every run's end goes through one termination policy (#824). Settle receipts and a storage-enforced job status graph are in place (#821). Supervisor reports are acknowledged and step invocations are kept separate (#823). Docker and kubernetes now follow one executor contract (#828, #869).
  • Shared admission. Running-task budgets (total, per org, per repo) are enforced in the claim across every driver and replica (#831). The cross-org claim scan is ordered the same on every replica. Board and driver expose Prometheus metrics. PRs are followed on an interval, so a lost webhook never strands a wait.
  • Replica-safe API. Migrations are serialized under one advisory lock, workspace clone and purge rows are owned by token, and artifacts are pinned across two API replicas (#830).
  • Security.
    • The agent's tool commands run under their own uid (#814).
    • The runner's GitHub token renews through a lease-authenticated endpoint, so long runs outlive the 1 h App token (#807).
    • Publication evidence is bound to its content, and log completeness is reported accurately (#825).
    • The transcript exporter has its own export-only control token (#827).
  • The API server no longer keeps a filesystem workspace or per-member repo selection (#777, #778).

Fixes

  • Supervised steps are released when the runner group ends mid-step (#967).
  • An agent container that ended without agent.exit is read as lost within seconds, on both executors (#920).
  • The pre-agent setup outcome carries to an adopting or taking-over holder (#917).
  • A takeover's rebind wait ends once the earlier group is confirmed finished (#928).
  • Block-helper effects are reconciled across claims with stable effect ids and receipts (#826).
  • Flaky executor tests are order- and load-independent (#905, #946).
  • Release pipeline: every image pull goes through mirror.gcr.io instead of ECR Public, whose per-IP data limit refused shared runner IPs. The executors' npm is bumped to 11.21.1.

Upgrade notes

  • Migrations 070–084 apply on boot, serialized under an advisory lock. 084 enforces the job status transition graph with a trigger.
  • Chart: the RWX workspaces volume is gone. Remove workspaces.* and dashboard.workspaceRoot from your values. Runners use one per-thread taskVolume instead (accessMode: ReadWriteOncePod, size: 20Gi, ttlMs, boundMs), so the cluster needs a StorageClass that provides RWOP volumes. EFS is no longer needed.
  • Chart: artifacts.backend defaults to s3. artifacts.s3.bucket and artifacts.s3.region (or endpoint) are required. Set artifacts.backend=filesystem only for a single-replica development install.
  • Chart: new values.
    • dashboard.admission.maxRunning / maxRunningPerOrg / maxRunningPerRepo (empty means unlimited).
    • dashboard.serviceAccount.
    • The driver gets metricsPort (9464), rebindGraceMs, orphanMinMs, stepWaitMs, repoCloneBase and artifactFlush.*.
  • Env.
    • ORG_WORKSPACE_ROOT is removed.
    • New: ADMISSION_MAX_RUNNING, ADMISSION_MAX_RUNNING_PER_ORG, ADMISSION_MAX_RUNNING_PER_REPO, TASK_VOLUME_TTL_MS, TASK_VOLUME_BOUND_MS, DRIVER_HANDOVER_MS, DRIVER_METRICS_PORT. All are optional.
  • CLI: @bellows-ai/bellows-cli@0.0.9 publishes under latest.

Validation

  • CI green on the release commit. Identical runtime code to v0.0.9-rc-4: the only commit after it removes marketing sources.
  • v0.0.9-rc-4 deployed to a no-EFS EKS stage install with per-thread EBS task volumes, running tasks end to end. The EKS failure-matrix walk was recorded on that stage (#779, #968).
  • Kind lane 536/0 and test:jobs 342/0 on main for #905.

Known limits

  • CVE-2026-109869 (undici, inside npm's own vendored tree in the executor images, not reachable at runtime) is in .trivyignore until an npm release vendors undici 6.29.1. Revisit 2026-11-24.
  • The dashboard stays at one replica (#913).
  • A workflow wait's re-entry can drop activity that arrived during the previous continuation (#937).
  • Kind-lane exit cleanup can hang on an API stall (#948).
  • Load-sensitive timeouts remain in npm test (#972).
  • Close-time session readouts still run as aux Jobs (#882).
  • The Atlassian CLI is not yet mirrored and pinned (#384).

v0.0.7

Choose a tag to compare

@AndrewGrachov AndrewGrachov released this 14 Oct 05:03

Highlights

Batch execution. Plan an epic as ordered stages of parallel tasks, each one an ordinary task thread with its own PR, and let the board run it overnight.

  • Persisted, validated stage plans with API contracts; durable parallel scheduling under a concurrency cap; lifecycle API (import, validate, update, start, cancel, accept, retry, report).
  • Two merge policies per batch or stage: human (people merge; the board observes and continues) and automerge (the board merges once GitHub's requirements pass, serialized per target, then verifies the target's checks before the next stage).
  • Bounded recovery: infrastructure retries and repairs with their own budgets; gates the environment failed (vitest worker timeouts, exits 124/125/137) are retried as infrastructure, a failing assertion or a conflicting PR is repaired by a follow-up in the same session; a time limit and a morning report.
  • Durable PR observation (checks and mergeability); a conflicted rebase is handed to the agent to resolve rather than failing the run.
  • Executor selection by scope (user / org), refused at validate when missing.
  • Web planning, monitoring and stage controls; the batch-control skill for driving a batch from a local session.
  • Finished batches report completed / cancelled / expired and close their task threads; a person can force-close one task.

CLI. factory batch … commands; durable mutation idempotency and replay keys; job retry, reopen and queued-command editing; question inspection and answers; file/stdin prompt input; request deadlines, interruption and bounded read retries; logical-task listing with cursors; full artifacts, activity and investigation; identity/configuration diagnostics and preflight.

Gates. on_demand gates the agent runs ad hoc (skipped by the end-of-run pass); the UI gate and its runner image are gone (the UI suite runs as a .bellows.yaml gate). Gate pods/containers are sized on their own with GATE_CPU_REQUEST, GATE_MEMORY_REQUEST, GATE_CPU_LIMIT, GATE_MEMORY_LIMIT (chart: runner.gateResources, default requests 2 CPU / 2Gi), falling back to the RUNNER_* values (#507).

Fixes

  • Repair prompts now name each failed gate with its exit code and output tail (#687).
  • An automerge task whose PR the board can still merge reads next: wait, not a person's merge, and no longer parks the batch, so batch wait stops exiting 6 while the board is acting (#688); the web batch page reads the same report and shows "Board merging" instead of "Waiting for you" (#693).
  • Behavior change: a follow-up on a thread that still has an unfinished job is refused with 409 NOT_FINISHED, like a retry, instead of queuing a second follow-up; an idempotent replay with the same key still answers the original (#698).
  • CI now runs the database suite (test-db) on every pull request; six DB tests that had drifted from their code are corrected (#694).
  • A credential lookup the database cannot answer is a 503, which the CLI retries, not a 500 (#682).
  • Runner git guard reads heredoc bodies as data — git commit -F - <<'EOF' and multi-line messages are no longer denied — while HEAD-moving commands stay denied (#504); routine commands (sed -i, grep, /tmp writes, cd into the worktree) no longer prompt (#505).
  • NPM_CONFIG_UPDATE_NOTIFIER=false by default in runner and gate environments, so npm notices no longer bury the test summary (#511).
  • The claude bridge forwards TERM/INT from before it spawns the CLI, so an early docker stop reaches claude (#506).
  • opencode exits 0 after a stop, like claude: the entrypoint maps opencode's "Aborted" exit to 0 only when the stop and its successful abort are both recorded; any other failure keeps its code (#697).
  • The test:jobs executor harness passes again against today's board contract (#697). It stays out of CI: 9 of its checks need host networking that ubuntu runners' docker does not give.
  • Driver: names why a runner never started; deterministic startup-sync refusals and executor refusals are reported as configuration; refuses RUNNER_NETWORK=bridge/host when services are on (#665); reaps gate environments no live attempt holds (#667); stages around .factory/ without failing publish; serves failureCause beside failureKind.
  • Runner: a denied compound command is not a refusal of its parts; the bridge keeps stdin open for subagents; runner git can push with the claim's GITHUB_TOKEN.
  • Task detail: activity pulse and a Task statistics modal.

Upgrade notes

  • Migrations 058–068 run on boot (job idempotency columns, the batch tables, failure_cause gains environment). All are additive; verified against a v0.0.6 database with data.
  • Chart: new runner.gateResources values. Gate pods now request 2 CPU / 2Gi by default — size the node pool, or set it back to the runner's values.
  • The UI gate's runner image is no longer built or referenced.
  • API clients that queued a follow-up onto a busy thread now get 409 NOT_FINISHED; wait for the thread to settle (waitFor=terminal) and follow up then.

Validation

Batch execution was exercised live on kind and the docker executor: human gate, automerge, stage verification hold/advance, bounded repair, budget exhaustion, infra retry, board force-kill mid-run, concurrency cap, idempotent accept/cancel, cancel, time-limit expiry, unanswered question on Kubernetes, org executor scope, and conflict repair (a second PR conflicting after the first merged, repaired by the agent and automerged).

On the release commit: typecheck, lint, npm test (5857), test:db (814), test:k8s, test:jobs (136/136) and verify:ui (98) pass; the migration upgrade from a seeded v0.0.6 database applies cleanly; a smoke batch ran on kind built from the release commit.

Known limits

  • A batch runs one target on its default branch; multi-repository (#657) and non-default-branch (#702) plans are refused at start (BATCH_UNSUPPORTED) — next release.
  • Not yet exercised live: two board replicas, duplicate webhook deliveries, real branch protection.
  • The runner refuses read-only pipelines that include tools outside its allowlist (e.g. awk), which can push an agent to work around them by hand (#703).