@nanocollective/roster 0.1.0-alpha.3 → 0.1.0-alpha.4
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.
- package/dist/cli.js +848 -164
- package/docs/README.md +10 -5
- package/docs/agents.md +320 -9
- package/docs/commands.md +5 -6
- package/docs/concepts.md +27 -9
- package/docs/cost.md +3 -2
- package/docs/doctor-codes.md +13 -4
- package/docs/export.md +2 -1
- package/docs/extending.md +11 -2
- package/docs/getting-started.md +89 -84
- package/docs/images/brain.jpg +0 -0
- package/docs/images/org.jpg +0 -0
- package/docs/images/prompt.jpg +0 -0
- package/docs/images/setup-org.jpg +0 -0
- package/docs/images/setup-plan.jpg +0 -0
- package/docs/images/staff.jpg +0 -0
- package/docs/manual-steps.md +36 -13
- package/docs/memory.md +9 -6
- package/docs/org-yaml.md +37 -9
- package/docs/portal.md +197 -31
- package/docs/prompts.md +50 -11
- package/docs/security.md +19 -7
- package/docs/session-workflow.md +8 -10
- package/docs/staff-yaml.md +3 -5
- package/docs/troubleshooting.md +17 -14
- package/docs/writing-a-charter.md +18 -17
- package/package.json +1 -1
- package/templates/brain/.github/workflows/%%STAFF%%-daily.yaml +1 -0
- package/templates/brain/.github/workflows/%%STAFF%%-mention.yaml +10 -4
- package/templates/brain/staff.yaml +0 -1
- package/templates/ops/.github/workflows/session.yaml +9 -26
- package/templates/ops/agents.mjs +121 -6
- package/templates/ops/compose.mjs +61 -4
- package/templates/ops/org/operating.md +0 -6
- package/templates/ops/prompts/_identity.md +8 -1
- package/templates/ops/prompts/mention.md +16 -2
- package/templates/portal/css/base.css +116 -8
- package/templates/portal/css/brain.css +8 -1
- package/templates/portal/css/diff.css +6 -2
- package/templates/portal/css/health.css +21 -2
- package/templates/portal/css/inbox.css +93 -5
- package/templates/portal/css/layout.css +26 -4
- package/templates/portal/css/markdown.css +23 -3
- package/templates/portal/css/setup.css +11 -6
- package/templates/portal/index.html +7 -1
- package/templates/portal/js/api.js +33 -0
- package/templates/portal/js/app.js +28 -8
- package/templates/portal/js/dialog.js +47 -4
- package/templates/portal/js/dom.js +25 -0
- package/templates/portal/js/icons.js +8 -1
- package/templates/portal/js/lightbox.js +273 -0
- package/templates/portal/js/md.js +23 -6
- package/templates/portal/js/mention.js +264 -0
- package/templates/portal/js/refresh.js +136 -6
- package/templates/portal/js/state.js +47 -5
- package/templates/portal/js/views/checklist.js +20 -7
- package/templates/portal/js/views/docs.js +94 -4
- package/templates/portal/js/views/files.js +58 -14
- package/templates/portal/js/views/health.js +163 -35
- package/templates/portal/js/views/inbox.js +882 -96
- package/templates/portal/js/views/memory.js +16 -1
- package/templates/portal/js/views/org.js +142 -62
- package/templates/portal/js/views/prompt.js +50 -63
- package/templates/portal/js/views/staff.js +62 -2
- package/templates/portal/js/yaml.js +134 -0
- package/templates/brain/.github/workflows/%%STAFF%%-pr-mention.yaml +0 -50
- package/templates/ops/prompts/pr-mention.md +0 -57
package/docs/README.md
CHANGED
|
@@ -11,7 +11,7 @@ An agent-run organisation, powered by GitHub.
|
|
|
11
11
|
A staff member is a private repository. The repo *is* the brain: what it knows, what it is
|
|
12
12
|
working on, what it has decided. A scheduled workflow wakes it each morning, hands it a prompt
|
|
13
13
|
composed from the org's shared rules plus its own charter, and it does a day's work and hands
|
|
14
|
-
off. You read the result
|
|
14
|
+
off. You read the result in a local portal, or on GitHub.
|
|
15
15
|
|
|
16
16
|
roster is the thing that sets that up and keeps it consistent.
|
|
17
17
|
|
|
@@ -22,18 +22,18 @@ Read in this order.
|
|
|
22
22
|
| | |
|
|
23
23
|
|---|---|
|
|
24
24
|
| [Getting started](getting-started.md) | One command, in a browser: stand up an org, or join one that exists. |
|
|
25
|
+
| [The portal](portal.md) | Where the work happens: setup, every screen, every action. |
|
|
25
26
|
| [Manual steps](manual-steps.md) | Every human action, why it cannot be automated, and what breaks if you skip it. **Read this one.** |
|
|
26
27
|
| [Concepts](concepts.md) | What a charter, a manifest, a surface and the ops repo are. |
|
|
27
28
|
| [Choosing a coding agent](agents.md) | Claude, Codex, Nanocoder, or anything with a command line. |
|
|
28
29
|
| [Writing a charter](writing-a-charter.md) | The one file nothing can generate for you. |
|
|
29
30
|
| [Extending it](extending.md) | The four seams, and which one to reach for. |
|
|
30
31
|
| [Memory](memory.md) | The grammar, and why deleting is the maintenance. |
|
|
31
|
-
| [Commands](commands.md) | Every CLI command and flag. |
|
|
32
|
-
| [The portal](portal.md) | Setup, every view, and every action. |
|
|
33
32
|
| [Upgrading](upgrading.md) | How framework changes reach a tenant without eating your edits. |
|
|
34
33
|
| [Troubleshooting](troubleshooting.md) | Every trap we have actually hit, and what it looks like. |
|
|
35
34
|
| [Hosting the portal](hosting.md) | Local is the default, and why. |
|
|
36
35
|
| [Cost](cost.md) | What this spends, and on what. |
|
|
36
|
+
| [Commands](commands.md) | Every CLI command and flag, for when you want the terminal. |
|
|
37
37
|
|
|
38
38
|
## Reference
|
|
39
39
|
|
|
@@ -45,7 +45,6 @@ Look things up.
|
|
|
45
45
|
| [`staff.yaml`](staff-yaml.md) | Every field in a staff member's manifest. |
|
|
46
46
|
| [Prompts](prompts.md) | The template syntax, the context, and what to guard. |
|
|
47
47
|
| [The session workflow](session-workflow.md) | Inputs, secrets, and what runs in what order. |
|
|
48
|
-
| [The portal](portal.md) | Every view and every action. |
|
|
49
48
|
| [`roster export`](export.md) | The JSON shape. |
|
|
50
49
|
| [doctor codes](doctor-codes.md) | Every finding, what it means, what to do. |
|
|
51
50
|
|
|
@@ -81,7 +80,7 @@ Nano-Collective/roster the framework. Never a runtime dependency of any
|
|
|
81
80
|
├── staff.yaml the machine-readable half of the charter
|
|
82
81
|
├── memory/INDEX.md one line per fact, read at every boot
|
|
83
82
|
├── memory/notes/ the argument behind a fact, read on demand
|
|
84
|
-
└── .github/workflows/
|
|
83
|
+
└── .github/workflows/ two callers, about forty lines each
|
|
85
84
|
```
|
|
86
85
|
|
|
87
86
|
**The framework never runs anything.** It writes templates out; a tenant runs its own copies.
|
|
@@ -97,3 +96,9 @@ deleted, and an air-gapped install is a supported case rather than a special one
|
|
|
97
96
|
- **Write `org/business.md`.** Everything the staff say is downstream of it.
|
|
98
97
|
- **Install a GitHub App.** Installing grants access to specific repositories and GitHub asks a
|
|
99
98
|
human which. See [manual steps](manual-steps.md).
|
|
99
|
+
|
|
100
|
+
None of those is a dead end. roster holds no model credential, so for the first two the portal
|
|
101
|
+
does both halves of the round trip instead: it copies a brief that carries every file it refers
|
|
102
|
+
to, and turns the reply you paste back into a file with a diff and a save button. For the third
|
|
103
|
+
it runs everything either side of the confirmation GitHub insists a human gives, and tells you
|
|
104
|
+
exactly which repositories to grant.
|
package/docs/agents.md
CHANGED
|
@@ -21,19 +21,56 @@ That is the whole interface. Everything else is a convenience.
|
|
|
21
21
|
|
|
22
22
|
## Picking one
|
|
23
23
|
|
|
24
|
-
In `org.yaml
|
|
24
|
+
In `org.yaml`. This block is the whole surface: choose an agent, say how much freedom it gets,
|
|
25
|
+
and pass anything else through in that agent's own words.
|
|
25
26
|
|
|
26
27
|
```yaml
|
|
27
28
|
agent:
|
|
28
29
|
id: codex
|
|
30
|
+
permissions: full # full | workspace | read-only
|
|
31
|
+
options: # optional, and in codex's vocabulary rather than roster's
|
|
32
|
+
model_reasoning_effort: high
|
|
29
33
|
```
|
|
30
34
|
|
|
31
|
-
|
|
35
|
+
The short form is the same thing with the defaults:
|
|
32
36
|
|
|
33
37
|
```yaml
|
|
34
38
|
agent: codex
|
|
35
39
|
```
|
|
36
40
|
|
|
41
|
+
### `permissions`
|
|
42
|
+
|
|
43
|
+
One word here, because "how much may this thing do without asking" is a question about your
|
|
44
|
+
org rather than about a vendor. Each agent hears it in its own vocabulary:
|
|
45
|
+
|
|
46
|
+
| | `read-only` | `workspace` | `full` |
|
|
47
|
+
|---|---|---|---|
|
|
48
|
+
| **claude** | `--allowedTools Read,Glob,Grep,WebFetch,WebSearch` | the same plus `Bash,Write,Edit` | plus `WebFetch,WebSearch` |
|
|
49
|
+
| **codex** | `--sandbox read-only` | `--sandbox workspace-write` | `--sandbox danger-full-access` |
|
|
50
|
+
| **nanocoder** | `--mode plan` | `--mode auto-accept` | `--mode yolo` |
|
|
51
|
+
|
|
52
|
+
`full` is the default and is what a daily session needs: the whole point of a run is that it
|
|
53
|
+
edits the checkout, commits and pushes. `workspace` keeps it off the network. `read-only` is
|
|
54
|
+
for a staff member you are not ready to trust yet, and it is genuinely read-only in all three:
|
|
55
|
+
nanocoder's `plan` mode reasons and proposes and edits nothing.
|
|
56
|
+
|
|
57
|
+
Codex also gets `approval_policy="never"` at every level. A sandbox that permits writes still
|
|
58
|
+
stops to ask by default, and a run that stops to ask at 07:00 is a run that times out having
|
|
59
|
+
done nothing.
|
|
60
|
+
|
|
61
|
+
A staff member can be trusted less than the org:
|
|
62
|
+
|
|
63
|
+
```yaml
|
|
64
|
+
# marketing/staff.yaml
|
|
65
|
+
permissions: workspace
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### `options`
|
|
69
|
+
|
|
70
|
+
Anything roster does not model, in the agent's own words, spelled onto its command line by the
|
|
71
|
+
preset: `-c key=value` for codex, `--key value` for the others. It is the escape hatch that
|
|
72
|
+
means a flag roster has never heard of is still reachable without waiting for us.
|
|
73
|
+
|
|
37
74
|
A staff member can override it in their own `staff.yaml`, which is worth doing when roles
|
|
38
75
|
differ in kind. A research role on a long-context model and an engineering role on a coding
|
|
39
76
|
model is a reasonable thing to want:
|
|
@@ -61,7 +98,15 @@ agent: claude-code-action
|
|
|
61
98
|
|
|
62
99
|
- credential: `CLAUDE_CODE_OAUTH_TOKEN`
|
|
63
100
|
- default model: `claude-opus-5`
|
|
64
|
-
- tool permissions come from `allowed_tools` on the caller
|
|
101
|
+
- tool permissions come from `allowed_tools` on the caller, which roster renders from
|
|
102
|
+
`defaults.allowed_tools` in org.yaml
|
|
103
|
+
|
|
104
|
+
That allowlist is Claude's own vocabulary, and it is the one setting on this page that does not
|
|
105
|
+
translate. Codex takes a sandbox mode rather than a tool list; nanocoder takes a development
|
|
106
|
+
mode. Both presets therefore ignore `$AGENT_TOOLS` entirely, and neither is any less restricted
|
|
107
|
+
for it: what bounds them is the sandbox flag in their own `run` command. If you switch agents,
|
|
108
|
+
`allowed_tools` stops being the thing that governs what the agent may touch, and the `run`
|
|
109
|
+
command becomes it.
|
|
65
110
|
|
|
66
111
|
It is the only preset that is a GitHub Action rather than a CLI. `uses:` in a workflow cannot
|
|
67
112
|
be an expression, so an Action-based runner has to be written into `session.yaml` literally.
|
|
@@ -97,7 +142,8 @@ produces a run that succeeds having done nothing. If you would rather keep it na
|
|
|
97
142
|
|
|
98
143
|
```
|
|
99
144
|
install: npm install -g @nanocollective/nanocoder
|
|
100
|
-
run:
|
|
145
|
+
run: NANOCODER_PROVIDERS_FILE="${NANOCODER_PROVIDERS_FILE:-roster-ops/agents.config.json}" \
|
|
146
|
+
nanocoder --model "$AGENT_MODEL" --mode yolo --trust-directory --plain run "$(cat "$AGENT_PROMPT_FILE")"
|
|
101
147
|
token_env: NANOCODER_API_KEY
|
|
102
148
|
```
|
|
103
149
|
|
|
@@ -105,8 +151,234 @@ Three flags matter for unattended use. `run` is its non-interactive mode. `--tru
|
|
|
105
151
|
skips the first-run directory trust prompt, which would otherwise hang the runner until it
|
|
106
152
|
times out. `--plain` avoids the TUI, which has nothing to draw to in CI.
|
|
107
153
|
|
|
108
|
-
Nanocoder
|
|
109
|
-
|
|
154
|
+
**Nanocoder needs one thing the other two do not: a provider.** It is a client rather than a
|
|
155
|
+
model, so `NANOCODER_API_KEY` on its own tells it nothing about where to send anything. That is
|
|
156
|
+
what the config file is for, and it is the part of this that catches people out. It has its own
|
|
157
|
+
section: [wiring up nanocoder](#wiring-up-nanocoder).
|
|
158
|
+
|
|
159
|
+
## Setting one up, end to end
|
|
160
|
+
|
|
161
|
+
The preset only says how to invoke the agent. Three more things have to be true before a run
|
|
162
|
+
works, and Health, or `roster doctor`, checks all three.
|
|
163
|
+
|
|
164
|
+
### 1. The credential exists, and you have it
|
|
165
|
+
|
|
166
|
+
| Agent | Where the credential comes from |
|
|
167
|
+
|---|---|
|
|
168
|
+
| `claude-code-action`, `claude` | `claude setup-token` in a terminal where Claude Code is signed in. It prints a long-lived OAuth token. A plain Anthropic API key also works if you would rather bill that way. |
|
|
169
|
+
| `codex` | an API key from the OpenAI platform console. `codex login` is for interactive use and does not produce something a runner can hold. |
|
|
170
|
+
| `nanocoder` | whatever the provider you point it at wants. Nanocoder is a client, not a model: the key belongs to the provider in `agents.config.json`. |
|
|
171
|
+
|
|
172
|
+
### 2. It is a secret on every brain repo, under the right name
|
|
173
|
+
|
|
174
|
+
Each staff member's caller workflow reads the secret **from their own repo**, so the credential
|
|
175
|
+
goes on each brain, not on the ops repo:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
gh secret set CODEX_API_KEY --repo playpip/technology --body "$KEY"
|
|
179
|
+
gh secret set CODEX_API_KEY --repo playpip/marketing --body "$KEY"
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
The name is the preset's `token_env`, and it is the same name the caller references. If you
|
|
183
|
+
override `token_env`, the callers have to be regenerated so they reference the new name:
|
|
184
|
+
`roster upgrade --apply`.
|
|
185
|
+
|
|
186
|
+
Inside the run it arrives twice: as `AGENT_TOKEN`, which is what the caller passes, and under
|
|
187
|
+
the agent's own `token_env`, which is what the agent reads. That indirection is why a preset
|
|
188
|
+
change does not require touching `session.yaml`.
|
|
189
|
+
|
|
190
|
+
### 3. The agent's own config, if it has one
|
|
191
|
+
|
|
192
|
+
`claude` and `codex` need none: they are a model and a client in one thing, and the model comes
|
|
193
|
+
from `$AGENT_MODEL`. `nanocoder` needs a providers file, below.
|
|
194
|
+
|
|
195
|
+
### Then prove it
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
roster doctor # secrets present, callers reachable, prompts compose
|
|
199
|
+
gh workflow run cto-daily.yaml --repo playpip/technology
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Read the log of that first run rather than waiting for the schedule. What goes wrong is
|
|
203
|
+
specific to the agent and obvious in the log: an unknown flag, a sandbox that refuses to write,
|
|
204
|
+
a model id the provider does not recognise, a first-run prompt waiting for a keypress that will
|
|
205
|
+
never come.
|
|
206
|
+
|
|
207
|
+
## Three worked examples
|
|
208
|
+
|
|
209
|
+
An org called `acme` with two staff members, `cto` in `acme/technology` and `cmo` in
|
|
210
|
+
`acme/marketing`. Every file each one touches, in full.
|
|
211
|
+
|
|
212
|
+
### Claude, through the Action
|
|
213
|
+
|
|
214
|
+
```yaml
|
|
215
|
+
# roster-ops/org.yaml
|
|
216
|
+
org: acme
|
|
217
|
+
agent:
|
|
218
|
+
id: claude-code-action # the default; the whole block can be left out
|
|
219
|
+
|
|
220
|
+
defaults:
|
|
221
|
+
model: claude-opus-5
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
claude setup-token # prints a long-lived token
|
|
226
|
+
gh secret set CLAUDE_CODE_OAUTH_TOKEN --repo acme/technology --body "$TOKEN"
|
|
227
|
+
gh secret set CLAUDE_CODE_OAUTH_TOKEN --repo acme/marketing --body "$TOKEN"
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Nothing else. No file in the brain repos, no per-staff config.
|
|
231
|
+
|
|
232
|
+
### Codex
|
|
233
|
+
|
|
234
|
+
```yaml
|
|
235
|
+
# roster-ops/org.yaml
|
|
236
|
+
agent:
|
|
237
|
+
id: codex
|
|
238
|
+
permissions: full # --sandbox danger-full-access, approval_policy never
|
|
239
|
+
|
|
240
|
+
defaults:
|
|
241
|
+
model: gpt-5-codex # what $AGENT_MODEL becomes
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
gh secret set CODEX_API_KEY --repo acme/technology --body "$OPENAI_KEY"
|
|
246
|
+
gh secret set CODEX_API_KEY --repo acme/marketing --body "$OPENAI_KEY"
|
|
247
|
+
roster upgrade --apply # repoints the callers at the new secret name
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Commit and push the regenerated callers. The secret name changed from
|
|
251
|
+
`CLAUDE_CODE_OAUTH_TOKEN` to `CODEX_API_KEY`, and that name is written into each caller.
|
|
252
|
+
|
|
253
|
+
### Nanocoder
|
|
254
|
+
|
|
255
|
+
Two files rather than one, because a provider has to be named.
|
|
256
|
+
|
|
257
|
+
```yaml
|
|
258
|
+
# roster-ops/org.yaml
|
|
259
|
+
agent:
|
|
260
|
+
id: nanocoder
|
|
261
|
+
permissions: full # --mode yolo
|
|
262
|
+
|
|
263
|
+
defaults:
|
|
264
|
+
model: qwen/qwen3-coder # must be one of the models listed below
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
`roster init --agent nanocoder` writes this file for you, with the blanks marked. Fill them in:
|
|
268
|
+
|
|
269
|
+
```json
|
|
270
|
+
// roster-ops/agents.config.json
|
|
271
|
+
{
|
|
272
|
+
"nanocoder": {
|
|
273
|
+
"providers": [
|
|
274
|
+
{
|
|
275
|
+
"name": "openrouter",
|
|
276
|
+
"baseUrl": "https://openrouter.ai/api/v1",
|
|
277
|
+
"apiKey": "${NANOCODER_API_KEY}",
|
|
278
|
+
"models": ["qwen/qwen3-coder"]
|
|
279
|
+
}
|
|
280
|
+
]
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
gh secret set NANOCODER_API_KEY --repo acme/technology --body "$OPENROUTER_KEY"
|
|
287
|
+
gh secret set NANOCODER_API_KEY --repo acme/marketing --body "$OPENROUTER_KEY"
|
|
288
|
+
roster upgrade --apply
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Commit `agents.config.json` and the regenerated callers. **The key is not in the file.**
|
|
292
|
+
`${NANOCODER_API_KEY}` is expanded from the environment when nanocoder reads it, and the
|
|
293
|
+
environment is where roster puts the secret.
|
|
294
|
+
|
|
295
|
+
## Wiring up nanocoder
|
|
296
|
+
|
|
297
|
+
The other two presets are one thing. Nanocoder is a harness you point at a model, so it needs
|
|
298
|
+
to be told which model, from whom, at what URL, with which key. That is `agents.config.json`.
|
|
299
|
+
|
|
300
|
+
### Where it looks
|
|
301
|
+
|
|
302
|
+
In order, and the first hit wins:
|
|
303
|
+
|
|
304
|
+
1. `$NANOCODER_PROVIDERS`: the JSON itself, in an environment variable.
|
|
305
|
+
2. `$NANOCODER_PROVIDERS_FILE`: a path to the JSON. Ignored if the file is not there.
|
|
306
|
+
3. `agents.config.json` in the **working directory**.
|
|
307
|
+
4. `agents.config.json` in the user config directory (`~/.config/nanocoder/` on Linux,
|
|
308
|
+
`~/Library/Preferences/nanocoder/` on macOS).
|
|
309
|
+
|
|
310
|
+
Options 3 and 4 are what you use at your own desk, and neither of them works in a session. The
|
|
311
|
+
working directory of a run is the **workspace root**: the directory the repos are checked out
|
|
312
|
+
*into*, one level above `technology/` and `roster-ops/`. It belongs to no repository, so there
|
|
313
|
+
is nothing there to commit a config into. And the user config directory on a fresh GitHub
|
|
314
|
+
runner is empty.
|
|
315
|
+
|
|
316
|
+
That is why roster's preset sets option 2 for you:
|
|
317
|
+
|
|
318
|
+
```
|
|
319
|
+
NANOCODER_PROVIDERS_FILE="${NANOCODER_PROVIDERS_FILE:-roster-ops/agents.config.json}"
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
The ops repo is checked out at a known path on every run, and it is the one repo every staff
|
|
323
|
+
member has. So the org's providers live in one version-controlled file, and each staff member
|
|
324
|
+
picks a model from it with `model:` in their own `staff.yaml`.
|
|
325
|
+
|
|
326
|
+
### The shape
|
|
327
|
+
|
|
328
|
+
Either of these; the wrapper is what nanocoder writes itself, the bare form is accepted too.
|
|
329
|
+
|
|
330
|
+
```json
|
|
331
|
+
{ "nanocoder": { "providers": [ … ] } }
|
|
332
|
+
{ "providers": [ … ] }
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
A provider is:
|
|
336
|
+
|
|
337
|
+
| Field | |
|
|
338
|
+
|---|---|
|
|
339
|
+
| `name` | what `--provider` and the model list refer to. Any string. |
|
|
340
|
+
| `baseUrl` | the OpenAI-compatible endpoint. Omit for a provider the SDK already knows. |
|
|
341
|
+
| `apiKey` | the credential. Use `${VAR}`, never a literal, in a file you are committing. |
|
|
342
|
+
| `models` | the models this provider offers. **If it is non-empty, `$AGENT_MODEL` must be one of them**, or the run fails with "Model not available for provider". |
|
|
343
|
+
| `sdkProvider` | `openai-compatible` (default), `anthropic`, `google`, `chatgpt-codex`, `github-copilot`. |
|
|
344
|
+
|
|
345
|
+
`${VAR}` and `$VAR` are both expanded, anywhere in the file, with `${VAR:-fallback}` for a
|
|
346
|
+
default. That is what lets a committed config carry no secrets.
|
|
347
|
+
|
|
348
|
+
### A local model
|
|
349
|
+
|
|
350
|
+
Nothing says the provider has to be remote. Ollama on a self-hosted runner needs no key at all:
|
|
351
|
+
|
|
352
|
+
```json
|
|
353
|
+
{
|
|
354
|
+
"providers": [
|
|
355
|
+
{
|
|
356
|
+
"name": "ollama",
|
|
357
|
+
"baseUrl": "http://localhost:11434/v1",
|
|
358
|
+
"models": ["qwen2.5-coder:32b"]
|
|
359
|
+
}
|
|
360
|
+
]
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
`token_env` still has to name a variable, because the session refuses to start an agent with no
|
|
365
|
+
credential at all. Point it at something harmless and set it to any non-empty string:
|
|
366
|
+
|
|
367
|
+
```yaml
|
|
368
|
+
agent:
|
|
369
|
+
id: nanocoder
|
|
370
|
+
token_env: NANOCODER_API_KEY # set it to "unused" on the brain repos
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### When it goes wrong
|
|
374
|
+
|
|
375
|
+
| In the log | What it means |
|
|
376
|
+
|---|---|
|
|
377
|
+
| `No agents.config.json found` | the file is not where nanocoder looked. Check it is committed to the ops repo at `agents.config.json`, and that `roster upgrade --apply` has been run so the caller carries the current preset. |
|
|
378
|
+
| `No providers configured` | the file is there but `providers` is empty, or spelled as an object instead of an array. |
|
|
379
|
+
| `Provider 'x' not found` | `--provider` names something the file does not define. |
|
|
380
|
+
| `Model 'y' not available for provider` | `model:` in `org.yaml` or `staff.yaml` is not in that provider's `models` list. This is the common one: the roster default is a Claude model, and nanocoder is strict about the list. |
|
|
381
|
+
| a run that hangs and then times out | a first-run prompt. The preset passes `--trust-directory` and `--plain` to avoid both known ones. |
|
|
110
382
|
|
|
111
383
|
## Writing your own
|
|
112
384
|
|
|
@@ -120,6 +392,19 @@ agent:
|
|
|
120
392
|
token_env: MY_AGENT_TOKEN
|
|
121
393
|
```
|
|
122
394
|
|
|
395
|
+
Five questions decide the `run` command, and they are the same five for every tool:
|
|
396
|
+
|
|
397
|
+
1. **What is its non-interactive mode?** Most have one, and it is rarely the default:
|
|
398
|
+
`-p` for Claude, `exec` for Codex, `run` for nanocoder.
|
|
399
|
+
2. **How does it take a long prompt?** Stdin (`< "$AGENT_PROMPT_FILE"`) if it accepts it, an
|
|
400
|
+
argument (`"$(cat "$AGENT_PROMPT_FILE")"`) if it does not. Never a literal.
|
|
401
|
+
3. **What does it do with no TTY?** Anything that draws a full-screen interface needs the flag
|
|
402
|
+
that turns it off, or CI gets a run full of escape codes and no work.
|
|
403
|
+
4. **Does it ask anything on first run?** Trust prompts, telemetry consent, a config wizard.
|
|
404
|
+
Each one hangs an unattended run until the job times out. Find the flag that skips it.
|
|
405
|
+
5. **Is it allowed to write?** A sandbox that forbids edits produces a run that reports success
|
|
406
|
+
having done nothing, which is the worst failure this arrangement has.
|
|
407
|
+
|
|
123
408
|
You can also override a single field of a preset, which is the common case when a flag changes:
|
|
124
409
|
|
|
125
410
|
```yaml
|
|
@@ -130,13 +415,34 @@ agent:
|
|
|
130
415
|
|
|
131
416
|
The rest of the preset still applies.
|
|
132
417
|
|
|
418
|
+
### Per staff member
|
|
419
|
+
|
|
420
|
+
Anything the org sets, a staff member can override in their own `staff.yaml`. Roles that differ
|
|
421
|
+
in kind are worth splitting: a research role on a long-context model, an engineering role on a
|
|
422
|
+
coding one.
|
|
423
|
+
|
|
424
|
+
```yaml
|
|
425
|
+
# marketing/staff.yaml
|
|
426
|
+
agent: nanocoder
|
|
427
|
+
model: qwen/qwen3-coder
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
```yaml
|
|
431
|
+
# technology/staff.yaml
|
|
432
|
+
model: claude-opus-5 # keeps the org's agent, changes only the model
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
Two staff members on two different agents need both credentials present, each on its own brain
|
|
436
|
+
repo, under each agent's own `token_env`. `roster doctor` reads the callers and tells you which
|
|
437
|
+
secret each repo is missing.
|
|
438
|
+
|
|
133
439
|
## What the runner gets
|
|
134
440
|
|
|
135
441
|
| Variable | |
|
|
136
442
|
|---|---|
|
|
137
443
|
| `$AGENT_PROMPT_FILE` | absolute path to the composed prompt |
|
|
138
444
|
| `$AGENT_MODEL` | the staff member's model, or the agent's default |
|
|
139
|
-
| `$AGENT_TOOLS` | the `allowed_tools` string from the caller |
|
|
445
|
+
| `$AGENT_TOOLS` | the `allowed_tools` string from the caller, which comes from `defaults.allowed_tools` in org.yaml |
|
|
140
446
|
| `$GH_TOKEN` | a token for the private trackers, already authenticated |
|
|
141
447
|
| `$PUBLIC_TOKEN` | a token for the public product repo, if there is one |
|
|
142
448
|
| *`token_env`* | the agent's credential, under whatever name it wants |
|
|
@@ -154,10 +460,15 @@ that only edits files and cannot run commands will produce a run that changes no
|
|
|
154
460
|
## Changing agent on a live org
|
|
155
461
|
|
|
156
462
|
1. Set `agent:` in `org.yaml`.
|
|
157
|
-
2. Put the new credential on each brain repo, named as `token_env
|
|
158
|
-
|
|
463
|
+
2. Put the new credential on each brain repo, named as `token_env`
|
|
464
|
+
(`gh secret set CODEX_API_KEY --repo <org>/<brain> --body "$KEY"`).
|
|
465
|
+
3. `roster upgrade --apply`, then commit and push the regenerated callers. This is what
|
|
466
|
+
repoints them at the new secret name; skipping it leaves every run reaching for the old one.
|
|
159
467
|
4. Trigger one run by hand and read the log before trusting the schedule.
|
|
160
468
|
|
|
469
|
+
The old secret can stay where it is until the new agent has had a clean run. Nothing reads it
|
|
470
|
+
once the callers have been regenerated, and it is the fastest way back if the first run is bad.
|
|
471
|
+
|
|
161
472
|
Step 4 is not optional. Prompts are written against a model's habits as much as its
|
|
162
473
|
capabilities, and the first run on a new agent is where you find out which parts of your
|
|
163
474
|
charter were load-bearing.
|
package/docs/commands.md
CHANGED
|
@@ -48,7 +48,7 @@ Will not write `org/business.md`. That is yours.
|
|
|
48
48
|
|
|
49
49
|
## `roster hire <handle>`
|
|
50
50
|
|
|
51
|
-
Scaffold a staff member: repo,
|
|
51
|
+
Scaffold a staff member: repo, two callers, manifest, memory index, charter stub, labels,
|
|
52
52
|
pinned status issue, and peer wiring in both directions.
|
|
53
53
|
|
|
54
54
|
```
|
|
@@ -58,7 +58,6 @@ pinned status issue, and peer wiring in both directions.
|
|
|
58
58
|
--model <id>
|
|
59
59
|
--timeout <n> daily ceiling, minutes
|
|
60
60
|
--mention-timeout <n>
|
|
61
|
-
--pr-timeout <n>
|
|
62
61
|
--secret-prefix <X> secrets become <X>_APP_ID and <X>_APP_PRIVATE_KEY
|
|
63
62
|
--app <slug> defaults to the pattern the peers use
|
|
64
63
|
--public-app <slug> the shared public identity
|
|
@@ -144,7 +143,7 @@ amend <who> change what a staff member is told, with the whole prompt attach
|
|
|
144
143
|
```
|
|
145
144
|
|
|
146
145
|
```
|
|
147
|
-
--kind <k> for amend: daily | mention
|
|
146
|
+
--kind <k> for amend: daily | mention (default: daily)
|
|
148
147
|
--want <text> for amend: what you want changed
|
|
149
148
|
--ops <dir>
|
|
150
149
|
```
|
|
@@ -177,14 +176,14 @@ roster brief discover > roster-ops/.claude/commands/discover.md
|
|
|
177
176
|
Compose and print what a staff member is actually sent.
|
|
178
177
|
|
|
179
178
|
```
|
|
180
|
-
--kind daily|mention
|
|
179
|
+
--kind daily|mention
|
|
181
180
|
--diff <workflow.yaml>
|
|
182
181
|
```
|
|
183
182
|
|
|
184
|
-
`mention`
|
|
183
|
+
`mention` needs trigger context:
|
|
185
184
|
|
|
186
185
|
```bash
|
|
187
|
-
ROSTER_CONTEXT='{"issue_number":"1","comment_id":"1","
|
|
186
|
+
ROSTER_CONTEXT='{"issue_number":"1","comment_id":"1","repo":"o/r"}' \
|
|
188
187
|
roster prompt cto --kind mention
|
|
189
188
|
```
|
|
190
189
|
|
package/docs/concepts.md
CHANGED
|
@@ -11,9 +11,10 @@ sidebar_order: 3
|
|
|
11
11
|
`<org>/roster-ops` holds two different kinds of thing, and the split matters.
|
|
12
12
|
|
|
13
13
|
**`org/` is yours.** `business.md`, `voice.md`, `guardrails.md`, `operating.md`. This is the
|
|
14
|
-
business truth and the shared half of every staff member's instructions. Edit it freely
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
business truth and the shared half of every staff member's instructions. Edit it freely: the
|
|
15
|
+
[Org screen](portal.md#org) lists every one of these off disk with an Edit button, and saving
|
|
16
|
+
commits and pushes. A change here reaches everybody on their next run, which is the point: a
|
|
17
|
+
concision rule that used to mean editing twelve files is now one file.
|
|
17
18
|
|
|
18
19
|
**Everything else is machinery** and belongs to the framework: `compose.mjs`, `agents.mjs`,
|
|
19
20
|
`runner-plan.mjs`, `.github/workflows/session.yaml`. Editing these works right up until the
|
|
@@ -34,12 +35,12 @@ A staff member's repository *is* their memory. There is no database.
|
|
|
34
35
|
| `memory/INDEX.md` | one line per fact, read in full at every boot |
|
|
35
36
|
| `memory/notes/` | the argument behind a fact, read only when that fact is in play |
|
|
36
37
|
| `log/decisions.md` | why things were decided. Not boot context. |
|
|
37
|
-
| `.github/workflows/` |
|
|
38
|
+
| `.github/workflows/` | two callers, about forty lines each |
|
|
38
39
|
|
|
39
40
|
## Charter and manifest
|
|
40
41
|
|
|
41
42
|
Two halves of one thing. The charter is prose for the agent; the manifest is fields for the
|
|
42
|
-
machinery. `roster lint
|
|
43
|
+
machinery. [Health](portal.md#health), and `roster lint`, fail if they disagree.
|
|
43
44
|
|
|
44
45
|
The charter is the only file roster refuses to generate. A generated charter produces a generic
|
|
45
46
|
agent, and a generic agent produces work that is plausible, competent-looking and about nothing
|
|
@@ -67,7 +68,9 @@ org/operating.md + org/guardrails.md + org/voice.md + org/business.md
|
|
|
67
68
|
+ <staff>/CHARTER.md + prompts/<kind>.md
|
|
68
69
|
```
|
|
69
70
|
|
|
70
|
-
Built at run time by `compose.mjs` in the tenant's own repo. See it for yourself
|
|
71
|
+
Built at run time by `compose.mjs` in the tenant's own repo. See it for yourself on the
|
|
72
|
+
[Prompt screen](portal.md#prompt), which shows the composed text and every layer that went into
|
|
73
|
+
it, or from a terminal:
|
|
71
74
|
|
|
72
75
|
```bash
|
|
73
76
|
roster prompt cto --kind daily
|
|
@@ -83,10 +86,25 @@ control.
|
|
|
83
86
|
|---|---|
|
|
84
87
|
| `daily` | the scheduled session. Boot, work, hand off. |
|
|
85
88
|
| `mention` | `@handle` in a comment or a new issue body. A task, not a session. |
|
|
86
|
-
| `pr-mention` | a review comment on the public product repo, forwarded in. |
|
|
87
89
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
+
A `mention` prompt refuses to compose without trigger context, because it is written for the
|
|
91
|
+
comment that woke it. That is correct behaviour, not a bug.
|
|
92
|
+
|
|
93
|
+
There used to be a third, `pr-mention`: a review comment on the public product repo, forwarded
|
|
94
|
+
into the brain by a workflow in that repo. It was removed. Two repos, a dispatch, a forwarder
|
|
95
|
+
with its own author gate and a second reaction path bought one thing: asking for a change
|
|
96
|
+
without leaving the diff. It cost more than that was worth, in explaining and in debugging.
|
|
97
|
+
|
|
98
|
+
What replaced it is the reply box. A pull request is on the product repo, and **nothing in a
|
|
99
|
+
product repo wakes anybody**: a staff member's caller workflow is in their own brain repo and
|
|
100
|
+
gates on their `@handle` appearing *there*. So naming somebody in a reply where a comment will
|
|
101
|
+
not reach them offers, under the box, to open the request on their tracker as well. One press
|
|
102
|
+
posts your words on the thread and sends them the pull request, the branch, the hunk you were
|
|
103
|
+
looking at if you started from a file, and an instruction to answer on the pull request rather
|
|
104
|
+
than in the tracker it arrived in.
|
|
105
|
+
|
|
106
|
+
It is two `gh` calls as you, rather than a workflow, a dispatch and a second gate, which is the
|
|
107
|
+
difference that got the forwarder deleted. See [the portal](portal.md#asking-for-a-change).
|
|
90
108
|
|
|
91
109
|
## Identities
|
|
92
110
|
|
package/docs/cost.md
CHANGED
|
@@ -22,7 +22,8 @@ words to about 6,000 by moving from a narrative status file to one line per fact
|
|
|
22
22
|
direct, repeated saving on every run of every staff member.
|
|
23
23
|
|
|
24
24
|
**Watch for sessions growing into their ceiling.** A run that gets killed at
|
|
25
|
-
`timeout_minutes` has been paid for and produced nothing. `roster doctor
|
|
25
|
+
`timeout_minutes` has been paid for and produced nothing. Health, and `roster doctor`, report
|
|
26
|
+
the ratio.
|
|
26
27
|
|
|
27
28
|
## GitHub Actions minutes
|
|
28
29
|
|
|
@@ -57,5 +58,5 @@ on your machine when you ask it to, and the machinery is vendored into the tenan
|
|
|
57
58
|
being raised is a session that has stopped fitting its job.
|
|
58
59
|
- **Give a mention workflow a shorter ceiling than a daily one.** A focused task that runs for
|
|
59
60
|
an hour has gone wrong, and the ceiling is the only thing that stops it.
|
|
60
|
-
- **Check the ratio, not the last run.**
|
|
61
|
+
- **Check the ratio, not the last run.** Health reports how many of the last ten runs
|
|
61
62
|
succeeded. One bad run is noise; four is a bill.
|
package/docs/doctor-codes.md
CHANGED
|
@@ -6,6 +6,9 @@ sidebar_order: 19
|
|
|
6
6
|
|
|
7
7
|
# doctor codes
|
|
8
8
|
|
|
9
|
+
The portal's [Health](portal.md#health) screen shows these same findings, each with its fix, and
|
|
10
|
+
turns the ones an agent could fix into a single brief. This page is the reference behind both.
|
|
11
|
+
|
|
9
12
|
Every finding `roster doctor` can emit. Each carries a stable `id`, which is what
|
|
10
13
|
`--json` reports and what to quote in an issue.
|
|
11
14
|
|
|
@@ -22,9 +25,12 @@ ran at all.
|
|
|
22
25
|
|---|---|
|
|
23
26
|
| `gh` | Whether `gh` is installed and authenticated. A warning here means every network check was skipped, not that anything is wrong. |
|
|
24
27
|
| `org.yaml` | The org manifest parsed, and how much it declares. |
|
|
25
|
-
| `human` | **fail.** `org.yaml`
|
|
28
|
+
| `human` | **fail.** `org.yaml` names nobody with a `github` login, in either `human` or `humans`. The mention callers gate on those logins, so nothing can wake an agent. |
|
|
29
|
+
| `human.login` | One of the people in `humans` has a name but no `github` login. They read as somebody the staff answer to and are not: the gate can never match them. |
|
|
26
30
|
| `repo` | Every repo in `org.yaml` is reachable. A failure means it does not exist or your `gh` cannot see it. |
|
|
27
31
|
| `repo.visibility` | A repo's real visibility disagrees with what `org.yaml` records. Cosmetic, but the posture it records is then fiction. |
|
|
32
|
+
| `agent` | Which runner this org uses, resolved from the tenant's own `agents.mjs`. **fail** if `org.yaml` names one it does not know. |
|
|
33
|
+
| `agent.config` | **fail.** The agent needs a config file of its own and it is missing, or still has a `FILL IN` in it. Nanocoder is the one preset that does: it is a client rather than a model, so without a provider it starts, finds nothing to call, and exits. |
|
|
28
34
|
| `business` | **fail** if `org/business.md` is missing. Every prompt is composed on top of it. |
|
|
29
35
|
| `business.stub` | `org/business.md` is still the questions it shipped with. Nothing errors; the agents just write competent work about a business that does not exist. |
|
|
30
36
|
| `actions-access` | **fail** unless the ops repo is callable from the whole organisation. This is the "workflow not found" trap. See [manual steps](manual-steps.md#1-allow-the-ops-repos-workflow-to-be-called). |
|
|
@@ -54,7 +60,7 @@ ran at all.
|
|
|
54
60
|
| `peer-labels` | The `from-<handle>` label exists on the *peer's* tracker, which is where this staff member's asks land. |
|
|
55
61
|
| `status-issue` | The declared status issue is actually pinned. If not, the place you look is not the place the agent maintains. |
|
|
56
62
|
| `runs` | A window of recent runs. See below. |
|
|
57
|
-
| `runs.timeout` | **fail.** Runs were killed at a ceiling. |
|
|
63
|
+
| `runs.timeout` | **fail.** Runs were killed at a ceiling. Drops to `ok` once the ceiling has been raised *and* a run has finished since the last kill: the fix is made and proved, and the old runs are history rather than a problem. |
|
|
58
64
|
| `runs.cancelled` | Runs were cancelled short of any ceiling, with their durations. |
|
|
59
65
|
|
|
60
66
|
## Reading `runs`
|
|
@@ -63,8 +69,11 @@ This is the only check that proves the whole chain works, so it is worth underst
|
|
|
63
69
|
|
|
64
70
|
- **"has never run"** is a warning, not an `ok`. Nothing has exercised the App grant or the
|
|
65
71
|
secrets, so nothing is known.
|
|
66
|
-
- **"all gated out
|
|
67
|
-
normal
|
|
72
|
+
- **"all gated out"** means every recent trigger was `skipped`, which is a mention workflow's
|
|
73
|
+
normal state: every comment on the tracker fires it and the gate drops all but the real ones.
|
|
74
|
+
It is only a warning when *nothing* in that repo has finished a run, because the App grant
|
|
75
|
+
belongs to the repository rather than to the workflow, so one finished run proves it for all
|
|
76
|
+
of them.
|
|
68
77
|
- **"ran to a Nm ceiling and were killed"** is a timeout. GitHub reports those as `cancelled`,
|
|
69
78
|
so doctor identifies them by duration. If the ceiling it names differs from the one the
|
|
70
79
|
caller sets today, it says so: those runs happened under the old setting.
|
package/docs/export.md
CHANGED
|
@@ -22,7 +22,8 @@ that reads it gets the same view without reimplementing the memory grammar.
|
|
|
22
22
|
| `org` | the GitHub organisation |
|
|
23
23
|
| `name` | the business name |
|
|
24
24
|
| `opsName` | the ops repo's directory, so a consumer can address `org.yaml` and `org/*.md` by path |
|
|
25
|
-
| `human` | the
|
|
25
|
+
| `human` | the first human, normalised: `{ github, name, marker, role }`. Kept for consumers written when an org had exactly one |
|
|
26
|
+
| `humans[]` | everyone the staff answer to, in order, read from `humans` or the singular `human`. `human` is `humans[0]` |
|
|
26
27
|
| `generatedAt` | ISO timestamp |
|
|
27
28
|
| `staff[]` | one entry per staff member |
|
|
28
29
|
|