> ## Documentation Index
> Fetch the complete documentation index at: https://docs.experio.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Admin Copilot

> A page-aware AI assistant for the admin panel that knows every admin page, answers from this guide, and proposes changes for a person to review — with page packs for Agent Flows and Settings.

## Overview

The Admin Copilot is a chat panel available across the admin, aware of the page you're on. It's built on the same agent harness (`deepagents`) as the rest of Experio's AI. On every admin page it knows where you are, can explain the page from this guide, and can take you to another page. Two **page packs** give it deeper tools on specific pages: the [Agent Flows](/admin-guide/agent-flows) list and editor (explain a flow, validate it, investigate why a run failed, build or edit flow definitions from plain text) and the **Settings** pages — the copilot's own settings and [System Settings](/admin-guide/system-settings) — where it explains each setting and proposes values.

<Note>
  The copilot **proposes, it never applies.** Every change it drafts — a new flow, an edit to an existing one, a settings value — lands as a card the person reviews and applies themselves. Saving, publishing, setting a version live, starting or resuming a run, and every other mutating action stay entirely with the human; the copilot has no tool for any of them.
</Note>

The panel, its settings, and the safety model below are general-purpose — a later release can add a page pack for another admin page without changing any of this.

## Opening the copilot

A **Copilot** button sits in the admin header on every admin page — click it, or press **⌘J** (Mac) / **Ctrl+J** (Windows/Linux) to toggle the panel from anywhere. It docks as a resizable panel; opening it moves keyboard focus in, and closing it returns focus to what you were doing. The empty panel is titled after the page you're on (**Ask about Access control**, **Ask about System Settings**, …) and offers a few starters for that page.

<Note>
  A screenshot of the docked panel next to the Agent Flows editor would help orient a reader here.
</Note>

## What it can do

Everywhere in the admin, the copilot:

