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

# Share instructions across repositories

> Keep agent instructions, policy and validators in one repository, and the one commit each of the others must make.

Every repository can keep its own agent instructions and checks. When you run several repositories, you can instead keep them in one, the control plane, and the others use them.

<Frame>
  <img className="block dark:hidden" src="https://mintcdn.com/outerlayer-stg/M2z6K_ww2sAtqE1w/images/control-plane-light.svg?fit=max&auto=format&n=M2z6K_ww2sAtqE1w&q=85&s=d5bc102f3d18c7d6270d0876d2b99ee1" alt="Where each repository's agent instructions and checks come from. Without a control plane, acme/factory, acme/web and acme/api each keep their own rules, and they can drift apart. With a control plane, acme/factory holds the rules and acme/web and acme/api use the same rules, so a change is made once for all. Rules are the agent instructions in AGENTS.md, the skills in .agents/skills/, and the policy and checks in .outerlayer/." width="660" height="350" data-path="images/control-plane-light.svg" />

  <img className="hidden dark:block" src="https://mintcdn.com/outerlayer-stg/M2z6K_ww2sAtqE1w/images/control-plane-dark.svg?fit=max&auto=format&n=M2z6K_ww2sAtqE1w&q=85&s=a20f2e5daf188d654df01d7e444a344c" alt="Where each repository's agent instructions and checks come from. Without a control plane, acme/factory, acme/web and acme/api each keep their own rules, and they can drift apart. With a control plane, acme/factory holds the rules and acme/web and acme/api use the same rules, so a change is made once for all. Rules are the agent instructions in AGENTS.md, the skills in .agents/skills/, and the policy and checks in .outerlayer/." width="660" height="350" data-path="images/control-plane-dark.svg" />
</Frame>

A factory's control plane is one of its linked repositories. It governs every other repository the factory links. Two things then come from the control plane instead of from each repository:

| What | Read from |
| - | - |
| Agent instructions and skills | The control plane, for every other repository the factory links |
| Policy and validators | The control plane, for every work item in the factory, whatever repository the pull request is in |

Use it when you want one place to change how agents behave and what evidence they must produce. A laptop session gets the shared instructions at launch from a copy kept outside the repository. As a result, nothing from the control plane is written into the working tree or committed there.

Pull request checks and work item evaluation use the control plane of the factory the repository belongs to. A control plane in another factory never governs it.

