Adopting the agent-operated backlog
This page is part of the documentation for Orleans.Lattice 9.9.0 (release line 9.9), built 2026-10-04. It is also published as markdown, with every table and list, at README.md, and llms.txt lists every page.A copyable template for running an agent-operated backlog in your own
repository: a durable work queue held in repocontext memory, drained
concurrently by agent sessions under fenced claims, and mirrored to GitHub
issues so a human keeps oversight and control of admission.
This directory is the base. Your repository supplies a small override with its own values; nothing here is edited. Orleans.Lattice itself adopts the template this way, so the base is exercised continuously rather than kept in step by hand.
What is here
| File | Role |
|---|---|
backlog-protocol.md |
The data model: item schema, relation vocabulary, grouping model, ready-set computation, mirroring, entry gating. |
backlog-worker.base.md |
Generic worker behaviour: claim one item, do the work, complete or release. |
backlog-pm.base.md |
Generic project-manager behaviour: curate, decompose, mirror, deploy workers. |
bindings.example.md |
The override table to copy, and how to obtain each value. |
1. Prerequisites
The claim surface ships in the Orleans.Lattice.Api.Mcp.RepoContext package and
is exposed as MCP tools, not as a public C# API. Register it with writes
enabled, in workspace mode:
builder.Services.AddRepoContextTools(
enableWrites: true,
workspaceMode: true,
workspaceRoot: "/workspace");
Without enableWrites the mutating tools are not contributed at all, so
repocontext_claim, repocontext_renew_claim and repocontext_release_claim
will not appear in the tool list. That is the fail-closed gate working, not a
fault. repocontext_claim_status is read-only and is always contributed.
workspaceMode: true is what contributes repocontext_list_repos, which every
backlog agent reads its {repoId} from, and the workspaceRoot is what lets
repocontext_add_repo onboard a repository: without a root, neither onboarding
tool is contributed. The container host registers exactly this. Your repository
must also be indexed, so that repocontext_list_repos reports a repoId. See
the container quickstart.
Leave RepoContextTtlOptions.DefaultMemoryTtl unset (its default) for your
repository. When it is set, repocontext_remember applies it to every newly
created entry that omits ttlSeconds - backlog items included - and the
protocol forbids a TTL on an item.
2. Copy the template
Copy this directory into your repository. Keeping it at
samples/AgentBacklog/template/ means the relative links below resolve
unchanged; any other location works if you fix the paths in your override.
3. Write the two overrides
Create .github/agents/backlog-worker.agent.md and
.github/agents/backlog-pm.agent.md. Each needs only front matter, a pointer to
its base, the bindings table from bindings.example.md,
and any rules genuinely specific to your repository:
---
name: Backlog Worker
description: <one-line description used for agent discovery>
---
Your behaviour is defined in `samples/AgentBacklog/template/backlog-worker.base.md`
and the data model in `samples/AgentBacklog/template/backlog-protocol.md`.
Read both now, before you do anything else.
## Bindings
| Binding | Value |
|---------|-------|
| `{repoId}` | `my-repo` |
...
Orleans.Lattice's own overrides are working examples:
backlog-worker.agent.md and
backlog-pm.agent.md. Each is
roughly 90 to 135 lines, which is the whole point: that is the complete
deviation from the base.
4. Add the always-on memory rules
Two protocol rules bind every agent that touches memory, not only backlog
agents, because an agent auditing or tidying memory will not have read the
protocol. Put them in a file that always applies (for Copilot, an
.instructions.md with applyTo: "**"):
- The backlog relations must never be pruned.
blockedBy,anchoredTo,claims,integratesandinformsextend the knowledge-linking vocabulary. Tooling that audits memory must recognise them, or it will prune ablockedByedge and silently release work that was deliberately gated. - Never set a TTL on a backlog item. Expiry is silent and unlogged, so a
lapsed item that others declare
blockedBystarves its dependents invisibly. Retire items withforgetinstead.
Orleans.Lattice keeps these in
.github/instructions/repocontext.instructions.md,
section ## The agent-operated backlog, which is otherwise a pointer to this
template.
5. GitHub-side setup
No file copy can do these, and the backlog does not work without them.
Labels. Create two labels on the mirror repository:
gh label create needs-specification --description "Agent-proposed backlog item awaiting human admission"
gh label create stale --description "Parked backlog item; exceeded the attempt threshold"
needs-specification is the entry gate: an agent-authored item carries it
and is excluded from the ready set until a human removes it. Without the label
the gate silently passes and agents pick their own work. stale parks poison
items so a failing item does not burn a session per tick.
Issue types and sub-issues. Epics mirror as GitHub issues with native
sub-issues. No configuration is needed, but the PM agent uses
gh api repos/{owner}/{repo}/issues/<n>/sub_issues, which requires a token with
issue write scope.
CI on epic branches. An epic uses one shared <type>/epic/<slug> branch that
sub-item pull requests target. Extend your workflow triggers so those pull
requests are still gated:
on:
pull_request:
branches: [main, '*/epic/**']
Without the second pattern a pull request into an epic branch runs no checks
at all. Do not put branch protection on the epic branch itself, and especially
not a required check with strict (up to date before merging): that is exactly
what serialises pull requests and what the epic branch exists to avoid.
6. Run a worker
A worker is one agent session. Start it on a schedule, or deploy it from the PM agent. It computes the ready set itself and takes its own claim; a dispatch may narrow where it looks, but must never hand it a pre-selected item, because the interchangeability is what makes two contending workers resolve to exactly one proceeding.
Set the schedule's concurrency where workers are started. A worker cannot count its siblings: it has no reverse index, so it will exit cheaply when every candidate's blast radius overlaps in-flight work rather than widening its own overlap tolerance.
7. Verify
repocontext_list_reposreports your{repoId}.repocontext_claim_statuson a memory key that holds no record (repo/{repoId}/mem/<topic>/<id>) returnsexists: falserather than an error, confirming the read-only tool is contributed. A key that is malformed or names any other record family is refused, because claims exist only on memory records.repocontext_claimon a real memory key returnsgranted: truewith afencingToken, confirming writes are enabled.- An unfenced
repocontext_updateagainst that claimed key is refused. If it succeeds, the fence is not being enforced and the backlog is not safe to drain concurrently.
The walkthrough steps through exactly that sequence.
See also
- The agent-operated backlog - the design rationale
- Tools - the claim surface reference
- Agent backlog walkthrough - the claim and fence, observed end to end