# Capabilities Everything cat-factory does, one entry per capability, grouped by what it is for. This is the long-form catalog behind the short tour in the [root README](../README.md#what-you-get). Two other places answer the follow-up questions: - **How do I use it?** The product documentation on [catfactory.ai](https://www.catfactory.ai/). - **How is it built?** The [feature guide](./README.md#feature-guide), which pairs each area with its website page and its design doc here. ## Contents - [Planning on the board](#planning-on-the-board) - [Pipelines and execution](#pipelines-and-execution) - [Review, verification and merge](#review-verification-and-merge) - [Bugs and test environments](#bugs-and-test-environments) - [Integrations and the public API](#integrations-and-the-public-api) - [Extending a deployment](#extending-a-deployment) - [People, access and spend](#people-access-and-spend) - [Operating and observability](#operating-and-observability) ## Planning on the board Where work is laid out, and how a repository becomes a board. ### Visual architecture boards A pannable/zoomable canvas of frames (services) with dependency edges, drag-drop reparenting, and semantic level-of-detail. ### Task swimlanes Inside each service, tasks sit in status lanes the board DERIVES rather than places: not started, in progress, needs you, plus a collapsed Done lane capped per workspace by count and by age. Each lane orders itself by what is actionable for it (what can start now / what has gone quiet / what has waited longest), overridable per person along with grouping by module, task type, initiative, epic or what the task needs. ### Queue view The board's second view: every task across every service, in the column that says what it needs (**Needs you**, **In flight**, **Ready to merge**). **In flight** also lists the tasks nobody has started or that wait on dependencies, under **Not started**. Done is a count under Ready to merge, inside the Done lane's retention window, with a show/hide toggle. A card carries the action its state implies when it has one: Answer or Review on a parked task, Start on an unstarted one, **Read outcome** and Merge on a pull request waiting to land (Merge is the secondary button when the outcome is readable). Running tasks and tasks waiting on dependencies carry no action. It reads the same verdict as the swimlanes, so a task never shows one state in the queue and another on the canvas. The app opens on the view a person used last in that browser; someone who has not chosen lands on the queue. The canvas's decision counter opens it. ### In-repo service spec A `spec-writer` agent maintains the service's PRESCRIPTIVE specification **in the repo** (`spec/`: sharded requirements with Given/When/Then acceptance criteria, rendered to Gherkin `.feature` files), reviewed as an ordinary PR diff. Each requirement carries an **implementation state**: `aspirational` (agreed, not yet built) or `established` (a tester actually observed it hold). Only `established` requirements read as standing behaviour in a build prompt, so an agreed-but-unbuilt requirement is never implemented by accident nor reported as a regression, and a tester's first observed pass PROMOTES it automatically, with no human bookkeeping. The board's Requirements window shows that state per requirement, with a per-group rollup and a state filter, and a tester step renders its per-requirement verdicts in-app. Because the state is recorded, the PR verification report can tell a **regression** (an `established` requirement now failing) from work simply not finished yet. See [`docs/initiatives/service-acceptance-criteria.md`](./initiatives/service-acceptance-criteria.md). ### Service blueprints A Blueprinter agent decomposes a repo into a `service → modules → features` map stored **in the repo** (`blueprints/`) and reconciles it onto the board. ### Repo bootstrap Adapt a reference architecture (or scaffold from scratch) into a pre-created empty repo and force-push the result, materialising a new service frame on the board. ### On-demand board scan Decompose an existing repo into a board structure / reusable blueprint anchored to file references. ### In-app assistant Type what you want done and the platform does it: **declare that one service depends on another** (so both are spun up when either is tested), **add a service from a GitHub or GitLab repository URL**, or **file a board task from an issue URL** (GitHub, GitLab, Jira, Linear). An inline model only ROUTES the sentence: it names one action from a closed catalog and copies its arguments out of what you wrote, and the platform then resolves every name against your own board and performs the action through the same service the equivalent button calls. So a name that matches nothing, or matches two services, comes back as a question with the candidates attached rather than as a guess, and nothing the model writes reaches your screen. Answers to the workspace model budget like every other billable call. See [`backend/docs/in-app-assistant.md`](../backend/docs/in-app-assistant.md). ## Pipelines and execution How a task becomes agent work, and what the engine does around each step. ### Agent pipelines Reusable, ordered chains of agent steps (architect → coder → blueprints → reviewer → tester → acceptance, plus mocker/playwright/deployer/custom kinds) applied per block. Builds start from a five-rung **ladder** varying the one thing that actually differs between tasks: how much design the work needs: **Standard build** (the default: design, challenge the design, implement, review, test, then gate and merge), **Simple build** for trivial work whose approach is not in question, **Adaptive build**, which sizes the task up itself and switches the design, testing and human-PR-review steps on only when the work warrants them, and **Complex build**, which settles the requirements with you and researches the problem before any of that, for work whose scope is the risky part. A fifth rung, **Unattended delivery**, is the default for a run nobody is watching (started over the API, dispatched from a ticket, fired by a schedule): it holds no requirements interview, and instead reaches a human test and a human PR review only on a task whose own estimate says the risk earns them. New tasks default to Standard build in the basic interface and Adaptive build in the advanced one, and a workspace can name its own default per scope: one for what the board starts, one for what nobody is watching. Steps can also be **conditional on what the change touches**, not just on how big it is: every rung carries both testers and runs the browser pass on a frontend service and the API pass on anything else, so one preset covers what used to need a separate frontend preset. The pickers and the builder mark which steps are conditional, so a pipeline never silently promises a step it may skip. Each carries a description (the pickers preview a pipeline's steps + description before you choose it) and a use-case **purpose** (`build`/`document`/`review`/`research`/`planning`) that scopes it to matching tasks: a document task offers only document pipelines (defaulting to the doc-writing one), a review task only the PR-review pipeline, and a feature/bug task is not offered the document, review or planning presets, and narrows the builder's agent palette as well as the saved-pipeline library the builder browses (relaxable there without touching what gets saved). Every pipeline declares one, so nothing sits unclassified in a picker it should have been offered by. The board's copy of the built-in library is kept current explicitly rather than silently: newly shipped pipelines are offered for adding, an improved one is adopted on request (keeping your labels and archive state), and one that has been withdrawn from the catalog is flagged as retired with its replacement named, so an obsolete pipeline can actually be removed instead of lingering in every board forever. ### Estimate-gated steps A pipeline step can declare the task estimate it is worth paying for, and the engine transparently **skips** it below that bar, so one pipeline covers the range that used to need several near-identical presets. It applies to any step whose result later steps read as context (a design, a review, an extra verification pass, a human PR review), never to one whose absence would break the run: the merge guards, the environment provisioner and the merge itself are always unconditional. A skipped design step takes its review companion with it rather than leaving it grading the wrong artifact. The estimate may **add** a human checkpoint but never cancel an approval pause a pipeline author asked for, so a model's own triage can never decide that nobody needs to look. The estimate itself has two producers, and the record says which one it came from: the **Task Estimator** FORECASTS it before any design work, and the optional **Task Reassessor** MEASURES the same three axes afterwards from the change that actually landed, either correcting that forecast (keeping it beside the new scores) or producing the ratings a preset with no estimator never had. Either satisfies a gate's "an estimate exists before this step" requirement. ### Configurable step gates A pipeline's human checkpoints carry a policy, not just an on/off flag: **who** may clear one (workspace roles, named people, or anyone entitled to write) and **how many of them** must, counted across distinct people so nobody clears a two-person gate by clicking twice. The bar and the approver set are pinned when the gate is raised, so editing the pipeline cannot move a bar under the people already counted toward it, and the same policy governs requesting changes and rejecting, not only approving: a checkpoint the wrong person can reject is not a checkpoint. The same per-step seam carries a **gate's own settings** (a CI gate's fixer attempts on this pipeline, a release watch window, a review grace period), which each gate DECLARES: a deployment registering its own gate gets an authoring form from that declaration alone, and what the builder can save is exactly what a run will accept. See [ADR 0038](../backend/docs/adr/0038-per-step-gate-config.md). ### Editable agent prompts Rewrite what any agent is told to do, per workspace, from the pipeline builder where its step is chosen. Every save appends a **version** rather than replacing the last one, so the whole history is there to compare against and switch back through, and "back to the built-in" is itself a recorded version that keeps tracking the shipped prompt as it improves rather than pinning a stale copy of it. The platform's own rules (the read-only guardrail, the answer-in-your-reply rule, the service's best-practice standards) are still appended on top and cannot be edited away. ### Durable, observable execution Runs are driven by a durable, checkpointed driver (Cloudflare Workflows on the Worker, pg-boss on Node) and stream live step/subtask progress, decision prompts, and failures to the board over WebSockets. ### Real code changes via PRs Coding agents (`coder`, `mocker`, `playwright`) run in a per-run container, clone the repo, implement, and open a PR; merge flips the block to done. ### Pre-PR validation A service declares check commands (install/lint/test/build): by hand, or autodetected from the repo's own manifests, scripts and tool configs. After the coder finishes, the container runs them against the checkout **before** any PR is opened; a failure goes back to the agent with the captured output and it retries under an attempt budget. Only a green checkout opens a PR: a spent budget fails the step with the output attached and opens nothing. See [`docs/initiatives/pre-pr-validation.md`](./initiatives/pre-pr-validation.md). ### Dependency prepopulation A service declares an install command (by hand, or autodetected alongside its validation checks) and the container runs it against the checkout **before the agent's first turn**, so agents read real installed packages instead of inferring what a library can do from a manifest entry. Applies to every agent that gets a checkout, reviewers and architects included, not only the ones that open a PR. Never a gate: a failed install is reported to the agent (which may install what it needs itself) and the run continues. See [`docs/initiatives/agent-dependency-prepopulation.md`](./initiatives/agent-dependency-prepopulation.md). ### Pre-dispatch input gate A task states nothing an agent could act on, an empty or placeholder-only description, a bug with no reproduction context, a review task naming no pull request, and the run parks **before its first agent step**, having spent nothing. The check is a deterministic read of the task's own fields, so it costs no model call where the requirements reviewer previously spent one to report the same absence. Fix the task and re-check (the fix is verified, not asserted) or run anyway; a workspace can turn it down to advisory or off. See [`docs/initiatives/pre-dispatch-input-gate.md`](./initiatives/pre-dispatch-input-gate.md). ### Prompt-fragment library A tenant-scoped, versioned catalog of best-practice guidelines (built-in ∪ account ∪ workspace), optionally sourced from a repo, selected per run. A long guideline is condensed automatically for the coding agents that re-read their whole prompt every turn (link your own short version to use that instead) and re-condensed whenever the guideline itself changes. ### Model picker Per-block model selection; each model runs on Cloudflare Workers AI by default and upgrades to its direct provider API when a key is set, or to **AWS Bedrock** for the models your account's `BEDROCK_MODELS` allow-list grants: which is what lets a single task be pinned to a residency-guaranteed route (and lets the account model policy's `trustedProviders` exemption be used per task) instead of repointing the whole deployment. The catalog declares each Bedrock model by its UNPREFIXED id and matches your Region's geo/global inference profile onto it, so one catalog is correct in every Region. ## Review, verification and merge How finished work is judged, explained and merged. ### Requirements review A stateless reviewer agent raises gaps/clarifications/assumptions/risks on a block; a human answers each, then the agent folds the answers back into the description. ### Deep PR review A `review` task runs a token-bounded reviewer over an open PR (slicing a huge diff into cohesive chunks) and surfaces prioritized findings. A human triages them in the dedicated window: **select** which matter, **dismiss** the noise, or **challenge** any one (with an optional concern) to dispatch a Challenge Investigator that re-examines it against the full source and either strengthens or retracts it: then resolves the review by finishing, feeding a fixer, or posting the findings as inline PR comments. Each slice's review is persisted the moment its subagent reports rather than at the finish line, so a review that dies, or wedges in its final aggregation pass, which looks identical to a wedge from outside, can be **resumed** for only the slices that never came back, folding the finished ones' findings into the new aggregate instead of re-reviewing 20 minutes of work. The target PR is checked against the service's repository as the task is created: a number that does not exist there, or a link belonging to another repository, is refused with a reason rather than becoming a run with nothing to review, and the task's inspector links the pull request it reviews. See [`backend/docs/adr/0023-pr-deep-review.md`](../backend/docs/adr/0023-pr-deep-review.md). ### Rubric judges A deployment registers its own **judge** step: an evaluator that scores a run's work against a rubric (scope adherence, house engineering standards, doc completeness) and, below the task's threshold, **sends the work back** to the producing agent with the findings as its rework brief, asks a human, or stops the run. Adding one is a registry entry, not a fork: the engine owns the whole loop, the rubric's per-workspace override is an ordinary prompt-library fragment, and the verdict lands on the PR verification report. See [`docs/initiatives/judge-registry.md`](./initiatives/judge-registry.md). ### Consensus review panels A review is a judgement, and a panel of independent models judges better than one. Any review step (the code reviewer, the deep PR reviewer, the document/design/spec companions) can be run as a multi-model **consensus** panel (independent drafts then neutral synthesis, a debate, or ranked voting) instead of a single agent. Because a panel costs several model calls, a workspace keeps a library of reusable **consensus groups**, each carrying the task-estimate bar it is worth paying for; a step names a SET of groups and the engine runs the most demanding one the task's estimate clears, falling back to the standard single agent when none does. So "a two-model review above 0.4 risk, the full five-model panel above 0.8" is one step rather than three conditional pipelines. The transcript records which panel fired. Requires the opt-in `@cat-factory/consensus` package (`CONSENSUS_ENABLED`). ### Agent-written PR briefings A pipeline-opened pull request is described by the agent that did the work, not by text composed before it started. A PR-opening coding agent ends its run by writing a reviewer briefing (the problem, the decisions it made and the alternatives it rejected, what to look out for) to a sentinel file the harness lifts onto the PR it opens (an optional leading heading sets the title, and a resumed run refreshes the PR it already opened). The briefing is secret-scrubbed, size-capped so the engine's verification report still fits in the same body, and made inert for the host, so an agent writing "closes #42" cannot close issue 42 on merge. When the target repository ships a pull-request template, the briefing IS that template, filled in: the harness finds it in the checkout (both GitHub's and GitLab's conventions) and the agent that did the work answers its sections, because neither host applies a template to a pull request opened through its API. When no briefing is written the PR falls back to what the pipeline knew at dispatch (the task and the human-chosen implementation approach) and says so explicitly. ### PR verification report The ENGINE (not the agent) maintains a structured verification report on each run's PR: the `ci` gate's aggregated check runs + fixer attempts, the tester's structured report, a **requirement → evidence** table pairing every requirement in the service's in-repo `spec/` with what the tester actually observed (`met` / `not met` / `not checked`, so a verified behaviour never looks like one nobody checked, and a failure against a requirement the service was OBSERVED to honour is counted and flagged as a **regression** rather than reading like unfinished work), the platform's OWN run of the service's pre-PR check commands against the exact tree that was pushed (each command's exit code, and the captured log of whatever failed; this is the one verdict here the platform ENFORCED, as opposed to the host's later opinion in CI), a **bugfix reproduction proof** (the declared reproducing check run against the pre-fix tree AND the finished one, with both captured logs, so failing-then-passing is shown rather than claimed; or, when the bug genuinely cannot be reproduced in a test, the agent's structural declaration with its reason and what it verified instead, which is never the same thing as nobody having tried), a **test environment lifecycle proof** (the ephemeral environment came up at a recorded time, the tester's observations and captured screenshots are either attributed to it or explicitly are not, each screenshot linked DIRECTLY to its bytes on the deployment's own authenticated endpoint rather than listed as an opaque id, and it was torn down again AND an independent probe afterwards found it actually gone, which is a different fact from the provider having accepted the destroy call and is reported separately when only the first one holds; the verdict over those three legs is COMPUTED by the platform with every missing leg named, never an agent's claim that it tested against a preview), the merger's scored assessment, **what the run built FROM** (each linked page its agents read with the revision the dispatch confirmed it at, flagging any that CHANGED while the run was in flight, so an implementation that misread a design and one that faithfully implemented a revision the designer has since moved past stop reading the same), run metadata (linked tracker issues, pipeline, per-step agent kind + model), a deep link into the run's observability panel for a person, and, for a machine, direct links to the run's **auditable tool-call trajectory** (what its agents actually did, in order) and to this same report served live as JSON over `/api/v1`, so a reviewer checking a claim in the report does not have to know the debug API exists to find the record behind it. Human-readable markdown plus a fenced, schema-validated JSON block, updated **idempotently in place** on re-runs. A cross-service run opens one pull request per repo it changed and **every one of them gets a report**, each composed for the PR it lands on: run-wide evidence (the CI verdict that blocks the whole set, the tester, the merge) appears on all of them, while the sections that are statements about the task's own service (its pre-PR checks, its reproduction proof, its `spec/`) are withheld from a connected service's copy and point at the own-service PR instead, rather than crediting one repo's evidence to another repo's diff. Credentials are scrubbed and the host's auto-link/issue-closing triggers are neutralised before anything is written; per-workspace opt-out via `publishPrVerificationReport`. Provider-neutral: a GitLab MR gets it too. See [`docs/initiatives/pr-verification-report.md`](./initiatives/pr-verification-report.md). ### Run outcome summary "Read the result" lands on an OUTCOME card, not a diff: what was asked (the requester's own words), which of the service's requirements the tester ruled on and what it observed against each, the tester's verdict and concerns, the screenshots it captured (paired with their reference designs when a human reviewed them), the linked pages the agents actually built from with the revision each was read at, and the checks that recorded a verdict. Composed from what the run already captured, never from a model asked to summarise itself, and every section that has nothing to show says WHICH producer was missing rather than rendering blank: "this pipeline has no tester step" and "the tester found nothing wrong" are opposite facts. In basic mode the card is what the board and the inspector open, with the pull request one click inside it; advanced mode keeps the direct PR link beside it. The in-app sibling of the PR verification report above, for the reader who is not going to open a diff, and it is not app-only: `GET /api/v1/runs/:runId/outcome` serves the same summary to anything holding a workspace key, so a bot posting "what shipped" to a channel says what the card says. Both documents are composed by the same code over one read of the run's evidence, so the coverage counts and the regression rule are the same numbers on the card, on the API and on the pull request. ### Merge track record Every merge decision leaves behind what KIND of change it was: a deterministic class derived from the changed files (`docs` / `test` / `dependency` / `config` / `source` / `schema`): the merger's scores, and a one-tap **reviewer-effort tag** a human leaves when confirming the merge. Per-class rollups are SQL aggregates, and a merge preset can carry per-class rules ("always auto-merge dependency bumps", "always review schema changes") that a workspace widens as the per-class record earns it. See [`./backend/docs/adr/0046-merge-track-record.md`](../backend/docs/adr/0046-merge-track-record.md). ### Runs scoped to who started them A merge preset can also say what a run may do based on WHO started it, so a product manager and an engineer running the same task are not held to the same policy. A workspace role can be narrowed per change class (auto-merge dependency bumps, but send a member's source changes to a human) and can be held to **dry runs**: the pipeline works and opens its pull request, and nothing merges, not automatically and not by tapping the review card either, so the person who started the run cannot be their own reviewer. A role can also be allowlisted to the kinds of change it may land at all, so a product manager keeps a real lane (copy, dependency bumps) on a service whose source they may not merge, refused at both exits like a dry run. Role entries are subtractive by construction: one can only ever take capability away from the preset it sits in, never add it, so it can be read on its own. Both are authored in workspace settings beside the rules they narrow, and anyone can ask for a sandboxed run of their own from the run control. A role the preset sandboxes cannot ask its way out, and the run says what it is from the start rather than looking like one that simply has not merged yet. See [ADR 0037](../backend/docs/adr/0037-role-scoped-merge-policy.md) and [ADR 0039](../backend/docs/adr/0039-role-scoped-submission-allowlists.md). ### Review-debt friction Opt-in, per-workspace friction on authoring new tasks while finished work waits on human review: past a warn threshold, creating a task needs an explicit acknowledgement, and (in enforce mode) is refused outright once too many tasks are in review or one has waited too long. Off by default. See [`backend/docs/review-debt-friction.md`](../backend/docs/review-debt-friction.md). ## Bugs and test environments Finding defects, proving fixes, and standing up somewhere to test them. ### Bug fishing expedition A `bug-fishing` task hunts a service's codebase for the defects nobody has reported yet. It reads the same tree once per **angle** (logic and control flow, failure handling, boundary conditions, concurrency and idempotency, state and resource lifecycle, interface contracts, footguns, and conformance with the product requirements you supplied), because a single pass told to "find bugs" returns the shallow half of everything while a pass told to think only about concurrency reads the same files with a question that makes the race visible. Nothing is changed and no pull request is opened: the deliverable is the catch. You triage it in the dedicated window as it lands (a finished angle's findings are actionable while the later ones are still being fished), and every finding you **mark** spawns its own bug-fix task, carrying the finding's evidence and reproduction, on the pipeline the board configures for them, defaulting to the test-verified bug fix below (overridable per batch). A failed angle is recorded with its reason rather than reported as an angle that found nothing, and an expedition that caught nothing still says so rather than finishing silently. Can be put on a schedule, so the hunt runs on a cadence and never becomes a backlog nobody owns. See [`docs/initiatives/bug-fishing-expedition.md`](./initiatives/bug-fishing-expedition.md). ### Bug hunt Pick a connected tracker and one of its boards, and cat-factory reads its **open, unassigned** bugs and rates each one on impact against how hard it looks to fix, so a team can answer "we have an afternoon, what is actually worth fixing?". You confirm a candidate and it is adopted onto the board as a bug task with the issue linked for context, running the standard bug-fix pipeline (investigate, triage with you, reproduce, fix, review, ship). Tracker facts and the model's judgement stay separate: the score is computed from the impact/effort ratings rather than taken from the reply, a rating naming an issue the board never returned is dropped, and a board whose rating was unavailable or failed still returns its scan, labelled as unrated rather than passed off as a ranking. The interactive counterpart of the recurring bug-triage schedule. See [`backend/docs/bug-hunt.md`](../backend/docs/bug-hunt.md). ### Test-verified bug fix A bugfix preset that proves the fix from the repository instead of from a live environment: after the fix and its review, one step stands the service's external dependencies up as mocks the repo owns, and the next writes the integration tests that drive the fixed behaviour through the seam a caller uses, commits them, and wires them into your CI so every future change re-runs them. What it will not do is quietly claim more than it checked: the step states the behaviour it could NOT cover, with the reason, and a run that ends up with no test at all says so rather than passing for a clean one. The ephemeral environment is still stood up, for exactly one question (does the fixed service still start, and can it be reached), and reclaimed the moment it has answered, because nothing in the run tests through it. This is the pipeline a marked bug-fishing finding runs by default. See [`backend/docs/test-verified-bugfix.md`](../backend/docs/test-verified-bugfix.md). ### Ephemeral environments Register your own preview-environment tooling via a declarative HTTP manifest so `deployer`/`tester` agents provision and run against it, or pick a built-in backend: per-PR Kubernetes/EKS namespaces, or a per-PR **Cloudflare Worker** driven over your repository's own preview workflow (no daemon, no cluster, works from every facade). A board picks its **default provisioning mechanism once** and every service added afterwards starts with it (still overridable per service); a board that hasn't chosen is prompted with a shareable link to the setting. A **`disposer`** step reclaims what a run stood up at the point in the pipeline its author places it (after the automated tester, or after a human has finished with the live URL) rather than leaving it to the TTL sweep; the recurring bug-triage preset ships with one at the end, because a scheduled run has nobody waiting to look at the environment it leaves behind. Every teardown (the step's, the sweep's, a manual Destroy) is followed by an INDEPENDENT probe, so an environment is reported reclaimed only when something actually looked and found it gone. Two **self-tests** on a service check the setup before a pipeline ever spends a run on it: **Test environment creation** exercises the whole provision-and-tear-down cycle against a throwaway branch, and **Test agent dry run** then hands that environment to an agent (HTTP for a service, a browser for a frontend) and reports which real operations it managed, which credential or endpoint it could not work out, and what a human has to supply: the failure that otherwise only surfaces at the end of a build. See [`backend/docs/per-service-provisioning.md`](../backend/docs/per-service-provisioning.md) and [`docs/initiatives/environment-disposal-and-teardown-proof.md`](./initiatives/environment-disposal-and-teardown-proof.md). ### Infra declared in code A service's full infra dependency set can be declared programmatically instead of filled into a form: hand `startNode`/`startLocal` a list of **shared stacks** (the long-lived compose infra its preview environments attach to) and every board comes up able to use them, seeded on boot and on board creation. A stack's ordered compose layers are no longer limited to files in one repo: each may be a path in the stack's own repo, a **compose document supplied inline**, or a **file in another repository** read without cloning it, so a stack can describe shared infrastructure that lives nowhere near the service using it, or own no repository at all. The same layer vocabulary works on a service's own stack recipe. Seeding is idempotent by name, so a stack an operator has since retuned is never overwritten from config. See [`docs/initiatives/stack-recipes-and-shared-stacks.md`](./initiatives/stack-recipes-and-shared-stacks.md). ### Self-hosted dogfooding cat-factory ships the per-PR test environments for **its own** repository: a single-origin docker-compose stack (Postgres + the Node facade + the SPA, built from the PR head) for a local deployment, and a per-PR Cloudflare Worker with its own D1 databases + a Pages preview of the SPA for the Worker facade. The Cloudflare half is a **built-in backend** anyone can point at their own repo, not a cat-factory-only script: it drives the repository's own preview workflow over the VCS Deployments API, so a Worker deployment with no Docker and no cluster can still stand one up. See [`deploy/preview`](../deploy/preview/README.md) and [`docs/internal/dogfooding.md`](./internal/dogfooding.md). ## Integrations and the public API What cat-factory connects to, and how other systems drive it. ### Source-control integration Connect a workspace to **GitHub** (a GitHub App) or **GitLab** (a per-workspace personal access token) for repo / pull-merge-request / issue read & write plus webhooks, with local projections kept fresh. The SPA offers whichever connect surfaces the deployment actually serves. See [`backend/docs/vcs-providers.md`](../backend/docs/vcs-providers.md). ### Document & task sources Link Confluence/Notion docs and Jira/Linear/GitHub/GitLab issues to a board: import, expand into structure, or attach as agent context. (GitLab issues are at parity with the other three for import, search, setup checks, the recurring `bug-intake` schedule, the bug hunt, push intake and writeback; filing a NEW issue from the `tracker` step is Jira/Linear/GitHub for now.) Requirements, RFCs, PRDs and tracker issues can be attached when creating a **task** or an **initiative**: an initiative's attachments feed its whole planning pipeline, so the interviewer stops asking what an attached document already answers and the analyst and planner ground the plan in it. A **design** is a first-class starting point rather than another document: a service frame carries a **Start from a design** button that takes a pasted Figma/Zeplin link, canonicalises it before anything is written (and says so separately when the frame you named could not be read and the whole file was attached instead), and hands it to the new task; a link pasted into any task description offers the same. Figma can be connected with **OAuth** where the deployment has registered an app, so a designer is not sent off to mint a personal access token, and expanding a design into board structure plans work INSIDE an existing service rather than proposing one service per Figma page. A linked design is also what the **visual-confirmation** gate compares against: the frames its last import retained populate the actual-vs-reference gallery on their own, labelled as the design's own rather than as somebody's attachment (an image a person uploaded for the same view still wins), and a design that retained fewer frames than it has says which of five reasons applies instead of just showing a shorter gallery. ### Tracker webhooks Point a Jira/Linear/GitHub/GitLab issue webhook at the workspace (a per-connection HMAC secret, or the shared token GitLab echoes instead of signing) so intake starts **within seconds** of an issue being filed or labelled, instead of at the next recurring-schedule tick: the polling schedule stays on as the sweep for missed deliveries. The same channel carries **answers back in**: a reporter replies to a parked requirements review in the ticket (`@cat-factory answer …`) and the run resumes exactly as if answered in-app. A schedule can instead dispatch **per ticket**, so a triaged feature request runs as its own task straight from the tracker it was filed in. Trackers beyond the four built-ins are registered in code by a deployment, and a registered one gets the whole loop (PR notices, the pickup claim, the questions and the acknowledgements) by declaring a writeback capability, rather than by being named anywhere. See [`backend/docs/adr/0032-tracker-webhook-intake.md`](../backend/docs/adr/0032-tracker-webhook-intake.md). ### Notification manager One place per board that decides which events reach which channel. The **in-app** push and **email** are toggled per notification type; Slack and the outbound webhooks keep their own event list where their destination is configured, so nothing is switched in two places. Email delivers through the account's own SendGrid/Resend connection to the people the board is visible to, and by DEFAULT carries only the high-impact events: a run stopped until a human acts (merge review, parked decision, exhausted CI/test fixer, a post-release regression) or a degraded deployment (platform health, dead infrastructure, budget-paused runs, encryption-key drift). Everything else stays opt-in, per type. Muting a type in-app stops the live toast only: the card is still persisted and still in the inbox. ### Inbox cards that name their action An inbox card that asks a person for something carries that ask as its primary button: **Answer** opens the parked decision, **Review** the approval or pull request, and so on per type, while **Mark read** is secondary. The button and the card's title open the same place, from one route table per notification type. A card whose window cannot open (its run is not loaded or no longer parked) says **Open**, because it only selects the task. ### Headless API + push A key-authenticated `/api/v1` surface an external tracker, CI system or bot builds on: discover pipelines, services and **the task types this workspace can create (with the per-case form each accepts, so a reusable operation can be invoked with its real brief)**, **provision the board itself** (list the repositories the workspace has connected and create a service backed by one, the last act of setup that used to need a browser), create/edit/start/stop/retry/delete a task (filing it from a tracker ticket and attaching the **spec-sized requirements documents** it is built against, named in a connected document source or uploaded whole, then listing, attaching and detaching them afterwards, since a spec routinely arrives after the task does), **order a batch of related tasks** so they run one after another instead of racing each other against one repository, read its run and the prose deliverable each step produced, stream it over SSE, **answer any decision the run parks on** (an approval gate, an agent's question, the requirements / clarity / brainstorm loops, an implementation fork, a judge verdict, the task's input check, a PR review's findings, the human-verdict gates), resolve the human-gated tails from a notification inbox, read the period's **usage and budget** for a dashboard, ingest a finished run's **evidence** (the engine's verification report, the same bundle it writes onto the pull request, plus the artifacts the run captured AND the reference designs its task carries, each saying which it is, bytes included), **read the service's in-repo specification** (the structured requirement tree with its acceptance criteria, plus the Gherkin rendered from it, at the commit it was read from, so a tool judging a run's outcome can fetch the criteria it was scored against without cloning the repository or holding a VCS credential: the requirement ids are the same join key on both), and **provision further keys headlessly**, so an operator with no browser can hand out per-tenant credentials (a key minted this way can never mint another, and revoking a key revokes what it minted). Keys carry an inclusive `read ⊂ write ⊂ decide ⊂ admin` scope, every list is keyset-paginated, and the OpenAPI spec ships in [`docs/openapi.json`](./openapi.json). To avoid polling, a workspace can register **several named outbound HTTPS endpoints** (each HMAC-signed with its own secret, SSRF-guarded, retried) and subscribe each to notification cards, **run-lifecycle events** (`run.started` / `run.completed` / `run.failed`), or both, so a second integration enrolls without displacing the first. **Official SDK clients ship for TypeScript, Python, Go and Java** (the Java one serving Kotlin too, with real null-safety rather than platform types): models and operations generated from that same spec, so a client cannot drift from the deployment it talks to. An **MCP server** ships from the same generator, exposing the surface as tools an MCP host can drive, reachable two ways: a **hosted endpoint** on the deployment itself (`POST /api/v1/mcp`, key-authenticated, so a host needs a URL and nothing installed) and a published stdio binary for hosts that spawn their own. The key's scope decides which tools are listed, and every tool call is one `/api/v1` request under that same key. See the [setup & usage guide](../backend/docs/public-api.md), the [SDK clients](../sdk/README.md), the [MCP facade](../sdk/mcp/README.md), [ADR 0030](../backend/docs/adr/0030-public-api-surface.md) and [ADR 0050](../backend/docs/adr/0050-public-api-headless-completeness.md). ### Remote run debugging A read-only `/api/v1/debug/*` surface that lets an agent outside the browser diagnose a run: an index of the board's runs, a per-run **overview** of steps, telemetry availability and precomputed signals (failed or truncated model calls, container evictions, provisioning failures, a cold prompt cache), then keyset-paginated drill-downs into the model calls, the exact context each dispatch was given, the searches agents ran, and the provisioning event log. Sized for a caller with a fixed context budget rather than a scrollbar: bodies are opt-in and byte-budgeted, every truncation reports what it left out, and a sink that isn't wired says so instead of reporting zero. See [`backend/docs/debug-api.md`](../backend/docs/debug-api.md). ## Extending a deployment What a deployment adds in its own code, without forking. ### Custom agent capabilities A deployment's own agent kinds declare what they KNOW and what they can REACH: **skills** (procedural playbooks; bundled in the deployment's own package, or synced from a repo) and **tool servers** (MCP; an issue tracker, an advisory database, an internal service), registered once and referenced by id from any number of kinds, or attached to a BUILT-IN kind without redefining it. Credentials are declared by name and resolved at dispatch, so a value never reaches a prompt or the run's telemetry. A tool the run cannot serve is stated to the agent rather than silently missing, and the step that ran records which servers it wired and which it dropped (with the reason), so a run that quietly went without one says so on the run surface; a registered server can also be TESTED from the Infrastructure window: the probe resolves that board's credentials through the same chain a run does, speaks MCP to the server, and reports a cause rather than a boolean, including whether the tools the declaration narrowed to actually exist, which nothing else in the platform can check. A REMOTE server that authenticates with **OAuth** (which most of the hosted MCP ecosystem does) is connected per board from that same window: someone with `secrets.manage` presses Connect, signs in at the vendor, and the sealed grant is refreshed for them from then on; a machine grant covers an internal server with nobody to press a button. See [`backend/docs/custom-agents.md`](../backend/docs/custom-agents.md) and, for the tool-server (MCP) model end to end, [`backend/docs/mcp-tool-servers.md`](../backend/docs/mcp-tool-servers.md). ### Reusable operations The work an organisation does over and over ("introduce an API on top of existing functionality", once per entity) is registered ONCE as a named operation in the deployment's own code, then invoked from the create-task form: someone picks it, fills a small per-case form (which entity, which operations to expose, which auth model), and one canned pipeline delivers the outcome. The collected answers are folded into every agent's prompt as the run's brief, and the standing context the org holds every such change to (its API guidelines, its auth requirements, its shared-services map) is attached automatically instead of being picked per task, so consistency does not depend on whoever filed the ticket remembering it. The operation carries its own pipeline as a read-only template the org rolls out and later updates by version, and a board that predates the operation adopts the pipeline the first time someone runs it rather than refusing. An org with twenty operations groups them under its own captions in the picker, and a workspace admin hides the ones that team does not run so its picker stays short. Operations are invocable headlessly too: `/api/v1` serves each type's form and takes the filled answers, so an external system can run one with the same brief a person would have typed. See [`backend/docs/reusable-operations.md`](../backend/docs/reusable-operations.md). ### Inline use cases The NON-container half of the same idea, for work that is one model call rather than a pipeline: a deployment registers named units of generation ("write the prose for this scene", "rewrite these barks in the villain's register") in its own code, each NARROWING the models it may run on and declaring the small form it accepts. `/api/v1/use-cases` publishes that catalog and runs one synchronously, so an external editor generates through this deployment's credentials, budget and telemetry instead of holding its own vendor keys. A model the use case does not declare, or one this deployment cannot serve inline, is refused rather than quietly swapped for another, and each published model says which of those two it is. See [`backend/docs/inline-use-cases.md`](../backend/docs/inline-use-cases.md). ### External tools + workspace metadata A deployment registers its own web applications (a map editor, an asset pipeline, an admin console) into an **External tools** sidebar section, each resolving its URL from the invocation context: the acting user, the open workspace, and the deployment's own **custom workspace metadata fields**, whose values operators fill in under Workspace settings. So a tool opens already switched to the right project instead of at its front door, and the platform needs no opinion about what `gameId` means. See [`consumer-extensions.md`](../frontend/app/app/docs/consumer-extensions.md). ### Foundational services Register the shared capabilities your organisation already runs (file storage, notifications, audit) in your deployment's own code, or at the account or workspace scope, each with a description and its API contracts (OpenAPI 3.x, `@toad-contracts/core` or `@lokalise/api-contract`), uploaded directly, or read from a repo that is cached and auto-refreshed: a folder of services, a whole contract folder (optionally including its subfolders) attached to one service, or a named list of files. The Architect designs against the catalog and declares which services its design consumes; the Researcher and Coder are then handed the API contracts of exactly those services, and told plainly about any the design named that the catalog does not have. Managed from the account settings and a board panel, where a tier can also opt out of a service it inherits (a board out of its account's, either out of one the deployment registered) without touching what anyone else sees. A workspace can also point the platform at the developer portal it already runs (Backstage, self-hosted behind a service token, a shared secret, an OAuth2 client, HTTP Basic or your gateway's own headers): its components arrive as services with their owner, system and lifecycle recorded, and the API definitions they publish arrive as their contracts. Triage and investigation agents read that estate to work out which service a report belongs to and who owns it. See [ADR 0031](../backend/docs/adr/0031-foundational-services.md) and [the import's design record](../backend/docs/service-catalog-import.md). ### Binary-output agent steps Register your own agent kinds whose deliverable is binary artifacts (image generation is the canonical example) stored through a foundational service the pipeline step selects from that same catalog (a service tagged `asset-storage`), with further catalog services selected as generation context (an entity inventory that can say what exists, what lacks an asset, and how each thing is described). The engine hands the agent the selected services' API contracts, refuses a run whose selection doesn't resolve, and records what the agent declares it stored on the run step: with every loss (undeclared, unparseable, invalid, over-cap, unknown service) counted rather than hidden. Register the generative integrations those steps produce WITH (an image, music or video API such as Retro Diffusion) in your deployment's own code, each declaring the content types it makes (`image`, `audio`, `video`, `3d-model`, `3d-scene`, `document`), the concrete formats it emits, its API contract and the credentials it needs by name (a list, so an API authenticating with a key and a secret is two named values rather than one joined string); a step picks from them and states both what it must deliver and, where the container is the requirement rather than a preference (as it is for a mesh an engine has to import), the exact formats it must arrive in; the run is refused if nothing selected can produce one of them (and a format nothing selected could be CHECKED against is admitted and reported rather than passed off as verified), and the credentials reach only that job's agent process, never a prompt. See [`docs/initiatives/binary-output-foundational-storage.md`](./initiatives/binary-output-foundational-storage.md). ### Custom artifact stores Put the bytes of the platform's binary artifacts (UI-tester screenshots and the reference designs they are reviewed against) wherever your organisation already keeps files: implement the kernel `BinaryBlobBackend` port, register it on your deployment's `BinaryStoreRegistry`, and it appears in the account settings storage picker beside the built-in filesystem / Postgres / S3 / R2 backends. The registered id is stamped on every artifact row, so an account can be moved between stores without losing track of where existing bytes live, and an account pointed at a store the current build no longer registers is NAMED in the settings panel and the log rather than quietly retaining nothing. See [`backend/docs/custom-binary-stores.md`](../backend/docs/custom-binary-stores.md). ### Per-workspace capability credentials The secrets a registered tool server (MCP) or generative integration asks for by name are stored per board, sealed at rest, instead of coming off one deployment-wide environment variable, so each tenant brings its own vendor account and a rotation is one form rather than a redeploy. The Infrastructure window renders them as a CHECKLIST projected from what the deployment's own code declares (who wants each key, whether it is required, when it was last set), so nobody reads the source to learn what to type, and it keeps apart the three things an empty row can mean: nothing stored but the deployment's environment may still answer, a stored key nothing asks for any more, and a declaration list that could not be read. `secrets.manage`-gated end to end, the read included. See [ADR 0041](../backend/docs/adr/0041-capability-credential-store.md). ### Bring-your-own runner pool Route coding jobs to your own Kubernetes/Nomad/scheduler pool instead of Cloudflare Containers, described by a manifest. ## People, access and spend Who sees what, who may do what, and what it costs. ### Accounts & workspaces A signed-in user switches between a personal account and any **orgs** they belong to; an account owns many **workspaces** (boards). Visibility is by membership. ### Roles On a first-ever launch the app asks one question: what do you work on? **Engineer** and **Product manager** get the whole product; **Designer** gets a much simpler board, holding the services already on it, the work in flight, and the three ways to bring new work in (a new task, a task from a tracker ticket, a task from a design) without any of the platform configuration behind them. It changes what you SEE, never what you are allowed to do (that is workspace RBAC, enforced server-side), so the way back is one dropdown at the top of the sidebar, present in every role. Closing the question without answering leaves the whole product in place and asks again next time, and a deployment cannot pin the answer: which job the person at the keyboard does is not something a build can know. [Details](../frontend/app/README.md#roles-engineer--product-manager--designer) ### Basic / advanced modes The SPA renders at one of two interface tiers. `basic` (the default) shows the everyday surface: the power-user navigation destinations are hidden and the run options that only exist to override a workspace default are left at that default; `advanced` shows everything. A deployment can pin the tier with `NUXT_PUBLIC_UI_MODE`, otherwise each user picks their own from the sidebar and the choice is remembered in their browser. The sidebar also collapses to an icon rail (basic mode starts collapsed). [Details](../frontend/app/README.md#interface-modes-basic--advanced) ### Agent tiers Agent kinds are classified `basic` / `intermediate` / `advanced`, and the two surfaces that list the whole catalog (the pipeline builder's palette and a model preset's per-agent overrides) show only the **basic** tier by default, so a first pipeline is assembled from the everyday kinds instead of the full roster. A control on each widens the view one level at a time, up to the whole catalog, and the choice is remembered. Independent of the basic/advanced interface mode. A deployment's own registered agent kinds declare their tier alongside their icon and label. [Details](../frontend/app/README.md#agent-tiers-basic--intermediate--advanced) ### In-app tutorial tours On first launch the app asks whether you want a guided tour and remembers the answer. Tours run inside the live app: they highlight the actual control for each step, tell you what to click, and follow along as you really do it (opening the real modal, creating a real task). Steps whose control your role or deployment doesn't show are skipped, and a **Tutorials** catalogue in the sidebar lists every walkthrough the deployment ships: with completion tracked per tour, so any of them can be started, resumed or repeated at any time, and the ones this board cannot demonstrate yet named alongside what would unlock them rather than quietly omitted. The built-ins cover the delivery loop end to end (find your way around, link a repository, create a task, run it, answer it when it pauses for you, review and merge it) and the platform behind it (connect a model provider, design your own pipeline, curate the standards agents follow, connect your tracker/documents/chat/monitors); the first-launch question stays the delivery arc, and the rest waits in the catalogue for when you go looking. Tours are declarative data over the same extension registry as the rest of the SPA, so a deployment ships its own beside the built-ins. [Details](../frontend/app/README.md#in-app-tutorial-tours) ### Enterprise SSO Sign in through your organisation's own identity provider: ONE generic OpenID Connect adapter configured by discovery URL and client credentials, so Okta, Microsoft Entra ID, Auth0, Keycloak, PingFederate, OneLogin, JumpCloud, Google Workspace and a Shibboleth IdP running the OIDC OP plugin all work with no per-vendor setup. Authorization Code + PKCE, ID tokens verified against the provider's JWKS (asymmetric algorithms only, with automatic key-rotation refetch). Who may sign in is the directory's app assignment, optionally narrowed to named groups or verified email domains, re-checked on every sign-in. Resolves to the same canonical user as every other login method, so nothing downstream changes. ### Audit log & session revocation Every privileged account action leaves a record of **who did what, when**: membership and role changes, invitations sent, accepted and withdrawn, budget and settings edits, board roster and access-mode changes. Append-only, kept in its own store on a retention window of its own (default two years) so it cannot be shortened by tuning something else, and read from an admin panel as translated sentences composed from machine-readable fields, so a row written today still reads correctly for someone in another language years later, and an action this build no longer recognises is named as unrecognised rather than dropped. Beside it, **session revocation**: sessions are signed tokens, so ending one used to mean waiting for it to expire. Each user now carries a session generation that every token is stamped with, and advancing it invalidates the lot in a single write. A person can sign themselves out of every device; an admin can do it for a member who has left or lost a laptop; and an SSO sign-in the directory now refuses cuts the sessions that person is already holding, which is what makes "we disabled them in the identity provider and they lost access" true rather than eventually true. See [`backend/docs/auth.md`](../backend/docs/auth.md#session-revocation). ### Agent write-path controls The credentials an agent run pushes with are governed rather than assumed. An account-wide policy, and, beneath it, each board, can refuse to let a run authenticate as its INITIATOR's personal access token, so every run is bounded by the GitHub App installation the operator scoped instead of by whoever pressed start; the account tier is the one a board admin cannot lift. Both ship permissive, because a personal token is the right credential for someone adopting cat-factory alone inside an org that has not, and that path stays fully supported. A stored token's breadth is stated when it is tested or saved (a classic `repo` token reaches every repository its owner can push to); and an on-demand preflight probes each linked repository's default branch for host-side protection (classic branch protection and rulesets alike) the one control over a stolen `Contents: write` token, which the platform can report but not enforce. Full trust-boundary model, layer by layer with its residual gaps: [`backend/docs/security-model.md`](../backend/docs/security-model.md). ### Spend safeguards Every LLM call is metered into an org-wide monthly budget; runs **pause** at the cap and resume when the period rolls over (or on an explicit override). Before that happens, a forecast sweep measures the trailing burn rate and projects the period total, raising a `budget_threshold` notification (in-app + Slack) once a board's or account's spend crosses 80% of its budget or is projected to overrun it, so there is time to raise the limit or stop a runaway. The alert fires once per crossing and re-arms each period; too little history withholds the projection rather than publishing a number nobody should act on. See [`docs/initiatives/spend-forecasting-and-alerts.md`](./initiatives/spend-forecasting-and-alerts.md). ## Operating and observability What an operator watches, and how a run is diagnosed. ### Operator observability A deployment-level admin dashboard of run health (outcomes, failure taxonomy, live/parked depth, duration percentiles) over time windows, plus opt-in **threshold alerting** (a `platform_health` notification when failure rate / run duration / backlog crosses a limit) and opt-in export of per-run LLM traces/metrics, those platform aggregates **and** the platform's own structured logs to Langfuse or any OpenTelemetry (OTLP) backend. ### Setup checklist **Configuration → Setup checklist** lists every prerequisite for "a task can run and merge" in one place: the model, source control, where agents run, test environments and screenshot storage. Each row has its state, the cause when it needs attention (unreachable, incomplete, rejected, under-scoped, a default preset that names an unusable model, a probe that failed, or an area the backend does not report), and the action that fixes it, when there is one: rows still checking, not needed, or not reported have none. A row adds no probe of its own: it reads what the matching setup banner or startup dialog reads and opens the same fix. The model row also keeps its own load-failure state, so it can show "probe failed" while no banner says anything. The entry is open to people who can manage integrations or settings. For a row they cannot act on, they see who can, or are told to switch to the full surface. ### Infrastructure reachability The setup banner reports infrastructure that is configured but **dead**, not only infrastructure that was never set up. An opt-in watcher probes each board's environment-provider and runner-pool connections and, on a transition, pushes the verdict to any open board and raises an `infra_unreachable` notification (in-app + Slack), so a provider that dies mid-session surfaces immediately instead of on whoever next reloads. Distinct from a setup gap: it reads as an outage, and it can only be silenced for the session, so the next occurrence nags again. Enable with `INFRA_REACHABILITY_WATCH`. ### Failing-call-first triage A run's observability panel leads with what broke rather than asking an operator to find it: the run's structured failure record, the last model call that failed, and the last **tool** call that failed, pinned above the lists with a jump into each. That second one is the failure class nothing else counts, because a tool that errors runs inside the container and the model call that requested it still reports success, so every token and latency number on the page reads healthy on a run whose edit loop is wedged. Both drill-downs narrow by outcome (failed / cut short / OK for model calls, failed / OK for the tool-call trajectory, which keeps its running order under every filter so a stuck loop is distinguishable from one tool that failed and was worked around), and the same narrowing is a `?outcome=` parameter on the debug API with a run-level count of failing tool calls beside it. When nothing failing can be pinned, the panel says which of three things that means: both sinks answered clean, neither recorded anything, or one stayed silent. ### Per-class token telemetry Every recorded model call keeps its input side in three orthogonal classes: **fresh** (uncached), **cache read** and **cache write**: instead of one lumped "cached" figure, so the step metrics bar and the model-activity panel show what a run actually burned. The headline number still counts **every** input bucket (the like-for-like of Claude Code's own context gauge, since a cached token occupies the context window exactly like a fresh one) with the three classes as the breakdown beneath it. The distinction is the point: a cache read is priced around a tenth of fresh input, while a cache **write** costs 1.25-2x MORE than fresh, so a run that keeps invalidating and re-writing its prompt prefix is expensive in a way a single summed number cannot show. Normalised at the recording site across the provider shapes (Anthropic reports the classes apart; OpenAI/DeepSeek/Codex report an inclusive prompt count), so the numbers mean the same thing whichever model served the run. See [`backend/docs/prompt-caching.md`](../backend/docs/prompt-caching.md). ### Per-phase token burn Every recorded model call is stamped with the PHASE that spent it (the agent's own edit loop, a pre-PR validation repair round, a reproduction-proof repair round, and so on) and the model-activity panel and the debug overview roll a run's spend up along that axis. Each phase reports its turns, its three input classes and a **carry cost**: its context counted once for every later turn in the same conversation that had to re-send it, which separates "this phase read a lot" from "this phase made everything after it expensive". A call nothing could attribute is its own row rather than a hidden one, so an un-phased channel never reads as a slice that spent nothing. See [`docs/initiatives/token-burn-instrumentation.md`](./initiatives/token-burn-instrumentation.md). ### Model-activity coverage A workspace's LLM calls land in one store whichever way they reached the model: a container agent's proxied calls, a subscription harness's calls lifted off its CLI stream, and the INLINE calls a one-shot agent kind, judge, consensus round or reviewer makes directly. So a step that never spins up a container still shows its tokens on the board and still drills down in the model-activity panel and the debug API: an inline-only run reporting zero model activity was previously indistinguishable from one that spent nothing. The store is workspace-scoped, so what is recorded is what a workspace can be billed and audited for; a call made outside any workspace reaches the optional external trace backend only. Per-call bodies stay behind the deployment's `LLM_RECORD_PROMPTS` switch and the workspace's own `storeAgentContext` opt-out, whichever path recorded them. ### Structured logging Every backend package (including the whole domain engine) emits through one injected `Logger` port (pino-backed, JSON lines on both runtimes), with `LOG_LEVEL` selecting the tier and correlation ids bound per run. Deliberately-swallowed background work logs its drops instead of vanishing. See [`backend/docs/logging.md`](../backend/docs/logging.md). ### Reports An admin analytics view over the same account scope, answering where the spend and the work actually go: **spend per model and per agent kind**, **spend per repository and per linked tracker ticket** (the TCO axes an organisation budgets against), and **spend + run activity per board, per service and per task type**, plus a spend trend over 24h/7d/30d/90d and an optional single-board filter. Real metered cost is kept strictly apart from the illustrative equivalent-API cost of flat-rate subscription usage, and a call whose run/service/task type can't be resolved is shown as its own **unattributed** slice rather than dropped. See [`backend/docs/reports.md`](../backend/docs/reports.md). ### Benchmarking A headless harness (`cat-bench`) that scores agents (requirement review / code review / implementation) across models and prompt versions.