@nanocollective/roster 0.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +129 -0
  3. package/dist/cli.js +5679 -0
  4. package/docs/README.md +99 -0
  5. package/docs/agents.md +163 -0
  6. package/docs/architecture.md +121 -0
  7. package/docs/commands.md +223 -0
  8. package/docs/concepts.md +112 -0
  9. package/docs/cost.md +61 -0
  10. package/docs/developing.md +147 -0
  11. package/docs/doctor-codes.md +74 -0
  12. package/docs/export.md +113 -0
  13. package/docs/extending.md +97 -0
  14. package/docs/getting-started.md +134 -0
  15. package/docs/hosting.md +72 -0
  16. package/docs/manual-steps.md +163 -0
  17. package/docs/memory.md +71 -0
  18. package/docs/org-yaml.md +143 -0
  19. package/docs/portal.md +342 -0
  20. package/docs/prompts.md +133 -0
  21. package/docs/security.md +122 -0
  22. package/docs/session-workflow.md +112 -0
  23. package/docs/staff-yaml.md +163 -0
  24. package/docs/troubleshooting.md +189 -0
  25. package/docs/upgrading.md +83 -0
  26. package/docs/writing-a-charter.md +83 -0
  27. package/package.json +60 -0
  28. package/templates/brain/.github/workflows/%%STAFF%%-daily.yaml +33 -0
  29. package/templates/brain/.github/workflows/%%STAFF%%-mention.yaml +65 -0
  30. package/templates/brain/.github/workflows/%%STAFF%%-pr-mention.yaml +50 -0
  31. package/templates/brain/CHARTER.md +49 -0
  32. package/templates/brain/README.md +18 -0
  33. package/templates/brain/drafts/README.md +7 -0
  34. package/templates/brain/log/decisions.md +6 -0
  35. package/templates/brain/memory/INDEX.md +28 -0
  36. package/templates/brain/staff.yaml +44 -0
  37. package/templates/brain/strategy/README.md +7 -0
  38. package/templates/briefs/amend.md +60 -0
  39. package/templates/briefs/charter.md +47 -0
  40. package/templates/briefs/discover.md +61 -0
  41. package/templates/briefs/voice.md +53 -0
  42. package/templates/ops/.github/workflows/session.yaml +333 -0
  43. package/templates/ops/agents.mjs +143 -0
  44. package/templates/ops/compose.mjs +333 -0
  45. package/templates/ops/org/guardrails.md +14 -0
  46. package/templates/ops/org/operating.md +82 -0
  47. package/templates/ops/org/voice.md +40 -0
  48. package/templates/ops/prompts/_identity.md +14 -0
  49. package/templates/ops/prompts/_paths.md +15 -0
  50. package/templates/ops/prompts/daily.md +82 -0
  51. package/templates/ops/prompts/mention.md +53 -0
  52. package/templates/ops/prompts/pr-mention.md +57 -0
  53. package/templates/ops/runner-plan.mjs +65 -0
  54. package/templates/portal/css/base.css +104 -0
  55. package/templates/portal/css/brain.css +106 -0
  56. package/templates/portal/css/diff.css +28 -0
  57. package/templates/portal/css/graph.css +34 -0
  58. package/templates/portal/css/health.css +41 -0
  59. package/templates/portal/css/inbox.css +79 -0
  60. package/templates/portal/css/layout.css +98 -0
  61. package/templates/portal/css/markdown.css +54 -0
  62. package/templates/portal/css/setup.css +106 -0
  63. package/templates/portal/index.html +55 -0
  64. package/templates/portal/js/api.js +74 -0
  65. package/templates/portal/js/app.js +282 -0
  66. package/templates/portal/js/dialog.js +70 -0
  67. package/templates/portal/js/dom.js +106 -0
  68. package/templates/portal/js/icons.js +94 -0
  69. package/templates/portal/js/md.js +386 -0
  70. package/templates/portal/js/refresh.js +59 -0
  71. package/templates/portal/js/router.js +20 -0
  72. package/templates/portal/js/state.js +160 -0
  73. package/templates/portal/js/textdiff.js +96 -0
  74. package/templates/portal/js/views/app.js +128 -0
  75. package/templates/portal/js/views/brain.js +260 -0
  76. package/templates/portal/js/views/changed.js +157 -0
  77. package/templates/portal/js/views/checklist.js +87 -0
  78. package/templates/portal/js/views/docs.js +84 -0
  79. package/templates/portal/js/views/files.js +95 -0
  80. package/templates/portal/js/views/graph.js +436 -0
  81. package/templates/portal/js/views/health.js +158 -0
  82. package/templates/portal/js/views/inbox.js +549 -0
  83. package/templates/portal/js/views/memory.js +135 -0
  84. package/templates/portal/js/views/org.js +175 -0
  85. package/templates/portal/js/views/paste.js +142 -0
  86. package/templates/portal/js/views/prompt.js +412 -0
  87. package/templates/portal/js/views/repos.js +92 -0
  88. package/templates/portal/js/views/setup.js +344 -0
  89. package/templates/portal/js/views/staff.js +290 -0
