System
A control layer between agent intent and consequential execution
Caretaker separates the decision to act from the authority to execute. Policy determines whether an action is consequential; a named person authorizes the exact proposed change; the decision is signed and recorded; execution is compared with the approved artifact.
Five stages: gate, approve, attest, execute, reconcile. Non-consequential actions bypass approval and proceed to constrained, recorded execution. Audit and provenance run beneath every stage.
The kernel is connector-agnostic. Everything below describes how it is implemented for the current connector, which operates on repositories hosted on GitHub.
Stages
What each stage specifies, and what is built today.
Gate
- Design objective
- Decide, before execution, whether a proposed action is consequential — without routing every operation to a human.
- Current implementation
- Each project carries its own path rules. A proposed change is classified by blast radius as infrastructure, functional, or data, using file-path and diff heuristics against those rules, with a model classification as a second opinion; on disagreement the stricter class wins. A separate, stronger tier refuses outright: a change touching a denied path is checked immediately after diagnosis produces its file list, before any commit, test run, or pull request.
- Verification
- Exercised live against a real repository: a genuine one-character bug in a denied auth module produced a correct diagnosis, which the denial check refused before any branch existed. Confirmed through the provider's API that no branch or pull request was ever created.
- Present limitation
- The denial model is a small set of path rules per project, not a rule language. Blast-radius-size and dependency-version-bump rules are deliberately deferred. Denial policies are drafted at onboarding and require human review, but two projects onboarded before that gate existed still carry empty denial policies and need a human to fill them in.
Approve
- Design objective
- Bind authorization to one exact proposed change, granted by a named person, usable once.
- Current implementation
- A consequential change routes to the project's owner with a link carrying a 32-byte nonce. Validation and consumption are a single atomic statement, with expiry in the same predicate. Read-only preview and consumption are separate call paths, so a mail client prefetching the link cannot burn a valid approval.
- Verification
- A 25-way concurrent stampede against one token yields exactly one winner. Mutation-checked: restoring the previous two-statement form fails three tests.
- Present limitation
- The approver identity is a string the caller supplies, not an identity cryptographically bound to a person. There is no SSO or OIDC binding, and the role model distinguishes only membership from non-membership.
Attest
- Design objective
- Record the proposal, policy decision, approval, and execution as a chain that cannot be edited without breaking a signature.
- Current implementation
- DSSE envelopes wrapping in-toto Statements, signed with Ed25519. Four keypairs, one per role. Verification checks signatures, role separation, the hash chain, and cross-references between proposal, policy, approval, and execution. Denied proposals get their own signed chain, terminating at the denial.
- Verification
- Re-run fresh on every read of an incident's ledger rather than cached, so a tampered row shows as broken the moment anyone looks. A live deny-path run produced a two-event chain that independently re-verified.
- Present limitation
- Tamper-evident, not independently verifiable: the rows and the trust map live in the same database, so the chain being wholesale replaced by a key holder is outside what verification can detect. No transparency-log anchoring or timestamping.
Execute
- Design objective
- Apply only the approved artifact, and never treat an interface gesture as authorization.
- Current implementation
- Approval merges the pull request using the project's own stored credential. Approval signing happens before the merge and blocks it on failure. Execution signing happens after the merge and records a signing failure as its own audit event, because the merge has already happened and cannot fail closed.
- Verification
- Authorization paths are covered by tests that assert security properties rather than behaviour — every organization-scoped read carries its scoping predicate, and approval and escalation reject each link of the project–incident–fix chain with no side effect: no merge, no status change, no decision row, no ledger entry.
- Present limitation
- There is no automated production deployment step. Merge is where the current connector's authority ends; deploying the merged change is a separate, manual process outside this loop.
Reconcile
- Design objective
- Compare what executed against what was approved, using evidence the executing system did not produce.
- Current implementation
- The proposed change is content-addressed over the changed files' blob SHAs at commit time. At merge time the diff is recomputed from the provider's live pull-request files API and compared. A mismatch hard-blocks the merge and is recorded as an escalation.
- Verification
- Verified against a real pull request — after the first run failed on the honest path and exposed a defect in our own hashing.
- Present limitation
- Scoped to one boundary. It cannot observe changes applied through any other route, and it cannot detect an execution that never had a proposal.
Cross-cutting substrates
Audit and provenance are not stages.
Audit
The signed attestation chain covers the proposal-to-execution loop and is append-only and hash-linked. Broader incident history — detection, diagnosis, tests, decisions, outcomes — is reconstructed relationally from incident, fix, and decision records rather than from a single global event log. Both are real; they are not the same thing, and we describe them separately.
Provenance
Every distinct piece of context a diagnosis received is recorded with a SHA-256 hash, in two trust classes. Quarantined inputs record both the raw untrusted input and the canonical extraction, so the extraction step itself is checkable. Direct inputs record a hash so a stored manifest can later be compared against live content to detect substitution after the fact.
Threat assumptions
What the model assumes, stated as assumptions.
| We assume | We do not assume |
|---|---|
| An attacker can get arbitrary text printed during a CI run. | That the attacker can modify tracked source, the intent document, or incident metadata. |
| The agent can write to the branch it proposed. | That the agent controls the hosting provider's API responses. |
| A model may alter its work product rather than refuse or escalate. | That the model is adversarial toward the operator in general. |
| Anyone who can set environment variables on the deployment can reach any secret it holds. | That key custody is enforced by anything stronger than configuration today. |
Current connector
Repositories hosted on GitHub.
A project is registered by supplying a repository URL and a credential. The system clones the repository once to verify access, reads its README, manifest, and tracked file list, and drafts an intent document together with sensitive, infrastructure, and denied path rules. Nothing is watched until a human reviews and confirms that draft.
Watching runs in one of two modes: polling a health endpoint, or polling the repository’s CI run status and turning a newly completed failed run into an incident. On an incident, the agent clones with per-project credentials, detects the project’s own package manager and test scripts rather than assuming a toolchain, reproduces the failure, diagnoses it, writes a fix on its own branch, and opens a pull request. Fix pull requests must add a test.
Verification record
What has actually been run.
| Date | What was run | Result |
|---|---|---|
| 16 July 2026 | Planted prompt injection inside a real CI log, processed through the real pipeline | Reader extracted only genuine technical facts; the diagnosis made no reference to the injected instructions. |
| 16 July 2026 | Diff recomputation against a real pull request | Failed on the honest path, exposing a blob-SHA abbreviation defect; matched exactly after the fix. |
| 17 July 2026 | Key-separation test, bypassing the code-level role guard | Decrypting the approver key with the agent secret fails authenticated decryption; the correct key still succeeds. |
| 27 July 2026 | Denied-path incident against the live database and a real repository | Incident reached blocked status with a deny decision, no fix rows, a two-event attestation chain, and no branch or pull request on the target. |
| 5 August 2026 | Concurrent approval-token stampede | Exactly one winner across 25 concurrent consumers; mutation-checked against the previous form. |