Two teams' agents can change the same code without knowing about each other and collide at merge. The work registry gives agents and people a live picture of who is working on what, per repository, and tells both sides when their work overlaps.
The registry is advisory only. Nothing in it ever blocks work. It holds metadata only: ids, repository and branch names, paths, symbols and service names. It never holds code, diffs, prompts or file content.
The agent host of the first-party apps registers and updates items for every governed session; humans see the same list in their apps. There is no console page yet.
Work items
A work item is one unit of agent or human work: owner (and the agent id and app, for an agent), team, repository, branch, base commit, a short intent label, areas, status, times, an optional work item URL and an optional parent item.
| Status | Meaning |
|---|---|
declared | Registered; nothing touched yet |
active | The first heartbeat, or the first observed areas, moves it here |
paused | Still takes part in overlaps; lives longer without a heartbeat |
finished | Closed cleanly |
abandoned | No heartbeat within its time to live |
An item without a heartbeat for KEYSTONE_CONTEXT_WORK_TTL (15 minutes; 24 hours when paused) is abandoned for every reader at once. Liveness uses the database clock, so client clock skew changes nothing. An abandoned item is not revived: a late heartbeat or update answers 409 context.work.item_abandoned, and the host declares a new item with the old one as parent.
Areas
kind | value |
|---|---|
path | A repository-relative file path; a trailing / makes it a directory |
glob | A path pattern with *, ? and ** (at most 3 ** and 8 wildcards) |
symbol | A qualified symbol of the code index, for example github.com/acme/api/auth.Login |
service | A lower-case service name; the only way two repositories overlap |
Declared areas come from the agent; observed areas are the paths of the files the session touched.
Overlaps
Overlaps are recomputed whenever an item changes, between live items of the same repository and, by service name, across repositories.
| Level | Meaning |
|---|---|
same_path | The same file |
same_symbol | The same symbol, through the code index's symbol graph |
dependent | A caller, callee or test of a symbol the other touches |
same_area | The same directory, a glob over the other's directory, or the same service |
- Symbol levels need a code index of the repository and work for Go only; other repositories get the path, directory and service levels.
- Items of one person overlap with each other, except along a parent link (an agent tree does not warn itself).
- An overlap ends when its shared areas are gone, or when either item finishes or is abandoned.
- Each side can acknowledge an overlap. The first acknowledgement by a person is an audit entry naming the person, the overlap and both owners, so a regulated organisation can show that a conflict was surfaced and who saw it. Acknowledging needs a person; an API key gets
403 context.person_required.
Suggestions in an overlap are coordinate, sequence, handoff and pair_session. An organisation that wants acknowledgement before merge enforces that in its CI merge gate; the registry itself never blocks.
Privacy
- Sensitive files are never recorded. Environment, secret, key and credential files (
.env*,*.env,.envrc,secrets.*,credentials*,.netrc,.npmrc,.pypirc,.git-credentials,kubeconfig,*.tfvars,*.tfstate,*.pem,*.key,*.p12,*.pfx,*.jks,*.keystore,id_rsa,id_ed25519,*.ppk,.aws/credentials,.azure,service-account*.json,.git/**and a few more) are dropped at the server edge, after folding case, separators, dot segments, Unicode look-alikes, zero-width characters, percent-encoding and NTFS stream suffixes. The request still succeeds and the item reports onlysensitiveAreasDropped. Under the policy valuehash_onlysuch a path is kept as a keyed hash only, so two people on the same secret file still hear of each other. - Keyed hashes. Every area is stored with an HMAC-SHA256 under a per-organisation key wrapped by the organisation's KMS key, and overlaps are computed on the hashes.
- Restricted other side. A person who may not see the other item still gets the warning, with
restricted: true, the level and the number of shared areas only: no item id, person or path. - The intent label is a single line of at most 120 characters, scanned with the organisation's DLP patterns on every write. A finding refuses it (
422 context.work.intent_sensitive) without echoing the text.
Who sees an item is decided by the collaboration policy: work_registry (on or off), work_registry_visibility (own team, department or organisation) and work_registry_sensitive_paths. Visibility never widens the space read model: a person who may not read the repository's Context space does not see another person's item. A policy error answers 503 context.work.policy_unavailable.
Routes
| Method | Path | What |
|---|---|---|
POST | /api/v1/context/work/overlap/check | Ask before starting: the overlaps described work would have; registers nothing |
POST | /api/v1/context/work/items | Declare an item (201; 200 when idempotencyKey repeats) |
GET | /api/v1/context/work/items | Items the caller may see: repo, branch, team, status, mine=true, limit |
GET | /api/v1/context/work/items/{id} | One item |
PATCH | /api/v1/context/work/items/{id} | Change intent, branch, URL, status and areas (owner only) |
POST | /api/v1/context/work/items/{id}/heartbeat | Keep alive: {seq?} |
POST | /api/v1/context/work/items/{id}/finish | Close: {seq?, reason?} |
GET | /api/v1/context/work/overlaps | Overlaps of the caller's items: status (open, ended, all) |
POST | /api/v1/context/work/overlaps/{id}/ack | Acknowledge |
Agent host sequence
POST /overlap/checkbefore editing.POST /itemsto declare, with anidempotencyKey.POST /items/{id}/heartbeateveryheartbeatSeconds(a third of the time to live), with a growingseq.PATCH /items/{id}with the full list of touched files asobservedAreas.POST /items/{id}/finishat the end.- Listen to the real-time feed for
work.item.*andoverlap.*.
A request whose seq is not above the item's last applied one changes nothing and answers 200 with applied: false.
Declare example
{
"repo": "https://github.com/acme/api.git",
"branch": "feature/login",
"intent": "rework the login flow",
"agent": {"id": "builder-1", "app": "editor"},
"workItemUrl": "https://github.com/acme/api/pull/7",
"declaredAreas": [
{"kind": "path", "value": "internal/auth/login.go"},
{"kind": "service", "value": "payments"}
],
"idempotencyKey": "sess-01J8-declare"
}
Audit
Declare, update, finish, abandonment, every overlap opened, updated and ended, every check that showed an advisory, every acknowledgement and the retention deletion are audit entries with ids, repository, levels and counts, never paths or intents. Heartbeats are not audited. Finished and abandoned items are kept for KEYSTONE_CONTEXT_WORK_RETENTION_DAYS (default 400), never less than the organisation's audit hot window.
Limits
| Variable | Default |
|---|---|
KEYSTONE_CONTEXT_WORK_TTL | 15m |
KEYSTONE_CONTEXT_WORK_PAUSED_TTL | 24h |
KEYSTONE_CONTEXT_WORK_MAX_ITEMS_PER_USER | 25 live items (429 context.work.too_many_items) |
KEYSTONE_CONTEXT_WORK_MAX_ITEMS_PER_ORG | 5000 (429 context.work.org_cap) |
KEYSTONE_CONTEXT_WORK_MAX_AREAS | 200 per list |
KEYSTONE_CONTEXT_WORK_CHECKS_PER_MINUTE | 60 overlap checks per person (429 context.work.check_rate_limited) |
KEYSTONE_CONTEXT_WORK_RETENTION_DAYS | 400 |
KEYSTONE_CONTEXT_WORK_RECONCILE | @every 1m (off disables the abandonment job) |
An invalid value turns the registry's routes into 503 context.work.unavailable and nothing else; a missing KMS answers 503 context.work.keys_unavailable.