* **Knows the page you're on.** On every turn it's told the page and its section ("You are on: …"), so it never asks which page you mean. On a page without a dedicated pack, its opening brief names the page and offers two starters — **What does this page do?** and **Where do I…?** — answered from this admin guide with links to the relevant documentation pages. See [Asking about a page](/admin-guide/overview#asking-about-a-page).
* **Lists admin pages** and **searches the admin guide** for a passage relevant to your question, ranked by the page you're on; the guide pages it drew on are listed under the answer as **References** (titled links, kept when you reopen the session) rather than pasted into the prose. If the guide's search index is out of date, it says so before answering from the guide and offers the re-index (see [Troubleshooting](#troubleshooting)).
* **Offers to navigate** you to another admin page — it asks first, you confirm before anything moves. It can only offer pages that actually exist: the admin app tells the copilot which pages it renders, so a route that isn't a real page is refused and the copilot picks from the pages next to it instead. Where a page belongs to one record rather than a list — the editor of a particular flow — it offers **that** record's page, not the list you would then have to search. And when the card follows a proposal it just made, it opens the page that can actually show the proposal, so "take me back to it" lands on the flow with the change in front of you.
* **Asks you a question** as a card with a few plain-language options, when it needs information before it can act.
* **Remembers for everyone**, when you ask it to ("remember this for everyone") from any page — a convention or decision the whole team should share goes into a page pack's shared **Notes** memory rather than your personal notes: the pack of the page you are on by default, or the one you name ("remember for agent flows: …"). See [Memory](#memory).

### On Agent Flows

On an **Agent Flows** page (the list or the editor), it also reads the open flow's current definition (including your unsaved edits), the flow's validation state, and its recent runs, and can:

* **Explain** the flow or one of its nodes, and answer "why did run *X* fail?" from the run's diagnostics.
* **Validate** a definition — the same checks that back the canvas **Validate** button and the publish gate, including the [hollow-pattern lints](/admin-guide/agent-flows#hollow-pattern-checks) — without saving anything.
* **Build a new flow** from a plain-language description, starting from the built-in templates when one fits.
* **Propose an edit** to the flow already open in the editor — anything from fixing a validation error to adding a review gate — as either a whole redrafted definition or a small set of targeted changes, whichever fits the request. Asking it to "apply it", "fix it" or "go ahead" after an explanation produces a proposal card, never a list of manual steps.
* **Recall past fixes.** When the page has a failed run or validation errors, the copilot is shown the **Similar past fixes** recorded in the Agent flows **Fixes** memory — what resolved the same error before, and which attempt did *not* — so it can steer away from a repair that already failed and reuse one that worked. See [Memory](#memory).

<Note>
  The copilot is **read-only on your MCP servers.** Its Agent Flows pack has no tool that saves, publishes, sets a version live, or starts/resumes/cancels a run — those stay on the canvas. See [Safety posture](#safety-posture).
</Note>

### On the Settings pages

On **Admin > AI & Agents > Copilot** (the copilot's own settings) and on **System Settings**, the copilot:

* **Explains a setting** in plain words — what it controls, what a value means in practice, and its current value on the page (with the default, on the copilot's own settings). On System Settings it reads the words of the [System Settings](/admin-guide/system-settings) reference for that key and cites the page; on its own settings page it also sees a short digest of your recent sessions (model in use, turns that ended in error or on the budget, denied tool calls) so it can recommend from evidence — for example, raising the model-call timeout when turns keep stalling.
* **Checks that the guide is indexed** before answering a settings question from it. When the index is empty or out of date, it tells you and offers the re-index instead of answering from memory.
* **Proposes a change** when you ask it to ("set it to enforce", "tune it for me", "is this right?") as a card listing each change as *setting · current → proposed · reason*. **Apply** patches the page's own form — nothing is saved yet — and you press the page's **Save** to persist it, with the page's usual validation and permissions. A proposed value is validated on the server against the settings schema before you ever see the card.
* **Never touches a secret.** Encrypted values show as *set* / *not set*, are never shown to the copilot or to you through it, and cannot be proposed — a person types those. A secret you type into the chat itself ("set OPENAI\_API\_KEY to …") is masked to `••••` in your message and in the reply before either is stored, so type it into the setting's field on the page instead. On its own settings page the copilot also can't propose changes to the MCP bindings or to the write / MCP-write consent policy; those are changed by a person on the form.

The opening brief on these pages offers starters such as **Walk me through each setting** and **Tune it for me** (Copilot settings), or **What do these settings do?** and **Is this set up right?** (System Settings). If you don't have write access to the page, the copilot says so and explains without proposing.

## Proposals

A proposal renders as a card in the transcript: a one-line **brief**, a short summary, and a diff of what changed — per-node before/after with inline text diffs, a count of added/removed edges, and any input port the proposal leaves unwired. Each change carries a **rationale** citing the rule or lint id it rests on (and a run id, when the reason comes from a run you asked about).

Occasionally the model sends the edits without a brief or summary; the card is then described from the edits themselves and marked **Auto-described** so you know those words are generated, not the model's own reasoning. The diff, validation and Apply gate are unaffected.

* **Apply** copies the proposal onto the canvas through the same path as a hand edit — the flow is not saved for you, so it still shows as unsaved and you can **Undo** it like anything else you typed. A proposal never renames the flow: the name you gave it (in the header, or in the **New flow** dialog a list-page proposal opens) stays, whatever name the copilot wrote into its draft.
* Apply is **gated** on the proposal's own validation: a card with errors can't be applied until they're fixed, and a card with warnings needs an explicit acknowledgement first.
* A card that is now behind the canvas — you edited or applied something else since it was made — is marked **stale**; applying it still works but warns you first (warn, don't block).
* Every applied change is chained together into the flow's **provenance**, which the [publish dialog reads back](/admin-guide/agent-flows#ai-provenance-and-publish-warnings) the next time you publish.

Two shapes of proposal exist, and the page decides between them: a **whole definition** for a brand-new flow only (the list page, or a canvas with nothing beyond start and end), and a small set of **typed edits** to the flow already open — add, update or remove specific nodes, edges or bindings — so a node you never touched can't lose its configuration or bindings the way a full re-emit could. On a flow that already has steps a whole definition is refused unless **Allow a whole re-emit of an existing flow** (`propose.rewrite_existing`, off by default) is on in the pack's settings.

### Checked on the server

A proposal of typed edits is not sent to you as the model wrote it. The server first applies the edits to its own copy of the flow and runs the full publish validation on the result — the same structural checks and [hollow-pattern lints](/admin-guide/agent-flows#hollow-pattern-checks) as the canvas, including the binding-type mismatch check that catches a list-typed input wired from a text port. A result that fails is rejected back to the copilot with the errors, and the copilot corrects its own attempt within the same turn, up to the pack's repair limit (see [Proposals](#configuration) in the Agent Flows settings); when it runs out of attempts it stops and tells you, in plain words, what still blocks the change. When the rejection already carries a repair recipe that itself validates clean, the server accepts that recipe as the card (**Fold a clean repair**, on by default) and the card names the folded repair — a placeholder the copilot still has to fill is never folded, and neither is a repair that would undo or rewrite a setting the edits themselves make (one that only adds to it, such as the reviewer-feedback lines appended to a prompt the edits rewrote, still folds). Asking to switch **Require a result** off on an exit that binds a producer is refused with the reason and never quietly switched back on, whether or not the exit had the setting before: the repair that would switch it back on is not even offered to the copilot, and only you can turn it back on, from the canvas. A condition the edits put on an edge is kept the same way: a repair that would put the canvas's own condition back over it is neither folded nor offered, while one that only drops a condition the edits added (the fix for an edge gated on a step that has not run yet) still folds. A problem that was already on the canvas before the change, on a step the edits do not touch — a hollow-pattern warning you chose to leave, say, or a node you are still wiring — does not block an unrelated card and is never folded away: it rides the card as a **warning**, marked *already on the canvas — not introduced by this change*, and Apply waits for your acknowledgement as it does for any warning. A problem the edits introduce, or one on a step they do touch, is still a rejection the copilot has to fix (or a fold, when its repair validates clean). A card that passed arrives marked **Checked on the server**, and you're never asked to apply a card and then wire the rest by hand.

When the rejection comes with a complete repair recipe that already validates, the server folds it in instead of sending it back: the card arrives with a **Folded in by the copilot:** line naming the repair (with its rule chips), the summary gains a short clause, and the copilot's repair budget is untouched. Turn it off with **Fold a clean repair** in the pack's settings if you would rather see the model's own corrected attempt.

When a new proposal builds on an earlier card you haven't applied yet — the copilot adds to the nodes that card introduced — the two are composed into one replacing card. The older card is marked **Replaced by a newer proposal**, with a link to the card that replaced it; it loses its Apply button, while cards you already applied keep their Undo.

Settings proposals are simpler cards — one row per change, *setting · current → proposed · reason* — and follow the Apply-then-Save path described under [On the Settings pages](#on-the-settings-pages).

## Settings — Admin > AI & Agents > Copilot

Three tabs, gated by the same **AI Managers** permission group as the rest of the AI settings (read-only without write access).

### Configuration

| Setting | What it controls |
| - | - |
| **Enabled** | Master switch — off hides the panel for every user. |
| **Model** / **Fallback model** | Which chat model answers; empty defaults to the org's reasoning model. |
| **System prompt extension** | Free text appended to the copilot's base instructions (house style, naming conventions, what to avoid). |
| **Skills** | Which named skills it may load (empty = every enabled one). |
| **Page packs** | Which registered page packs are active (empty = all). |
| **MCP bindings** | MCP servers the copilot may call **through the current user's own connection** — v1 is user-credential only; there's no way to bind an org-credential server, so one user's copilot session can never reach another user's or the org's external connections. Each binding can whitelist specific tool names. |
| **Risk policy** | What happens when the copilot reaches a tool that isn't a plain read: **Write tools** and **MCP write tools** are each `confirm` (park behind an approval card) or `deny` (not offered at all); **Ask the user** is `allow`/`deny` for whether it may pause a turn with a question card. |
| **Interview** | How it interviews before drafting: `skeleton_first` (draft, then ask), `always_ask`, or `never_ask`; caps on questions per card, options per question, and question rounds per task; **skip phrases** (e.g. "just draft it") that jump straight to defaults; whether the opening brief is off, computed deterministically, or written by the model; and the **act-intent backstop** (on by default) that sends a short "apply it" / "fix it" reply back to the model once if it answered in prose with no card. |
| **Budget** | Per-turn limits (max model calls, max tool calls, optional max tokens, max wall-clock seconds) and an optional per-session token ceiling; **model call timeout** — a silent model call is abandoned and retried up to twice, and must stay under a third of the per-turn wall-clock limit. |
| **Memory** | On/off; which **modules** the copilot is given each turn (**User notes**, **Domain fixes**, **Domain notes** — each its own switch); **Shared writes** — who may add to or undo the shared Fixes and Notes files: **AI admins with write access** (default) or **Anyone who can open the copilot**; and the character cap **per module** (see [Memory](#memory)). Profiles saved before these options existed keep their values: every module on, shared writes limited to AI admins with write access. |

Below the base settings, each active page pack gets its own **collapsible form**. The **Agent Flows** pack currently exposes:

| Setting | What it controls |
| - | - |
| **Validation profile for proposals** | The profile `validate_for_publish` runs on every proposal — `ai` (default) turns cross-branch reads and a failed dry compile into errors, on top of the [hollow-pattern lints](/admin-guide/agent-flows#hollow-pattern-checks). |
| **Run context** | How many of the flow's recent runs are rendered into the page context every turn (0–10). |
| **Run access** | **Tier** — `0` (diagnostics only, content-free except the run's declared parameter values: status, failed node, error shape, timings, tokens, plus each parameter the flow declares, secret-scrubbed and cut at 200 characters; a failed step's or tool call's error is reported as its class, shape and length — never a value a node produced, never an error's text) or `1` (also allow-listed, capped, audited excerpts of specific fields — see [Safety posture](#safety-posture)); the **run window** in days, how many of the newest runs are **in scope**, and the cap on rows returned per call. |
| **Proposals** | Max repair attempts per turn before the copilot must stop and explain what's still blocking the proposal, instead of retrying forever; **Fold a clean repair** (on by default) accepts a rejection whose attached recipe already validates, so the model does not spend a retry bouncing it back; **Allow a whole re-emit of an existing flow** (`propose.rewrite_existing`, off by default) lets the copilot answer with a whole definition on a flow that already has steps — off, it is refused and the change comes as typed edits. |
| **Lookup tools** | Toggle the model/MCP/template/document-template lookup tools individually (MCP lookup starts a subprocess to discover tools, so it defaults off). |
| **Interview extras** | Additional skip phrases for this page, on top of the base ones. |
| **Open with an observation** | Whether opening the page shows a deterministic opening brief (validation state, the last failed run, hollow-pattern warnings). |

### Skills

A **skill** is a Markdown playbook (`SKILL.md`: a frontmatter block with `name`, `description`, `allowed-tools` and a version, then instructions) that shapes how the copilot approaches a page — the Agent Flows pack ships with one (`agentflow-author`) describing the authoring loop, the grammar, an interview checklist and the anti-patterns to avoid.

The Skills tab lists every skill, seeded and admin-authored side by side. You can **import** a `SKILL.md` file, edit one in the drawer, and **reseed** to re-materialize every seeded skill from the shipped source (useful after an upgrade). A seeded skill can't be deleted — disable it instead; an admin-authored one can be deleted freely.

### Memory

With memory on, the copilot keeps short note files across sessions — the same kind of running notes a person keeps — so it doesn't re-ask what it has already been told and doesn't repeat a fix that didn't work. Memory is **modular**: one file is yours, and each page pack keeps two files shared by every admin.

| Module | Who sees it | Who writes it | What goes in |
| - | - | - | - |
| **User** notes | Only you | The copilot, as you work | Your preferences, conventions and decisions. |
| **Fixes** (one per page pack, e.g. *Agent flows · Fixes*) | Every admin | The server, automatically | *Symptom → fix* recipes with evidence — the failed run, the run that succeeded, the proposal that was applied, and who applied it. |
| **Notes** (one per page pack, e.g. *Agent flows · Notes*) | Every admin | The copilot, only when you ask it to "remember this for everyone" | Conventions the team agreed on. |

**How a fix is recorded.** No model is involved. When a run of a flow completes after an earlier run of the same flow had failed, and a copilot proposal was applied between the two, the server writes a *fix* entry to the Agent flows **Fixes** file: the error, the node it failed at, the applied proposal's brief, and the run and proposal ids as evidence. If instead the next run fails with the **same** error, it records that the *attempt did not fix* it. On later diagnose-or-fix turns the copilot is shown the **Similar past fixes** for the error in front of it and is instructed to do something different when an entry says an attempt didn't work. Only the error's headline is recorded — never a value a node produced or the run's content.

**"Remember this for everyone."** Ask the copilot to remember something for everyone and it appends one dated line to a page pack's shared **Notes** (or **Fixes**) file, signed with your email. The entry is filed under the area it is about — the pack of the page you are on unless you name another one ("remember for agent flows: every review step needs a closed loop", said on the Settings page, lands in *Agent flows · Notes*); on a page no pack owns (Admin Home, for instance) the copilot asks which area you mean. Whether it may is decided by the **Shared writes** setting; by default it needs the same AI-admin write access as editing the copilot's settings. The copilot cannot write a shared file any other way — its own free writes go to your User notes only. A preference you state about yourself ("remember that I prefer review gates on every deliverable") is yours, not the team's: it goes to your User notes, and the shared file refuses it even if the copilot tries; say "for everyone", "for the team" or "our convention" when it should be shared, and when the copilot can't tell, it asks — **Just for me** or **For everyone** — before writing anything.

**What you see.** Every write to memory — by the copilot, by the server, or by you — shows up as a card in the transcript: **Remembered for you** for your own notes, **Remembered for everyone — Agent flows · Fixes** (naming the file) for a shared one, marked *recorded from a run* when the server wrote it, each with a one-click **Undo**. The copilot's own cards stay with the conversation: come back to the session after navigating away — or fork it — and each is still there under the step that wrote it, with its Undo, or reading **Forgotten** when that write was undone in the meantime, from the card or from the Memory tab. A fix the server recorded while you weren't looking is announced as a card at the start of your next turn in that session (an announcement, not part of the conversation — it isn't kept on reload); a session you open later sees it on the Memory tab instead. When the Memory tab is the page the panel is docked on, it refreshes as the copilot writes.

The **Memory tab** is the durable record of it all, one section per module: **User** first, then each page pack's **Fixes** and **Notes**. Each section shows the file's current content and a **History** of every revision — who made it (*model*, *you*, another admin's email, or *recorded from a run*), when, the run and proposal ids behind a recorded fix, and an **Undo** on every row (an already-undone row says so). **Forget** clears one module's file entirely; it's undoable from the history, because forgetting overwrites the file rather than deleting the record. Forgetting or undoing a shared file follows the **Shared writes** setting.

Each module is capped at the **Max characters per module** setting: your User notes are truncated with a marker when they outgrow it, while the shared Fixes and Notes files drop their oldest entries first. A module switched off in Configuration is no longer given to the copilot but stays readable on the Memory tab.

## Sessions

Each session is private to the user who started it, titled automatically from the first message (or renamed by hand from the session menu). Opening the panel resumes your most recent open session (a fresh one is created only when you have none, so the opening brief always shows); the **+** in the panel header or **New session** in the menu starts a new one, and the menu's search box finds an older session by title. Closing a session doesn't delete it — it just stops appearing as active; a session whose turn is still running cannot be closed until it finishes.

Every message in the transcript has a small row of actions under it (shown on hover, always reachable from the keyboard). **Copy** puts the message's words on your clipboard — never a tool's parameters or results, the copilot's reasoning, or a card. On the copilot's answers, **Fork from here** starts a new session — titled *Fork of …* — whose conversation is a copy of this one up to and including that answer: the same files, plan and proposals as at that moment, nothing that came after. The original session is untouched, and the panel switches to the fork. The fork also inherits the original's token usage, so forking never resets the per-session token ceiling (see [Budget](#configuration)). An answer that is still streaming, or one whose turn is waiting on a consent card, can't be forked yet — answer or finish it first.

The same row also lets you change course without starting over. On your own messages, **Edit** takes the conversation back to just before that message and puts its words in the composer for you to rewrite — everything after it is gone, because it will be answered again — and **Retry** does the same but sends the same words straight away. On the copilot's answers, **Regenerate** asks the same question again, **Delete turn** removes that exchange (your message and its answer) and sends nothing, and **Quote in reply** drops the first line of the answer into the composer as a `> …` quote for you to reply under. Rewinding removes later turns too, so when there are any, one inline "This removes N later messages. Continue?" asks first; on the last exchange nothing later is at stake and the action runs at once. None of these are available while the copilot is working, while a question or approval card is waiting, or while another such action is still under way (a message sent from another tab while a rewind is landing is refused with a note to send it again) — and none of them touch what the copilot remembers — memory files written during a removed turn stay, and can be undone from the Memory tab as usual — nor the session's token count: a removed turn's tokens were spent, so rewinding never resets the per-session ceiling either.

## Safety posture

* **Proposes only.** No tool in the Agent Flows pack saves, publishes, sets a version live, deletes a flow, toggles sub-agent access, or starts/resumes/cancels a run — every one of those stays a human action on the canvas. On the Settings pages, a proposal only patches the form; the page's own **Save**, under the page's own permissions, is what persists it.
* **Secrets stay with people.** Encrypted system settings are masked as *set* / *not set* before anything reaches the model, and the copilot refuses to propose a value for one. The copilot's own MCP bindings and write-consent policy can't be proposed either.
* **No MCP writes.** MCP bindings are user-credential only (v1); a write-capable tool anywhere is gated by the risk policy's `confirm`/`deny` choice, never auto-approved.
* **Navigation is limited to real pages.** The copilot can only offer routes the admin app has told it it renders; anything else is refused.
* **Raw run content never reaches the model at the default tier.** Tier 0 diagnostics are built to be content-free by construction — status, error shape, timings, token counts, never a value a node produced. A step or tool call that failed shows up as its error class, a shape word and the length of its text, not the text. One kind of content does ride along at Tier 0: the values of the parameters the flow declares (what you typed when you started the run — a topic, a title, a date), secret-scrubbed and cut at 200 characters each, because a run's diagnosis usually starts with what it was asked to do. Nothing a node produced is among them. Turning run access up to **Tier 1** allows a small, allow-listed set of fields (a reviewer's comment, the run's error message, one failed step's error text, the error bodies one step's tool calls returned, one named node output, a DOCX's headings) to be read in capped excerpts, only when you ask for the words; every such read is wrapped as untrusted data and recorded in an audit log, one row per read.
* **Memory is data, not instructions.** Every memory module is handed to the model fenced as notes it may not treat as instructions, and the shared files are append-only for the model, size-capped, written only through the gate above, and undoable — so one admin's session can't quietly re-instruct every other admin's copilot.
* **A hidden system file, and the skills directory, are write-denied** to every proposal — the copilot cannot rewrite its own instructions or context.
* **Run text is never pasted into a proposal.** A proposal that echoes text verbatim from a run excerpt you read this turn is refused outright.

## Troubleshooting

* **A turn stops with a budget message.** The turn hit its per-turn model-call, tool-call, token, or wall-clock limit (see [Budget](#configuration)). Nothing is lost — send another message to continue, or raise the relevant limit in Configuration if this happens often.
* **A session seems stuck or is answering oddly.** Start a **New session** — the old one and its transcript are still there if you need to look back at it.
* **The copilot says the admin guide index is out of date.** The guide is re-indexed automatically at server start when a page changed on disk, so this usually means a page changed since the server last started. **Admin > Monitoring > Startup Health** shows the *Admin Help doc index* check as READY or DEGRADED with an **Ensure** action that re-indexes the changed pages; `python manage.py index_admin_docs` from the server directory does the same. Until then the copilot answers page and settings questions with that caveat rather than from memory.
* **The panel doesn't appear, or a turn always fails immediately.** Check **Admin > Monitoring > Startup Health** for the copilot's checks: whether its model resolves, whether the stored profile still validates, whether sessions can checkpoint, and whether every enabled skill is synced to the store. A failing check here explains most panel-wide issues before you need to look further.

## Deployment

The copilot runs inside the API process: a turn is an in-process task streaming to the socket that started it, and a REST rewind holds that session's busy slot for the length of its request (a close is refused while it is held). Two things follow for anyone running more than one API process (several Daphne workers, or several replicas behind one ingress).

* **A session's socket must reach the process running its turn.** A turn's stream can only be delivered by the process that runs it. A panel whose socket lands on a different process after a reconnect sees no turn on connect, and its next message is refused with *a turn is running on this session on another worker* until that turn ends — the turn itself finishes normally, and the transcript catches up on reload, but nothing streams. Route the copilot's WebSocket path (`/ws/admin-copilot/`) to **one API process**, or turn on **session affinity (sticky sessions) on the ingress** for it, so a reconnect goes back to the process it came from.
* **The busy claim is shared; nothing else needs configuring.** Whether a session is busy — a turn running, a rewind under way — is recorded in the default cache (Redis, the same `REDIS_URL` the channel layer uses), so a message, rewind or close served by *any* process while another runs the turn is refused with a 409 or the socket error above rather than racing it. A process that dies mid-turn stops holding its sessions within a minute. If Redis is unreachable, each process falls back to its own view and logs a warning: the copilot keeps working on a single process, and only the cross-process guard is off for the outage.
* **Startup Health says whether that claim is actually shared.** **Admin > Monitoring > Startup Health** carries an *Admin copilot shared busy claim* check. It reads the configured default cache backend and reports READY when it is Redis; when `REDIS_URL` is unset the default cache is Django's in-memory one, the claim is per-process, and the check reports MISSING naming the backend it found and telling you to set `REDIS_URL`. A deployment running one API process is unaffected — the per-process half of the slot is the whole slot there — so the check is a warning about scaling out, not about the copilot being broken today.
