Changelog

v0.9.7

The job images publish again after 0.9.6's failed their security scan, so the cheaper review stage of 0.9.6 reaches cloud runs once a repository refreshes its images.

Highlights
  • The job images publish again: the 0.9.6 release could not publish them, because their vulnerability scan stopped on a Python library bundled inside two copies of pip that nothing in the image uses, and 0.9.7 removes those copies, so the scan passes and the cheaper review stage of 0.9.6 reaches cloud runs once a repository refreshes its images.

For operators

1. Per machine — upgrade the CLI on every machine that runs Fugaro: brew upgrade dimipaun/tap/fugaro.

2. Per repo checkout — run fugaro upgrade: it pins the plugin to this release, updates it in Claude Code and refreshes the repository's job images from this release's base image, which is what carries the cheaper review stage to your runs; it builds images, which costs money (--yes confirms each build). Do not run it against 0.9.6: that release published no job images.

3. Everything else in 0.9.6's notes (fugaro setup and fugaro update replacing fugaro init) applies unchanged; this release changes no cloud resource by itself.

Known limits

  • The 0.9.6 job images do not exist. Its tag stays as the CLI-only release it is; its base image was never published, so a repository cannot refresh its images from 0.9.6.
  • The scan still lists HIGH findings that do not stop a release: the Go standard library inside the bundled gcloud helper, crush, docker, gh and yq, and one in fugaro itself; they gate nothing today and are tracked for the next release.

v0.9.6

The first-run and upgrade commands are now fugaro setup and fugaro update, and cloud runs on long-lived branches review only their own change, so they cost far less.

Highlights
  • fugaro setup and fugaro update replace fugaro init: setup is the first run, which creates the installation or adopts one that already exists, and update re-converges an installation after a CLI upgrade; fugaro init now only says which of the two to run.
  • fugaro setup asks its questions once, in a fixed order, each with a suggestion that Enter takes and a flag that answers it (the GCP project's ID, the region, whether to create the project or adopt an existing one, and the project's name), and a run nobody can ask names every missing flag in one error instead of one at a time.
  • A cloud run's review stage now compares the branch with the base the run actually works on instead of with main, so a run on a long-lived branch no longer reads the whole branch against main, a follow-up uses the pull request's current base, binary files reach the reviewer as one line each, and a very large diff is flagged to the reviewer up front; runs on such branches cost a fraction of what they did.
  • fugaro --version and fugaro -v print the version, like fugaro version.
  • The working and parallelism skills warn about the review cost of runs on long-lived branches and teach a daily look at the most expensive runs.

For operators

1. Per machine — upgrade the CLI on every machine that runs Fugaro: brew upgrade dimipaun/tap/fugaro.

2. Per repo checkout — run fugaro upgrade: it pins the plugin to this release, updates it in Claude Code and refreshes the repository's job images. The cheaper review stage reaches your runs only after this refresh, because the runner that does the review is the one in the job image; it builds images, which costs money (--yes confirms each build).

3. Per installation — wherever a document or a script of yours says fugaro init, say fugaro setup for a first run and fugaro update after an upgrade; fugaro update --help lists the flags, and it refuses by name the flags that only setup takes. This release changes no cloud resource by itself.

Known limits

  • The setup wizard is not finished. fugaro setup and fugaro update work as described, but the questions that suggest a project ID, pick a region apart from the Firestore location, find your billing accounts and ask for a first repository belong to the next release, so for now the flags the old command took keep their names and still have to be typed.
  • fugaro init no longer does anything. It refuses and names the two commands that replaced it, so a script or a CI job that calls it fails until it is changed.
  • The credentials service, the managed deployer and the Batch backend are not in this release. They are built on their own branch and ship with the next one.
  • Cloud runs on branches other than main keep their old review cost until step 2 above has run for the repository, because the images still carry the previous runner.

v0.9.5

The desktop console has a new dark design with a rail for your installations and full keyboard navigation, and a repository's spending caps and kill switch can be set from inside its checkout without typing its name. A project can now list which repositories are enrolled against those that have actually run, and enroll the repositories its GitHub App is installed on in one step.

Highlights
  • The desktop console has its new look: one dark stylesheet bundled into the app, a two-column layout with an installation rail beside the screens, styled Live, Spend, Projects and Run screens, keyboard navigation (the arrow keys through the navigation and the rail, Command+digit on macOS or Control+digit elsewhere for a rail row) and the website's mark as its icon; it stays read only and is published for this release as its own download, console-v0.9.5.
  • fugaro budget set, fugaro budget kill and fugaro budget resume take --repo with no value to mean the repository this checkout belongs to, so fugaro budget set --repo --daily 150 sets the caps of the repository you are standing in, and the bare form is refused outside a checkout whose origin names a repository rather than guessed.
  • fugaro project repos ls lists the project's allow-list against the repositories that have actually run there (read from the runs bucket and the daily spend records rather than from the runs in flight), marks each one admitted or not, and says so plainly when a repository's name cannot be recovered.
  • fugaro project sync is new: it asks GitHub which repositories the project's GitHub App is installed on and offers the ones the allow-list does not admit yet as one list confirmed once, only ever adding names (reading the App's private key needs --github-app-key, which is refused in a coding agent's session).
  • fugaro init --provision-batch is turned back on, with the reaper's role reduced to the five permissions Google defines (batch.jobs.cancel, which is not a permission Google has, is gone, so the role can be created), and fugaro backends ls lists the compute backends a project config may name and which one it uses; both are groundwork, since no run can use the Batch backend yet.
  • The provider price catalog was checked on 2026-10-20 and carries 5 models over 95 endpoints (75 ZDR-eligible).

For operators

Only the database rules move in this release, so the order is short: upgrade the CLIs first, because a 0.9.0 fugaro doctor reads this release's rules as a skew and prescribes the redeploy that would undo them.

1. Per machine — upgrade the CLI on every machine that runs fugaro doctor or fugaro init against the installation: brew upgrade dimipaun/tap/fugaro.

2. Per repo checkout — run fugaro upgrade --local, so the plugin pin follows the CLI (the setup and working skills changed) without a cloud call.

3. Per installation, in your own terminal, outside any repository checkout — run fugaro init --firebase <gcp-project>. It deploys this release's database rules (the credentials service's groundwork: the creds/ node denied to every run token, and new optional children only a credentials-cascade run writes) and records their generation at config/limits/credsRules; it prints what it changes before it applies anything. Until it runs, this release's fugaro doctor reports policy-rules-skew as a warning, and nothing else behaves differently. In a checkout it would also pin this release's base image and move the shared jobs onto it, which this release does not need.

Nothing else is required: no policy republish, no fugaro init --repo and no image rebuild. The runner's changes are the wording of one error and fields only a credentials-cascade run writes, and no launch check asks for a 0.9.5 base image. When you do move a repository's images with fugaro upgrade (its cloud step) or fugaro image refresh, the shared jobs follow only on a later fugaro init --firebase <gcp-project>, per installation, run on that machine, as in 0.9.0.

