Writing a workflow
Write a workflow that does what you meant.
Generated from Agent-Clubhouse/Goobers@a512230, as of 2026-09-10.
Four kinds, one shape: Instance, Gaggle, Goober, Workflow. Every object is Kubernetes-style
YAML — apiVersion: goobers.dev/v1alpha1, kind,
metadata.name, spec — and every kind is a closed schema, embedded
in the binary: an unrecognized field fails validation, it is not silently dropped.
Write dslVersion: "2.0" on every workflow. Omitting it resolves to the
deprecated 1.4, and the schema's own generated example text is stale on this point —
goobers fix --to 2.0 migrates one step at a time.
A minimal workflow that actually runs
examples/hello-world.yaml, imported verbatim from the product repo, not
transcribed:
apiVersion: goobers.dev/v1alpha1
kind: Workflow
dslVersion: "2.0"
metadata:
name: implementation
spec:
gaggle: acme-web
displayName: Implementation (issue -> PR, reviewer gate, CI-poll repass)
# The flagship V0 workflow (ARCHITECTURE.md §12, issue #27): claim a
# goobers:ready issue, implement it in a worktree, pass it through a
# reviewer gate and a local CI gate, open a PR, then poll CI
# with a bounded repass loop back to implementation on failure. When this
# closes, the core loop exists: feed issues in, PRs come out.
triggers:
- type: schedule
schedule: "3,18,33,48 * * * *"
readiness:
# maxParallel 1 initially — claiming (SEC-047 + claim ledger, WF-031)
# already guarantees two runs never double-process the same issue, so
# this is a conservative starting concurrency, not a safety requirement;
# raise once the loop is proven out.
maxConcurrentRuns: 1
maxRunsPerHour: 8
start: query-backlog
tasks:
# Built-in backlog-query stage kind (WF-055/SCH-041): claims exactly one
# item carrying both the trust label (SEC-047) and the curator's
# goobers:ready output marker (#25). Narrowed to a single item (unlike
# curation's batch) because each run of this workflow produces one PR.
- name: query-backlog
type: deterministic
goal: Claim one ready, trust-approved issue to implement.
run:
command: ["goobers", "backlog-query", "--claim"]
inputs:
trustLabel: "goobers:approved"
requireLabels: "goobers:ready"
# Issue #361/#355: close-out now sets status=in-review at PR-open
# time instead of closing the issue outright, so an issue with a PR
# still cycling through merge-review stays open (not closed) and
# would otherwise remain eligible here. The durable GitHub label
# (not the claim ledger, whose lease is sized for one run, not a
# multi-day review cycle) is what excludes it from re-claim.
excludeLabels: "goobers/status:in-review"
maxItems: "1"
# Lifts the claimed item into a journal artifact (executor.go's
# InputResultFile convention) — the "implement" goober reads it as
# context via its ContextPointers, not via Task.Inputs/InputsFrom.
resultFile: "claimed-item.json"
capabilities:
- github:issues:write
# Read-only in practice here (issue #414's open-PR eligibility
# backstop) — no dedicated read-only PR capability exists yet, so
# this reuses github:pr:write the same way #361's post-merge stage
# does for its own PR reads.
- github:pr:write
policyActions:
- claim-backlog-items
expectedOutputs:
- claimed-item
next: gather-implement-context
# Workflow enrichment: one bounded journal artifact carries the shipped
# reviewer verdict taxonomy and a hot-file map from current sibling PRs plus
# exact conflict files in recent run journals. The reviewer can consume this
# unapproved provider evidence; the maintainer-only implementation task
# explicitly excludes it below.
- name: gather-implement-context
type: deterministic
goal: Gather the reviewer verdict taxonomy and current/recent-conflict hot-file map for implementation.
run:
command: ["goobers", "gather-implement-context"]
inputs:
resultFile: "implementation-context.json"
maxHotFiles: "100"
capabilities:
- github:pr:write
- journal:read
next: implement
# Agentic implementation in a fresh, isolated worktree (§16/#19). Scoped
# to repo:push only — this stage never talks to the issues or PR API.
# Review and local-CI repasses re-enter here. Provider-authored CI check
# evidence uses the compatible remediate-ci task below.
- name: implement
type: agentic
goober: implementer
minimumIntegrity: maintainer
# Provider-derived enrichment remains unapproved and is intentionally not
# routed into this maintainer-only consumer.
contextFrom:
- query-backlog
- implement
- remediate-ci
- review
- local-ci
goal: >-
Implement the claimed issue end to end: read the issue and its
acceptance criteria, plan, implement, verify with fast, targeted
tests for what you changed, and commit to the run's branch (push is a
separate deterministic stage — see push-branch below). Do NOT run the
full `npm run ci` suite in-session — the
deterministic local-ci stage below owns that authoritatively; running
it here only burns the session's wall-clock budget on test execution
that is about to run again anyway (#724). On a repass, first read the
reviewer rationale or local-CI failure evidence attached as context and
address it before making further changes.
capabilities:
- repo:push
- agent:model
policyActions:
- modify-repository
retry:
maxAttempts: 2
backoffSeconds: 15
# #724: bound the implement session to think-time, not test wall-clock.
# If the session still times out with a viable committed diff, salvage it
# and advance to review + local-ci instead of discarding the run.
onTimeout: salvage
expectedOutputs:
- changed-files
# A recognized non-retryable failure bypasses review evaluation but uses
# review's escalate control branch below to park the issue first.
next: review
# CI check details are provider-authored and therefore remain unapproved.
# A distinct remediation consumer accepts that grade without weakening the
# maintainer-only implementation task.
- name: remediate-ci
type: agentic
goober: implementer
minimumIntegrity: unapproved
contextFrom:
- query-backlog
- implement
- remediate-ci
- review
- local-ci
- ci-poll
goal: >-
Remediate the failed CI checks using the attached provider evidence,
run fast targeted tests for the change, and commit the fix to the run's
branch. Do not run the full CI suite; local-ci runs it authoritatively
after review.
capabilities:
- repo:push
- agent:model
policyActions:
- modify-repository
retry:
maxAttempts: 2
backoffSeconds: 15
onTimeout: salvage
next: review
# The local CI-equivalent gate, run on the reviewed, durably checkpointed
# implementation branch after syncing base. The command declared here is
# this gaggle's own stack-native suite, and it matches the `ciCommand:
# ["npm", "run", "ci"]` gaggle.yaml declares — so what an operator reads in
# this file is what the local-ci stage actually runs (#2554). At runtime
# ApplyGaggleCICommand still resolves the gaggle's ciCommand into this
# stage (MGV-1/#1009); a gaggle that declares none falls back to whatever
# is written here, which is why a Go literal in a non-Go example is
# misleading rather than harmless.
- name: local-ci
type: deterministic
goal: Run the project's local CI-equivalent (build, lint, tests) in the worktree.
run:
command: ["npm", "run", "ci"]
# Pull in build/test behavior fixes that landed after this run branched.
syncBase: true
retry:
maxAttempts: 1
next: local-gate
# Deterministic branch checkpoint (#237/#3272): publish the reviewed commit
# before long local CI, but still before open-pr, so infrastructure failures
# preserve a durable branch without creating a pull request prematurely.
- name: push-branch
type: deterministic
goal: Push the run's branch to origin.
run:
command: ["goobers", "push-branch"]
capabilities:
- repo:push
policyActions:
- push-repository-branch
next: local-ci
- name: open-pr
type: deterministic
goal: Open (or update, on repass) the PR for the run's branch.
run:
command: ["goobers", "open-pr"]
inputs:
# BranchName() = goobers/<workflow>/<run-id> (providers.BranchName,
# #13) is stable across repasses, so a second call here updates the
# existing PR rather than opening a duplicate.
runIdFooter: "true"
# Lifts prNumber/pull-request-url into ResultEnvelope.Outputs (not
# just an artifact) so ci-poll's inputsFrom below can reference them
# (executor.go's InputResultFile doc: a flat JSON result file also
# merges into Outputs).
resultFile: "pr-result.json"
capabilities:
- provider:pr:write
policyActions:
- open-or-update-pr
expectedOutputs:
- pull-request-url
# Well-known handoff key for ci-poll's inputsFrom below.
- prNumber
# #947: opened=false when the claimed issue closed mid-flight, so
# open-pr-gate can abort the run instead of proceeding to ci-poll on a
# PR it deliberately did not open.
- opened
next: open-pr-gate
- name: ci-poll
type: deterministic
goal: Poll the PR's CI checks and review state until they conclude.
run:
# internal/executor.TaskExecutor (#18, #132) dispatches this built-in
# stage kind via inputs.kind — it never shells out `command` for
# kind=ci-poll (CIPollExecutor calls the PR provider directly).
# `command` stays a placeholder because DeterministicRun.Command is a
# required field in schema v0.
command: ["goobers", "ci-poll"]
inputs:
kind: "ci-poll"
# prOwner/prRepo default from RepoRef when unset (they match here).
inputsFrom:
# prNumber arrives via open-pr's declared Outputs, threaded by the
# runner per this explicit mapping (Task.InputsFrom, #132) — not a
# static value, and not blanket-propagated the way a gate's Inputs
# flatten every upstream Output automatically.
prNumber: prNumber
capabilities:
- provider:pr:write
expectedOutputs:
- ciStatus
# Passed through by the built-in so timeout can re-enter this task.
- prNumber
next: ci-gate
- name: close-out
type: deterministic
goal: >-
Comment on the originating issue linking the open PR and mark it
in-review — issue #361/#355: the work isn't done until the PR
merges, so this no longer closes the issue outright. The actual
close happens at the merge event (`goobers post-merge`, run by the
separate opt-in merge-review workflow after its verdict + CI +
SHA-pin conjuncts all hold, issue #360).
run:
command: ["goobers", "issue-close-out"]
inputs:
status: "in-review"
resultFile: "issue-close-out-result.json"
capabilities:
- github:issues:write
policyActions:
- update-issue
# Terminal: no `next`. The core loop's product — issue in, PR out — is
# complete once the issue carries the closing breadcrumb.
- name: park-escalated
type: deterministic
goal: Park an escalated issue with the terminal escalation reason.
run:
command: ["goobers", "issue-close-out"]
inputs:
# #2028/#2333: every route into this stage (an empty implementation
# diff, repass-budget exhaustion, identical-diff loop, an
# infra/executor failure, a CI-poll timeout) is a mechanical failure
# someone needs to act on, not a policy question — so it parks
# goobers:needs-remediation, not goobers:needs-human. A genuine policy
# decision (the reviewer's explicit "fail" verdict) parks through
# park-needs-human instead.
status: "needs-remediation"
resultFile: "issue-close-out-result.json"
capabilities:
- github:issues:write
policyActions:
- update-issue
# Terminal @escalate, NOT @abort. The issue still has to leave
# goobers:ready (that is this PR's whole point), but the run's phase is
# what every escalation surface selects on: `goobers run` exit 3, the
# read API's escalationCause, and `goobers trace`'s escalation summary
# all key on PhaseEscalated. Collapsing this into park-needs-human's
# @abort would make a repass-exhausted or non-retryable-disposition run
# indistinguishable from an ordinary failure — and would regress #415,
# which exists precisely so these terminate escalated.
next: "@escalate"
- name: park-needs-human
type: deterministic
goal: >-
Park an issue only when implementation surfaced a policy or product
decision a human must make, stating the exact question in the terminal
review reason.
run:
command: ["goobers", "issue-close-out"]
inputs:
status: "needs-human"
resultFile: "issue-close-out-result.json"
capabilities:
- github:issues:write
policyActions:
- update-issue
next: "@abort"
gates:
# Agentic gate: the reviewer invocation IS the evaluator (gate.md's
# "Agentic" evaluator kind) — there is no separate preceding review task.
# The runner attaches the preceding task's ("implement") artifacts as
# evidence context pointers before invoking the reviewer
# (internal/gate.ReviewerInvocation, #20). Verdict decisions map directly
# to branches (GT-002: never a silent pass): "needs-changes" repasses to
# implement with the rationale as evidence; "fail" parks the issue before
# aborting rather than spending repass budget on a fundamentally rejected
# approach. The gate's escalate control branch routes empty diffs and
# bounded-repass exhaustion through mechanical-remediation parking.
- name: review
evaluator: agentic
agentic:
goober: reviewer
workspace: repo
branches:
pass: push-branch
needs-changes: implement
fail: park-needs-human
escalate: park-escalated
# Retryable infrastructure failures re-run local-ci against the exact
# reviewed commit under their own bounded budget. Exhaustion takes the
# escalation branch and terminal cleanup retains the pushed checkpoint.
- name: local-gate
evaluator: automated
automated:
check: failure-class
branches:
pass: open-pr
fail: implement
infra: local-ci
escalate: park-escalated
# #947: open-pr re-checks the claimed issue's state immediately before
# opening (the claim was validated once, at query-backlog, but implement +
# review + local-ci can take 30+ minutes). When the issue closed or was
# superseded in that window, open-pr emits opened=false and opens no PR;
# this gate routes that straight to @abort so the run terminates with a
# clear, distinguishable reason instead of producing a PR for moot work.
# opened=true proceeds to ci-poll exactly as before.
- name: open-pr-gate
evaluator: automated
automated:
check: output-equals
params:
key: opened
equals: "true"
branches:
pass: ci-poll
fail: "@abort"
# ci-poll sets Outputs[ciStatus] to the normalized providers.CheckState
# value ("passing"/"failing"/"pending" — providers/model.go); the
# ci-status check reads that well-known key.
- name: ci-gate
evaluator: automated
automated:
check: ci-status
params:
equals: "passing"
# Pinned explicitly at the 1.4 -> 2.0 migration: DSL 2.0's ci-poll
# input builder injects this default where DSL 1.4 left it unset, so
# the pin keeps compiled behavior identical across the version bump.
pollIntervalSeconds: 10
branches:
pass: close-out
# Provider-authored failure evidence remains unapproved and is routed
# to the explicitly compatible remediation consumer.
fail: remediate-ci
escalate: park-escalated
# A poll timeout (#239) is not evidence CI failed — just that it
# didn't finish in this polling window. Re-enter ci-poll at the
# journaled stage boundary so the same PR remains claimed and keeps
# waiting without consuming implementation or repass budget (#3325).
timeout: ci-poll
Two kinds of stage, and only two
Stages come in two kinds, and both get a fresh, disposable workspace. The YAML key is always tasks:
| deterministic | run.command (an argument vector, no shell interpretation) or
run.script (sh -c on Unix, cmd.exe /D /S /C on
Windows) — mutually exclusive.
|
|---|---|
| agentic |
invokes a named goober through a harness — exactly copilot or
claude-code — with an invocation envelope of goal, context pointers, and
capability grants, and must return a result envelope of status, outputs, and artifact
pointers.
|
Stages exchange only envelopes and artifact pointers. A second class of built-in stage
kinds — backlog-query, open-pr, push-branch,
merge-pr, and more — appear in the CLI reference tagged "a workflow stage."
Workflows invoke these as building blocks; the full set renders below, collapsed.
Gates are a third thing, not a stage kind
A gate is a decision state that branches the workflow. One primitive, three evaluator kinds:
| automated |
one of 11 built-in checks — ci-status, output-equals,
output-numeric-gte, land-outcome,
queue-outcome, and six more.
|
|---|---|
| agentic | a reviewer goober evaluates the work and returns a structured verdict. |
| human |
optional named approvers. Not yet live: the schema
marks timeoutSeconds and onTimeout reserved — a nonzero
timeout is rejected at validation, and the runner waits indefinitely for an explicit
decision instead.
|
branches maps outcome to next, with pass as the
success key. A failed verdict can route back for another pass, bounded by
maxRepasses; when the budget is exhausted, or a gate routes to
@escalate, the run terminates in phase escalated with a
structured cause.
Where a stopped run parks, beyond a run's own terminal phase — only the first is decision-required:
| Label | Kind | Applies when |
|---|---|---|
goobers:needs-human | Decision | An explicit reviewer fail, an unattributed block, a detected circular dependency, or a curator-flagged fork. Assigns a human when needsHumanAssignee is configured. |
blocked-on-sibling | Status | Self-heals once the sibling resolves. |
needs-remediation | Status | Routed back into a repair loop, not a human queue. |
Triggers
Five types: manual, schedule, backlog-item,
signal, and signed GitHub webhook. A manual trigger
must be the only item in spec.triggers. Configuring a webhook trigger is four
lines of YAML — the listener, its shared secret, HMAC verification, and delivery dedupe are
the daemon's job, not the workflow's; see
the webhooks guide
.
A backlog-item trigger narrows candidates additively: the provider-side
selector (required labels) narrows first, then a deliberately restricted CEL
labelPredicate, then fieldPredicate over native provider fields.
All configured filters must pass. An unavailable native field fails closed —
trigger evaluation errors rather than silently treating it as a non-match.
trustLabel is an explicit approval label; selector labels are routing criteria
and never imply approval.
How work crosses a stage boundary
One branch (goobers/<workflow>/<run-id>), a fresh worktree per
stage — only committed work crosses. When a stage writes its declared
artifactFile, the runner lifts it into an ArtifactPointer, which
automatically becomes a ContextPointer in the next stage's envelope. There is
no context: YAML key and no per-stage opt-in.
At a parallel join, pointers are appended in declaration order, not
completion order — maxConcurrentBranches can change when branches finish, not
where their pointers land in the join input.
contextFrom routes input-integrity grades (trusted /
maintainer / unapproved / derived); it does not just trim noise. Omitting it means a stage
receives every accumulated pointer at whatever grade it carries, so an allowlist is how a
maintainer-only task avoids unapproved content entirely. Separate from
inputsFrom, which maps named scalar outputs into task inputs rather than
carrying artifact content.
The three things called "capability," and they are not the same thing: spec.requires.capabilities is provider support the workflow needs from its
gaggle's connected provider; task.requiredCapabilities is runner/toolchain
selection (node@20, os=linux); task.capabilities is
the credential and effect grant. The schema itself calls this distinction out because the
three fields share a name and nothing else — this is the one place it's resolved.
Parallel branches
spec.parallels: at least two statically declared branches, one join,
failurePolicy required with no default (fail_fast,
all_or_nothing, or continue_on_error). Concurrency above 1
restricts branch tasks to scratch or repo-readonly workspaces — a
writable repo workspace is rejected at compile time, since two branches
writing to the same worktree would race.
parallels:
- name: focus-areas
join: collate
failurePolicy: continue_on_error
maxConcurrentBranches: 2
branches:
- name: security
start: review-security
- name: performance
start: review-performance Writing your own deterministic stage
Keep short workflow policy inline as run.script; put toolchain-dependent or
substantial logic in a run.command committed to the target project, reviewed
through that project's own PR process. Neither form is sandboxed — both run as native host
processes with the task's declared capabilities.
resultFile is a flat JSON object, never nested under
outputs (nested JSON is not flattened) and never carrying status
yourself — the executor derives that from the process outcome. Exit 0 without the declared
file is missing_result_file; {"noWork": true} yields
status: no-work and the declared next is not evaluated.
GOOBERS_RUN_ID,
GOOBERS_WORKFLOW, and GOOBERS_INSTANCE_ROOT are only injected when
run.command invokes the goobers CLI itself — a project script
reading them otherwise gets nothing, silently, not an error.
Full authoring guidance — choosing a form, the result-file contract in depth, limits and environment, validating and tracing — is in the custom-stage cookbook .
Artifact I/O for agentic stages
The goobers-io service is harness-constructed and credential-free — do not
declare it in tools: or mcpServers:. Name its tools explicitly in
instructions (get_run_info, publish_output,
list_inputs, grep_input, read_input) or the model
falls back to file and shell tools instead.
Check it before you ship it
validate and lint are the same engine — --json,
--strict, --github-annotations. validate --source-tree
and daemon reconciliation call the identical loader, so a passing tree will not fail at
apply time. Validate the config repo in CI, and pin the action version — upstream names no
default and no latest, so a workflow that omits one silently tracks a moving target.
Let your coding agent write it
goobers agent-kit install --harness copilot|claude|generic into a git repo
root installs the release-matched skills; agent-kit check is a CI gate and is
only "current" at exit 0 — it never replaces your own AGENTS.md or
CLAUDE.md. One caveat worth carrying forward: goobers trace is not read-only — it opens the telemetry
rollup and will create and migrate telemetry.db, so copy a forensic instance
first if you're inspecting one you don't want to touch.
Ask the binary
goobers examples list|show, scaffold {workflow,goober,gaggle},
schema, explain <selector>, features,
versions — all embedded, all offline, all exactly what the running binary
actually supports.
Field reference
Generated from the closed JSON schemas. A word in the support column comes from the workflow-DSL feature matrix, not this page.
Workflow
A deterministic state machine belonging to one gaggle: triggers make it eligible to run, and tasks and gates connect into the process.
| Field | Type | Required | Support | Description |
|---|---|---|---|---|
displayName | string | no | ga | Human-facing workflow name shown in the portal. |
docsRoots | string[] | no | ga | In-repo documentation roots this workflow keeps current (docs-updater, #472/#1016). Ordered repo-relative paths (files or directories); the run's writes are confined to these roots. Each must be non-empty and must not escape the repository. |
dslVersion | string | no | Workflow DSL version in major.minor form. Omitting it resolves to the deprecated 1.4, not the supported 2.0 — pin explicitly. | |
gaggle | string | yes | ga | Name of the gaggle this workflow belongs to. |
gates | gate[] | no | ga | Validation and branching states in the workflow state machine. |
outboxMirrorPath | string | no | ga | Absolute local filesystem root (or ~/ path) where durable journal outbox files are additionally mirrored. Tasks may override it. |
parallels | parallel[] | no | ga | Static fan-out/fan-in states in the workflow state machine. |
readiness | object | no | ga | Admission limits that bound when runs may start. maxChainDepth is reserved and is not currently enforced. |
requires | object | no | Provider-capability requirements this workflow needs from its gaggle's connected provider (CONF-6, #2079). Distinct from a task's requiredCapabilities (runner/toolchain) and capabilities (credential grants). Omit to derive the requirement set from the stages this workflow uses; an explicit value here replaces that derivation entirely. | |
runControls | runControls | no | ga | Workflow-level overrides for inherited runner safety budgets. |
start | string | yes | ga | Name of the first task, gate, or parallel state. |
tasks | task[] | no | ga | Work states in the workflow state machine. |
triggers | trigger[] | yes | ga | Conditions that may start a run once readiness checks also pass. A manual trigger must be the only trigger. |
tutorScope | object | no | ga | Declares this workflow as a Tutor-role definition and its topology tier (TUT-A4): per-workflow (confined to target's own subtree) or per-gaggle (the gaggle's shared config). |
One real example, from config-examples/
apiVersion: goobers.dev/v1alpha1
kind: Workflow
dslVersion: "2.0"
metadata:
name: implementation
spec:
gaggle: acme-web
displayName: Implementation (issue -> PR, reviewer gate, CI-poll repass)
# The flagship V0 workflow (ARCHITECTURE.md §12, issue #27): claim a
# goobers:ready issue, implement it in a worktree, pass it through a
# reviewer gate and a local CI gate, open a PR, then poll CI
# with a bounded repass loop back to implementation on failure. When this
# closes, the core loop exists: feed issues in, PRs come out.
triggers:
- type: schedule
schedule: "3,18,33,48 * * * *"
readiness:
# maxParallel 1 initially — claiming (SEC-047 + claim ledger, WF-031)
# already guarantees two runs never double-process the same issue, so
# this is a conservative starting concurrency, not a safety requirement;
# raise once the loop is proven out.
maxConcurrentRuns: 1
maxRunsPerHour: 8
start: query-backlog
tasks:
# Built-in backlog-query stage kind (WF-055/SCH-041): claims exactly one
# item carrying both the trust label (SEC-047) and the curator's
# goobers:ready output marker (#25). Narrowed to a single item (unlike
# curation's batch) because each run of this workflow produces one PR.
- name: query-backlog
type: deterministic
goal: Claim one ready, trust-approved issue to implement.
run:
command: ["goobers", "backlog-query", "--claim"]
inputs:
trustLabel: "goobers:approved"
requireLabels: "goobers:ready"
# Issue #361/#355: close-out now sets status=in-review at PR-open
# time instead of closing the issue outright, so an issue with a PR
# still cycling through merge-review stays open (not closed) and
# would otherwise remain eligible here. The durable GitHub label
# (not the claim ledger, whose lease is sized for one run, not a
# multi-day review cycle) is what excludes it from re-claim.
excludeLabels: "goobers/status:in-review"
maxItems: "1"
# Lifts the claimed item into a journal artifact (executor.go's
# InputResultFile convention) — the "implement" goober reads it as
# context via its ContextPointers, not via Task.Inputs/InputsFrom.
resultFile: "claimed-item.json"
capabilities:
- github:issues:write
# Read-only in practice here (issue #414's open-PR eligibility
# backstop) — no dedicated read-only PR capability exists yet, so
# this reuses github:pr:write the same way #361's post-merge stage
# does for its own PR reads.
- github:pr:write
policyActions:
- claim-backlog-items
expectedOutputs:
- claimed-item
next: gather-implement-context
# Workflow enrichment: one bounded journal artifact carries the shipped
# reviewer verdict taxonomy and a hot-file map from current sibling PRs plus
# exact conflict files in recent run journals. The reviewer can consume this
# unapproved provider evidence; the maintainer-only implementation task
# explicitly excludes it below.
- name: gather-implement-context
type: deterministic
goal: Gather the reviewer verdict taxonomy and current/recent-conflict hot-file map for implementation.
run:
command: ["goobers", "gather-implement-context"]
inputs:
resultFile: "implementation-context.json"
maxHotFiles: "100"
capabilities:
- github:pr:write
- journal:read
next: implement
# Agentic implementation in a fresh, isolated worktree (§16/#19). Scoped
# to repo:push only — this stage never talks to the issues or PR API.
# Review and local-CI repasses re-enter here. Provider-authored CI check
# evidence uses the compatible remediate-ci task below.
- name: implement
type: agentic
goober: implementer
minimumIntegrity: maintainer
# Provider-derived enrichment remains unapproved and is intentionally not
# routed into this maintainer-only consumer.
contextFrom:
- query-backlog
- implement
- remediate-ci
- review
- local-ci
goal: >-
Implement the claimed issue end to end: read the issue and its
acceptance criteria, plan, implement, verify with fast, targeted
tests for what you changed, and commit to the run's branch (push is a
separate deterministic stage — see push-branch below). Do NOT run the
full `npm run ci` suite in-session — the
deterministic local-ci stage below owns that authoritatively; running
it here only burns the session's wall-clock budget on test execution
that is about to run again anyway (#724). On a repass, first read the
reviewer rationale or local-CI failure evidence attached as context and
address it before making further changes.
capabilities:
- repo:push
- agent:model
policyActions:
- modify-repository
retry:
maxAttempts: 2
backoffSeconds: 15
# #724: bound the implement session to think-time, not test wall-clock.
# If the session still times out with a viable committed diff, salvage it
# and advance to review + local-ci instead of discarding the run.
onTimeout: salvage
expectedOutputs:
- changed-files
# A recognized non-retryable failure bypasses review evaluation but uses
# review's escalate control branch below to park the issue first.
next: review
# CI check details are provider-authored and therefore remain unapproved.
# A distinct remediation consumer accepts that grade without weakening the
# maintainer-only implementation task.
- name: remediate-ci
type: agentic
goober: implementer
minimumIntegrity: unapproved
contextFrom:
- query-backlog
- implement
- remediate-ci
- review
- local-ci
- ci-poll
goal: >-
Remediate the failed CI checks using the attached provider evidence,
run fast targeted tests for the change, and commit the fix to the run's
branch. Do not run the full CI suite; local-ci runs it authoritatively
after review.
capabilities:
- repo:push
- agent:model
policyActions:
- modify-repository
retry:
maxAttempts: 2
backoffSeconds: 15
onTimeout: salvage
next: review
# The local CI-equivalent gate, run on the reviewed, durably checkpointed
# implementation branch after syncing base. The command declared here is
# this gaggle's own stack-native suite, and it matches the `ciCommand:
# ["npm", "run", "ci"]` gaggle.yaml declares — so what an operator reads in
# this file is what the local-ci stage actually runs (#2554). At runtime
# ApplyGaggleCICommand still resolves the gaggle's ciCommand into this
# stage (MGV-1/#1009); a gaggle that declares none falls back to whatever
# is written here, which is why a Go literal in a non-Go example is
# misleading rather than harmless.
- name: local-ci
type: deterministic
goal: Run the project's local CI-equivalent (build, lint, tests) in the worktree.
run:
command: ["npm", "run", "ci"]
# Pull in build/test behavior fixes that landed after this run branched.
syncBase: true
retry:
maxAttempts: 1
next: local-gate
# Deterministic branch checkpoint (#237/#3272): publish the reviewed commit
# before long local CI, but still before open-pr, so infrastructure failures
# preserve a durable branch without creating a pull request prematurely.
- name: push-branch
type: deterministic
goal: Push the run's branch to origin.
run:
command: ["goobers", "push-branch"]
capabilities:
- repo:push
policyActions:
- push-repository-branch
next: local-ci
- name: open-pr
type: deterministic
goal: Open (or update, on repass) the PR for the run's branch.
run:
command: ["goobers", "open-pr"]
inputs:
# BranchName() = goobers/<workflow>/<run-id> (providers.BranchName,
# #13) is stable across repasses, so a second call here updates the
# existing PR rather than opening a duplicate.
runIdFooter: "true"
# Lifts prNumber/pull-request-url into ResultEnvelope.Outputs (not
# just an artifact) so ci-poll's inputsFrom below can reference them
# (executor.go's InputResultFile doc: a flat JSON result file also
# merges into Outputs).
resultFile: "pr-result.json"
capabilities:
- provider:pr:write
policyActions:
- open-or-update-pr
expectedOutputs:
- pull-request-url
# Well-known handoff key for ci-poll's inputsFrom below.
- prNumber
# #947: opened=false when the claimed issue closed mid-flight, so
# open-pr-gate can abort the run instead of proceeding to ci-poll on a
# PR it deliberately did not open.
- opened
next: open-pr-gate
- name: ci-poll
type: deterministic
goal: Poll the PR's CI checks and review state until they conclude.
run:
# internal/executor.TaskExecutor (#18, #132) dispatches this built-in
# stage kind via inputs.kind — it never shells out `command` for
# kind=ci-poll (CIPollExecutor calls the PR provider directly).
# `command` stays a placeholder because DeterministicRun.Command is a
# required field in schema v0.
command: ["goobers", "ci-poll"]
inputs:
kind: "ci-poll"
# prOwner/prRepo default from RepoRef when unset (they match here).
inputsFrom:
# prNumber arrives via open-pr's declared Outputs, threaded by the
# runner per this explicit mapping (Task.InputsFrom, #132) — not a
# static value, and not blanket-propagated the way a gate's Inputs
# flatten every upstream Output automatically.
prNumber: prNumber
capabilities:
- provider:pr:write
expectedOutputs:
- ciStatus
# Passed through by the built-in so timeout can re-enter this task.
- prNumber
next: ci-gate
- name: close-out
type: deterministic
goal: >-
Comment on the originating issue linking the open PR and mark it
in-review — issue #361/#355: the work isn't done until the PR
merges, so this no longer closes the issue outright. The actual
close happens at the merge event (`goobers post-merge`, run by the
separate opt-in merge-review workflow after its verdict + CI +
SHA-pin conjuncts all hold, issue #360).
run:
command: ["goobers", "issue-close-out"]
inputs:
status: "in-review"
resultFile: "issue-close-out-result.json"
capabilities:
- github:issues:write
policyActions:
- update-issue
# Terminal: no `next`. The core loop's product — issue in, PR out — is
# complete once the issue carries the closing breadcrumb.
- name: park-escalated
type: deterministic
goal: Park an escalated issue with the terminal escalation reason.
run:
command: ["goobers", "issue-close-out"]
inputs:
# #2028/#2333: every route into this stage (an empty implementation
# diff, repass-budget exhaustion, identical-diff loop, an
# infra/executor failure, a CI-poll timeout) is a mechanical failure
# someone needs to act on, not a policy question — so it parks
# goobers:needs-remediation, not goobers:needs-human. A genuine policy
# decision (the reviewer's explicit "fail" verdict) parks through
# park-needs-human instead.
status: "needs-remediation"
resultFile: "issue-close-out-result.json"
capabilities:
- github:issues:write
policyActions:
- update-issue
# Terminal @escalate, NOT @abort. The issue still has to leave
# goobers:ready (that is this PR's whole point), but the run's phase is
# what every escalation surface selects on: `goobers run` exit 3, the
# read API's escalationCause, and `goobers trace`'s escalation summary
# all key on PhaseEscalated. Collapsing this into park-needs-human's
# @abort would make a repass-exhausted or non-retryable-disposition run
# indistinguishable from an ordinary failure — and would regress #415,
# which exists precisely so these terminate escalated.
next: "@escalate"
- name: park-needs-human
type: deterministic
goal: >-
Park an issue only when implementation surfaced a policy or product
decision a human must make, stating the exact question in the terminal
review reason.
run:
command: ["goobers", "issue-close-out"]
inputs:
status: "needs-human"
resultFile: "issue-close-out-result.json"
capabilities:
- github:issues:write
policyActions:
- update-issue
next: "@abort"
gates:
# Agentic gate: the reviewer invocation IS the evaluator (gate.md's
# "Agentic" evaluator kind) — there is no separate preceding review task.
# The runner attaches the preceding task's ("implement") artifacts as
# evidence context pointers before invoking the reviewer
# (internal/gate.ReviewerInvocation, #20). Verdict decisions map directly
# to branches (GT-002: never a silent pass): "needs-changes" repasses to
# implement with the rationale as evidence; "fail" parks the issue before
# aborting rather than spending repass budget on a fundamentally rejected
# approach. The gate's escalate control branch routes empty diffs and
# bounded-repass exhaustion through mechanical-remediation parking.
- name: review
evaluator: agentic
agentic:
goober: reviewer
workspace: repo
branches:
pass: push-branch
needs-changes: implement
fail: park-needs-human
escalate: park-escalated
# Retryable infrastructure failures re-run local-ci against the exact
# reviewed commit under their own bounded budget. Exhaustion takes the
# escalation branch and terminal cleanup retains the pushed checkpoint.
- name: local-gate
evaluator: automated
automated:
check: failure-class
branches:
pass: open-pr
fail: implement
infra: local-ci
escalate: park-escalated
# #947: open-pr re-checks the claimed issue's state immediately before
# opening (the claim was validated once, at query-backlog, but implement +
# review + local-ci can take 30+ minutes). When the issue closed or was
# superseded in that window, open-pr emits opened=false and opens no PR;
# this gate routes that straight to @abort so the run terminates with a
# clear, distinguishable reason instead of producing a PR for moot work.
# opened=true proceeds to ci-poll exactly as before.
- name: open-pr-gate
evaluator: automated
automated:
check: output-equals
params:
key: opened
equals: "true"
branches:
pass: ci-poll
fail: "@abort"
# ci-poll sets Outputs[ciStatus] to the normalized providers.CheckState
# value ("passing"/"failing"/"pending" — providers/model.go); the
# ci-status check reads that well-known key.
- name: ci-gate
evaluator: automated
automated:
check: ci-status
params:
equals: "passing"
# Pinned explicitly at the 1.4 -> 2.0 migration: DSL 2.0's ci-poll
# input builder injects this default where DSL 1.4 left it unset, so
# the pin keeps compiled behavior identical across the version bump.
pollIntervalSeconds: 10
branches:
pass: close-out
# Provider-authored failure evidence remains unapproved and is routed
# to the explicitly compatible remediation consumer.
fail: remediate-ci
escalate: park-escalated
# A poll timeout (#239) is not evidence CI failed — just that it
# didn't finish in this polling window. Re-enter ci-poll at the
# journaled stage boundary so the same PR remains claimed and keeps
# waiting without consuming implementation or repass budget (#3325).
timeout: ci-poll
Goober
A role-specialized AI worker, defined by this YAML plus a markdown instructions file. A goober never schedules itself: an agentic task invokes it through a harness, scoped to exactly the capabilities it's granted.
| Field | Type | Required | Support | Description |
|---|---|---|---|---|
capabilities | "repo:read" | "repo:push" | "github:issues:read", … (25 values)[] | no | ga | Credential capability grants this goober may use. Invoking tasks may grant only a subset. |
conditionalPolicyActions | "approve-issue" | "assign-milestone" | "claim-backlog-items", … (41 values)[] | no | ga | Persona actions enabled only when an invoking task explicitly declares the action and grants its canonical capability. |
displayName | string | no | ga | Human-facing goober name shown in the portal. |
gaggle | string | no | ga | Owning gaggle. Omit only for an instance-shared persona in the goobers directory beside config/. |
harness | "copilot" | "claude-code" | no | ga | Agent harness used for this goober. Omitted defaults to copilot. |
harnessOptions | object | no | ga | Opaque harness-specific settings validated by the selected adapter. |
instructions | string | yes | ga | Markdown instruction file path relative to the goober definition directory. |
mcpServers | mcpServer[] | no | ga | External stdio or remote MCP servers materialized for this goober's harness invocation (Copilot or claude-code). |
model | string | no | ga | Harness-specific model name, validated by the selected adapter before a run starts. |
policyActions | "approve-issue" | "assign-milestone" | "claim-backlog-items", … (41 values)[] | no | ga | Closed vocabulary of externally mutating actions this persona unconditionally prescribes. Invoking tasks must redeclare every action and grant its canonical capability. |
role | string | yes | ga | Role specialization, such as coder, reviewer, or perf-hunter. |
scaleFactor | integer | no | ga | Desired replica count for concurrent work. Omitted defaults to one. |
skills | string[] | no | ga | Named skills available to this goober. |
timeoutSeconds | integer | no | ga | Default per-attempt wall-clock limit for this goober's agentic stages. A task timeout takes precedence. |
tools | string[] | no | ga | Default-deny allowlist of tools or MCP servers reachable by this goober. |
workflows | string[] | no | ga | Names of workflows that invoke this goober. |
One real example, from config-examples/
apiVersion: goobers.dev/v1alpha1
kind: Goober
metadata:
name: coder
spec:
gaggle: acme-web
role: coder
displayName: Coder
# Markdown (with YAML frontmatter) defining behavior/persona/scope, resolved
# relative to this goober definition directory (CFG-003, GBO-002).
instructions: instructions.md
harness: copilot
model: auto
harnessOptions: {}
capabilities:
- agent:model
skills:
- implement
- run-tests
tools:
- github
- shell
# Single-threaded to start; raise + redeploy to add concurrent replicas
# (GBO-030).
scaleFactor: 1
workflows:
- default-implement
Gaggle
A siloed workforce: one project codebase, one singleton backlog, and its own isolation boundary. An instance can run many gaggles side by side.
| Field | Type | Required | Support | Description |
|---|---|---|---|---|
additionalRepos | repoRef[] | no | ga | Optional reference repositories supplied read-only to runs in addition to the primary project. |
backlog | backlogRef | yes | ga | Singleton source of work-item truth for this gaggle. |
branchNamespace | string | no | ga | refs/heads/ root this gaggle's run branches live under (providers.BranchName). Empty defaults to 'goobers/'. A trailing slash is optional. Must be a valid ref-path segment sequence (#965/#1010). |
ciCommand | string[] | no | ga | Per-gaggle local CI-equivalent command run by the deterministic local-ci stage instead of its declared default (MGV-1/#1009). |
cost | object | no | Overrides external cost publication without disabling local usage accounting. | |
displayName | string | no | ga | Human-facing gaggle name shown in the portal. |
isolation | object | yes | Per-gaggle namespace and workload-identity boundary. | |
outboxMirrorPath | string | no | ga | Default absolute local filesystem root (or ~/ path) where workflows additionally mirror durable journal outbox files. |
project | repoRef | yes | ga | Primary code repository this gaggle works on. |
requiredCapabilities | runnerCapability[] | no | ga | Runner (toolchain/platform) capabilities every run of this gaggle needs, matched at schedule time against a runner's advertised set (RRQ-1/#1101). DSL ≤2.0 only: DSL 3.0 removes this field in favor of spec.runsOn (dsl-3.0.md D1/D12); the 3.0 interpreter refuses it with a pointer diagnostic. |
requireLabels | string[] | no | ga | Default requireLabels value every workflow's backlog-query task in this gaggle inherits unless the task declares its own requireLabels input (full replace, not merge — same shape as branchNamespace/headPrefix) (MIRC-2, #1901). |
runControls | runControls | no | ga | Gaggle-level overrides for instance runner safety budgets. |
runsOn | object | no | ga | Gaggle-level placement floor (DSL 3.0 only, dsl-3.0.md §2): OS, toolchain capability tags, and required runner restrictions that merge into every stage of every workflow in this gaggle — capabilities and restrictions union with the stage's own; an OS conflict with a stage is a compile error. No quantities at gaggle level. Activates only for gaggles whose workflows pin DSL 3.0. |
sandbox | object | no | preview | Per-gaggle override of the instance-wide isolation posture for agentic stages (#1305). Omitted inherits the instance posture; the default is disabled (opt-in). |
selfIdentity | string | no | ga | Provider login used as this gaggle's self identity for assignment-aware backlog operations. Omitted inherits instance.yaml selfIdentity. |
siblings | gaggleSibling[] | no | ga | Other gaggles/instances known to independently work the same target repo, for the sibling-overlap validation warning (MIRC-2, #1901). |
workcopies | object | no | Per-gaggle override for managed working-copy placement. The gaggle name and repository key are appended beneath this base path. |
One real example, from config-examples/
apiVersion: goobers.dev/v1alpha1
kind: Gaggle
metadata:
name: acme-web
spec:
displayName: Acme Web
# The project codebase this gaggle works on.
project:
provider: github
owner: acme
name: web
branch: main
# The singleton backlog this gaggle draws work from (GAG-004).
backlog:
provider: github
project: acme/web
labels:
- goobers
# Per-gaggle isolation boundary: namespace + workload identity (SEC-001/002).
isolation:
namespace: gaggle-acme-web
identityRef: acme-web-identity
# Per-gaggle CI command (MGV-1/#1009): this is a non-Go (web) stack, so the
# deterministic `local-ci` stage runs this suite, resolved into the stage at
# config-load time — no need to fork the shared workflow template. The
# workflow declares the same argv (#2554), so this override changes nothing
# an operator reads there; it is what makes the same template reusable by a
# gaggle on any stack. A non-zero exit fails the gate exactly as the Go
# default `["make", "ci"]` does elsewhere, and only ever fails this gaggle's
# own PRs.
ciCommand: ["npm", "run", "ci"]
# Runner capabilities every run of this gaggle needs (RRQ-1/#1101). These are
# matched at *schedule* time against the runner's advertised set
# (instance.yaml `runner.capabilities`, e.g. `runner: {capabilities:
# [node@20]}`): a run is refused to schedule — with a diagnostic naming the
# missing capability — on a runner that does not claim `node@20`, rather than
# scheduling it to fail at run. Toolchains are assumed preinstalled; the
# scheduler never installs anything.
#
# #735 adds a second, host-side check: before any stage of a run executes, the
# runner preflights the probeable toolchains among these tokens (`node@N`,
# `dotnet@N`, `python@N`, `go@N`, `os=<goos>`) against the actual host — using
# the same base environment the stage will run under — and fails the run closed
# with a clear diagnostic (e.g. `node@20: host has version 18.19.0 but the run
# requires 20`) if a claim is not truly satisfied. This turns a runner that
# *falsely* advertised a capability from an opaque mid-run error into an
# actionable preflight failure. Tokens whose family has no probe (`xcode`,
# `netfx@4.8`) are matched at schedule time but not host-probed.
requiredCapabilities:
- node@20
Manifest
The top-level desired state for an instance. It owns every named connection a gaggle can reference, and lists which gaggles belong to the instance's desired state.
| Field | Type | Required | Support | Description |
|---|---|---|---|---|
connections | connection[] | no | Named reusable links to external systems that gaggles and goobers may reference. | |
gaggles | string[] | no | Names of gaggle definitions included in this instance's desired state. | |
instance | object | yes | Identity of the deployed Goobers instance configured by this manifest. |
One real example, from config-examples/
apiVersion: goobers.dev/v1alpha1
kind: Manifest
metadata:
name: acme-instance
spec:
instance:
name: acme
environment: dev
# portal:
# brand:
# # Human-readable deployment name shown in the sidebar and browser tab.
# name: "Acme Ops"
# # One-line tagline shown under the brand name.
# tagline: "AI workforce platform"
# # Single-character mark shown in the topbar.
# scopeMark: "A"
# # Optional same-origin assets served from <instance-root>/assets/.
# logoUrl: "/assets/logo.svg"
# faviconUrl: "/assets/favicon.ico"
# theme:
# # Optional accent token overrides for light and dark mode.
# accentLight: "#6847d9"
# accentDark: "#a98cff"
# accentSoftLight: "#eee9ff"
# accentSoftDark: "#2c2445"
# accentInkLight: "#4c2db8"
# accentInkDark: "#c5b4ff"
# support:
# # Optional support hooks shown in the sidebar footer.
# docsUrl: "https://acme.example/docs/goobers"
# issuesUrl: "https://acme.example/support"
# chatUrl: "slack://channel/C000EXAMPLE"
# links:
# - label: "Runbooks"
# url: "https://acme.example/runbooks"
# - label: "On-call"
# url: "https://acme.example/oncall"
# Named, reusable connections. Credentials are Key Vault references — never
# inline tokens (CFG-009, SEC-010).
connections:
- name: github-main
type: repo
provider: github
secretRef:
name: github-pat
keyVault: acme-kv
- name: github-backlog
type: backlog
provider: github
secretRef:
name: github-pat
keyVault: acme-kv
# Gaggles included in this instance's desired state. Each must have a Gaggle
# object under gaggles/<name>/.
gaggles:
- acme-web
# Fleet posture (#2777): claude-code adoption alongside Copilot, not a
# conversion — acme-web's Copilot config above is untouched. Additive
# parallel gaggle working the same reference repo under claude-code, so
# the shipped fleet exercises both harnesses rather than leaving one
# undogfooded. In a real instance manifest, running both is optional —
# this listing just proves neither one costs the other.
- acme-web-claude
# Polyglot reference (PLY-4 / #1093): a C#/.NET gaggle sharing the same
# connections + workflow machinery, differing only in its declared .NET CI
# command and `dotnet@9` runtime requirement.
- dotnet-service
# Java/Maven reference (TSN-2 / #2169), using the same polyglot seams with
# its declared Maven CI command and `java@21` runtime requirement.
- java-service
# Python reference (TSN-3 / #2170): exercises Python 3.12 admission, pytest
# as the gaggle CI command, and the allowlisted Python environment.
- python-service
Workflow-stage commands — these go in YAML, not in your shell
| Command | Description |
|---|---|
goobers apply-verdict | publish a managed or advisory merge-review verdict (a workflow stage) |
goobers backlog-assignment | assign eligible backlog items from a configured roster (a workflow stage) |
goobers backlog-dedupe | surface ranked duplicate candidates for curator judgment (a workflow stage) |
goobers backlog-health | snapshot ready-pool depth and age (a workflow stage) |
goobers backlog-query | query/claim one eligible backlog item (a workflow stage) |
goobers cancel-pending-ci | cancel pending provider CI for an exact reviewed PR head (a workflow stage) |
goobers check-fail-first | enforce fail-first evidence for a new workflow gate (a workflow stage) |
goobers check-issue-staleness | route a PR to remediation if its linked issue changed since implementation began (a workflow stage) |
goobers docs-churn | emit the docs-drift churn digest since the watermark (a connector stage) |
goobers elect-lander | elect the landing PR among a merge-review cohort (a workflow stage) |
goobers file-issues | file a validated nominations artifact as deduped, budgeted issues (a workflow stage) |
goobers gate-removal-guard | block a tutor run that removes/loosens its own flagged gate without proof (a workflow stage) |
goobers gather-ci-failures | add failing CI diagnostics to a remediation brief (a workflow stage) |
goobers gather-implement-context | load first-pass implementation review and hot-file context (a workflow stage) |
goobers gather-issue-context | add originating issue bodies to a remediation brief (a workflow stage) |
goobers gather-pr-context | pr-remediation entrypoint: select and load a PR's context (a workflow stage) |
goobers gather-review-threads | add native reviews and anchored inline threads to a remediation brief (a workflow stage) |
goobers gather-sibling-context | load other open PRs as review evidence (a workflow stage) |
goobers ios-simulator-test | run XCUITest on an iOS simulator and parse its xcresult (a workflow stage) |
goobers issue-close-out | comment + close out the claimed issue (a workflow stage) |
goobers mcp-io | run the generic publish/read/list MCP server the harness spawns for a goober (a workflow stage) |
goobers merge-pr | conjunctive auto-merge via direct-merge or merge-queue (a workflow stage) |
goobers merge-queue-poll | watch an enqueued PR until merged, evicted, timed out, or opted out (a workflow stage) |
goobers open-pr | open or update the run's PR (a workflow stage) |
goobers post-merge | post-merge fan-out + close the referenced issue (a workflow stage) |
goobers pr-claim | check PR liveness or release its remediation claim (a workflow stage) |
goobers pr-comment-watch | label open goober PRs carrying unaddressed human comments (a workflow stage) |
goobers pr-select | select one managed or advisory open PR for merge-review (a workflow stage) |
goobers preflight-repo-write | check whether the configured credential can push this run's branch namespace, without mutating anything (a workflow stage) |
goobers publish-batch | publish a verified decomposition batch behind one eligibility barrier (a workflow stage) |
goobers push-branch | push the worktree's checked-out branch to origin (a workflow stage) |
goobers push-remediated | force-push the remediated branch and clear needs-remediation (a workflow stage) |
goobers rebase-pr | rebase-first, finding-driven remediation routing (a workflow stage) |
goobers reconcile-branches | report bounded stale goobers/* branch candidates (a workflow stage) |
goobers reconcile-post-merge | reconcile late merge-queue merges (a workflow stage) |
goobers record-merge-refusal | record a merge refusal and demote a persistently-stuck lander (a workflow stage) |
goobers recovery-resume | restore retained implementation into the receiving run (a workflow stage) |
goobers remediation-checkpoint | durable per-cause attempt budgets + same-diff escalation (a workflow stage) |
goobers report-pr-status | publish goobers' verdict + CI evidence as a policy-gate-able PR status (a workflow stage) |
goobers resolve-review-threads | reply to and resolve remediated native review threads (a workflow stage) |
goobers respond-to-findings | post a validated per-finding remediation response to the claimed PR (a workflow stage) |
goobers security-alerts-query | emit bounded, untrusted security alerts for work nomination (a connector stage) |
goobers select-source | select and claim an unconsumed L6 decomposition disposition (a workflow stage) |
goobers set-milestone | assign an existing milestone to an issue (a workflow stage) |
goobers telemetry-query | emit versioned candidate findings (a connector stage) |
goobers update-behind-pr | API-update a clean behind-base PR, else route to remediation (a workflow stage) |
goobers validate-plan | validate a decomposition plan against its selector artifact and the live parent (a workflow stage) |