22 KiB
OMA Maintainer Bot
Product boundary
OMA Maintainer Bot is a repository-local, evidence-first path from a narrowly
authorized GitHub issue to a structured Draft PR proposal. It is not a general
coding agent. In activation v1,
maintainer-bot.yml is the thin
GitHub Actions control layer and @open-multi-agent/maintainer-host owns event
validation, durable claims, credential isolation, status, and deterministic
Draft PR writes. @open-multi-agent/maintainer-bot remains the real OMA
execution kernel.
The local engine never creates a branch, commits, pushes, comments, labels,
opens a pull request, marks one ready, approves, merges, closes an issue,
publishes, tags, releases, or deploys. Its strongest successful output is
DRAFT_PR_PROPOSAL_READY. Only a separate credential-holding host may create a
Draft PR, after calling the deterministic safe-output revalidation boundary.
The host may then acknowledge the result as DRAFT_PR_CREATED; that state is
never inferred from a proposal.
The workflow handles only an issues.labeled delivery whose exact label is
agent-ready. It never scans or develops all open issues, does not use
pull_request_target, and does not depend on a maintainer's local computer or
a long-running server.
GitHub Actions activation flow
- The default-branch workflow publishes or updates one BOT status comment with
STARTED, Actions run URL/ID, and a freshly resolved base SHA. If an older claim exists, the visible STARTED notice does not replace that machine claim before duplicate/stale checks finish. - GitHub serializes runs by repository and Issue number. The job uses a
GitHub-hosted ephemeral runner, a 45-minute timeout, pinned Node/npm
versions, and explicit
actions: read,contents: write,issues: write, andpull-requests: writepermissions. - The host refetches repository, Issue, labels, material comments, timeline, actor permission, current default-branch SHA, trusted setting attestation, existing BOT state, branch, and PR metadata. It performs admission before a model call or repository edit.
- A separate child receives
DEEPSEEK_API_KEYplus a small non-secret environment. It receives no GitHub, npm, Actions runtime, App, SSH, or other write credential. - If OMA reaches
DRAFT_PR_PROPOSAL_READY, a new deterministic host process refetches all authorization facts and callsrevalidateDraftPrSafeOutput()against the actual worktree before any branch, commit, push, or PR call. - The writer stages exactly the reviewed files, rejects extra/untracked/ deleted/renamed/symlinked/out-of-scope content and unsafe local Git hook, proxy, credential, include, filter, or URL-rewrite configuration. Writer Git processes disable hooks plus global/system credential configuration, use a deterministic branch and canonical HTTPS destination, and create at most one Draft PR. Every terminal path updates the single status comment and Actions summary.
GitHub only triggers an issues workflow when its workflow file exists on the
default branch. Repository Actions settings must also enable “Allow GitHub
Actions to create and approve pull requests”; activation v1 uses only the
creation half and never approves. See GitHub's
trigger documentation
and repository Actions settings.
Fixed execution flow
- Deterministic admission computes the material issue revision and enforces
Definition of Ready, manual-only classes, write-authorized
agent-readyevidence, exact revision, and exact base SHA. - The deterministic context builder verifies a clean isolated worktree at the fixed base SHA and writes a versioned, hash-bound context manifest.
- One explicit OMA
runTasks()task performs schema-bound, read-only triage. The deterministic host starts no planner or implementer unless triage saysproceed, reports no uncertainty or manual-risk blocker, and exactly echoes the authorized issue revision and acceptance criteria. - Only after that host check, a second explicit OMA
runTasks()DAG executes read-only repository planning followed by a schema-bound implementation proposal. A rejected triage therefore spends no planner/implementer tokens and cannot reach an edit capability. - The host applies compare-and-swap full-content edits through the restricted edit capability. The model has no filesystem or shell tool.
- The host runs every preregistered validation command as argv with
shell: falseand a credential-stripped environment. - A new OMA team and agent perform fresh-context review using only confirmed requirements, acceptance criteria, the final diff, validation evidence, bounded current-file snapshots, and relevant context. Implementer reasoning and conversation history are not passed to the reviewer.
- A rejected but bounded and repairable result may run at most two repair
loops. Repairs use
currentFiles[].contentHashfrom the fresh review bundle for compare-and-swap, then repeat deterministic validation and fresh review. - Deterministic TypeScript emits a Draft PR proposal only when context is
sufficient, every validation passes without truncation, every acceptance
criterion passes fresh review, all paths remain in scope, and each proposed
afterHashstill equals the content reviewed after validation.
All model outputs use Zod schemas. A single OMA structured-output correction is still available inside a role; task retries are disabled. Cumulative token, configured price-based cost, wall-clock, edit-size, diff-size, context-size, and repair-loop limits fail closed.
Admission and state
The structured states are:
READY_CANDIDATE,NEEDS_CLARIFICATION,MANUAL_ONLY,BLOCKEDAGENT_READY,RUNNINGDRAFT_PR_PROPOSAL_READY,DRAFT_PR_CREATEDNEEDS_HUMAN,FAILED
DRAFT_PR_PROPOSAL_READY is intentionally separate from
DRAFT_PR_CREATED. The latter requires a matching host acknowledgment and
proposal hash.
Definition of Ready requires a clear problem, current and expected behavior, verifiable acceptance criteria, target workspace and paths, explicit out-of-scope behavior, no unresolved product or architecture decision, no active PR/run/blocker, and fixed issue revision/base SHA. Bugs additionally require deterministic reproduction steps or a constructible failing-test procedure. Bounded docs, test, and single-workspace refactor tasks do not need traditional reproduction steps when current/expected behavior, acceptance, scope, and deterministic validation are explicit.
Architecture design, major public API changes, breaking changes, broad
cross-workspace refactors, security, permissions, privacy, licenses, CI,
release/publication, dependency upgrades without fixed compatibility targets,
trackers/discussions/questions, and work without deterministic validation are
MANUAL_ONLY. riskFlags are structured control-plane evidence reviewed by
the maintainer before granting agent-ready; a model may suggest risk but
cannot issue or renew authorization.
The issue revision hashes material issue fields, comments, confirmed scope, acceptance criteria, linked work, and blockers. Editing that material after authorization makes the authorization stale. A run key hashes repository, issue number, issue revision, and base SHA. The base SHA is included so a maintainer can deliberately reauthorize the same issue content against a newer base without receiving a false duplicate.
policyVersion does not enter the run key because it is not itself a
maintainer authorization fact. A policy change does not silently authorize a
rerun. The control plane must revalidate the issue and issue a new
revision/base authorization when a rerun is intended; the proposal and final
safe-output gate still require the exact policy and prompt versions.
Cross-run authority, crash, and stale semantics
FileRunStateStore is authoritative only inside one live engine/writer handoff.
Because GitHub-hosted runners are ephemeral, $RUNNER_TEMP is never the
cross-run authority. Activation v1 instead uses one identity-checked
github-actions[bot] status comment containing a bounded machine-readable
claim ledger, Actions run status, and deterministic branch/PR metadata. A user
comment containing the same marker is ignored. The ledger retains prior
runKeys when a later revision becomes visible and fails closed at 64 claims
instead of silently dropping idempotency history.
GitHub concurrency prevents two workflow jobs from editing the same Issue at
once. A repeated terminal runKey is a duplicate and does not invoke the model,
push, or create another PR. A lost RUNNING claim becomes NEEDS_HUMAN; the
fallback preserves its revision/runKey and automatic model-conversation resume
is forbidden. Recovery means inspecting the Actions run, ensuring the prior
work is not active, selecting a clean base or material Issue revision, removing
and reapplying authorization deliberately, and starting from a fresh model
conversation. Local checkpoints are not recovery authority.
OMA reasoning content, conversation history, telemetry, and checkpoints are execution artifacts, not long-term repository memory and not the cross-process authority for issue/run state.
Context manifest and trust
Every actual model run has a persisted manifest with policy/prompt version,
issue and acceptance criteria, issue revision, base SHA, workspace map
(including the root package identity when declared),
root-to-target AGENTS.md chain, contribution rules, package and TypeScript
configuration, relevant docs/README/source/tests/fixtures/examples,
TypeScript/JavaScript relative-import relationships, bounded relevant Git
history and linked evidence, validation commands, and source-level SHA-256
provenance.
System policy is highest priority. Repository policies are identified
separately. Issue text, comments, commit messages, ordinary repository files,
diffs, and external material are all untrusted evidence, never instructions.
Missing target files, unresolved conflict markers, a moved base, a dirty
worktree, symlinks, required truncation/omission, byte/file limits, protected
paths, or other evidence conflicts set context sufficiency to false and route
the run to NEEDS_HUMAN before model edits.
config.allowedPaths is only the deployment-level maximum. The manifest also
records approvedEditScopes derived from the maintainer-authorized
issue.targetPaths, including whether each target is a file or directory.
Planner files, initial edits, repairs, the restricted editor, and the final
diff must pass both boundaries. A file target authorizes exactly that file; a
directory target authorizes paths below that directory.
Tool, credential, and validation boundary
Triage, planner, and reviewer roles receive only immutable read-only evidence
tools and must read their assigned evidence at least once. Repeated reads remain
read-only but consume budget. Triage uncertainty and manual-risk arrays contain
only unresolved blockers; a safe case uses empty arrays rather than reassuring
text. The implementer also receives no write tool: it returns bounded
full-content edits with expected hashes, and deterministic host code applies
them. Every role explicitly denies built-in bash, file-write/edit,
delegation, and search tools. OMA bash is not treated as a sandbox.
The model process refuses to start when known GitHub/npm write credentials,
including credential names with host-specific prefixes, are present. Launch
the custom engine with DEEPSEEK_API_KEY only. Validation
subprocesses receive an environment with token/key/secret/password/cookie and
credential-like variables removed. Secret values are never written to model
context, artifacts, or command output intentionally; output redaction remains
best effort.
Validation commands come only from trusted configuration. Issue or model text cannot choose an executable, argv, cwd, or timeout. All registered commands run, results and skipped checks are recorded, and failed or truncated evidence blocks proposal eligibility. The current-file repair snapshots are non-symlink regular files, path checked, per-file bounded, and capped at 180 KB total.
DeepSeek inference is remote. The minimum relevant public-repository context —
Issue text and material comments, selected repository files/history, planned
diffs, validation evidence, and fresh-review bundle — is sent to the configured
DeepSeek API. GitHub credentials, npm credentials, Actions runtime credentials,
local private paths, and model reasoning content are not intentionally sent or
published. Do not grant agent-ready to an Issue or repository context that
contains material that must not cross this provider boundary.
CLI and artifacts
Build first, then use one of three commands:
node packages/maintainer-bot/dist/cli.js admit \
--request request.json
node packages/maintainer-bot/dist/cli.js dry-run \
--request request.json \
--config packages/maintainer-bot/config/config.example.json
DEEPSEEK_API_KEY=... node packages/maintainer-bot/dist/cli.js run \
--request request.json \
--config config.json \
--state-dir /outside/repository/state \
--artifact-dir /outside/repository/artifacts \
--run-id host-event-id
State and artifact directories must stay outside the isolated repository;
otherwise their files would correctly fail the clean-diff gate. A full run
persists <runKey>.context.json and, only after every gate passes,
<runKey>.draft-pr-proposal.json. The proposal includes issue/revision/base,
acceptance criteria, files and reasons, all validation commands/results,
skipped checks, model/prompt/policy versions, risks, fresh review, context hash,
and its own proposal hash.
config.example.json is a read-only fixture/canary configuration. Its
packages/maintainer-bot allowlist is not a production capability boundary.
The production host builds effective edit scope as trusted
production-policy.json allowlist intersected with the write-authorized
Issue revision's exact targetPaths, minus protected/manual-only paths.
config.example.json contains an explicit model-pricing snapshot solely for
cost-limit arithmetic. Operators must refresh those configured rates from the
current provider contract; the engine does not claim they remain current.
gh-aw contract and actual v1 host
config/gh-aw-adapter.example.json
is a repository-owned future custom-engine contract, not an invented gh-aw workflow
schema. It keeps trigger/permission assertions, credential isolation, engine
command, immutable pin policy, host revalidation fields, and prohibited
actions auditable and replaceable. The example deliberately contains
REQUIRED_IMMUTABLE_COMMIT_SHA; ghAwAdapterDefinitionSchema rejects it until
an operator replaces it with a reviewed 40-character commit SHA.
Activation v1 does not depend on an unverified gh-aw workflow schema. The
runnable path is the repository-owned GitHub Actions workflow plus
@open-multi-agent/maintainer-host; the adapter contract remains available for
a later host replacement.
Before a credential-holding host creates a Draft PR, it must call
revalidateDraftPrSafeOutput() with the exact repository root and command
runner that will be used for the host action. The public gate first rechecks
current agent-ready authorization, issue revision/base, manifest and proposal
hashes, path scope, validation set/results, policy/prompt versions, reviewer
approval of every authorized criterion, and the matching authoritative
DRAFT_PR_PROPOSAL_READY run record. It then checks the actual worktree: HEAD
must equal the proposal base SHA, the complete changed-path set must exactly
equal proposal.changedFiles, and every current regular file SHA-256 must equal
its reviewed afterHash. Extra untracked files, deletions, renames, protected
or out-of-scope changes, changed HEAD, or content drift fail closed. The gate
performs no network request. Draft PR creation remains a separate deterministic
host action, and no authorization here extends to Ready, approval, merge,
close, release, publish, tag, or deploy.
The adapter boundary and schemas are locally tested. This MVP does not deep fork gh-aw and has not been run end-to-end against a live gh-aw version.
Status and operations
The Issue's single BOT comment and Actions job summary use these public states:
STARTED: the label delivery is visible and deterministic checks are starting.RUNNING: fixed revision/base, permission, DoR, scope, duplicate, and setting-attestation checks passed; the isolated engine is running.NEEDS_CLARIFICATION: the strict Issue template or Definition of Ready is incomplete or conflicting.MANUAL_ONLY: policy classifies the work as architecture, security, permissions, privacy, license, CI/release, broad API/refactor, uncontrolled dependency, or otherwise human-only.NEEDS_HUMAN: stale/crashed state, drift, setting, conflicting branch/PR, or safety gate requires intervention.FAILED: infrastructure or engine failure produced no eligible Draft PR.DRAFT_PR_CREATED: exactly one open Draft PR exists; human review remains required.
To configure activation without exposing a value:
- Add or rotate an Actions repository secret named
DEEPSEEK_API_KEYunder Settings → Secrets and variables → Actions. Never put the value in an Issue, workflow, repository file, log, artifact, or comment. - Under Settings → Actions → General → Workflow permissions, enable the repository option allowing GitHub Actions to create pull requests. Do not enable or change it as a side effect of a code deployment.
- After an administrator verifies that setting, add a repository Actions
variable named
OMA_MAINTAINER_BOT_PR_CREATION_ENABLEDwith the exact valuetrue. The host checks this trusted non-secret attestation before model execution and again before writing; a missing, empty, or different value fails closed. Remove or change the variable before disabling the setting. - Keep the workflow's explicit permissions and branch protections under human review. The default workflow permission may remain read-only because this workflow declares its required scopes explicitly.
The REST endpoint that reads this repository setting requires repository
Administration: read, which workflow GITHUB_TOKEN cannot request. v1
therefore does not introduce an administration-capable token merely to inspect
the setting. The operator attestation is the minimum safe pre-model gate; the
actual Draft PR API remains the final capability check. See GitHub's
Actions permissions API.
At the implementation audit on 2026-08-11, the repository API reported
default_workflow_permissions: read and PR creation disabled. No setting or
secret was changed by implementation work. The explicit workflow permissions
are compatible with the read default, but disabled PR creation is a deliberate
activation blocker until a separately authorized operator step. Secret presence
and the attestation variable were not inspected or configured.
To disable the bot, disable the workflow in GitHub Actions for an immediate
operational stop, or merge a trusted policy change setting enabled to
false. The latter may still allow the trigger to publish a disabled terminal
status, but never runs the model or writer. For key rotation, replace the
Actions secret, verify no run is active, and revoke the old provider key; runs
never persist the key in state or artifacts.
GITHUB_TOKEN-authenticated writes do not recursively trigger most new
workflow runs. Therefore a Draft PR created by v1 should not be assumed to have
automatically started the repository's ordinary pull_request CI; the trusted
pre-PR validation registry is mandatory, and maintainers must inspect the PR's
actual checks. This recursion behavior is documented in GitHub's
GITHUB_TOKEN security reference.
Verification and first live canary
Unit/integration tests use a mocked GitHub client and scripted OMA adapters and
need no key. The synthetic #488-style path proves that
packages/create-oma-app/tests/runtime.test.ts becomes an exact one-file scope,
runs both OMA_MODEL=ambient-model and unset focused tests, and receives the
trusted create-oma-app lint/test/template checks. Default tests make no GitHub
write and no live provider call.
A first live canary is a separate, explicitly authorized operation:
- Confirm Issue #488 is open and
agent-readyis absent; inspect its material revision rather than reusing an old authorization. - Configure/verify the DeepSeek secret, repository PR-creation setting, and exact attestation variable.
- Verify the workflow exists on
main, its action/runtime pins and production policy are reviewed, and no conflicting BOT branch/PR/run exists. - Reapply
agent-readyonce with a write/maintain/admin actor and observe STARTED/RUNNING plus run URL, ID, revision, and base SHA. - Verify only
packages/create-oma-app/tests/runtime.test.tschanged, every registered validation passed without truncation, and exactly one Draft PR was created and linked. - Repeating the same authorization must reuse the terminal runKey/PR and must not create a second branch, push, or PR.
- Confirm no Ready, approval, merge, close, release, publish, tag, or deploy action occurred.
Do not perform these steps during ordinary local implementation or testing.