Known limits

  • The Batch backend is still disabled. backend: cloud-batch is refused by this build, so no run can use it, and once it is enabled a run with a sealed budget token will be refused on it by design (the seal's key cannot be kept out of a Batch job's definition). fugaro init --provision-batch creates billable, lasting resources (a Cloud NAT and router, a boot image rebuilt on a schedule) that nothing uses yet, and its apply has not been run against a live project: the owner-run Check 57b of docs/gcp-live-checklist.md is NOT RUN. Run it only to try the groundwork.
  • The credentials service is not usable. This release carries only its groundwork (the database rules above, the policy's credentials block and per-repository admissions); there is no service to deploy. routing.claude.via: cascade is accepted in a routing block but changes nothing yet (a Claude call still goes direct), and publishing it with fugaro project policy publish makes every older CLI refuse each launch and publish of the project and every older runner halt its runs, which the publish's own diff warns about, so do not publish it.
  • An older CLI can undo this release's rules. Once step 3 has run, a 0.9.0 fugaro doctor reports policy-rules-skew and prescribes fugaro init --firebase; run with that older CLI, it redeploys the older document and takes the credentials children away. Upgrade that machine's CLI instead: this release's fugaro doctor then reports the older document as policy-rules-skew in its turn, and its fugaro init --firebase puts this release's rules back.
  • The price catalog has no Claude rows. A Claude model named in a route (the anthropic/<id> a provider route claims) must be priced under model_prices in the project config; the catalog prices none of them.
  • Managed projects stay disabled, exactly as in 0.9.0. Every command that would create a managed project or act as its deployer still refuses before any cloud call, and the deployer's redesign is not in this release.
  • The console still asks for each installation by hand. Its installations window takes a project name you type and a fugaro binary, a config file and a checkout you choose; finding them by itself is planned for 0.9.6.
  • The live checks have not been run, and only you can run them. Every owner-run, sandbox-only check in docs/gcp-live-checklist.md that this release's changes touch is still NOT RUN, Check 57b among them, and fugaro project sync has not been run against a live GitHub App.

v0.9.0

A project's repositories can now share Cloud Run jobs, and a project can name several Claude accounts so a person's own subscription serves the project's runs from its own job. A run's budget token is sealed so no other run can use it, spend can be reported per person, and the watch screen names who launched each run.

Highlights
  • A run's budget token is sealed: the launcher writes it at tokens/<slug>/<run>/budget-token encrypted under a key generated for that one launch, which travels in the run's own environment and nowhere else, so a peer run of a shared job that reads the object gets ciphertext it cannot use.
  • A project runs its repositories on shared Cloud Run jobs: a profile names one provider and the Claude accounts it serves, fugaro init deploys one job and one job service account per (profile, provider, account), and a checkout: clone workflow launches onto them instead of onto a job of its own.
  • A project can name several Claude accounts (claude_accounts), so a person's own subscription serves the project's runs from its own job: each account's token is mounted only on that account's job, and a launch is admitted only for the repositories the account's published list covers.
  • fugaro runs stats --by user reports spend and jobs per person over a window, and fugaro runs ls and fugaro watch name who launched each run in a BY column.
  • The desktop console accepts this release's documents: job, profile, provider, account and launched_by no longer make it refuse a watch --json or runs ls --json document whole, so the Live screen draws the run rather than a skew screen — in a console built from this release, which is a separate download (an older console still shows the skew screen).
  • The desktop console is published for this release as its own download, console-v0.9.0: a signed, notarised and stapled macOS app, installed separately from the command line.

For operators

The order matters, steps 2 and 6 are one command split in two, and step 7 is step 3 again: an image built from this release refuses a policy node published by an older one, and an older image refuses this release's node, so the policy is republished before any image moves, and the shared jobs move last.

1. Per machine — upgrade the CLI: brew upgrade dimipaun/tap/fugaro.

2. Per repo checkout — run fugaro upgrade --local, so the plugin pin follows the CLI without a cloud call. Its cloud step (the base image's pin, the daily check job and each workflow's build) is step 6, and it is deliberately not run here.

3. Per installation, in your own terminal, outside any repository checkout — run fugaro init --firebase <gcp-project> (in a checkout it would also pin this release's base image and move the shared jobs onto it before step 5). It deploys one job and one job service account per (profile, provider, account), replaces the launchers' bucket binding with the one that reaches tokens/ as well as runs/ (the old binding is replaced, never deleted) and writes each shared job account's read-only grant on tokens/. fugaro doctor names a launcher still holding the pre-0.9.0 runs/-only condition and says this init is the fix.

4. Per repo checkout, in your own terminal — run fugaro init --repo .. It writes each job account's read-only grant on tokens/<slug>/, beside the repository's own job. fugaro doctor names a repository that has not been re-applied; until it is, that repository's runs with a budget token stop at bootstrap, reading the token as a missing object.

5. Per installation — republish the policy, and read the list out first, because the publish that follows rebuilds it: run fugaro project policy show and keep what it prints, then fugaro project policy publish --delete, then fugaro project policy publish, then read the diff the publish prints and repair the list with fugaro project repos add <owner/name> and fugaro project repos rm <owner/name>. A repository you had added by hand is dropped, and one you had removed comes back while the config still names it. Runs on a shared job halt in between, which is the fail-safe direction.

6. Per repo checkout — rebuild the images with fugaro upgrade (its cloud step) or fugaro image refresh. This release's base image is resolved at its -pinned tag, its cosign signature is verified, and the digest is read back through the installation's own remote repository and pinned in base_images — nothing is copied and nothing is pushed — then the daily image check job is pointed at it and each workflow's image is rebuilt on it. A 0.9.0 policy read by an older runner image is refused, and the fix is the image, not the policy.

7. Per installation, in your own terminal, on a machine where step 6 ran — run fugaro init --firebase <gcp-project> again. The shared jobs run the installation's base image, and neither fugaro upgrade nor fugaro image refresh touches them: only this re-apply of the installation moves them onto the base image step 6 pinned (this machine's base_images). Until it runs, a shared job's runs stop at bootstrap, because the older runner refuses the policy step 5 published, and neither the launch check nor fugaro doctor says so, since both read the pin rather than the deployed job.

Then run fugaro project policy show again, per installation, and read it back: it prints the profiles and, for each account a profile names, the repositories its token may be used by.

Known limits

  • Managed projects stay disabled, exactly as in 0.8.6 and 0.8.7. The groundwork is in this release and out of reach: the second mark the managed read now requires beside the creation marker (the fugaro_managed=true label on the project itself), fugaro managed flag, the inventory-aware fugaro managed apply, and the bounded form every value it quotes from elsewhere is printed in. Every command that would create a managed project or act as its deployer refuses with a plain message before any cloud call — fugaro init --create-project --managed, fugaro init --managed, fugaro managed flag, fugaro managed apply and fugaro restore — so no managed apply runs at all, nothing writes the project label or the marker, and nothing skips the typed confirmation it would have skipped. What holds all of it off is the deployer's scoped role: the installation module's managed_deployer_scoped defaults to false and fugaro init never sets it, because the fmgd- fence its one conditioned binding carries is an IAM condition GCP does not accept as written (9 clauses joined by 17 logical operators where 12 are allowed; resource.name.contains(), which it does not support; and resource.name on Artifact Registry repositories, Cloud Run jobs, Cloud Scheduler jobs, IAM service accounts and API keys, where it is not supported). The deployer account, its create-and-read role and your impersonation grant are created as before, and the fence is still to be redesigned and measured against a live project. fugaro doctor reports the scoped role's absence as an info line, not a warning, and --strict does not fail on it.
  • The Batch backend is still disabled. fugaro init --provision-batch refuses with a plain message before any cloud call, as it has since 0.8.6: its reaper role names batch.jobs.cancel, which is not a permission Google has, and the reaper is still to be redesigned. This and managed projects are the two things 0.8.6's notes said would return in 0.9.0, and neither returns here.
  • Only checkout: clone workflows reach a shared job, and only on GitHub. A baked workflow keeps its per-repository job, and no shared job serves Bitbucket: fugaro init deploys none for a Bitbucket profile, so such a repository keeps its per-repository job. A shared job also carries less than a per-repository one — no FUGARO_CHECKOUT (a full clone rather than a blobless one, and no mise install), no owner-configured model provider, no local compute-price override, the default budget grace, and no repository-level workflows.<n>.secrets — all of which are listed under "Which runs work on a shared job today" in docs/project-layer.md.
  • A shared job's service account can read every repository's tokens/ objects. That reach is what the seal is for: what it reads there is ciphertext whose key it does not hold. The installation-wide reach and the reasoning are stated in full in SECURITY.md.
  • No per-account budget dimension. The budget is keyed by model, repository, project and person, never by credential: an OAuth run adds to the notional (subscription, unbilled) counters, and a named account's own limits are Anthropic's, not Fugaro's.
  • fugaro project export is not in this release. The project-centric plan's phase 6 (a project's record written into a repository or a folder of one) is still to come, as are the project-level phases that would let a project be created and managed from a repository rather than from an installation. The credentials service, and the mobile and voice work, are not part of this release either.
  • The live checks have not been run, and only you can run them. These are owner-run, sandbox-only, and every one of them is still NOT RUN: Check 60 of docs/gcp-live-checklist.md, the sealed budget token end to end (a sealed run works, the tokens/ object is ciphertext, and the widened launcher condition and the job's read-only grant are read off the live policy); Check 53, a shared project job end to end (reserved by the project-centric plan's Task 3.7, its section not yet written into the checklist); the named-account path, for which no live check has yet proved an account-gated shared-job run end to end; and the managed checks (67a, 67b and 68, reserved in the managed-projects plan) — which cannot run in this release at all, since every managed command refuses, so the project label, the inventory-aware apply and the applying identity remain unmeasured. Check 38, the clone workflows, passed on 2026-10-17 and its record is still to be written up, which is why checkout: clone ships here rather than earlier.