@@ -0,0 +1,134 @@
1
+ ---
2
+ title: "Getting started"
3
+ description: "Stand up an org and a first staff member, in seven steps."
4
+ sidebar_order: 1
5
+ ---
6
+
7
+ # Getting started
8
+
9
+ ```bash
10
+ npx @nanocollective/roster
11
+ ```
12
+
13
+ Run that in an empty directory. It opens a portal in your browser and walks the whole setup:
14
+ it checks `gh`, lists the organisations you can see, and asks which one.
15
+
16
+ **Two answers, and it works out which you need.** An organisation that does not run roster yet
17
+ gets one stood up. One that already does gets checked out here instead, ops repo and every
18
+ staff repo side by side, which is the shape the CI runner uses. That is how a second person on
19
+ a team joins an org somebody else set up.
20
+
21
+ From there the page carries the rest: the Actions setting that has to be clicked, the repos
22
+ your staff work in, hiring, each GitHub App, and a prompt you paste into your own AI to write
23
+ `org/business.md` and the charters.
24
+
25
+ You need `gh` authenticated, and a credential for whichever [coding agent](agents.md) you want
26
+ to run.
27
+
28
+ ---
29
+
30
+ The rest of this page is the same setup from a terminal. Everything the portal does, these do;
31
+ nothing writes without `--apply`.
32
+
33
+ ## 1. Stand up the org
34
+
35
+ ```bash
36
+ roster init --org acme --name "Acme Robotics"
37
+ ```
38
+
39
+ That prints the plan. Read it, then:
40
+
41
+ ```bash
42
+ roster init --org acme --name "Acme Robotics" --apply
43
+ ```
44
+
45
+ You now have `acme/roster-ops`: the org layer, the runner machinery, and a recorded merge base
46
+ so later upgrades are merges rather than copies.
47
+
48
+ Then do the one thing that cannot wait: **Settings -> Actions -> General on `roster-ops`, set
49
+ access to "accessible from repositories in the organisation".** Skip it and every workflow
50
+ later fails with "workflow not found", which reads like a typo and is not one.
51
+
52
+ ## 2. Say what the business is
53
+
54
+ Open `roster-ops/org/business.md`. It ships as questions. Answer them, or:
55
+
56
+ ```bash
57
+ roster brief discover # paste into whatever agent you use
58
+ ```
59
+
60
+ Or, in Claude Code, `cd roster-ops && claude` then `/discover`. Both print the same brief:
61
+ `roster init` generates the slash command from it.
62
+
63
+ Do this before hiring anyone. It is composed into the top of every prompt, and an agent that
64
+ cannot answer these questions writes plausible work about a business that does not exist.
65
+
66
+ ## 3. Hire someone
67
+
68
+ ```bash
69
+ roster hire cto --name "Chief Technology Officer" --dir technology
70
+ ```
71
+
72
+ Read the plan. It lists every file, every label, the peer wiring in both directions, and the
73
+ things it cannot do for you. Then `--apply`.
74
+
75
+ For the first hire in a new org there is nobody to copy an identity from, so name them:
76
+
77
+ ```bash
78
+ roster hire cto --name "Chief Technology Officer" --dir technology \
79
+ --app acme-cto --public-app acme-robot --apply
80
+ ```
81
+
82
+ Later hires infer both from whoever is already there.
83
+
84
+ ## 4. Give them an identity
85
+
86
+ ```bash
87
+ roster app cto
88
+ ```
89
+
90
+ A browser opens, GitHub asks you to confirm, and the App's id and private key go straight into
91
+ the repository's secrets. The key never touches disk.
92
+
93
+ Then **install it**, using the URL that command prints, granting it every tracker the staff
94
+ member writes to. This is the step that most often looks done and is not. See
95
+ [manual steps](manual-steps.md#3-install-the-app-and-grant-it-the-right-repositories).
96
+
97
+ ## 5. Write the charter
98
+
99
+ ```bash
100
+ roster brief charter cto # paste into whatever agent you use
101
+ ```
102
+
103
+ Or, in Claude Code, `cd technology && claude` then `/charter`. Same brief either way.
104
+
105
+ This is the file that decides everything else. [Writing a charter](writing-a-charter.md).
106
+
107
+ ## 6. Check, then run one by hand
108
+
109
+ ```bash
110
+ roster doctor cto
111
+ ```
112
+
113
+ Fix what it says. Then trigger the daily workflow once from the Actions tab and read the log.
114
+
115
+ **A workflow that has never run has proved nothing.** Not that the App is installed, not that
116
+ the grant took, not that the secrets are right. `doctor` says `unproven` rather than `fine` for
117
+ exactly this reason.
118
+
119
+ ## 7. Look at it
120
+
121
+ ```bash
122
+ roster portal
123
+ ```
124
+
125
+ Everything open across the org, every staff member's memory, what changed since yesterday, and
126
+ whether anything is unhealthy. Reads the repositories on disk, so keep them checked out
127
+ alongside each other.
128
+
129
+ ## Where things go from here
130
+
131
+ - A second staff member: `roster hire`, then `roster app`. Peer wiring happens both ways.
132
+ - A change to how everyone writes: edit `org/voice.md` once. It reaches everybody on their next
133
+ run.
134
+ - A framework update: `roster upgrade`. See [upgrading](upgrading.md).
@@ -0,0 +1,72 @@
1
+ ---
2
+ title: "Hosting the portal"
3
+ description: "Local is the default, and the honest options if you want it elsewhere."
4
+ sidebar_order: 11
5
+ ---
6
+
7
+ # Hosting the portal
8
+
9
+ `roster portal` is local-first and that is the intended way to run it. It reads the checked-out
10
+ repositories from disk, needs no authentication, hits no API rate limits, and works offline.
11
+
12
+ ```bash
13
+ roster portal # http://localhost:4300
14
+ roster portal --port 4400
15
+ ```
16
+
17
+ ## Why local is the default
18
+
19
+ **Setup is local only.** The screen that stands up a tenant also creates GitHub Apps and writes
20
+ repository secrets, and none of that belongs behind a tunnel or an access proxy. Everything below
21
+ is about a portal over an org that already exists.
22
+
23
+ The portal can write. It replies to issues, closes them, reopens them and opens new ones,
24
+ acting as you through your own `gh`. That is the point: it is where a person answers their
25
+ agents. It also means it is not a thing to leave listening on a network.
26
+
27
+ Three things guard it, and they only work locally:
28
+
29
+ - it binds to `127.0.0.1`
30
+ - a write needs a `POST` with an `x-roster` header, which forces a CORS preflight that fails
31
+ from any other origin
32
+ - an `Origin` header, if present, must be localhost
33
+
34
+ `--host` overrides the bind address and prints a warning. Do not use it to "just have a look
35
+ from my phone".
36
+
37
+ ## Reading it from elsewhere
38
+
39
+ The honest options, in order of how much you should like them.
40
+
41
+ **A tunnel, briefly.** `ssh -L 4300:localhost:4300 you@yourmachine`, or Tailscale, or
42
+ `cloudflared tunnel --url http://localhost:4300`. Your machine stays the only thing running it
43
+ and the write path stays behind your own network identity. Turn it off afterwards.
44
+
45
+ **A read-only static build.** `roster export` produces the whole org as one JSON document, and
46
+ the portal's rendering is decoupled from its parsing precisely so this is possible:
47
+
48
+ ```bash
49
+ roster export --out org.json
50
+ ```
51
+
52
+ Serve that plus the portal HTML from anything static (Cloudflare Pages, S3, a directory). You
53
+ get memory, the graph, the brain and what-changed. You do not get the inbox or any write
54
+ action, because both need `gh`.
55
+
56
+ Put access control in front of it. Cloudflare Access with a one-person policy is the least
57
+ work. **This is private business content and a map of what your agents know**, so an
58
+ unauthenticated URL is not an option even if nobody knows it.
59
+
60
+ **A hosted instance with write access.** Not documented, and not recommended. It would need a
61
+ GitHub App of its own, a session layer, and a credible answer to "what can this thing do if
62
+ someone reaches it". None of that exists, and the local server plus a tunnel gets you the same
63
+ outcome with none of it.
64
+
65
+ ## Keeping it current
66
+
67
+ The portal reads your working tree, so a stale checkout shows stale data. `/api/sync` fetches
68
+ and fast-forwards every repository on each refresh. It refuses to pull one that is dirty or has
69
+ diverged, and says which in a banner rather than guessing.
70
+
71
+ If you host a static export, the export is a point in time. Regenerate it on a schedule, or
72
+ accept that it is yesterday's.
@@ -0,0 +1,163 @@
1
+ ---
2
+ title: "Manual steps"
3
+ description: "Every human action, why it cannot be automated, and what breaks if you skip it."
4
+ sidebar_order: 2
5
+ ---
6
+
7
+ # Manual steps
8
+
9
+ Everything a human has to do, why it cannot be automated, and what it looks like when you skip
10
+ it. This page exists because every item on it has cost somebody real time.
11
+
12
+ **The portal walks you through most of this now.** `roster` with no arguments opens a setup screen
13
+ that deep-links item 1, runs items 2 and 3 for you as far as GitHub allows, and hands you a prompt
14
+ for items 5 and 6. This page is still the *why*: it is what to read when one of them bites, and
15
+ what to check when the page says something is not done.
16
+
17
+ `roster doctor` checks most of these. Run it after each one.
18
+
19
+ ---
20
+
21
+ ## 1. Allow the ops repo's workflow to be called
22
+
23
+ **Do:** `<org>/roster-ops` -> Settings -> Actions -> General -> *Access* -> **Accessible from
24
+ repositories in the organisation**. The setup screen links straight to that page.
25
+
26
+ **Why not automated:** it is an organisation permission on a repository, and the API for it
27
+ needs admin rights that a token created for a different purpose should not have. roster reads
28
+ it and tells you, but setting it is one click and it is yours.
29
+
30
+ **If you skip it:** every caller fails with **"workflow not found"**. That reads like a typo in
31
+ a path, or a missing file, or a bad branch reference. You will check all three. It is none of
32
+ them, it is this.
33
+
34
+ **Check:** `roster doctor` reports `roster-ops is callable from the whole org`.
35
+
36
+ ---
37
+
38
+ ## 2. Create the GitHub App
39
+
40
+ **Do:** the **GitHub App** button on a staff card in the portal, or `roster app <handle>` in a
41
+ terminal. Either opens a browser, GitHub asks you to confirm, and you come back. Credentials go
42
+ straight into the repository's secrets.
43
+
44
+ **Why not fully automated:** there is no API that creates a GitHub App. The only route is the
45
+ App Manifest flow: POST a manifest to a settings page, a human confirms, GitHub returns a
46
+ one-time code. roster does everything either side of that confirmation.
47
+
48
+ **If you skip it:** the run fails at the token-minting step with a message about the app not
49
+ existing.
50
+
51
+ **Note:** the private key is handed to `gh` on standard input. It is never written to a file,
52
+ never passed on a command line, and never appears in the process table. If the secret write
53
+ fails after the App is created, the key is gone: generate a new one from the App's settings
54
+ page and set the secret by hand. roster tells you this if it happens.
55
+
56
+ ---
57
+
58
+ ## 3. Install the App, and grant it the right repositories
59
+
60
+ **Do:** open the URL the portal shows, or that `roster app` prints. Choose repositories.
61
+
62
+ **Why not automated:** installing is a grant of access to specific repositories, and GitHub
63
+ requires a human to choose them. This is the correct behaviour and should not be worked around.
64
+
65
+ **Grant it on every tracker the staff member writes to**, not just their own. The token is
66
+ minted organisation-wide, and a peer's board is where a brief lands. Both the portal and
67
+ `roster app` say so.
68
+
69
+ **If you skip it, or under-grant it:** this is the trap that costs the most time, because of
70
+ how it fails.
71
+
72
+ > The API reports an App's **declaration** separately from an installation's **grant**.
73
+ > `GET /apps/<slug>` will happily tell you the App exists and has `contents: write`. That says
74
+ > nothing about whether it has been installed on the repository you care about. Two of our
75
+ > Apps declare permissions they were never granted.
76
+
77
+ So: **do not verify an installation by reading the API.** The only thing that proves the whole
78
+ chain (App created, installed, granted, secrets right, workflow reachable) is a run that
79
+ finished. `roster doctor` reads a window of recent runs for exactly this reason, and reports a
80
+ workflow that has never run as **unproven** rather than as fine.
81
+
82
+ **Check:** `roster doctor <handle>`, then trigger one run and look again.
83
+
84
+ ---
85
+
86
+ ## 4. Set the agent's credential
87
+
88
+ **Do:** put the coding agent's credential on each brain repo as a secret. The name follows the
89
+ credential: `CLAUDE_CODE_OAUTH_TOKEN`, `CODEX_API_KEY`, and so on. See
90
+ [choosing a coding agent](agents.md).
91
+
92
+ **Why not automated:** it is your account's credential and roster has no way to obtain one.
93
+
94
+ **If you skip it:** the run fails immediately with `the caller passed no agent credential`.
95
+ That check exists so it fails there rather than forty lines later inside the agent, after the
96
+ checkouts have already happened.
97
+
98
+ **Check:** `roster doctor` lists the secrets each caller references and whether they exist.
99
+
100
+ ---
101
+
102
+ ## 5. Write `org/business.md`
103
+
104
+ **Do:** answer the questions `roster init` leaves in it. The setup screen has a **Copy the
105
+ prompt** button that carries every file it refers to, and a box to paste the answer back into;
106
+ `roster brief discover` prints the same brief for a terminal.
107
+
108
+ `roster doctor` reports `business.stub` while it is still the questions.
109
+
110
+ **Why not automated:** an agent that does not know the business writes work that is plausible
111
+ and generic. That is worse than no work, because it takes longer to notice. This file is
112
+ composed into the top of every prompt, every run.
113
+
114
+ **If you skip it:** nothing errors. That is the problem. You get competent-looking output about
115
+ a business that does not exist.
116
+
117
+ ---
118
+
119
+ ## 6. Write each staff member's `CHARTER.md`
120
+
121
+ **Do:** **Write the charter** on that staff member's card in the portal, or
122
+ `roster brief charter <handle>` and paste it into your agent. Or write it by hand;
123
+ [writing a charter](writing-a-charter.md) has the shape.
124
+
125
+ **Why not automated:** same reason, one level down. The charter is what makes a staff member
126
+ different from the others.
127
+
128
+ **If you skip it:** `charter` reports it as present, because the stub is a file. `charter.stub`
129
+ is the finding that says nobody has answered it. The agent has no personality and produces
130
+ whatever the shared layer implies.
131
+
132
+ ---
133
+
134
+ ## 7. Commit and push what roster wrote into other repos
135
+
136
+ **Do:** `roster hire` and `roster upgrade` write into brain repos on disk. Review, commit, push.
137
+
138
+ **Why not automated:** roster does not commit on your behalf into repositories it did not
139
+ create in that command. And **App tokens cannot push a change under `.github/workflows/` in any
140
+ repository**, which is a GitHub restriction and not a configuration mistake. That is also why
141
+ agents can never update their own workflows, and why upgrades are human-run by design.
142
+
143
+ **If you skip it:** the change exists locally and nowhere else. `roster upgrade` will report it
144
+ as still pending next time, which is the intended behaviour.
145
+
146
+ ---
147
+
148
+ ## Order
149
+
150
+ For a new organisation:
151
+
152
+ In the portal, this order is the screen you are looking at. From a terminal:
153
+
154
+ ```
155
+ roster init --org <org> --apply # 1 applies here
156
+ roster hire <handle> --apply # then 7
157
+ roster app <handle> # 2, then 3
158
+ # 4, 5, 6
159
+ roster doctor <handle>
160
+ ```
161
+
162
+ Then trigger one run by hand before trusting the schedule. A workflow that has never run has
163
+ proved nothing.
package/docs/memory.md ADDED
@@ -0,0 +1,71 @@
1
+ ---
2
+ title: "Memory"
3
+ description: "The grammar, the five rules, and why deleting is the maintenance."
4
+ sidebar_order: 7
5
+ ---
6
+
7
+ # Memory
8
+
9
+ A staff member's memory is markdown in its own repository. There is no database, deliberately:
10
+ an agent writes markdown well and writes to a schema badly.
11
+
12
+ ## The two files
13
+
14
+ ```
15
+ memory/INDEX.md one line per fact. Read in full at every boot.
16
+ memory/notes/*.md the argument behind a fact. Read only when that fact is in play.
17
+ ```
18
+
19
+ That split is the whole design. Boot context here went from about 52,000 words to about 6,000
20
+ by making it, and the saving repeats on every run forever.
21
+
22
+ ## The grammar
23
+
24
+ ```
25
+ - **`<slug>`** · [<will|measured|derived>] <the fact, one line>. **So:** <what it changes>. · [note](notes/<slug>.md)
26
+ ```
27
+
28
+ `roster lint` enforces it, and the portal parses it. Provenance and the note link are optional.
29
+ The slug, the fact and the `So:` are not.
30
+
31
+ **Provenance** is one of three things: a human ruled it, it was measured, or it was derived.
32
+ A `[measured]` fact without an `n` fails lint, because a number without a sample size is a
33
+ rumour.
34
+
35
+ ## The five rules
36
+
37
+ 1. **One line per fact.** If it needs more, the extra goes in `notes/<slug>.md` and the line
38
+ stays one line.
39
+ 2. **Correct in place. Never append "updated:".** An update chain is how one fact becomes six
40
+ paragraphs that contradict each other.
41
+ 3. **Every fact says what it changes.** If you cannot write the `So:`, it is not memory. Do not
42
+ add it.
43
+ 4. **Measurements carry `n` and a date. Constraints do not expire; measurements do.** Anything
44
+ with a `review:` date is re-read or deleted on that date.
45
+ 5. **Deleting is the maintenance.** Cut every line that no longer changes a decision, and log
46
+ the cut. A memory that only grows is a memory nobody reads.
47
+
48
+ Rule 5 is the one that gets skipped and the one that matters. Everything else degrades slowly;
49
+ this one degrades the boot cost of every future run.
50
+
51
+ ## What does not go in memory
52
+
53
+ - **Why something was decided.** That is `log/decisions.md`, and it is not boot context.
54
+ - **How a thing works.** That is a draft or a strategy document.
55
+ - **What is outstanding.** That is the pinned status issue.
56
+
57
+ Nothing is copied between them. Four places, four jobs, and a fact that appears in two of them
58
+ will disagree with itself within a month.
59
+
60
+ ## Checking it
61
+
62
+ ```bash
63
+ roster lint # everyone
64
+ roster lint cto # one staff member
65
+ ```
66
+
67
+ Lint catches: a missing `So:`, a duplicate slug, a note nothing links to, a link to a note that
68
+ does not exist, an over-long line, a `[measured]` fact with no `n`, and an "updated:" chain.
69
+
70
+ The portal's **Health** screen shows the same findings and will open an issue in the staff
71
+ member's own repository asking them to fix it, which is usually the right move: they wrote it.
@@ -0,0 +1,143 @@
1
+ ---
2
+ title: "org.yaml reference"
3
+ description: "Every field in the org manifest, and what reads it."
4
+ sidebar_order: 13
5
+ ---
6
+
7
+ # `org.yaml` reference
8
+
9
+ The org manifest. Lives at the root of the ops repo, and is read at the top of every composed
10
+ prompt, by `runner-plan.mjs`, by `agents.mjs`, and by every CLI command.
11
+
12
+ Parsed by `parseYaml` in `compose.mjs`, which reads a deliberately small, strict subset of
13
+ YAML: scalars, nested maps, block lists, and inline maps in a list. It does not do anchors,
14
+ multi-line scalars, or flow maps at the top level. **A manifest that needs more than that has
15
+ outgrown being a manifest**, and the parser refuses to guess rather than misparsing.
16
+
17
+ ## A complete example
18
+
19
+ ```yaml
20
+ org: acme
21
+ name: Acme Robotics
22
+ ops_dir: roster-ops
23
+
24
+ human:
25
+ name: Will
26
+ github: you
27
+ marker: will
28
+ role: founder
29
+
30
+ experiment_private: true
31
+
32
+ agent:
33
+ id: claude-code-action
34
+
35
+ defaults:
36
+ model: claude-opus-5
37
+ timeout_minutes: 60
38
+ allowed_tools: [Bash, Read, Write, Edit, Glob, Grep, WebFetch, WebSearch]
39
+
40
+ staff:
41
+ - { handle: cto, dir: technology, name: Chief Technology Officer, schedule: "0 7 * * 1-5" }
42
+ - { handle: cmo, dir: marketing, name: Chief Marketing Officer, schedule: "40 7 * * 1-5" }
43
+
44
+ repos:
45
+ - { name: acme-web, visibility: public, role: product }
46
+ - { name: technology, visibility: private, role: brain }
47
+ - { name: roster-ops, visibility: private, role: ops }
48
+ ```
49
+
50
+ ## Fields
51
+
52
+ ### Top level
53
+
54
+ | Field | Required | Means |
55
+ |---|---|---|
56
+ | `org` | yes | The GitHub organisation. Every repo name is resolved against it. |
57
+ | `name` | yes | What the business is called, in prose. Appears in prompts. |
58
+ | `ops_dir` | no | Directory name of the ops repo in the runner checkout. Defaults to `roster-ops`. |
59
+ | `experiment_private` | no | Whether the fact that this org is agent-run is itself private. Read by the guardrails fragment. |
60
+
61
+ ### `human`
62
+
63
+ Who the staff answer to. There is exactly one.
64
+
65
+ | Field | Required | Means |
66
+ |---|---|---|
67
+ | `github` | yes | Login. **The mention callers gate on this**, so without it nothing can wake an agent. |
68
+ | `name` | no | What to call them in prose. Defaults to the login. |
69
+ | `marker` | no | Provenance tag on a fact they ruled on, as in `[will]`. Also used as a label. |
70
+ | `role` | no | Prose only. |
71
+
72
+ ### `agent`
73
+
74
+ Which coding agent runs a session. Either a string, or a map. See
75
+ [choosing a coding agent](agents.md).
76
+
77
+ | Field | Required | Means |
78
+ |---|---|---|
79
+ | `id` | no | A preset name, or your own label. Defaults to `claude-code-action`. |
80
+ | `install` | if `id` is unknown | Shell command that installs the agent on the runner. |
81
+ | `run` | if `id` is unknown | Shell command that runs it, reading `$AGENT_PROMPT_FILE`. |
82
+ | `token_env` | if `id` is unknown | Environment variable its credential goes in. |
83
+ | `model` | no | Default model for this agent. A staff member's own `model` wins. |
84
+
85
+ Any field given overrides the preset's, so a preset that is right except for one flag needs
86
+ one line.
87
+
88
+ ### `defaults`
89
+
90
+ Fallbacks for staff members who do not set their own.
91
+
92
+ | Field | Means |
93
+ |---|---|
94
+ | `model` | Model id passed to the agent. |
95
+ | `timeout_minutes` | Ceiling on a daily session. |
96
+ | `allowed_tools` | Tool permission string. Meaningful to agents that take one, ignored by those that do not. |
97
+
98
+ ### `staff`
99
+
100
+ The registry. One inline map per staff member. **This is the org's view of them**; the rest
101
+ lives in their own `staff.yaml`.
102
+
103
+ | Field | Required | Means |
104
+ |---|---|---|
105
+ | `handle` | yes | Short identifier. Must match the handle in their manifest. |
106
+ | `dir` | no | Directory and repo name. Defaults to the handle. |
107
+ | `name` | no | Role name in prose. |
108
+ | `schedule` | no | Cron. Informational here; the caller workflow is what actually schedules. |
109
+
110
+ `roster hire` appends to this list. An empty list (`staff: []`) is valid and is what a fresh
111
+ org has.
112
+
113
+ ### `repos`
114
+
115
+ Every repository the org owns, and what it is for.
116
+
117
+ | Field | Means |
118
+ |---|---|
119
+ | `name` | Repo name, resolved against `org`. |
120
+ | `visibility` | `public` or `private`. `roster doctor` warns when this disagrees with reality. |
121
+ | `role` | `brain`, `product` or `ops`. |
122
+
123
+ `role: product` is load-bearing: `roster hire` uses it to fill a new staff member's
124
+ `works_in`, and the prompts refer to the product repo by name.
125
+
126
+ ## What reads what
127
+
128
+ | Reader | Uses |
129
+ |---|---|
130
+ | `compose.mjs` | `org`, `name`, `human`, `ops_dir`, `staff` |
131
+ | `runner-plan.mjs` | `org`, `staff`, and each manifest's `works_in` and `peers` |
132
+ | `agents.mjs` | `agent`, `staff` |
133
+ | `roster hire` | all of it, plus every existing manifest |
134
+ | `roster doctor` | all of it |
135
+
136
+ ## Editing it
137
+
138
+ It is yours. `roster upgrade` never touches it: it has no template, because a tenant's registry
139
+ is not something a framework can have an opinion about.
140
+
141
+ The exception is `roster hire --apply`, which appends a `staff` entry and a `repos` entry.
142
+ Those are inserted textually under the existing keys rather than by re-serialising the file,
143
+ so your comments and formatting survive.