A control plane governs the factory's repositories in every GitHub organization the factory links, not only its own. A control plane in `acme` governs a repository in `acme-labs`. OuterLayer reads the control plane through the installation that covers `acme`. It reads each governed repository through its own organization's installation. See [Repositories from more than one GitHub organization](/connect-repository#repositories-from-more-than-one-github-organization).

Without a control plane, each repository commits its own agent instructions: `AGENTS.md`, `.agents/skills/` and `.mcp.json`, with `.outerlayer/` for its policy. A repository that has not [moved to that layout](/upgrade#move-a-repository-to-the-agentsmd-layout) commits `.outerlayer/` and the files [`outerlayer context emit`](/reference/cli-context#outerlayer-context-emit) writes from it. A factory with one repository and no control plane named checks each pull request under the policy in its own repository, at its base branch. The control plane itself is checked the same way.

<Note>
  The dashboard, CLI messages and API errors all call it the control plane. The API path still says `context-source`, and older versions of the dashboard said context source.
</Note>

## The default control plane

A factory with several linked repositories and none named has its first-linked repository as its control plane, read at that repository's default branch. First-linked means the repository whose link to the factory is oldest. A repository that is unlinked and linked again counts from its new link.

No step is needed. Linking a second repository makes the first one the control plane. Unlinking the default control plane makes the next first-linked repository the default. A factory with one repository is its own control plane and builds as before.

The Repositories list marks a control plane chosen this way with a **Default control plane** badge. An admin can name another repository as the control plane, as described below. A named control plane replaces the default.

In such a factory, the other repositories stop reading their own `.outerlayer/` folder. They follow the control plane's instructions and checks. A build's image and setup commands follow it too. See [Build a repository's image from its devcontainer file](/build-image-from-devcontainer).

## Set the control plane

Link the repository to the factory first, as you would any repository. Then open the factory's **Settings → General**. In the Repositories list, choose **Use as control plane** on the repository's row. The dialog asks you to confirm by typing the name. The control plane is read from the branch that row links.

The row of the control plane then carries a **Control plane** badge, and every other row offers **Use as control plane**. To remove it, choose **Stop using as control plane** on its row.

Setting the control plane takes the `governance.insert` permission, and removing it takes `governance.delete`. Through the API, a repository the factory does not link is refused with `repository_not_linked`. A factory has at most one control plane. Choosing another repository replaces the one in place, and the dialog names the one it replaces.

Unlinking a control plane you named removes it. The factory then uses its first-linked repository as the control plane, or none when only one repository is left. Renaming the repository on GitHub does not: it stays the control plane under its new name.

Linking a repository puts it under the control plane. Nothing else needs to name it. The gateway does not read a `governs` list to decide this. The control plane itself is not governed: it keeps its own instructions.

OuterLayer stores, for each linked repository, whether it is governed and the commit of the control plane's branch that answer was read at. It refreshes that when you set or remove the control plane, and when you link a repository. It also refreshes it when someone pushes to the control plane's branch, and every 15 minutes. If the control plane cannot be read, a governed repository stays governed and keeps the commit it had.

A `governs` list in the control plane's `.outerlayer/config.json` decides nothing: the `governs` list is ignored. To remove one, see [A `governs` list is no longer read](/upgrade#a-governs-list-is-no-longer-read).

## What a governed repository commits

A governed repository keeps its own instructions next to the shared ones. A governed repository commits its own `CLAUDE.md`, `AGENTS.md` and skills. Claude Code reads them together with the control plane's.

One rule protects the shared skills. A repository cannot replace a shared skill. If it commits a skill with the same name as a shared skill, in `.claude/skills/` or `.agents/skills/`, `outerlayer claude` refuses to start. The message names the folder where the repository keeps the skill, and it names `.claude/skills/` when the skill is in both. Rename or remove the repository's skill.

A repository that an earlier CLI governed may still hold `.outerlayer-pin.json`. See [Files an earlier CLI left in a governed repository](/upgrade#files-an-earlier-cli-left-in-a-governed-repository) before you commit its own `AGENTS.md` or delete the pin.

## What a laptop session does in a governed repository

A laptop session gets the control plane through `outerlayer claude`. It starts Claude Code, and passes the caller's arguments on unchanged:

```bash theme={"system"}
outerlayer claude [claude arguments]
```

Before it starts Claude Code, the launcher asks OuterLayer which repository supplies this checkout, and which commit of it OuterLayer stored. It fetches exactly that commit from your git host with your own git credentials, once per commit, and keeps a composed copy under `~/.outerlayer/context/<owner>/<repo>/<sha>/`. It starts Claude Code with that directory added, so the agent and every subagent follow the shared `AGENTS.md`, skills, agents and hooks. A session therefore reads the same commit that the pull request check later judges its work against, not the branch's newest commit. The launcher records the copy it used in the repository's git directory, which is not part of the working tree.

It copies only four paths from the control plane, as committed there: `AGENTS.md`, everything under `.claude/`, everything under `.outerlayer/`, and everything under `.agents/`. A root `CLAUDE.md`, `.cursor/` and `.mcp.json` are not copied. So a governed repository gets Claude Code's skills and commands, the skills the control plane keeps in `.agents/skills/`, and the shared `AGENTS.md`. It does not get Cursor's skills or any MCP servers. It also adds the five maintained skills at the installed CLI's version.

In three cases, `outerlayer claude` starts Claude Code with your arguments alone and makes no network call. It does this outside a git repository, in a repository that no control plane governs, and when you are not signed in.

If OuterLayer cannot be reached, the launcher starts with the last copy it stored and says that copy may be out of date. With no stored copy it stops, and says the control plane could not be fetched. It does not start a governed session without the shared rules.

`outerlayer init` can make plain `claude` do the same. It asks whether to add a `claude` function to your zsh or bash startup file. The function runs `outerlayer claude` with the same arguments. Run `outerlayer init --shell-function` to add it without being asked, or `outerlayer init --no-shell-function` to remove it. The function exists only in an interactive shell.

### Sessions that do not get the control plane

The Claude desktop app, IDE extensions, cloud sessions, and scripts that run `claude` directly do not get the control plane. They do not start through a shell that has the function.

A session started that way says so. The session-start hook shows a message that the shared instructions and skills are missing, and names `outerlayer claude`.

A script gets the control plane by running `outerlayer claude`. Give it the arguments you gave `claude`.

## What a build does in a governed repository

A container or microVM build of a governed repository gets the control plane the same way. The runner delivers it to a directory outside the checkout, so the build cannot commit it. A command in your runner config, such as `claude -p "Build the issue"`, needs no change. See [A governed repository's context](/container-builds#a-governed-repositorys-context).

## Hooks in a control plane

A hook in the control plane must reach its scripts through `${OUTERLAYER_CONTROL_PLANE_DIR:-$CLAUDE_PROJECT_DIR}/.claude/…`, because the control plane's `.claude/` is not in the build's checkout. Delivery also rewrites `$CLAUDE_PROJECT_DIR/.claude`, `"$CLAUDE_PROJECT_DIR"/.claude` and `${CLAUDE_PROJECT_DIR}/.claude`. Run `outerlayer doctor` in the control plane to name any hook that reaches `.claude/` another way.

## Keep skills in `.agents/skills/`

A control plane may keep its skills in `.agents/skills/`. Codex and other tools that follow the `SKILL.md` standard read that folder. Claude Code looks only in `.claude/skills/`, so the CLI delivers each skill from `.agents/skills/` into the delivered `.claude/skills/` under its own name. A skill is copied whole. When the control plane has a skill of the same name in both folders, the `.claude/skills/` copy is delivered. No file from the other copy is mixed in.

A path under `.agents/` in a skill is never rewritten, so a skill should name its own files relative to its folder, or with `${CLAUDE_SKILL_DIR}`. In the control plane's `AGENTS.md`, a path to a file under `.agents/` that the delivery holds names that file in the delivery directory. A path to a folder, or to a file the control plane does not deliver, keeps naming the checkout.

A control plane that keeps skills in `.agents/skills/` can declare `"layout": 2` in `.outerlayer/config.json`. A control plane with no `layout` key is layout 1. A control plane that declares any other layout is refused, by `outerlayer claude` and by a build, with a message that names what the file says. For a layout newer than 2 the message says the CLI needs upgrading. For anything else, such as `0`, `1.5` or `"two"`, or a `config.json` that is not valid JSON, it says to fix the file. A build ends with `context_layout_unsupported`.

### Move a control plane without breaking older CLIs

A CLI or runner older than the first release that reads `.agents/skills/` delivers only `.claude/skills/`. It gives no warning for a control plane that keeps its skills elsewhere. A control plane can move without breaking those CLIs and runners if it keeps both folders for a while.

1. Keep `.claude/skills/` as an identical copy of `.agents/skills/`. Older CLIs deliver it, and newer CLIs prefer it.
2. Make every edit in both folders. An edit to `.agents/skills/` alone reaches nobody, because the `.claude/skills/` copy wins.
3. Delete `.claude/skills/` once every CLI and runner your team uses runs the first release that reads `.agents/skills/`. That release is 0.21.0. `outerlayer runner status` shows a runner's version. The control plane's owner decides when.

### A repository's own skills

A repository can keep its own skills in `.agents/skills/` too, in a control plane or in a repository that no control plane governs. See [Agent context](/agent-context#where-a-session-finds-skills).

## Set a check's level everywhere at once

Give a check a level in the control plane's policy, and it applies to every repository it supplies. This turns `code-review` off everywhere:

```yaml theme={"system"}
validators:
  code-review: off
```

A policy error row has no level and cannot be turned off. See [Policy and validators](/policy-and-validators).

## Manage it through the API

The factory's settings page calls `/v1/orgs/{orgName}/apps/{appId}/context-source`, and a script can call it too. `{appId}` is the factory's id.

| Method | Operation | Permission |
| - | - | - |
| `GET` | Read the designation | `governance.read` |
| `PUT` | Mark a linked repository as the control plane | `governance.insert` |
| `DELETE` | Remove the control plane | `governance.delete` |

A signed-in member's session works on every plan. A [management API key](/organization-api) works on an Enterprise plan. A factory's own API key never can. See [Organization API](/organization-api) for keys and permissions.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.