v0.8.7

The desktop console is published reliably again, because Apple's notarisation and stapling now retry the part that fails when Apple's service drops the connection. The command line itself is unchanged from the previous release.

Highlights
  • The desktop console's release now builds on a fixed pipeline. Apple's notarisation and its ticket stapling run in a script that retries only the stapling when Apple's service drops the connection, which is what stopped the console from being published with 0.8.5. The console, a macOS app, is published as its own release, console-v0.8.7, as a draft that you publish after checking it.
  • The CLI is otherwise identical to 0.8.6.

For operators

1. Upgrade the CLI (brew upgrade dimipaun/tap/fugaro) and, in each repository checkout, run fugaro upgrade so the plugin pin follows it. Nothing needs to be re-run per installation: there is no change to an installation's cloud resources.

2. The console is a separate download from the console-v0.8.7 release; it does not update itself. The site's console download buttons stay off until that release is published and the site's console flag is switched on.

v0.8.6

Setup completes again on every installation, after the previous release stopped while creating an account's role. Managed projects and the batch provisioning step are switched off in this release, and both are planned to return after a redesign.

Highlights
  • fugaro init completes again on every installation. 0.8.5's could not: it stopped while creating the managed deployer's custom role, which named a permission Google does not allow (cloudscheduler.jobs.resume; Google's name for it is cloudscheduler.jobs.enable).
  • Managed projects and fugaro init --provision-batch are turned off in this release. Neither could complete in 0.8.5, and both are planned to return in 0.9.0 after a redesign. fugaro init --create-project --managed, fugaro managed apply, fugaro restore and fugaro init --provision-batch now stop with a plain message before any cloud call, instead of failing partway through an apply.

For operators

1. Upgrade the CLI (brew upgrade dimipaun/tap/fugaro).

2. Once per installation, re-run fugaro init --firebase <gcp-project> in your own terminal. 0.8.5's fugaro init could not complete on any installation, so this is the run that finishes what 0.8.5 started. Its plan creates the fugaro-deployer account and its create-and-read role (unless 0.8.5's partial apply already created them), but not the deployer's scoped role or that role's binding (see Known limits).

3. In each repository checkout, run fugaro upgrade so the plugin pin follows the CLI.

fugaro doctor reports one info line, managed-deployer-disabled, saying that the scoped role is absent on purpose. It is not a warning, and --strict does not fail on it.

Known limits

  • Managed projects are disabled. fugaro init --create-project --managed, fugaro managed apply and fugaro restore refuse with "managed projects are disabled in this release". The deployer's scoped role is fenced by an IAM condition on the fmgd- name prefix, and GCP does not accept that condition as written: it has 17 logical operators, and GCP allows 12. It also uses resource.name.contains(), which GCP does not support, and it fences resource types for which GCP does not support resource.name. The role and its binding are therefore not created. The fence will be redesigned and tested against a live project for 0.9.0 ([design](../design/managed-projects.md)). A project created without --managed is unaffected.
  • fugaro init --provision-batch is disabled. The Batch backend's reaper role names batch.jobs.cancel, which is not a permission Google has, so its provisioning could not complete. The reaper will be redesigned for 0.9.0.

v0.8.5

Fugaro can now manage a whole project, where an agent's infrastructure changes are applied by a deterministic gate, a lost project can be rebuilt from zero, and the project's budget alerts are recorded where no run can change them. The desktop console ships as its own signed macOS download, and a run can be given a title that names it in the watch screen and on the pull request it opens.

Highlights
  • A project can now be managed: fugaro init --create-project --gcp-project ID --managed --budget N --budget-currency CCC --billing-account ID creates one in which a coding agent's Terraform is applied by fugaro managed apply with no prompt, because a deterministic gate over the saved plan (a resource-type allowlist, the plan's cost-bearing attributes, the exact delete list you write with fugaro allow-delete, and a recorded budget) decides what may change, and the apply runs as a dedicated deployer account, never as you.
  • fugaro restore NEW_PROJECT_ID ROOT rebuilds a lost managed project from zero into a fresh project ID through the same gate; it is owner-run, asks for the project name at a terminal, and refuses a coding agent's session.
  • A managed project's budget (1 to 10,000,000 whole units in a three-letter currency) is recorded in a marker that no credential a run or the deployer holds can write, and its billing-account budget sends 50%, 90% and 100% alerts that only notify.
  • A project's policy can define job profiles, fugaro init deploys one shared Cloud Run job per profile and provider (GitHub only: a Bitbucket profile gets no job), and a workflow selects one with job_profile: (fugaro validate checks the name's form), but checkout: clone stays refused, so no run completes on a shared job yet.
  • fugaro run --title TEXT names a run in fugaro watch, fugaro runs ls and the pull request it opens (unless the agent writes its own pr.md), where the first line of the task named it before.
  • The desktop console ships as its own signed, notarised macOS release, console-vX.Y.Z: it reads an installed fugaro, opens on the installation you pick, shows a reduced view with a banner when that fugaro is outside the 0.7 and 0.8 minors it was built for, refuses to draw its dashboard below 0.6.0, is read-only, and does not update itself; the CLI also gains fugaro config list, fugaro plan graph and a files-changed record per run in fugaro runs ls.

Terraform 1.9 or newer is now required, and fugaro init and fugaro managed apply check for it (see the first operator step).

For operators

1. Upgrade Terraform to 1.9 or newer wherever fugaro init runs (terraform version). The floor is new in this release: the project module this release adds validates one variable against another, which Terraform allows only from 1.9, while 0.8.0 and 0.8.1 declared >= 1.7.0 with no such validation, so an installation on 1.7 or 1.8 first meets the requirement here.

