How it works
You define the workflow. Goobers runs it.
You describe a workforce and its workflows in files you own. Goobers turns each workflow into a pipeline, runs it against your backlog, and calls you in at the decisions you reserved.
workflow = trigger + stages + gates
None of the three is fixed. Any of five trigger kinds. Any mix of scripted and agentic stages, in any order. Gates and telemetry pointed at what you already run, not a service you adopt.
You define the workforce
goobers init --guided proposes one neighboring Goobers Instance and keeps
your definitions there by default; --instance-path <path> pins a
custom durable location instead. A separate, reviewed configuration source is an
advanced opt-in past that default, not the standard model. All of it is
Kubernetes-style YAML with apiVersion: goobers.dev/v1alpha1, grouped by
who writes it and when.
A goober is a definition. Behavior lives in a markdown instructions file and config in YAML; at runtime it materializes as an ephemeral environment, a fresh git worktree plus a local process.
Why YAML? It's GitOps for your agent workforce: source-controlled, PR-reviewed, diffable, revertable — the same operational model you already run your infrastructure on. A workflow isn't a script you kick off by hand; it's a versioned part of your product.
gaggles/acme-web/
gaggle.yaml
goobers/
coder/
goober.yaml
instructions.md
workflows/
implementation.yaml # goober.yaml (abridged)
apiVersion: goobers.dev/v1alpha1
kind: Goober
metadata:
name: coder
spec:
gaggle: acme-web
role: coder
instructions: instructions.md
harness: claude-code
capabilities:
- repo:push
- github:issues:write
workflows:
- implementation Goobers compiles your pipeline
One execution of a workflow, with pinned inputs. The run records the workflow name, version, and compiled digest, and completes on that version.
A run starts however fits: on demand, on a schedule, or triggered by backlog, signal, or
a signed webhook. Stages come in two kinds — deterministic runs a
command (inline or a project-owned script), agentic invokes a goober — in any mix, any
order you declare. Only envelopes and artifacts pass between stages; agentic stages read
and publish them through the automatically supplied
goobers-io tools.
What feeds a trigger is pluggable the same way. The shipped examples watch Goobers' own run journal, but a signal source can just as easily be a product-telemetry connector — same interface, no SDK embedded in your app.
implement, a spent repass budget
escalates instead of looping. In a live run, violet marks the stage a goober holds.
One run, from the acceptance record
The trace below is that same run, 924e2b3d, verbatim — recorded
2026-07-15 against the Goobers repository itself. The implementation workflow claimed
issue #317, passed its review and CI gates, and opened
PR #324
in 505.7 seconds. A different workflow would draw a different shape here — its own
trigger, its own stage mix, its own gates.
run: 924e2b3d4d4236521259bf2ea66fbe11
workflow: implementation (v1)
trigger: manual implementation
phase: completed (machineState="", lastSeq=39)
[3-8] stage query-backlog → success (claimed issue #317)
[9-11] stage implement → success
[15] gate review → verdict=pass target=local-ci
[16-19] stage local-ci → success
[20] gate local-gate → verdict=pass target=push-branch
[21-24] stage push-branch → success
[25-30] stage open-pr → success → ref.touched kind=pr id=324
url=https://github.com/Agent-Clubhouse/Goobers/pull/324
[31-32] stage ci-poll → success (real GitHub Actions `make ci`
green, job 87304277074)
[33] gate ci-gate → verdict=pass target=close-out
[34-38] stage close-out → success (issue #317 closed out)
[39] run.finished → status=completed
The same acceptance record keeps the failing sibling: run 1c93168e failed
local-ci four times, burned its three repasses, and finished in
status=escalated. The recorded cause was a platform bug, found by reading
the journal.
Workflows invoke goobers; goobers never invoke workflows. A goober's own output can
become a signal the scheduler admits as a trigger, so chaining between workflows always
routes back through the scheduler, never through a direct call. Readiness conditions gate
every trigger before a run starts: per-workflow and per-instance max-parallel limits,
maxRunsPerHour and maxRunsPerDay budgets,
maxOpenPRs, and provider-quota gating all have to clear first —
maxChainDepth is part of the same contract but reserved, not yet enforced.
Beyond the PR
Landing a PR is not the end of the story. Three workflows run at two altitudes and never
call each other directly: implementation is the executor that opens the PR;
merge-review is the decider that looks at the whole set of open PRs at once,
not just the one it is judging; pr-remediation is the executor that reworks
one flagged PR on its own branch. They compose only through durable labels, and each label
is paired with a machine-readable sticky comment payload that is the actual source of
truth, not the label text — see the full label set in shipped workflow examples.
Draining a cluster of PRs
When several open PRs touch overlapping files, merge-review's elect-lander
stage picks one to land, merges it, and post-merge rebases the rest onto it
before repeating — draining the whole cluster one PR at a time instead of racing them.
Merging itself is never on by default: it is a separate, explicit per-workflow capability
grant, gated further by a conjunctive safety check that must also pass.
Escalation feeds decomposition
Escalation is not always a dead end. When an implementer returns a non-retryable
ISSUE_OVER_SCOPE or NEEDS_DECOMPOSITION failure, the runner
skips review and ends the run escalated — and that terminal state is itself a
signal a decomposition workflow consumes. Decomposition mirrors the executor: an agentic
stage proposes the split with no issue-write capability of its own, and a deterministic
publisher performs every issue mutation from that validated plan, committing the parent
issue's published record as a single batch so a backlog query can never claim a
half-published child. One oversized issue becomes a batch of independently claimable
goobers:ready children.
The journal feeds the Tutor
The run journal is not only an audit trail — it is the Tutor's input too. The tutor workflow mines many runs' history for recurring failure patterns and opens a PR against the config source proposing a better instruction, skill, gate, or workflow stage; the same review-and-gate policy that governs every other change decides whether it lands. The action-root enforcement below is the containment half of that story, not the whole story of what the Tutor is for.
It runs by your rules
Claiming is atomic and lease-based, so concurrent runs never fight over one backlog item; declared caps bound how much runs at once.
Changing the workforce takes a PR
A workflow can grant an agent real effects: commit to a run branch, update an issue, open a pull request. Changing the workforce itself is stricter — an autonomous agent never edits active definitions in place, only proposes a PR against the config source, and validation, required CI, and human review decide whether it becomes desired state. The reference Tutor workflow's own PRs go through that same gate, plus one more: its open-pr stage verifies the whole diff resolves inside exactly one declared action root — a config subtree or a skills tree — and fails closed otherwise.
Every artifact and invocation-context pointer Goobers hands to a stage carries a
provenance grade —
trusted, maintainer, unapproved, or
derived. A task's minimumIntegrity is fail-closed: on public
repos, an unlabeled backlog item is unapproved by default until a
maintainer's trust label grades it up, and a maintainer-only task refuses lower-graded
input before its workspace or credentials even provision.
Identity and isolation
What an agent may do is declared, not inherited: capability grants are default-deny,
scoped per gaggle and per goober. Where it runs is disposable: a repo-backed stage gets
a fresh git worktree on the run branch each time, deterministic stages run with
network: none, and sandboxed execution is opt-in and still in preview.
It tags you in when it needs you
A human gate parks the run durably at that decision point: reaching one durably records
gate.paused. From there, goobers approve <run-id> <gate>
resumes it, goobers override --rationale=… force-passes a nondeterministic
gate, and goobers rerun-stage --addendum=… re-runs one stage of a run that
finished failed or escalated. Every intervention is journaled as
a run.resumed event carrying the actor, the decision, and the rationale; a
rerun addendum is one-off and is never written back into the workflow definition.
paused, not a
terminal phase; approve resumes it. override and
rerun-stage re-enter a run already at a terminal phase (failed
or escalated) through ResumeFromTerminal.
Escalation is terminal and records a structured cause.
goobers escalations show prints the cause and the artifact timeline around
each stage. Reaching escalated here parks the issue
goobers:needs-remediation — mechanical status (repass exhaustion, an
identical-diff loop, an infra failure), not a decision, and nobody is assigned. "Needs
you" is the decision queue specifically: an explicit reviewer fail verdict
parks goobers:needs-human instead, through aborted, and only
that park assigns the configured human.
goobers dashboard serves a read-only portal from the same binary —
escalations and every gate verdict are on the run's page. Gaggle and workflow names are
pivots: Runs and Insight links scope to them and stay bookmarkable via the URL. An
Overview page's Needs attention list surfaces failed and escalated
runs; dismissing one is local to your browser, not synced across devices.
Your backlog moves
Issues get claimed, work gets implemented, and PRs land under your gate policy.
Five surfaces, one arrow each. See shipped workflow examples that draw these routes.
One instance, many gaggles. Repo and backlog multiply — one pair per gaggle you run — while goobers config and the run journal stay singleton at the instance level, spanning every gaggle. That's how one instance coordinates multiple projects side by side, each still fully siloed: its own repo, its own backlog, its own isolation boundary.
Every run appends to a content-digested journal on disk; goobers trace
reads it back. A run id is a valid OpenTelemetry trace id.
An instance is meant to keep running this loop unattended for a week without an operator: installed as a supervised service, restarting cleanly through one shutdown contract, and self-updating without you touching config. See running it on a server for what that actually takes.