Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ The guild screen's API ([the guild](world.md), [spec](superpowers/specs/2026-10-

While the settings document turns the game layer off (`ui.game: false`, [turning the game off](world.md#turning-the-game-off)), the three routes below answer 404 with `{"detail": "The game layer is off: ui.game is false in the settings document."}`, so a client can tell it from a job that does not exist. The review routes' deprecated aliases under `/world/quests/{job_id}/…` are review, not the game, and answer either way.

- `GET /api/v1/world` (read): `characters` (with `state`: idle, travelling, working, waiting, returning, defeated, recalled or following; the `job_id` it is drawn at; `stats`; `idle_too_long`; `can_quest` and `quest_template`), `parties` (jobs out now and back in the last three minutes: `leader`, `party`, `working`, `lead_id`, `stuck`, `failure`; a defeated one also its `error_code` and `error_message`, the message as the job stored it, at most 2,000 characters; a waiting one held by a spent account its `account_wait`: `provider`, `reason`, `until`, `model`, `agent`, `chosen`), `quests` (the board: each proposal with `level` 1-126, `verb`, `creature`, `leader`; a proposal judged [slop](glossary.md#slop) is a `slop` lead: verb `Clean up`, creature `slop_ooze`, leader `slopmaster`), `dungeons` (one [Slop Dungeon](glossary.md#slop-dungeon) per repository and area with open slop or recent cleanups: `repo`, `area`, `score` 0-100, `open`, `cleaned` in the last 30 days, and `meter`, its [Slop-O-Meter](glossary.md#slop-o-meter) reading `{score, level, name, provisional, computed_at}` or `null`; an area at Spaghetti Bolognese or worse has one even with no open slop; worst first), `inbox` (finished quests a person sent, or that brought back a pull request, not yet judged, each with its `failure` and `error_code`), `loot`, `recent`, `regions`, `frontier`.
- `GET /api/v1/world` (read): `characters` (with `state`: idle, travelling, working, waiting, returning, defeated, recalled or following; the `job_id` it is drawn at; `stats`; `idle_too_long`; `station`, `can_quest` and `quest_template`, from its agent row's `station` and `quest_template`, where `quest_template` is `null` for a task that names no template), `parties` (jobs out now and back in the last three minutes: `leader`, `party`, `working`, `lead_id`, `stuck`, `failure`; a defeated one also its `error_code` and `error_message`, the message as the job stored it, at most 2,000 characters; a waiting one held by a spent account its `account_wait`: `provider`, `reason`, `until`, `model`, `agent`, `chosen`), `quests` (the board: each proposal with `level` 1-126, `verb`, `creature`, `leader`; a proposal judged [slop](glossary.md#slop) is a `slop` lead: verb `Clean up`, creature `slop_ooze`, leader `slopmaster`), `dungeons` (one [Slop Dungeon](glossary.md#slop-dungeon) per repository and area with open slop or recent cleanups: `repo`, `area`, `score` 0-100, `open`, `cleaned` in the last 30 days, and `meter`, its [Slop-O-Meter](glossary.md#slop-o-meter) reading `{score, level, name, provisional, computed_at}` or `null`; an area at Spaghetti Bolognese or worse has one even with no open slop; worst first), `inbox` (finished quests a person sent, or that brought back a pull request, not yet judged, each with its `failure` and `error_code`), `loot`, `recent`, `regions`, `frontier`.
- `GET /api/v1/lead-kinds` (read, any signed-in caller, the game on or off): `{"kinds": [...]}`, every kind of work on offer from the one lead-kind catalogue in `embry0/services/lead_kinds.py`, in its order, each with its plain names: `id`, `label` (one, as a badge says it), `plural` (several, as a filter says them), `about` (what it is) and `found_by` (what brings it in), and `lead`, true for a kind a finder, the scout or a knowledge map stores as a lead (`GET /api/v1/leads`). The dashboard names each kind from it (the quest board, the lead dialog, the map and the Decisions page's kind filter), and falls back to the kind's id when it is not served. The game's words for a kind (its verb, creature, leader and level) are not served here: each quest of `GET /world` carries its own. It changes only with a release: `Cache-Control: private, max-age=300`.
- `GET /api/v1/world/characters/{agent_type}` (read): the character, its `stats` and its `quest_log` (each job it ran in: outcome, pull request, its share of cost and time, `rating`, `verdict`).
- `GET /api/v1/world/quests/{job_id}` (read): the scroll: `report`, `failure`, `error_code`, `error_message`, `pr` (files, additions, deletions, from the forge when it answers in time), `merge_approval`, `verification`, `party`, `fallbacks` (the runs that ran on a [provider fallback](providers.md#provider-fallback) model: `agent_type`, `model`, `instead_of`, `provider`, `reason`, `until`, `runs`), `account_wait` (what a paused quest waits for, as on its party; `null` otherwise), `work_nature` (the slopometer's verdict on the job: `nature` bug, slop or noise, `slop_score`, `kinds`, `evidence`, and a Slopmaster cleanup's `cleanup` `{added, removed}`; `null` when unclassified), `xp` per character and skill (a slop cleanup's pull request is the Slopmaster's, and its merge adds `cleaned` experience), `cost_usd`, `seconds`, `evaluation`. A `failure` anywhere is the reason in words; an account failure (a spent usage window, a rate limit, a refused token) quotes the provider's own words when the job kept them (`the LLM account it ran on refused it: weekly limit · resets ...`).
Expand Down Expand Up @@ -321,6 +321,15 @@ Agent definitions: the model, tools, skills and prompt each agent type runs with

The roster, the palette and the builtins' own values are in [`docs/contracts/identity.json`](contracts/identity.json), written from `embry0/agents/identity.py`; an id outside them is a 422 that lists the allowed ones. These columns are not nullable, so a `PUT` that sends one as `null` is a 422. `POST /agents/{type}/reset` restores a builtin's identity with the rest of the row.

**In the guild.** Two columns say how the [guild](world.md) treats the agent (#167), which `POST` accepts, `PUT` changes, both reads return, and the MCP tools `agent_create` and `agent_update` take; the agent form edits them under *Guild*:

| Field | Default | Rule |
|---|---|---|
| `station` | `desk` | Where the guild hall draws it when it is home: one of the `stations` in [`docs/contracts/identity.json`](contracts/identity.json) (`desk`, `anvil`, `lectern`, `alchemy_table`, `map_table`); not nullable |
| `quest_template` | `null` | The task the guild can give it: `null` gives none (it works only inside another job), `""` a task that names no template (triage picks the route; only a coding agent's, `developer` or `slopmaster`, since that route's coding step is theirs), a name a task that runs that template; at most 100 characters |

A value outside these is a 422 naming the field, and so is a `quest_template` that names no pipeline template (`quest_template: no pipeline template named '...'`), or `""` for an agent that does not code. Only a value the write changes is checked: the form sends every field, and a stored task whose template was deleted since does not stop the agent's other fields from saving. A task whose template is deleted is no task: the guild stops offering it; renaming the template moves every task that names it, in the same transaction. The reads return `coding_agent`, read-only: whether the developer node runs the agent, so it can take a task that names no template. `PUT` with `station: null` is a 422; `quest_template: null` takes the task away, which MCP `agent_update` sends as `clear_quest_template=true`. Each builtin's values are its seed's, the same the guild drew by name before (`POST /agents/{type}/reset` puts them back): the developer at the anvil with a task naming no template, the reviewers and the interrogator at lecterns, QA and kintsugi at the alchemy table, research, analysis, ops, the area mapper and blast-radius at the map table, and every specialist, research, analysis, ops and the Slopmaster with a task that runs its own template.

**Definitions no job runs.** Each definition carries `runs_in_jobs`, read-only: false only for the brief writer's voices, `proposal-brief` and `job-brief` ([briefs](configuration.md#proposal-and-job-briefs)). Such a definition is no template step, no per-agent model of a job, and no character of the guild; its model is not read. `PUT` refuses its `system_prompt` with 422 when it defines one of the answer's keys (a quoted key and a colon, such as `"title": ...`): the format is embry0's. A voice may name JSON or quote a word.

**Tools at run time.** A definition whose `tools` is empty has no tools: the CLI offers it none and the hook allows none. Only a definition that names no tools at all (null, which the API never writes) runs with its agent type's default tools. A template step's `tools`, which an operator wrote, replaces the definition's. Triage's `pipeline_config.agent_tools` only narrows that list: a tool it lacks is not added and is logged as `triage_tools_not_granted`, and a list triage empties, or a value that is not a list, keeps only the read-only tools (`Read`, `Glob`, `Grep`) the list had, which may be none (`agent_tools_emptied`, `triage_tools_malformed`). `POST` and `PUT` answer an agent that jobs run with an empty `tools` with a warning in `warnings`: it runs with no tools and finishes with text only. Triage's `pipeline_config.system_prompts` is ignored and logged as `triage_system_prompt_ignored`, with the agent's name only.
Expand Down Expand Up @@ -483,7 +492,25 @@ The saved pipeline graphs the editor and the Pipelines page work on, under `/api
| Endpoint | Answer |
|---|---|
| `PUT /api/v1/pipelines/templates/{id}` | Updates the fields sent. `default_for_kind` set to a kind makes the template that kind's default route; an explicit `null` clears it. 409 when another template already holds the kind (clear it first), or when the new name is taken |
| `POST /api/v1/pipelines/templates/{id}/duplicate` | `{"name": "..."}` copies the graph and sandbox profile under the new name and answers 201. 409 when a template of that name already exists |
| `POST /api/v1/pipelines/templates/{id}/duplicate` | `{"name": "..."}` copies the graph, the sandbox profile and what the row says of its jobs (below) under the new name and answers 201; the copy is no kind's default. 409 when a template of that name already exists |

**What a template's row says of its jobs.** Five columns (#167) decide how embry0 treats a job of the template, where it once went by the built-in templates' names. `POST` accepts them, `PUT` changes the ones sent, and every read, the list included, returns them; on the Pipelines page a template's menu edits them under *Behaviour…*:

| Field | Default | What it does |
|---|---|---|
| `fixed_route` | `false` | A job that names the template skips triage: its route is fixed, and triage's classifier is not asked. Triage still runs when the job comes back to it with something to decide (a QA failure, a person's answers) |
| `offered_to_triage` | `true` | Triage's list of templates names it, and plan_route keeps triage's pick of it. `false` runs it only when a job names it: triage's pick of it is dropped and the kind's default routes |
| `job_kind` | `null` | An override of the kind (`code`, `research`, `analysis`, `ops`) a job of it is recorded as when it is no kind's default; `default_for_kind` wins. `null` reads the route as it is: a route whose every step but the output is a specialist is `analysis`, any other leaves triage's kind |
| `delivers` | `""` | What a job of it hands back, in the person's words, one line of at most 300 characters: the job's [brief](configuration.md#proposal-and-job-briefs) says it. `""` says what the kind hands back |
| `leader_agent` | `null` | The agent a job of it is drawn as in the guild before one starts. Must name an agent that runs in jobs. A job led by the context auditor is an audit |

A malformed value is a 422 naming the field; a `leader_agent` that names no agent a job runs is `leader_agent: no agent named '...' runs in jobs`, checked only when the write changes it. A template may not take the name of a pipeline the code defines (`qa`, `onboard`, `pr-review`), on create, rename or duplicate: a 422 naming `name`.

**Credit is recorded on the job.** A job that names a template takes its row's leader when it is created (`jobs.leader_agent`), and when plan_route plans the job it records that leader again with the agent its route's coding step runs as (`jobs.coding_agent`). Progression and the world credit the job from those: its pull request to the coder (the developer when it recorded none), a cleanup to the Slopmaster when it coded the job, so an edit or a deletion of a template row later changes no credit a job already earned. Migration 143 gave every older job what the template names gave it.

**Which jobs are context audits** is one rule (`embry0/storage/repositories/audit_jobs.py`): a job the context auditor led, and not a finding's fix job. Progression credits it as an audit and the findings feed offers its findings, so a copy of the context-audit template is an audit to both, and a context-audit job planned after an operator gave the template another leader is neither. The context audit's scheduler counts every such audit for its cadence, and also every job of its own template and every job it dispatched, whatever led them, so it never starts an audit again on every tick; the job it starts is always its own template's.

**When the templates cannot be read** (the database fails, not when there is none), nothing a row would allow is assumed: no route is fixed, so triage runs; triage is offered no template, and its pick of one is dropped for the kind's default; the guild offers no task. Only with no database at all do the built-in rows as seeded answer. On `PUT`, an explicit `null` clears `job_kind` and `leader_agent` and is a 422 for the other three. A template with `fixed_route` and a `qa` step is saved with a warning: only triage asks for QA, so that step never runs. The built-in templates' values reproduce what their names did: the ten specialist templates are fixed, not offered and analysis, each led by its specialist; `research-default`, `analysis-default` and `ops-default` are led by their agents, and `slop-cleanup` by the Slopmaster; `analysis-default`, `context-audit` and `map-areas` say what they deliver. The boot seed writes them on a template's first insert only, like `default_for_kind`, so what an operator changes stays across restarts. A job of a pipeline the code defines (`qa`, `onboard`, `pr-review`) has no row; what it delivers and who leads it is the code's (`embry0/workflows/row_behaviour.py`).

**A step that leaves its agent without a server.** A step's `tools` replace its agent's, and an MCP server that runs a command reaches only an agent whose tools name `Bash` or `Monitor` ([Agents](#agents), "MCP servers"). So `POST` and `PUT /api/v1/pipelines/templates`, when they save a graph, answer in `warnings` each step whose `tools` name neither while its agent has such a server: a job runs that step without it. A QA step's warning names the default browser backend's server, since a repository picks its own; with any backend the step runs without its browser. The template editor shows the warnings when it saves.

Expand Down
1 change: 1 addition & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -1389,6 +1389,7 @@ The whole pass is serialised across processes by a session-level Postgres adviso
| 140 | the API key that created a credential: `credentials.created_by_key_id`, so a credential nothing uses yet is the own of the key whose id it records, never of another key of the same name; NULL for one a session, the master key or the importer created, and for every credential created before it, none back-filled, so no restricted key owns one of those | `credentials.created_by_key_id` |
| 141 | the repository of a job posted with a git context alone: fills `jobs.repo` from `context->>'repo'` where `repo` is NULL and the context is git, so `GET /jobs?repo=`, the world view and the stats find it (`POST /jobs` stores it now); a row naming a repository and any other context are left alone, `updated_at` is kept, and a second run changes nothing | `jobs.repo` (data) |
| 142 | the skills triage may add to an agent: `agent_definitions.triage_may_add_skills`, a JSON array of `source:skill` names (NOT NULL, default `[]`); triage adds one only from a source the agent's skills already load. The built-in developer row gets `superpowers:brainstorming` and `superpowers:writing-plans`, the two the triage prompt always offered it, so an upgraded deployment keeps its behaviour; every other row, and a deployment without that row, gets none | `agent_definitions.triage_may_add_skills` |
| 143 | behaviour from agent and template rows, not built-in names (#167): `pipeline_templates.fixed_route` (NOT NULL, default false), `offered_to_triage` (NOT NULL, default true), `job_kind` (an override; NULL reads the route), `delivers` (NOT NULL, default `''`) and `leader_agent`; `agent_definitions.station` (NOT NULL, default `desk`) and `quest_template` (NULL none, `''` a task naming no template); `jobs.coding_agent` and `jobs.leader_agent`, which plan_route records and progression credits from. Every row gets what the code said of its name before: the ten specialist templates fixed and not offered, the leaders and deliverables of the old maps, the old stations and guild tasks; a slop-cleanup job coded by the Slopmaster and every job led by its template's old leader. A template named `qa`, `onboard` or `pr-review` is renamed `<name>-template` | `pipeline_templates`, `agent_definitions`, `jobs` |

Note: LangGraph's checkpoint tables (`checkpoints`, `checkpoint_blobs`, `checkpoint_writes`) are created and mutated by `langgraph-checkpoint-postgres` / `orchestration/checkpoint.py`, outside `embry0_migrations` entirely — they're excluded from this ledger by design, not an oversight.

Expand Down
2 changes: 2 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,8 @@ Budget controls, context injection, and sandbox profiles are configurable via th

- **Context audit**: `PATCH /api/v1/config/settings/context_audit?owner={owner}` with `{"days": N}` (1 to 365, `false` for off) audits the files that steer a coding agent in each of the owner's registered repositories on a schedule, and `GET /api/v1/orgs/{owner}/context-audit` reads what it sees. The interval, whether an audit opens a pull request and the files it reads are the `context_audit` section's, in the organization's or a repository's layer for their own; its two caps are the `budget` section's `context_audit_job_usd` and `context_audit_max_jobs_per_tick`, in the organization's layer for its own. See [context-audit.md](context-audit.md).

- **Agent and template behaviour** (#167): what embry0 does with an agent or a pipeline template is a column of its row, not a list of the built-in ones' names, so an operator's own take part the same way. A template's `fixed_route` (a job that names it skips triage), `offered_to_triage`, `job_kind`, `delivers` (what its jobs' briefs say they hand back) and `leader_agent` (who the guild draws its jobs as and credits their work to): `POST`/`PUT /api/v1/pipelines/templates` and *Behaviour…* in a template's menu on the Pipelines page. An agent's `station` and `quest_template` (the task the guild can give it): `POST`/`PUT /api/v1/agents`, the MCP tools `agent_create` and `agent_update`, and *Guild* on the agent form. Each value a write changes is checked, a 422 naming the field; the boot seed writes a built-in's on its first insert only, so a change survives a restart. A job records who coded it and who led its template when it is planned, and is credited from that, so editing a template later changes no past credit. The rules: [api.md](api.md#pipeline-templates) and [api.md](api.md#agents).

See [api.md](api.md) for request examples.

## Settings document
Expand Down
8 changes: 8 additions & 0 deletions docs/contracts/identity.json
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@
},
"default_avatar": "golem",
"default_color": "slate",
"default_station": "desk",
"display_name_max": 60,
"palette": [
"gold",
Expand All @@ -180,5 +181,12 @@
"sky",
"slate"
],
"stations": [
"desk",
"anvil",
"lectern",
"alchemy_table",
"map_table"
],
"tagline_max": 140
}
Loading
Loading