2. Upgrade the CLI everywhere (brew upgrade dimipaun/tap/fugaro, or this release's archive with its cosign check), including CI pins and teammates.

3. Run fugaro init once per installation, in your own terminal. Its plan adds the managed-project pieces to every installation, managed or not: the fugaro-deployer account with its two custom roles, a custom Cloud Asset reader role, and the Cloud Asset API. Read the plan and type the project name.

4. In each checkout run fugaro upgrade (its cloud step is fugaro image refresh) so each job runs a runner image built from 0.8.5; this also pins the plugin, which carries the updated setup skill.

5. Run fugaro doctor. An installation that published a project policy under 0.8.0 or 0.8.1 gets policy-older-than-binary, and until it is fixed fugaro run refuses that project's launches: run fugaro project policy publish --delete, then fugaro project policy publish, after step 4 (an older runner refuses a version-2 policy).

6. Run the owner-run live checks for managed projects (67 and 68) against a real sandbox project, after the release: neither has been run before the tag, and neither has its steps in docs/gcp-live-checklist.md yet (what each must assert is in docs/plans/2026-10-16-managed-projects.md), so writing them comes first.

fugaro init --firebase is not needed for 0.8.5: no managed or project-policy change touches the database rules. The rules on main also carry a mailbox for the mobile app, as generation 2, still undeployed. Installations whose marker is generation 1 get a policy-rules-skew warning from fugaro doctor in the meantime; it is not a failure: a generation-1 installation still reads and enforces its published policy and can still publish another. Run fugaro init --firebase only when you want that warning to go away, or the mailbox, with a 0.8.5 binary and in your own terminal.

The console's own owner steps — the first signed build and its Gatekeeper verification (Check 59a) — belong to the console's release, console-vX.Y.Z, and not to this one. The console has no auto-update, and the round trip that would check one (Check 59b) needs a published update feed, so it is a later task and not a step of this release.

Known limits

  • An existing project cannot be made managed. There is no command for it in this release (planned for 0.9.0): a managed project is created fresh with --create-project --gcp-project ID --managed --budget N --budget-currency CCC --billing-account ID, and init on a project that already exists warns that the flag was not applied.
  • The project inventory and the created-resources fallback are not wired into managed apply. The inventory is built and tested, but the apply takes no inventory, so a resource created outside Fugaro in a managed project is not detected, and in a coding agent's session a plan the allowlist admits applies without a confirmation. The plan gate, the exact delete list, the budget gate and the plan-hash re-check still apply; wiring the inventory is unscheduled, with flagging existing projects in 0.9.0.
  • The marker and the delete list stop the accident, not an agent that goes around the program. A coding agent shares the session's user and its credentials, so it can write fugaro/managed.json or the delete list itself, or apply directly; what the marker, the delete list and the gate stop is a session that goes through fugaro managed apply.
  • There is no billing hard stop. The budget alerts only notify, the marker's amount is the alert amount, and nothing stops spending.
  • The delete list holds resource addresses, not identities. An address stays approved after the delete it approved has been applied: nothing prunes the list (unscheduled).
  • A shared project job cannot run a clone checkout. checkout: clone is refused for it in this release.
  • Compute-module IAM grants stay refused, and compute network types are excluded from managed plans.
  • The Cloud Batch job-name check is a latent fail-safe in this release, since the Compute Engine/Cloud Batch backend is not shipped.

v0.8.1

A project's Claude model claims are judged the way a run judges them, so validation stops halting on configurations that would work. A missing price is reported once, against the model it belongs to, and the watch screen hides pull requests that are already merged or closed when the GitHub CLI is installed.

Highlights
  • fugaro validate no longer halts on a layer's Claude claim the run would accept: a project layer that claims a model for a route the repository does not have, or overlaps one it has, is now judged exactly as the run judges it, so the false halts the 0.8.0 routing check could print are gone and the message names the real fix.
  • A routed Claude pin's price problem is now reported once and against the right pin: when only agent.model is set, validate no longer prints the reviewer's missing price twice or words it for the wrong run.
  • When validate cannot read your project config, the loader's own reason (and the fix it names) is printed first and the file path last, so a long path can no longer cut the instruction off.
  • fugaro watch hides merged and closed pull requests from "Ready for your review" when gh is installed (--no-pr-state turns it off); without gh, or when it cannot tell, the list renders as before and says so.

For operators

Upgrade the CLI (brew upgrade dimipaun/tap/fugaro, or this release's archive with its cosign check). There is no rollout step: no image rebuild, no rules deploy and no init is needed for this patch.

v0.8.0

A project's spending limits and the repositories allowed to run are now the project's own published data, and every run reads them before it starts. An operator can record which Claude models go direct and which go through a provider route, a secret can be stored once for the whole project, and a recipe can deliver a file instead of a pull request.

Highlights
  • A project's limits and its allow-list are now the project's own data instead of each job's environment: fugaro project policy publish writes the ceiling, the prices and the repositories that may run (allow_repos), fugaro project show and fugaro project repos ls|add|rm read and edit them, and a run reads the policy before it starts — one it cannot read halts the run rather than falling back to the job's own ceiling, for an installation whose rules and marker are in place (see the operator steps below). The policy's providers block is carried but not applied yet, and an installation that publishes nothing behaves exactly as it does today.
  • The operator can now write down where Claude goes: routing.claude in your own config takes an explicit list of Claude model IDs and via: direct | openrouter | auto (an absent switch means direct), and fugaro project policy publish copies it into the published policy, which is how a run receives it. Your config is its only definition site — a repository's fugaro.yaml has no routing key, and a project layer that writes one fails to load rather than half-applying it. A run resolves each named ID to the route's own ID (read by the pins, the price lookup and the recorded serving_model/priced_as), fugaro validate prints that resolution or says it is unresolved offline (and checks a routed pin with the same price, variant and alias rules the run applies), and fugaro doctor reports the same resolution; a Claude pin with no Anthropic key and no switch is a note from validate, never a refusal, since only the cloud knows which keys exist.
  • fugaro secrets set NAME now stores the project's copy by default for a name that has a project-wide meaning (a reserved secret such as anthropic-api-key, a provider key, a named Claude account), with --repo for a repository's own copy and --project-wide still accepted as a synonym; a repository copy still wins over the project's, and secrets ls follows the same default. A project-scope key mounts on the repository's job without an extra opt-in, and fugaro doctor gains a secret-scope check that lists which names are project-scope and warns when a repository copy shadows one.
  • A route may claim one exact anthropic/<id>, which is the operator's opt-in to have that one Claude model served through the provider route — from your project config or from the project layer's claude_models, never from a repository's fugaro.yaml. That stage needs agent.auth: api-key, budget.mode: enforce, the repository inside the route's allow_data_to and a price for the claimed ID (your own model_prices when the claim comes from the layer), and a routed Claude that reports no cache reads is charged at the full input rate.
  • A recipe can be mode: artifact — a run whose deliverable is a file rather than a pull request: it declares its outputs, runs the optional plan stage and then the repository's own commands.render command, ends with outcome: artifacts and neither pushes a branch nor opens a pull request. fugaro budget preview prints that run's compute ceiling beside the model ceiling and says it is not capped — the compute is bounded by timeouts.total, not by budget.per_run_usd. The outputs are selected, capped, hashed and uploaded to runs/<slug>/<run>/artifacts/ and recorded in result.json; the optional commands.probe runs over the staged set before the upload and fails closed; fugaro run --attach uploads files in with the launch; fugaro artifacts ls|get takes the files back, with runs ls and diagnose showing them; the user guide is docs/artifact-runs.md.

For operators

1. Upgrade the CLI (brew upgrade dimipaun/tap/fugaro, or this release's archive with its cosign check).

2. If your installation has a database, deploy the rules first: fugaro init --firebase <firebase-project-id> in the installation root writes the rules a policy is read under and the marker that records them. fugaro project policy publish refuses to publish without that marker, and an older CLI's init --firebase is exactly what redeploys the older rules under an unchanged marker — so use a 0.8.0 binary for this step. fugaro doctor reports the two skews separately, and only one of them halts: a marker whose generation is below this Fugaro's leaves no run reading a published policy — every run keeps the ceiling from its own job's environment, silently, and publish refuses until the marker is brought up — while deployed rules older than the marker make the policy node unreadable, so every budget-on run halts.

3. Roll the runner out in this order, because neither fugaro upgrade nor fugaro image refresh writes a job's environment and only fugaro init --repo does: (a) build and publish each workflow's image from this release, (b) fugaro init --repo in each checkout to write FUGARO_RTDB_URL and FUGARO_FIREBASE_API_KEY into each job, (c) fugaro upgrade (its cloud step is fugaro image refresh) so the jobs point at the new image. A rollout that skips (b) leaves every run reading its ceiling from the job's environment and never seeing the policy, and no check reports that state — fugaro doctor's policy-vs-jobs reports the neighbouring one, jobs still on a runner image that predates this Fugaro, which is what skipping (c) leaves. Step 2 must come before (b): an init --repo planned from a local config with no budget.rtdb_url removes the backend from the jobs that have it, and the plan says so — pass the installation's --budget-mode (and --firebase) again if you use them.

4. Then adopt the model, once per installation: fugaro project policy publish writes the policy (the ceiling, the prices and allow_repos) and fugaro project repos add|rm edits the allow-list afterwards. The first publish seeds allow_repos from your config's repos: map and the shared config's repository list, and a later publish keeps the node's own list — so removing an entry is how you un-enroll a repository. A published policy with an empty allow_repos admits no repository at all, which is not the same as an installation that has published nothing: fugaro run makes the same check before it creates an execution.

Nothing changes until step 4: an installation with no published policy spends and is capped exactly as it does today. The one change that lands earlier is step 2's: an installation whose local config has budget.rtdb_url now depends on Firebase and Cloud Storage at launch and at run start — every job carries the backend's address and key whatever the mode, the launch reads the policy and leaves a token object in the runs bucket, and each run reads it before it starts — so either service being down fails closed (the launch is refused, or the run halts) instead of running on the environment ceiling. --budget-mode off still stops only the cap session, and an installation that never configured a database keeps today's behaviour exactly.

Not in this release: project-centric phases 3 to 6 (shared project jobs, enrollment as data, the OAuth pool and the backing Git repository, including the monorepo-folder export), the Compute Engine/Cloud Batch execution backend (the code is inert and backend: cloud-batch is refused by the config), the desktop console, and checkout: clone — still not usable, exactly as 0.7.5's notes said: fugaro init --repo and fugaro image refresh refuse to deploy or move a clone workflow's job, and the flip belongs to the shared project jobs this release does not ship. Checks 44 to 46 — the price catalog and the two senior-reviewer checks — had not been run when this release was cut: they are run against the sandbox installation after the tag, and anything they find becomes a 0.8.0.x patch release, never a moved tag. Policy as data, the routing switch, project scope by default and artifact runs have no live check written yet, so they are not covered by one before the tag.

v0.7.5

The base image is served from your registry rather than copied into it, and Fugaro checks a release's signature before it pins that image. A global per-run spending cap now bounds any repository without one of its own, a secret can be stored once for the whole project, and a configuration change that removes a workflow or an account can retire it without hand-run state surgery.

Highlights
  • The base image is no longer copied into your registry: it is served through an Artifact Registry remote repository over ghcr.io, and fugaro init --base, fugaro image refresh and fugaro upgrade pin only an image whose release signature they have verified with cosign, which must now be on your PATH.
  • fugaro init --repo --retire retires what a fugaro.yaml change removes (a workflow variant, a named Claude account) without a hand-run terraform state rm: it shows the plan, asks you to type the project's name twice, and never deletes a secret or a secret version.
  • One global per-run cap now bounds every repository that has none of its own (fugaro budget set --global --per-run P), and fugaro budget show says where each cap comes from; it needs the new rules, deployed with fugaro init --firebase (see the operator steps).
  • Provider-routed models are priced from a catalog built into the binary (5 models, 91 endpoints, checked 2026-10-14) at the ZDR tier's cheapest end, printed by fugaro budget prices and charged at the dearest end once the file is 30 days old, while your own model_prices still win; the require_zdr and price_policy switches are refused in this release and arrive in a later one.
  • A secret can be stored once for the whole project with fugaro secrets set NAME --project-wide, and anthropic-api-key is no longer needed when every model of an enforcing agent.auth: api-key repository is served by a provider allow-listed for it.
  • Smaller: result.json records each stage's model cost, fugaro budget preview gives a worst-case ceiling for a batch (and a typical: line from your own runs), derived image builds no longer print mise's cache Permission denied warning, and the hidden fugaro ls pointer is gone (fugaro runs ls is unchanged).

For operators

1. Upgrade the CLI (brew upgrade dimipaun/tap/fugaro, or this release's archive with its cosign check), and install cosign on your PATH (brew install cosign, or your distribution's package): fugaro init --base and fugaro image refresh refuse without it, and fugaro doctor names it on every run.

2. If you use the budget backend, deploy its rules before any runner image moves: with the new CLI, fugaro init --firebase <firebase-project-id> (add --print-vars first to see what it writes; docs/gcp-setup.md's "Turning the shared budget on" section has the read-only check of what landed) writes the new per-run-cap rules as the last write of its database step. Also point Application Default Credentials at your installation's project — gcloud auth application-default set-quota-project <gcp-project>, with the Cloud Billing API enabled there — because init and doctor read billing through that quota project and name it when the API is off.

3. Then fugaro upgrade in each checkout (its cloud step is fugaro image refresh): it now pins the release's base image through the installation's registry remote repository at the digest whose signature it verified, and rebuilds each workflow that is not yet on it. It asks before every billable build, and the base image must move before any repository's image does, which that command keeps in order.

4. For project-wide secrets, in this order: (a) in the installation root, fugaro init with no --repo plans the project's secret containers as creates only — one per eligible name, no destroys, no replacements and no update to an unrelated resource — and applying it is your step, not something a repository's job can do; (b) then fugaro secrets set NAME --project-wide for each name stored once for the project (the value is read from stdin, and it takes no --repo; a value stored before (a) is adopted by that apply rather than conflicting); (c) for a repository that adopts a project-wide name while its own copy still has a version, that copy must be disabled before the repository uses the project's — gcloud secrets versions disable needs secretmanager.versions.disable, which is project-owner-only; (d) then fugaro init --repo in its checkout plans replacements of the per-repository IAM addresses, which Terraform's guard refuses until each removal is named with --allow-delete.

5. If a plan ever tells you it would retire resources, use a binary built from this release: fugaro init --repo --plan-only first (it prints the predicted retirements and, when Terraform's prevent_destroy blocks the plan, refuses with "Rerun with --retire"), then fugaro init --repo --retire, as docs/gcp-setup.md's "Changing auth, or removing a workflow, secret or provider" section describes. An older binary keeps failing on prevent_destroy the way it does today and needs that page's manual terraform state rm route.

Two notes. checkout: clone is not usable in this release: the runner side is in the tree (the blobless --filter=blob:none clone, the mise install provision stage, the prefetch), but the switch that turns it on has not been flipped, so its job cannot exist at all — fugaro init --repo and fugaro image refresh refuse to deploy or move a clone workflow's job, and fugaro run refuses a clone launch on a job image with no release tag or one older than this release, telling you to use checkout: baked. checkout: baked workflows are unchanged, and the flip lands after its own live check. And the two items 0.7.0's notes left unverified have since been checked live on the sandbox installation: the runs bucket's launcher grants (Check 32) and the one base image's cold start (Check 41, ~10.5 s median against the old image's ~11.7 s).

v0.7.0

Every project now runs on one base image, so a repository declares the runtimes it needs instead of choosing a language image, and moving to it is a migration with its own steps. Recipes gained a catalog, a review mode for existing pull requests and check steps that gate a run, and the cloud storage that holds run data is hardened so a launcher reads it and writes only its own runs.

Highlights
  • One base image for every project: the go, web-node and java-services images are gone, replaced by a single language-agnostic fugaro-base (Debian, mise, Claude Code and the other agent harnesses, a broad tooling kit, graphify, built for amd64 and arm64). A project declares its runtimes in a mise.toml (or image.tools in fugaro.yaml) instead of choosing a language image; base:, image.node and image.jdk are now refused with a message that points at docs/base-image-migration.md, and a 0.6 CLI refuses the new fugaro.yaml, so this is a breaking release.
  • Recipes grow up: a starter catalog with a "use when" line each (fugaro recipes ls --verbose), a coder that can be the reviewer, a review mode for existing pull requests (trusted authors only: review.allow_forks is removed), a bounce between coder and reviewer, check steps that gate a run, and commands.lint / commands.fix with autofix commits before the review. The routing skill picks a recipe from the catalog, and fugaro run --attempts N runs a best-of-N batch under one budget.
  • fugaro runs ls lists runs (the old fugaro ls prints a pointer to it), and the run registry now carries the last action and token count. A workflow can say how its repository is provisioned with workflows.<name>.checkout: baked | clone; clone is accepted by the schema but refused at launch until 0.7.1, because the runner side is not released yet, and fugaro init --repo and fugaro image refresh refuse to deploy a clone workflow.
  • The runs bucket is hardened: launchers read it and write only under runs/; operators keep full control. It takes effect only when an operator applies the Terraform (fugaro init), and fugaro doctor gains a runs-bucket-iam check that fails closed.
  • Provider models (a non-Anthropic model through OpenRouter) report real cost, so the budget caps count what a run actually spent (0.6.7), and a DeepSeek coder and reviewer has run end to end. The README's "Why Fugaro" is rewritten around four reasons.

For operators

1. Upgrade the CLI: brew upgrade dimipaun/tap/fugaro.

2. Migrate each repository before merging anything else: remove base:, image.node and image.jdk from fugaro.yaml, declare the runtimes in mise.toml (the /fugaro:setup skill does it), merge, then run fugaro image refresh in the checkout. It copies fugaro-base (published by this release's tag) into your registry, moves the daily image check onto it and rebuilds each workflow's image; it asks before every billable build. Until the 0.7.0 tag has published fugaro-base, fugaro image refresh refuses with a clear error. A shared config published by an older Fugaro that still lists go, web-node or java-services is accepted with a warning and cleaned the next time someone publishes.

3. The old per-language images (fugaro-go, fugaro-web-node, fugaro-java-services) stay on ghcr.io forever as history, but no release after 0.6.x refreshes them.

4. To get the bucket hardening, run fugaro init for the installation in your own terminal and read its plan: it deletes only the launchers' old objectAdmin grants, behind its own banner and confirmation. Until then nothing changes, and a one-person installation plans no change at all.

5. Not yet verified live: the cold-start time of a run on the new base image (the 0.6 image measured about 12 seconds on the sandbox; the owner repeats it after migrating) and the bucket hardening's live Check 32 (run on the fugaro installation, never on belong).

v0.6.7

Runs on a provider model now report what they really cost, so a spending cap counts the actual amount instead of a worst-case estimate, and the first complete provider run works end to end. Fugaro also tells you when the cloud still carries an old copy of your project configuration.

Highlights
  • Provider-model runs (a non-Anthropic model through OpenRouter on agent.auth: api-key with the budget gateway on) now report what they really cost: the gateway used to settle every streamed OpenRouter call at its worst-case reservation, so a $0.01 DeepSeek run showed as $1.19 and a per-run cap would have tripped on estimates. It now reads the usage OpenRouter reports in the stream's final events; a call whose usage it cannot trust still settles at the reservation, never lower than before, and the run's log says why (usage_unparsed with a reason).
  • The first complete provider-model run works end to end: DeepSeek (deepseek/deepseek-v4.1-flash) coded and reviewed a change through OpenRouter on the sandbox. Two things to know before you try it: OpenRouter refuses a route whose providers may train on your prompts when your account setting forbids it (the error is "Paid model training violation"; allow only zero-data-retention providers in your OpenRouter provider settings), and a model that is not in model_prices is priced at the table maximum, so add an entry for the model you route (docs/multi-model.md §6).
  • fugaro upgrade now tells you when the cloud job still carries an old copy of your project config (providers, model prices, budget settings) and names the one command that fixes it, fugaro init --repo .; it never runs it, and --yes does not cover it.

For operators

1. Upgrade the CLI (brew upgrade dimipaun/tap/fugaro), then run fugaro upgrade in each checkout: the gateway ships inside the job image, so the usage fix only takes effect on an image built from 0.6.7, which fugaro upgrade rebuilds when it finds it stale.

2. If fugaro upgrade prints the stale-config note, run fugaro init --repo . in your own terminal and read its plan before confirming.

3. For provider models, add a model_prices entry for the exact model id you route and check your OpenRouter provider and data-policy settings (see docs/multi-model.md).

4. Repositories that use only Claude models see no change except one stricter rule: a negative usage count in a late or duplicate message_start, or a bad message_delta after message_stop, now settles the call at its reservation.

v0.6.6

Runs that drive a non-Anthropic model through OpenRouter now get past their first model call, so a coder and a reviewer on such a model can complete a task. Repositories that use only Claude models are unaffected.

Highlights
  • Runs that use a non-Anthropic model through OpenRouter (a provider model on agent.auth: api-key with the budget gateway on) now get past their first model call: Claude Code sent a context_management field and a first-request safeguards key that the gateway refuses on provider routes, so the runner now starts Claude Code with CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1 and MAX_THINKING_TOKENS=0 for such stages (and writes them into the managed settings, so a repository's own .claude/settings.json cannot undo them). Claude-model stages are unchanged, the gateway's refusals are unchanged, and no fugaro.yaml key was added.
  • This is the first fix from the first live provider-model run, so a provider-model run can still meet further, undiscovered incompatibilities between Claude Code and OpenRouter's endpoint (for example the system-role message Claude Code sends mid-conversation); docs/multi-model.md §7 and the live checklist's Check 25 now record what Claude Code was measured to send and what is still open.
  • In a run with a provider coder and a Claude reviewer whose background model is not pinned, the background model defaults to the provider's, so the Claude reviewer stage also runs without thinking; pin agent.models.background to a Claude model to keep it (documented in docs/multi-model.md).

For operators

1. Upgrade the CLI (brew upgrade dimipaun/tap/fugaro), then run fugaro upgrade (or fugaro image refresh) in each checkout that runs provider models: the runner ships inside the job image, so the fix only takes effect on an image built from 0.6.6.

2. Nothing changes for repositories that use only Claude models.

3. Not yet verified live: a complete provider-model run end to end (the sandbox's DeepSeek run is the next live check).

v0.6.5

A repository can use several named accounts instead of one, chosen per run or per repository, so a run that reaches an account's usage limit can be relaunched on another. Fugaro also tells you, at most once a day, when a newer release is available.

Highlights
  • A repository can now use several named Claude accounts instead of one: you define accounts in your own local config (claude_accounts, never published), store each token with fugaro secrets set claude-oauth-token-<name>, and pick one per run with fugaro run --token NAME or per repository with agent.oauth_token in fugaro.yaml; each account gets its own job, fugaro ls and diagnose show which account a run used, and fugaro doctor checks that every allowed account has a stored token and that a repository's chosen account is allowed.
  • When a run fails because its Claude account hit its usage limit, the plugin skills can now relaunch the work on another account the repository is allowed to use (fugaro accounts ls lists them; an explicit --token on a follow-up now switches the account, one account at most once per task, and the skill stops when all are tried). The exact usage-limit wording Claude Code prints has not been verified, so a missed match simply means no automatic switch.
  • fugaro now tells you when you are behind the latest release: at most once a day it checks the latest stable GitHub release (in the background, never slowing or failing a command) and prints one line on stderr with the brew upgrade command, only on a terminal and never in CI or the runner; fugaro doctor always reports it as a warning, and FUGARO_NO_UPDATE_CHECK=1 or update_check: false in your local config turns the nag off.
  • Named accounts are opt-in: a repository that configures none behaves exactly as before.

For operators

1. Upgrade every CLI and CI pin first (brew upgrade dimipaun/tap/fugaro), then run fugaro upgrade in each checkout to rebuild the job images: a named-account run is refused on a job image built before 0.6.5 (its runner would reject the new oauth_token field), and an older CLI refuses the new agent.oauth_token key in fugaro.yaml.

2. The secret-name prefix claude-oauth-token- is now reserved: a workflow secrets: entry in fugaro.yaml that uses it (other than through claude_accounts) is refused by this release (rename the secret and run fugaro secrets set again).

3. To adopt named accounts, add them to your local config's claude_accounts (with the repositories each may serve), store each token with fugaro secrets set claude-oauth-token-<name> in your own terminal, then run fugaro init per repository so each allowed account gets its own job; removing an account later plans deletes that need --allow-delete / --allow-job-delete.

4. Not yet verified against live infrastructure: minting a second account's token with claude setup-token and how Claude Code reports an expired or revoked token (docs/gcp-live-checklist.md, checks 33 and 35).

v0.6.0

One command now moves a checkout to the newest Fugaro, pinning the plugin, updating it and refreshing the job images, and it can report what is out of date without changing anything. A project can publish defaults and named profiles that its repositories inherit, so a repository's own configuration file can shrink to a few lines. Fugaro's trust model and known limits are documented in one place.

Highlights
  • fugaro image refresh --yes confirms every step unattended, including each billable build, so the cloud step can run from CI or a script instead of needing a real terminal.
  • fugaro upgrade [PATH...] and the /fugaro:upgrade skill move a checkout to this release in one command: it pins the plugin, updates it through the claude CLI, and refreshes the job images. --local runs only the parts a coding agent's own session can do, --check reports drift without changing anything, --yes confirms the cloud step unattended, and --allow-fork moves a hosted fork's marketplace pin instead of dimipaun/fugaro's.
  • A project can now publish a project layer (fugaro config show, layer, init and publish) holding project-wide defaults and named workflow profiles, so a repository's fugaro.yaml can shrink to a few lines naming the project and, when needed, a profile; fugaro ls, diagnose and doctor all show the layer a run used, and fugaro run only embeds a layer on a job image built from this release's base, refusing older ones by name. Until the 0.7.0 bucket IAM hardening ships, config publish's --executable-changes gate is a CLI check, not a permission: any launcher can still write the project layer object directly. The daily image check and the rebuilds it starts now resolve each repository's fugaro.yaml over the same layer, so a layered project's job image is built and checked against the layer it runs with.
  • A run whose container was killed or crashed no longer blocks fugaro run --pr N for the rest of its branch lock's lifetime: the CLI now clears a lock once the backend reports its execution over, instead of waiting out the full expiry, and fugaro cancel says when a lock is still held on an already-finished run.
  • fugaro watch shows finished runs with a cursor and a run detail view, filters them with a (toggle showing every finished run) and x (acknowledge) in the interactive screen or --all and --keep/--keep-count on the command line, and lists the ones ready for review separately; fugaro logs --url prints a run's Cloud console link instead of its logs.
  • SECURITY.md now states Fugaro's trust model and its known limits in one place, including that every launcher still holds broad write access to the runs bucket until the 0.7.0 IAM hardening ships.

For operators

1. Upgrade the CLI (brew upgrade dimipaun/tap/fugaro).

2. Run fugaro upgrade in each checkout (it pins the plugin and refreshes the job images). This is required before any run can carry a project layer: a layered fugaro run is refused on a job image older than 0.6.0.

3. For projects adopting layered configuration, run fugaro config publish from an operator's own terminal; add --executable-changes when a profile carries commands, image.apt, image.setup or image.skip_build_scripts.

4. Then merge each repository's minimal fugaro.yaml. Do this only after every CLI and CI pin that touches the repository is on 0.6.0 or later: the strict parser refuses a file with new keys like profile: on an older release.

v0.5.1

Work in progress is no longer lost when a run's container dies, because new commits are pushed to the run's own branch within about a minute and a draft pull request opens at the first push. One command moves a repository onto the release's base image, and runs can be watched as they queue, start and finish.

Highlights
  • fugaro image refresh moves a repository onto this release's base image in one command, run in the repository's checkout in your own terminal. It copies the base image, points the daily image check job at it, and rebuilds the workflow images that need it, with a confirmation for each cloud change (each build is billable and typed) and no Terraform. It stops with a one-line fix on a development or hand-pushed base image, and it ends by naming fugaro init --anchor when fugaro.yaml still lacks the gcp_project: line. The init --anchor and recipe image refusals now name it instead of a four-step sequence.
  • Work is no longer lost when a run's container dies. While a run works, new commits are pushed to its own branch within about a minute (fast-forward only, never forced), a draft pull request opens at the first push marked "work in progress, not verified", and every stage boundary pushes at once. Readiness is unchanged: a pull request is ready only with a passing test on the final commit and a ship review. Only commits are saved, not uncommitted changes.
  • Checkpoints go through the same guards as the final push: a commit touching .github/workflows and any commit containing a known secret value stop checkpoints for that run, and a failed checkpoint is a warning, never a run failure. Set git.pr.checkpoints: false to opt out; every push to a draft pull request runs your repository's CI (at most 60 pushes an hour).
  • The agent is now told to commit early and never to end its turn waiting for background work, because a headless run ends the moment the agent stops and loses whatever was still running.
  • fugaro watch shows runs that have launched but not started as queued rows with their age, and warns when one has not started after 10 minutes. It reads the runs bucket, so it needs a launcher or operator role: a plain Firebase Viewer sees a note that queued runs are unavailable.
  • A rare race that could put a budget lease on the wrong day around midnight is fixed: the database clock now follows the newest request, not the last response to arrive.

For operators

1. Upgrade the CLI (brew upgrade dimipaun/tap/fugaro), and every CI job and teammate's CLI that runs fugaro against a repository, before any repository sets git.pr.checkpoints: in fugaro.yaml: older versions refuse the key as unknown. Leaving it unset keeps checkpoints on and breaks nothing.

2. Run fugaro image refresh in each repository's checkout, in your own terminal. It is interactive by design: it needs a real terminal, has no --yes, --json or --plan-only, and cannot run in CI. Checkpoint pushes only apply to runs whose job image was built from a 0.5.1 base, so refresh each repository once to get them.

3. Not yet verified against the real Cloud Run API: the direct update of the check job (the raw job is read, only the image and the base_images in its spec are changed, and it is written back with its etag). Nothing has exercised it outside tests with a fake. Run the sandbox-only live check, Check 30 in docs/gcp-live-checklist.md, on a sandbox repository before relying on it. The command's own prompt says the same.

4. On hosts without draft pull requests, the pull request opened at the first push is an ordinary one and may request code-owner reviewers on unverified work: set git.pr.early_draft: false there (branch checkpoints still happen). If your CI is expensive, set git.pr.checkpoints: false.

v0.5.0

The task loop is now a selectable recipe. You can pick one of the three that ship with Fugaro or write your own and share it with a whole project. A recipe only arranges the loop, so the models, the spending caps and the rule that a pull request is ready only with a passing test and a ship verdict are unchanged.

Highlights
  • Recipes: the task loop is now a named, selectable recipe. Pick one with fugaro run --recipe NAME or agent.recipe: NAME in fugaro.yaml; three ship built in, default (exactly today's loop), cheap-loop-senior (a short first-line review, then one senior review) and claude-solo (one model implements and reviews). A repository with no recipe behaves exactly as before.
  • A project can define its own recipes once for all its repositories with fugaro recipes publish, and a repository can add its own under .fugaro/recipes/; the first match wins in the order repository, project, built-in catalog. fugaro recipes ls, show and validate inspect them, and fugaro validate checks a repository's recipes.
  • Recipes only arrange the loop. They never name a model (models still come from agent.models), never change budget or token caps, and a pull request is still ready only with a passing test on the final commit and a ship verdict from the review.
  • fugaro watch and fugaro ls show which recipe a run used, and a follow-up (fugaro run --pr) keeps the recipe of the run it continues, including one the runner chose from agent.recipe.
  • fugaro run prints the recipe it will use before launching and refuses to launch a recipe run on a job image built from a base older than 0.5.0, naming the steps that fix it.

For operators

1. Upgrade the CLI (brew upgrade dimipaun/tap/fugaro) and every CI job and teammate's CLI that runs fugaro against a repository before any repository merges agent.recipe: into fugaro.yaml: older versions refuse the key as unknown.

2. In your own terminal, run fugaro init --firebase <firebase-project-id> for each installation (the Firebase project it already uses). Its Firebase stage deploys the updated database rules, and asks its usual confirmations; until then runs work, but the dashboard shows no recipe name.

3. For each repository that will use a non-default recipe: fugaro init --base <kind> from outside the checkout (a custom base image in your local config is never replaced: remove its base_images.<kind> entry first), fugaro init --repo in the checkout, then fugaro image build --repo <owner/name> --workflow <name>. A recipe run is refused on an image built before this release, and the refusal lists these steps.

4. To share a recipe across a project, run fugaro recipes publish <file> in your own terminal (it refuses inside a coding-agent session). Project recipes need the default-named runs bucket, fugaro-runs-<gcp-project>.

A project recipe named default replaces the built-in default for every repository in the project, and then every launch carries it and needs 0.5.0 images. Recipes cannot yet run checks, branch or extend one another; see docs/recipes.md.

v0.4.1

A checkout can be pointed at its installation's cloud project on its own, so a repository no longer has to be brought fully up to date first. Editors and the schema check accept that setting, and the setup skill now asks which models you want for the coder and for the reviewer.

Highlights
  • New fugaro init --anchor writes the gcp_project: line into a checkout's fugaro.yaml without a full converge. It first checks that every workflow's image was built from a base image new enough to read the line, refuses with one ordered fix per workflow when one was not, and shows the diff before writing. The init summary now points at it for checkouts that lack the line.
  • fugaro.yaml's JSON schema now accepts gcp_project:, so editors and the schema check in CI no longer flag the line.
  • The setup skill asks which models to use for the coder and the reviewer as an open question, explains the three roles (coder, reviewer, and the background model Claude Code uses for its own housekeeping), and no longer guesses from the repository's domain.
  • The docs now say that the Claude Code plugin may need a manual install (/plugin marketplace add dimipaun/fugaro, then /plugin install fugaro@fugaro), and give the correct rollout order for the gcp_project: line: plain fugaro init or init --anchor writes it, init --repo does not.

For operators

For a repository whose fugaro.yaml still lacks gcp_project:, in its checkout and your own terminal: fugaro init --base <kind> from outside the checkout (a custom base image in your local config is never replaced: remove its base_images.<kind> entry first), fugaro init --repo in the checkout, fugaro image build --repo <owner/name> --workflow <name>, then fugaro init --anchor, and merge that change last. --anchor names whichever of these steps is still missing.

v0.4.0

A teammate can now launch runs in a repository they were merely given access to, because the installation's shared settings come from the cloud instead of from a setup step each person runs. Moving an existing Firebase project into Fugaro no longer needs hand-edited state, and a second project can sit beside the first.

Highlights
  • Teammates need no setup: fugaro init publishes an installation's non-secret settings to its runs bucket, and anyone allowed to launch runs can use fugaro run, ls, diagnose and the rest in a checkout whose fugaro.yaml names gcp_project:, without running fugaro init themselves.
  • fugaro init --firebase now adopts a Firebase project that already holds a previous installation's database, web API key, token signer and minter role, instead of failing with "already exists"; it refuses look-alikes and any foreign member who could mint run tokens, so moving an installation no longer needs hand-edited Terraform state.
  • fugaro init --name creates a second project when you already have one, init says so when an installation-level flag will also onboard the checkout's repository, and it warns when XDG_CONFIG_HOME points at a temporary directory.
  • The classifier that decides which Terraform plans init applies under one confirmation now accepts an import only when that stage's own discovery found it, for the installation, repository and Firebase stages alike.
  • The README is rewritten with safety first: the agent never runs on your machine, and it lists plainly what a run can and cannot reach (the network is unrestricted, and on GitHub a run holds the App's private key). From this release, GitHub releases carry these highlights above a changelog without commit hashes, and are normal releases, no longer marked pre-release.

For operators

1. Upgrade the CLI (brew upgrade dimipaun/tap/fugaro), and every teammate's CLI and every CI job that runs fugaro against a repository, before any repository merges gcp_project:: older versions refuse the key as unknown.

2. In your own terminal, run fugaro init --publish-config once per installation to publish its shared settings (it works for installations whose runs bucket has the default name fugaro-runs-<gcp-project>, and refuses inside a coding agent's session).

3. For each repository, in its checkout and in your own terminal: run plain fugaro init first, so the 0.4.0 base image for the repository's base kind is copied into the registry and recorded (a repository whose local config uses a development base image needs fugaro init --base <kind> first, for example --base go). Only then rebuild its job image (fugaro image build --repo <owner/name> --workflow <name>): a derived image takes its fugaro binary from the base image, so a build before the base image is copied bakes in the old binary and refuses gcp_project:.

4. Run fugaro init again in the checkout (not fugaro init --repo, which does not write the line): it asks Write this to <path>? [y/N], then adds gcp_project: to fugaro.yaml, and must print no "built before fugaro.yaml could carry gcp_project" warning. Merge that change last.

Adopting an existing Firebase root works while the installation's GCP project stays the same; moving to a different GCP project is still refused by the existing database check (see "Known limits" in docs/design/adopt-firebase-root.md).