roost-config
Skill32 starsChanged 18 days agoMITView on GitHub
Load the team's roost config (which tracker, which repos and sessions, the local stack's commands, branching rules, deploy and design details) and the adapter notes for its tracker and test tool. Every other roost skill uses this first, before it reads or changes anything. Use it directly when the user asks "what does roost know about our setup?" or "which config is roost using?".
In this repository22 other files it gives its agents.
- automate-testsSkill
- automate-xray-testsSkill
- bug-bashSkill
- build-epicSkill
- close-out-epicSkill
- create-xray-testSkill
- cross-repo-handoffSkill
- live-verified-bddSkill
- pair-runbookSkill
- release-notesSkill
- release-qaSkill
- retroSkill
- review-epicSkill
- run-bugSkill
- run-sliceSkill
- setupSkill
- shape-epicSkill
- slice-handoffSkill
- stacked-prsSkill
- triage-ticketSkill
- verify-slice-testsSkill
- write-runbookSkill
The file
We split this skill into the rules it gives an agent
7 rules. About 0 are common, 3 are distinctive, and 4 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 15
Use the first one that exists:
- Very specific: line 28
If there's none,: stop: say roost has no team config yet and that /roost:setup makes one.
- Very specific: line 43
Use the merged result everywhere.
- Very specific: line 52
Write it as one line: "roost 1.0.0, installed" or "roost 1.0.0, checkout at ~/src/roost, main@66cdcd9, uncommitted changes".
- Very specific: line 62
If the config names one that isn't supported yet, stop and say which key and value, and that roost doesn't support it yet.
- Distinctive: line 105
When the config has only one repo, there's one session, no cross-repo handoff, and "each repo, in order" is just that repo.
- Distinctive: line 120
A key a skill needs isn't set: ask the user for the value once, use it for this run, and suggest adding it to the config.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Roost Config
roost's skills describe a way of working. Everything specific to one team (its tracker and project, its repos, its local stack, its branching and deploy rules, its design system) lives in one config file. Load it before doing anything else, and use its values wherever a skill names a config key.
Read the config before any other call. Don't send a tracker, GitHub or other tool call in the same batch as reading the config, and never guess a value it holds (a site, a cloud ID, a project key, a repo name). Wait for the config, then use its values.
1. Find the config
Use the first one that exists:
- A path the user gave in this conversation.
- The
ROOST_CONFIGenvironment variable (echo "$ROOST_CONFIG"). .roost/config.ymlin the current directory or the nearest parent that has one (stop at the repo root).~/.claude/roost/config.yml(often a link to the team's shared config in its private repo).
Read the whole file. Say in one line which file you're using, and its team.name, when the user asks, when it isn't the one they'd expect, or when more than one of these places has a config (for example $ROOST_CONFIG names one team's and ~/.claude/roost/config.yml another's), so nobody runs one team's work on another's config.
One machine, several teams. A session outside every repo (an orchestrator) finds only $ROOST_CONFIG or ~/.claude/roost/config.yml. To work for a team whose config isn't the one in ~/.claude/roost/config.yml, start the session with ROOST_CONFIG=<path to that team's config> claude, or name the path in the conversation.
Orchestrators record their config. run-slice and build-epic write the config's path and team.name into their state file when a run starts. On a resume, if the config you loaded isn't the recorded one, stop and say which each is: the run continues on the recorded config once the user names it, or on the new one if they say so.
If there's none, stop: say roost has no team config yet and that /roost:setup makes one. Never fill a missing value in from memory, from the example config, or from another team's config.
The config you found is the config. Use its values as they are, even when they look made up: a config the user names may be the example one (examples/config.example.yml, the made-up Acme team), and that's their choice. Don't stop to ask whether it's "really" theirs, and don't check it against who the user seems to be (their email, signed-in accounts, other configs on the machine). If a value is wrong, the first call that uses it fails and says so; report that then.
Then apply this machine's overrides for this team. They hold only the values that differ on this machine (for example, a local database instead of the shared one). Overrides belong to one team, so another team's never apply:
~/.claude/roost/local/<team.name>.yml, if it exists. Use it, and ignorelocal.yml.- Otherwise
~/.claude/roost/local.yml, if it exists, but only if itsteam.nameis this config'steam.name, or it has noteam.name(the older form). Skip one that names another team. For the older form, say once that it applies to every team's config on this machine, and that moving it to~/.claude/roost/local/<team.name>.ymlmakes it this team's only.
The override's values win over the team config's:
- a mapping is merged key by key, so
stack.database.localin an override changes only that key; - any other value, a list included, replaces the team config's value whole;
reposentries are matched byname, so an override can change one repo's keys without restating the others.
Use the merged result everywhere. When a value you act on came from an override, say so once, naming the file ("using this machine's database, from local/Acme.yml"), so nobody mistakes it for the team's.
Which roost is running
Sessions can run different roosts: the installed plugin, or a checkout loaded with claude --plugin-dir <checkout> for that session only. Work out which one this session runs from this skill's own base directory, <plugin root>/skills/roost-config:
- The version:
versionin<plugin root>/.claude-plugin/plugin.json. - Installed or a checkout: it's a checkout when
git -C <plugin root> rev-parse --show-toplevelis the plugin root. Then add its branch, short commit, and whether it has uncommitted changes (git -C <plugin root> status --porcelain).
Write it as one line: "roost 1.0.0, installed" or "roost 1.0.0, checkout at ~/src/roost, main@66cdcd9, uncommitted changes". Give it when the user asks which roost or which version is running. Orchestrators record it (see run-slice).
2. Check it's supported
| Key | Supported now | Not yet |
tracker.kind | jira, github | linear |
tests.kind | xray, evals | repo-gherkin, none |
vcs.stacking | gh-stack, none | graphite |
If the config names one that isn't supported yet, stop and say which key and value, and that roost doesn't support it yet.
Skills the config names must be installed. When a step uses a skill the config names (for example roadmap.skill) and it isn't in this session's skill list, stop that step and tell the user which skill is missing and which plugin provides it. Don't look for its files on disk or do the step from memory: a fix that only works on this machine is worse than a clear stop. Carry on with steps that don't need it.
3. Read the adapter notes you'll need
In this skill's references/ folder:
config-schema.md: what every key means, and its default. Read it when a key's meaning isn't obvious from its name.- The tracker adapter for
tracker.kind, before any tracker change:tracker-jira.mdortracker-github.md. Each says how to do every tracker operation (create, set a parent, edit a comment in place, attach a file, change a status, link, search) and how its keys are written.
The skills say what to do in tracker-neutral words (a ticket, a comment, attach a file to the epic, move it to a status); the adapter says how, and how its keys are written ("Keys").
- The tests adapter for
tests.kind, before touching tests:tests-xray.md(Xray tests and their Cucumber scenarios), ortests-evals.md(eval cases in the repo). Each says what a test is, and its operations ("A story's tests", "Read a test's scenarios", "Run a test", "Mark a test automated", "Create a test", …) say how to do each thing the skills ask, with this team's tests.
How skills refer to the config
A skill writes a config key in backticks, for example tracker.statuses.ready_for_release or stack.services. Use the value from the config. A few names come up everywhere:
- The project and its keys: where the tracker's tickets live, and how a key is written, are in the tracker adapter ("Keys"). The skills' examples write keys as
PROJ-1234. - The repos: the
reposlist. Each has aname, arole, anorder, and asessionname (for example "API session"). "Each repo, in order" means byorder. A repo withdepends_onwaits for those repos' work and follows itscontractto consume it. - The lead repo:
planning.lead_repo, where the tech lead plans, the plan and runbooks live, and the stack's owner usually runs. Default: the repo with the lowestorder. - A repo's session: the Claude session whose working directory ends in that repo's
name. - The local stack:
stack. Itsservicesare what "the stack answers" means; its commands start, inspect and migrate it.stack.neverlists commands not to use, andstack.test_datasays how to put data into it. A repo withlocal_stack: false(an infrastructure repo, say) neither runs on it nor uses it: leave the stack's instructions, checks and browser testing out of everything sent to its session. - Sections a config can leave out:
stack,deploy,design,qaandroadmap, and a repo'se2e(a plugin or a library may have none of them). A step that needs one the config doesn't have is skipped, and the skill says once what it skipped and why ("this config has no local stack, so there's nothing to start or check"). A missing key is neither true nor false: "ifdeploy.migrations_run_on_deployis false" doesn't apply when there's nodeploy. With nostack, every repo is treated aslocal_stack: false. When a skill can't do its main job without one (write-runbookwithoutdeploy.runbooks), ask the user for the value once, as for any key that isn't set. - Without stacking (
vcs.stacking: none): there are no stacks. Wherever a skill says "the stack", "on the stack" or "the epic's stack", read: each branch starts frommainand is its own PR againstmain, and "the epic's stack" is the epic's set of PRs. Nothing is rebased, linked or merged as a stack:stacked-prs' stack steps and "One deploy per stack" don't apply; its rules for commits, PR titles and the Release order comment do. A branch that needs another branch's code starts from that branch, and its PR, againstmain, says it waits for that one ("Waits for #<PR>"); once that one merges, rebase it ontomain. Nothing waits for a merge before it can start, so a slice's later repos aren't held up by a review that comes after them.review-epiccovers every PR the epic has, merged or open (the Release order comment lists them), notmain...<top branch>. "Ready for release" means merged, waiting for the release: so before a story or bug moves totracker.statuses.ready_for_release(the end ofrun-slice,run-bug, and the before-release work inbuild-epic), its PRs must have passed their reviews and checks, and the user merges them, in Release order. Ask them to, naming each PR, and check each one merged before moving the ticket. - Repos that change real infrastructure: a repo with
apply_gate(see below). - A repo's review focus: its
review_focus, or elseplanning.review_focus. - Person-owned tasks: tickets under an epic labelled
tracker.person_task_label(defaultperson-owned). They're work no repo session can do, with a named owner and a "When" (before release, after release, or after a named slice). Never send one to a session, never move one out of its epic, and never count one as leftover work; remind its owner when its "When" arrives. - The state directory:
team.state_dir, where orchestrators keep their records. Default~/.claude/roost/state/<team.name>/, so two teams on one machine never share state files. Say which folder you're using the first time you read or write one.- A run's record may be somewhere else. When an orchestrator finds no state file for its ticket in this folder, before it treats the run as new or rebuilds it from the tracker, it looks for
<KEY>.mdin the other folders under~/.claude/roost/state/and in that folder itself. If one is there, stop: say where it is and which config it records, and carry on only once the user says which config the run uses. - Older records. Before per-team folders, a config without
state_dirkept its records directly in~/.claude/roost/state/. When this team's folder is the default and that folder has state files of its own (*.md, not in a team's folder), say how many there are and ask the user which team they belong to. Move them into that team's folder only once the user says; never assume they're this team's, and never leave them unmentioned.
- A run's record may be somewhere else. When an orchestrator finds no state file for its ticket in this folder, before it treats the run as new or rebuilds it from the tracker, it looks for
The apply gate
A repo with apply_gate holds code that changes real resources when applied (OpenTofu or Terraform against a cloud account, for example), not code tested on the local stack. Its work isn't done when its PR merges; it's done when a reviewed plan is applied. Every skill follows this:
- The session plans, and posts the plan. It runs
apply_gate.planand puts the output (or its summary of what's added, changed and destroyed, with the full output linked) in the PR. Every push that changes the plan gets a new one. - Nothing is applied without the user. No session runs
apply_gate.applyon its own, against anything. The orchestrator stops at an apply checkpoint: it shows the plan's summary, anything destroyed or replaced first, and asks the user. Withapply_gate.who: user(the default), the user applies it themselves; withagent_after_approval, the user may tell the session to apply that exact plan instead. - Apply when
apply_gate.whensays:before_merge(the default) applies the plan from the PR's head, so a failed apply is fixed on the same PR andmainalways matches what's applied;after_mergeapplies frommainonce the PR merges. - Record it. A comment on the story starting
apply:with the date, who applied, the PR and commit, the plan's summary, and the result. Then a fresh plan should show no changes; anything else is a finding.
deploy.runbooks.never_run_against applies to every session, not only pair-runbook: a session never changes anything there itself. Reading (a plan, a describe, a list) is fine unless the user says otherwise.
When the config has only one repo, there's one session, no cross-repo handoff, and "each repo, in order" is just that repo.
When you stop for the user
Every roost skill stops for the user at its checkpoints. End each stop with the exact next step, as a question that names the skill or phase it starts ("Next: slice-handoff for PROJ-2789, then the PROJ-2790 kickoffs. Go ahead?"), with your recommendation when there's a choice. The user should never have to work out what "keep going" will do.
A stop for a blocker isn't a checkpoint. When you stop because something only the user can fix is missing (a session that isn't open, a tool or connection this session doesn't have, a sign-in), say what's missing and what you'll do once it's there, as a statement: "Once both sessions are open, I'll pick up at slice 4." Don't ask whether to go ahead; being asked to do the work was the go-ahead. A decision the skill gives the user is different: if one is open (which artifact to build, where a doc goes), still put it to them, with your recommendation, so they can answer while the blocker is being fixed.
The signed-in browser
With tracker.kind: github, gh does everything and no browser is needed for the tracker. Jira steps the MCP can't do (attachments, parent changes, comment edits) use an API token when JIRA_API_TOKEN is set, and need no browser (see the Jira adapter notes). Only without the token, or to file Xray tests without the Xray keys, do they use the user's signed-in browser (the Playwright MCP). Every session on the machine shares that one browser, and only one session can use it at a time.
- "Browser is already in use": another session holds it. Never kill the browser or its processes; that ends another session's work. Tell the user which step is waiting, and ask them to free it (finish or close the other session's browser work), then retry.
- Checks that don't need anyone signed in (opening a local prototype, a page on the local stack) use an isolated browser if one is set up (a second Playwright MCP server started with
--isolated). If none is, and the shared one is busy, say plainly that the check wasn't done and why. Never present unchecked work as checked.
A key a skill needs isn't set: ask the user for the value once, use it for this run, and suggest adding it to the config. Don't invent it.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

