@nanocollective/roster 0.1.0-alpha.2 → 0.1.0-alpha.20

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 (96) hide show
  1. package/README.md +65 -84
  2. package/dist/cli.js +4733 -2490
  3. package/docs/README.md +19 -11
  4. package/docs/agents.md +328 -13
  5. package/docs/architecture.md +13 -5
  6. package/docs/charters/cmo.md +69 -0
  7. package/docs/charters/cto.md +71 -0
  8. package/docs/charters/support.md +60 -0
  9. package/docs/commands.md +90 -11
  10. package/docs/concepts.md +64 -12
  11. package/docs/cost.md +39 -3
  12. package/docs/developing.md +16 -21
  13. package/docs/doctor-codes.md +21 -6
  14. package/docs/export.md +2 -1
  15. package/docs/extending.md +13 -4
  16. package/docs/getting-started.md +118 -80
  17. package/docs/images/brain.jpg +0 -0
  18. package/docs/images/org.jpg +0 -0
  19. package/docs/images/prompt.jpg +0 -0
  20. package/docs/images/setup-org.jpg +0 -0
  21. package/docs/images/setup-plan.jpg +0 -0
  22. package/docs/images/staff.jpg +0 -0
  23. package/docs/manual-steps.md +94 -101
  24. package/docs/memory.md +29 -8
  25. package/docs/org-yaml.md +76 -11
  26. package/docs/portal.md +261 -47
  27. package/docs/prompts.md +77 -11
  28. package/docs/security.md +51 -7
  29. package/docs/session-workflow.md +51 -21
  30. package/docs/staff-yaml.md +16 -7
  31. package/docs/troubleshooting.md +23 -20
  32. package/docs/upgrading.md +6 -0
  33. package/docs/writing-a-charter.md +33 -17
  34. package/package.json +1 -1
  35. package/templates/brain/.github/workflows/%%STAFF%%-daily.yaml +7 -0
  36. package/templates/brain/.github/workflows/%%STAFF%%-mention.yaml +16 -4
  37. package/templates/brain/CHARTER.md +3 -3
  38. package/templates/brain/README.md +1 -0
  39. package/templates/brain/log/decisions.md +3 -0
  40. package/templates/brain/staff.yaml +0 -1
  41. package/templates/brain/strategy/ideas.md +7 -0
  42. package/templates/ops/.github/workflows/session.yaml +117 -40
  43. package/templates/ops/agents.mjs +127 -8
  44. package/templates/ops/compose.mjs +77 -7
  45. package/templates/ops/inflight.mjs +157 -0
  46. package/templates/ops/org/operating.md +21 -7
  47. package/templates/ops/org/voice.md +9 -0
  48. package/templates/ops/prompts/_identity.md +8 -1
  49. package/templates/ops/prompts/_inflight.md +14 -0
  50. package/templates/ops/prompts/_paths.md +2 -1
  51. package/templates/ops/prompts/daily.md +16 -7
  52. package/templates/ops/prompts/mention.md +18 -2
  53. package/templates/ops/run-record.mjs +144 -0
  54. package/templates/portal/css/base.css +238 -64
  55. package/templates/portal/css/brain.css +30 -20
  56. package/templates/portal/css/diff.css +15 -10
  57. package/templates/portal/css/graph.css +12 -7
  58. package/templates/portal/css/health.css +32 -11
  59. package/templates/portal/css/inbox.css +117 -14
  60. package/templates/portal/css/layout.css +93 -41
  61. package/templates/portal/css/markdown.css +57 -15
  62. package/templates/portal/css/runs.css +13 -0
  63. package/templates/portal/css/setup.css +83 -39
  64. package/templates/portal/index.html +24 -3
  65. package/templates/portal/js/api.js +65 -4
  66. package/templates/portal/js/app.js +112 -12
  67. package/templates/portal/js/dialog.js +94 -4
  68. package/templates/portal/js/dom.js +25 -0
  69. package/templates/portal/js/icons.js +8 -1
  70. package/templates/portal/js/lightbox.js +273 -0
  71. package/templates/portal/js/md.js +23 -6
  72. package/templates/portal/js/mdedit.js +84 -0
  73. package/templates/portal/js/mention.js +264 -0
  74. package/templates/portal/js/refresh.js +136 -6
  75. package/templates/portal/js/state.js +55 -8
  76. package/templates/portal/js/views/app.js +23 -5
  77. package/templates/portal/js/views/checklist.js +29 -10
  78. package/templates/portal/js/views/credential.js +98 -0
  79. package/templates/portal/js/views/docs.js +94 -4
  80. package/templates/portal/js/views/files.js +58 -14
  81. package/templates/portal/js/views/graph.js +1 -1
  82. package/templates/portal/js/views/health.js +178 -37
  83. package/templates/portal/js/views/inbox.js +938 -98
  84. package/templates/portal/js/views/memory.js +16 -1
  85. package/templates/portal/js/views/org.js +124 -104
  86. package/templates/portal/js/views/orgedit.js +213 -0
  87. package/templates/portal/js/views/paste.js +33 -7
  88. package/templates/portal/js/views/prompt.js +61 -67
  89. package/templates/portal/js/views/repos.js +20 -15
  90. package/templates/portal/js/views/runonce.js +94 -0
  91. package/templates/portal/js/views/runs.js +165 -0
  92. package/templates/portal/js/views/setup.js +311 -83
  93. package/templates/portal/js/views/staff.js +143 -22
  94. package/templates/portal/js/yaml.js +134 -0
  95. package/templates/brain/.github/workflows/%%STAFF%%-pr-mention.yaml +0 -50
  96. package/templates/ops/prompts/pr-mention.md +0 -57
@@ -0,0 +1,60 @@
1
+ # Charter — Acme's Head of Support
2
+
3
+ > **An example to adapt, not a template.** Acme is invented: a small company whose product is an
4
+ > open-source scheduling app, `acme/acme-web`, run by one founder, Sam. Replace every specific
5
+ > with your own. The shape is what has worked; the words have to be yours.
6
+
7
+ *Who I am and what only I do. The shared half lives in `roster-ops/org/`. This file is the
8
+ difference between me and the rest of the staff, and nothing else.*
9
+
10
+ ---
11
+
12
+ ## Who I am
13
+
14
+ Acme's Head of Support. I make sure nobody who asks for help is left waiting, and that what
15
+ people ask about reaches the staff who can fix the cause.
16
+
17
+ ## The mission
18
+
19
+ **Every question answered, and every repeated question made unnecessary.** An answer fixes one
20
+ person's day; a fixed doc or a filed bug fixes it for everyone after them.
21
+
22
+ ## How I work, that others here do not
23
+
24
+ - **Oldest first.** Every run starts with the support queue, the `question` issues on
25
+ `acme/acme-web`, oldest unanswered at the top. Nothing sits past two working days without a
26
+ reply, even if the reply is "not yet, here is why".
27
+ - **I draft replies; Sam sends them.** Each is a `reply` issue on my tracker with the exact text
28
+ and a link to the thread. He sends it or edits it.
29
+ - **Three of the same question is a bug.** I file it on the CTO's tracker, labelled
30
+ `from-support`, with the three links. One-off questions do not become issues.
31
+ - **Docs are mine to fix.** A wrong or missing answer in `acme-web/docs/` gets a PR from a branch.
32
+ - **Weekly, one line per theme** in `strategy/themes.md`: what people asked about, how often. The
33
+ CMO reads it for copy; the CTO for priorities.
34
+
35
+ ## Decision rights
36
+
37
+ | I do freely | I file an issue, then carry on |
38
+ |---|---|
39
+ | Drafting replies, and labelling support issues | Sending anything to a customer: a `reply` issue |
40
+ | PRs to the docs | Refunds, credits, anything touching money |
41
+ | Filing bugs for the CTO, and themes for the CMO | Anything involving a customer's personal data |
42
+ | Closing my own issues when the thread is answered | Promising a fix or a date |
43
+
44
+ ## Guardrails on top of the org's
45
+
46
+ 1. **Never ask a customer for a password, a card number, or anything they would not post in
47
+ public.** Point them at the account page instead.
48
+ 2. **Never promise.** "The team is looking at it" is true; "fixed next week" is a commitment only
49
+ Sam makes.
50
+ 3. **Personal data stays out of my repo.** A theme is written without names or emails.
51
+
52
+ ## Where the rest of it lives
53
+
54
+ | | |
55
+ |---|---|
56
+ | How I operate | `roster-ops/org/operating.md` |
57
+ | What matters this month | `roster-ops/org/priorities.md` |
58
+ | What people ask about | `strategy/themes.md` |
59
+ | What I know | `memory/INDEX.md` |
60
+ | What is outstanding | the pinned status issue |
package/docs/commands.md CHANGED
@@ -6,8 +6,10 @@ sidebar_order: 8
6
6
 
7
7
  # Commands
8
8
 
9
- Every command prints a plan and changes nothing unless you pass `--apply`, except `lint`,
10
- `prompt`, `export` and `portal`, which never change anything at all.
9
+ Every command that changes anything prints a plan and changes nothing unless you pass
10
+ `--apply`. `lint`, `prompt`, `export`, `brief`, `doctor` and `fix` never change anything.
11
+ `portal` is the exception: it is interactive, and each change there is a button you press after
12
+ seeing what it will do.
11
13
 
12
14
  ## `roster fix`
13
15
 
@@ -44,11 +46,15 @@ Stand up a new tenant: the ops repo, the org layer, and the recorded merge base.
44
46
  --apply
45
47
  ```
46
48
 
49
+ With `--apply` it also sets the ops repo's Actions access to "accessible from repositories in
50
+ the organisation", which is what lets every brain call its workflow. If GitHub refuses (it needs
51
+ admin on the repo), it prints the reason and the settings page to click instead.
52
+
47
53
  Will not write `org/business.md`. That is yours.
48
54
 
49
55
  ## `roster hire <handle>`
50
56
 
51
- Scaffold a staff member: repo, three callers, manifest, memory index, charter stub, labels,
57
+ Scaffold a staff member: repo, two callers, manifest, memory index, charter stub, labels,
52
58
  pinned status issue, and peer wiring in both directions.
53
59
 
54
60
  ```
@@ -58,13 +64,23 @@ pinned status issue, and peer wiring in both directions.
58
64
  --model <id>
59
65
  --timeout <n> daily ceiling, minutes
60
66
  --mention-timeout <n>
61
- --pr-timeout <n>
62
67
  --secret-prefix <X> secrets become <X>_APP_ID and <X>_APP_PRIVATE_KEY
63
68
  --app <slug> defaults to the pattern the peers use
64
69
  --public-app <slug> the shared public identity
70
+ --no-review-gate leave the product repos' branch rules alone
65
71
  --apply
66
72
  ```
67
73
 
74
+ With `--apply` it also:
75
+
76
+ - commits and pushes, as you, what it changed in repos that already exist: each peer's
77
+ `staff.yaml`, `org.yaml`, and the new `staff.yaml` once the status issue has a number. The
78
+ plan lists each commit first, and a push that fails is reported and left for you.
79
+ - adds the new brain to the agent credential's org secret, if there is one, so the credential is
80
+ never asked for again. See [`roster credential`](#roster-credential).
81
+ - adds a review-before-merge ruleset to each product repo that does not already require an
82
+ approving review. See [security](security.md#the-review-gate).
83
+
68
84
  ## `roster app <handle>`
69
85
 
70
86
  Create the GitHub App and put its credentials in the brain repo's secrets.
@@ -73,9 +89,55 @@ Create the GitHub App and put its credentials in the brain repo's secrets.
73
89
  --public create the shared public identity instead
74
90
  --port <n> localhost port for the hand-off. Default 4310.
75
91
  --no-open print the URL rather than opening a browser
92
+ --apply actually create it; without it, prints the App name, secrets and repos
93
+ ```
94
+
95
+ Cannot install the App: GitHub asks a person to confirm which repos it reaches. It prints a link
96
+ to the install page with the org and every repo the staff member needs already selected (its
97
+ brain, its peers' trackers, the product repos), so confirming is one click. The pre-selection uses
98
+ `suggested_target_id` and `repository_ids[]`, which GitHub's own links use but does not document;
99
+ when the ids cannot be read the link is the plain install page. See
100
+ [manual steps](manual-steps.md).
101
+
102
+ ## `roster credential`
103
+
104
+ Store the coding agent's credential once for the org.
105
+
106
+ ```
107
+ --repo-secrets a secret on each brain repo, even where an org secret would work
108
+ --apply read the credential and store it
109
+ ```
110
+
111
+ By default it is one organisation secret, named after the agent's `token_env`, shared with every
112
+ brain repo; `roster hire` adds each new brain to it. It uses a secret on each brain instead, and
113
+ the plan says why, when an org secret would not arrive: on GitHub Free an org secret does not
114
+ reach a private repo, and only an org owner can set one. Setting an org secret also needs the
115
+ `admin:org` scope on your gh token (`gh auth refresh -h github.com -s admin:org`); if it is
116
+ refused, the credential goes on each repo and the output says so.
117
+
118
+ The value comes from standard input, or a prompt that does not echo, and goes to `gh` on its
119
+ standard input. It is never on a command line and never on disk.
120
+
121
+ ```bash
122
+ claude setup-token # Claude Code; see docs/agents.md for the others
123
+ roster credential --apply
76
124
  ```
77
125
 
78
- Cannot install the App. See [manual steps](manual-steps.md).
126
+ ## `roster run <handle>`
127
+
128
+ Start one daily run now and follow it to the end.
129
+
130
+ ```
131
+ --no-wait start it and print the link, without following it
132
+ --apply start the run
133
+ ```
134
+
135
+ Runs `gh workflow run <handle>-daily.yaml`, finds the run it started, and polls it until it
136
+ finishes. It prints the outcome, the step it failed at if it did, and the log's link. A success
137
+ is what `roster doctor` counts as proof that the App, its grant, the secrets and the callers all
138
+ work, so its "unproven" warning goes away. It is a real run and spends what a scheduled one
139
+ would, which is why it needs `--apply`. The staff card and Health have the same as
140
+ **Run once now**.
79
141
 
80
142
  ## `roster retire <handle>`
81
143
 
@@ -125,7 +187,8 @@ Carry framework changes into the tenant. See [upgrading](upgrading.md).
125
187
 
126
188
  ## `roster lint [handle]`
127
189
 
128
- Check memory against the grammar. See [memory](memory.md).
190
+ Check memory against the grammar, and warn when a fact, the index or the decision log is over
191
+ its [budget](memory.md#budgets). See [memory](memory.md).
129
192
 
130
193
  ```
131
194
  --quiet print only problems
@@ -144,8 +207,9 @@ amend <who> change what a staff member is told, with the whole prompt attach
144
207
  ```
145
208
 
146
209
  ```
147
- --kind <k> for amend: daily | mention | pr-mention (default: daily)
210
+ --kind <k> for amend: daily | mention (default: daily)
148
211
  --want <text> for amend: what you want changed
212
+ --example <e> for charter: cto, cmo, support or none (default: matched to the role)
149
213
  --ops <dir>
150
214
  ```
151
215
 
@@ -156,6 +220,11 @@ roster brief charter cto | pbcopy
156
220
  roster brief voice > /tmp/brief.md
157
221
  ```
158
222
 
223
+ `charter` carries one of the [worked examples](writing-a-charter.md#worked-examples) as a
224
+ model to adapt, matched to the role by handle or name. It is a model for the shape, not content
225
+ to copy, and the brief still interviews you first. `--example` picks another; `--example none`
226
+ leaves it out. The portal's copy-a-prompt has the same choice.
227
+
159
228
  `amend` is the different one. It carries the composed prompt and every file it is assembled
160
229
  from, so the agent you paste it into does not have to ask for any of them. The portal's Prompt
161
230
  screen builds the same thing, and offers it per audit finding.
@@ -177,14 +246,19 @@ roster brief discover > roster-ops/.claude/commands/discover.md
177
246
  Compose and print what a staff member is actually sent.
178
247
 
179
248
  ```
180
- --kind daily|mention|pr-mention
249
+ --kind daily|mention
181
250
  --diff <workflow.yaml>
251
+ --inflight
182
252
  ```
183
253
 
184
- `mention` and `pr-mention` need trigger context:
254
+ A run also carries the pull requests people have open on the product repos. `--inflight` reads
255
+ them through your own `gh` and includes them; without it that section is left out, so the output
256
+ does not move with somebody else's branch.
257
+
258
+ `mention` needs trigger context:
185
259
 
186
260
  ```bash
187
- ROSTER_CONTEXT='{"issue_number":"1","comment_id":"1","pr_number":"1","repo":"o/r"}' \
261
+ ROSTER_CONTEXT='{"issue_number":"1","comment_id":"1","repo":"o/r"}' \
188
262
  roster prompt cto --kind mention
189
263
  ```
190
264
 
@@ -197,8 +271,12 @@ which is the shortest way in.
197
271
  --port <n> default 4300
198
272
  --host <a> default 127.0.0.1. Anything else exposes write actions to the network.
199
273
  --dir <path> where a tenant would be created or checked out. Default: here.
274
+ --no-open don't open a browser
200
275
  ```
201
276
 
277
+ It opens the page in your browser when it starts. It stays closed in CI, over SSH, when output
278
+ is not a terminal, or with `BROWSER=none`.
279
+
202
280
  **With no tenant where you started it, this is the setup screen**: it stands up a new org, or
203
281
  checks out one that already runs roster. Local only. See [the portal](portal.md).
204
282
 
@@ -206,7 +284,8 @@ Views: Inbox, Org, Staff, Docs, and per staff member Brain, Prompt, Graph, What
206
284
 
207
285
  It can act as you through your own `gh`: reply, close, reopen and open issues; hire and retire;
208
286
  edit and commit the org layer, prompt fragments and charters; create a staff member's GitHub App;
209
- and copy a prompt for authoring the two files nothing can generate.
287
+ store the agent credential; set the ops repo's Actions access; start one run and follow it; and
288
+ copy a prompt for authoring the two files nothing can generate.
210
289
 
211
290
  ## `roster export`
212
291
 
package/docs/concepts.md CHANGED
@@ -6,23 +6,64 @@ sidebar_order: 3
6
6
 
7
7
  # Concepts
8
8
 
9
+ ## The six things you need to know
10
+
11
+ Enough to set up an org and read what it does. Everything after this section is detail you
12
+ can learn when you need it.
13
+
14
+ 1. **The org layer.** One private repo, `<org>/roster-ops`, holds what every staff member
15
+ shares: what the business is (`org/business.md`), what matters this month
16
+ (`org/priorities.md`), the house voice and the guardrails. Change it once and every staff
17
+ member has it on their next run. [More](#the-ops-repo).
18
+ 2. **A staff member is a repo.** Each one has a private repo, its *brain*: what it knows, what
19
+ it is working on, and what it has decided. There is no database and no server; the portal
20
+ reads the repos. [More](#the-brain).
21
+ 3. **The charter.** `CHARTER.md` in the brain says who this staff member is and what it
22
+ decides alone. You write it, with a brief that interviews you; roster never generates one,
23
+ because a generated charter makes a generic agent. [More](#charter-and-manifest).
24
+ 4. **Memory.** `memory/INDEX.md` is one line per fact, read at the start of every run. The
25
+ agent writes it and deletes from it; you can read and correct it in the portal. That is how
26
+ a staff member remembers yesterday. [More](memory.md).
27
+ 5. **The daily run.** A scheduled GitHub Actions workflow in each brain wakes the staff member,
28
+ hands it a prompt built from the org layer plus its charter and memory, and it does one piece
29
+ of work and writes down what happened. [More](#kinds-of-run).
30
+ 6. **Mentions.** Write `@handle` in an issue or comment on a staff member's own tracker and it
31
+ runs to answer that, between daily runs. Nothing on a product repo wakes anybody; you ask
32
+ them on their tracker. [More](#kinds-of-run).
33
+
34
+ Everything below, and the rest of the docs, is detail: identities, peers, surfaces, the
35
+ prompt's layers, upgrading. None of it is needed to get a first run.
36
+
9
37
  ## The ops repo
10
38
 
11
39
  `<org>/roster-ops` holds two different kinds of thing, and the split matters.
12
40
 
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. A
15
- change here reaches everybody on their next run, which is the point: a concision rule that used
16
- to mean editing twelve files is now one file.
41
+ **`org/` is yours.** `business.md`, `priorities.md`, `voice.md`, `guardrails.md`,
42
+ `operating.md`. This is the business truth and the shared half of every staff member's
43
+ instructions. Edit it freely: the
44
+ [Org screen](portal.md#org) lists every one of these off disk with an Edit button, and saving
45
+ commits and pushes. A change here reaches everybody on their next run, which is the point: a
46
+ rule every staff member should follow is one edit, not one per repo.
17
47
 
18
48
  **Everything else is machinery** and belongs to the framework: `compose.mjs`, `agents.mjs`,
19
- `runner-plan.mjs`, `.github/workflows/session.yaml`. Editing these works right up until the
49
+ `runner-plan.mjs`, `inflight.mjs`, `run-record.mjs`, `.github/workflows/session.yaml`. Editing these works right up until the
20
50
  framework changes the same file, at which point your change is a conflict at best and silently
21
51
  reverted at worst. Fix machinery in the framework, then `roster upgrade`.
22
52
 
23
53
  `roster upgrade` enforces this distinction. It reports an edit to a framework-owned file even
24
54
  when nothing has collided yet, because "not broken yet" is the state a lost fix sits in.
25
55
 
56
+ ## Priorities
57
+
58
+ `org/priorities.md` is the one direction every staff member shares: what matters this month,
59
+ ranked, and what is out of scope. It is composed into every daily run, a run picks work that
60
+ serves it, and a PR names the priority it serves. Keep it to three priorities or fewer, and
61
+ rewrite it when the month turns.
62
+
63
+ Without it each staff member picks its own work from its own charter, and they drift. `roster
64
+ init` writes a stub; `roster doctor` warns while it is missing or still the stub. An org that
65
+ predates it just adds the file.
66
+
26
67
  ## The brain
27
68
 
28
69
  A staff member's repository *is* their memory. There is no database.
@@ -34,12 +75,12 @@ A staff member's repository *is* their memory. There is no database.
34
75
  | `memory/INDEX.md` | one line per fact, read in full at every boot |
35
76
  | `memory/notes/` | the argument behind a fact, read only when that fact is in play |
36
77
  | `log/decisions.md` | why things were decided. Not boot context. |
37
- | `.github/workflows/` | three callers, about forty lines each |
78
+ | `.github/workflows/` | two callers, about forty lines each |
38
79
 
39
80
  ## Charter and manifest
40
81
 
41
82
  Two halves of one thing. The charter is prose for the agent; the manifest is fields for the
42
- machinery. `roster lint` fails if they disagree.
83
+ machinery. [Health](portal.md#health), and `roster lint`, fail if they disagree.
43
84
 
44
85
  The charter is the only file roster refuses to generate. A generated charter produces a generic
45
86
  agent, and a generic agent produces work that is plausible, competent-looking and about nothing
@@ -64,10 +105,12 @@ exports declares `gallery` and `table`; nothing in the portal knows what a CMO i
64
105
 
65
106
  ```
66
107
  org/operating.md + org/guardrails.md + org/voice.md + org/business.md
67
- + <staff>/CHARTER.md + prompts/<kind>.md
108
+ + org/priorities.md + <staff>/CHARTER.md + prompts/<kind>.md
68
109
  ```
69
110
 
70
- Built at run time by `compose.mjs` in the tenant's own repo. See it for yourself:
111
+ Built at run time by `compose.mjs` in the tenant's own repo. See it for yourself on the
112
+ [Prompt screen](portal.md#prompt), which shows the composed text and every layer that went into
113
+ it, or from a terminal:
71
114
 
72
115
  ```bash
73
116
  roster prompt cto --kind daily
@@ -83,10 +126,19 @@ control.
83
126
  |---|---|
84
127
  | `daily` | the scheduled session. Boot, work, hand off. |
85
128
  | `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
129
 
88
- `mention` and `pr-mention` prompts refuse to compose without trigger context, because they are
89
- written for the comment that woke them. That is correct behaviour, not a bug.
130
+ A `mention` prompt refuses to compose without trigger context, because it is written for the
131
+ comment that woke it. That is correct behaviour, not a bug.
132
+
133
+ **Nothing in a product repo wakes anybody.** A pull request is on the product repo, and a staff member's caller workflow is in their own brain repo and
134
+ gates on their `@handle` appearing *there*. So naming somebody in a reply where a comment will
135
+ not reach them offers, under the box, to open the request on their tracker as well. One press
136
+ posts your words on the thread and sends them the pull request, the branch, the hunk you were
137
+ looking at if you started from a file, and an instruction to answer on the pull request rather
138
+ than in the tracker it arrived in.
139
+
140
+ It is two `gh` calls as you, rather than a workflow in the product repo with its own gate and
141
+ its own credential. See [the portal](portal.md#asking-for-a-change).
90
142
 
91
143
  ## Identities
92
144
 
package/docs/cost.md CHANGED
@@ -12,17 +12,53 @@ Three separate bills, and they behave differently.
12
12
 
13
13
  The largest by far, and the one that scales with how much work you ask for.
14
14
 
15
+ On a Claude subscription, through a Claude Code OAuth token, it is a flat subscription rather
16
+ than spend per token, and what grows with a session is how much of its usage you take. That is
17
+ how Pip's staff run.
18
+
15
19
  A session's cost is roughly its length. Ours run 11 to 55 minutes of wall clock, and a longer
16
20
  session is a bigger bill as well as a slower one. The lever that matters is not the model
17
21
  setting, it is how much you ask a staff member to do each morning and how much context it has
18
22
  to read to start.
19
23
 
20
24
  That is why the memory system is shaped the way it is. Boot context here went from about 52,000
21
- words to about 6,000 by moving from a narrative status file to one line per fact. That is a
25
+ words to about 10,000 today by moving from a narrative status file to one line per fact. That is a
22
26
  direct, repeated saving on every run of every staff member.
23
27
 
24
28
  **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` reports the ratio.
29
+ `timeout_minutes` has been paid for and produced nothing. Health, and `roster doctor`, report
30
+ the ratio.
31
+
32
+ ## What each run cost
33
+
34
+ Every session writes down what it was, after the agent finishes or fails: staff, kind, outcome,
35
+ duration, and turns, cost and tokens where the agent reports them. It goes in the job summary,
36
+ and into an artifact called `roster-run` holding one `run.json`.
37
+
38
+ Which agents report what:
39
+
40
+ | Agent | Turns, cost, tokens |
41
+ |---|---|
42
+ | `claude-code-action` | yes, from the Action's execution file |
43
+ | `claude` | yes, from `--output-format json` |
44
+ | anything else | only if its `run` writes the agent's result JSON to `$AGENT_RESULT_FILE` |
45
+
46
+ What is not reported is recorded as unknown, never as zero. A total built from guesses is worse
47
+ than none, so every total says how many runs it could price. The cost is the agent's own figure:
48
+ on a subscription it is what the tokens would have cost, not what you were billed.
49
+
50
+ The portal's [Runs](portal.md#runs) screen lists them per staff member, with a link to each log
51
+ and a 30-day total.
52
+
53
+ ## Budgets
54
+
55
+ An optional [`budget`](org-yaml.md#budget) in `org.yaml`, in dollars over any trailing 30 days,
56
+ for the org or for one staff member. Past it, `roster doctor` warns (`budget`) and the Runs
57
+ screen marks the total.
58
+
59
+ It is a warning and never a cap. Stopping a session mid-run fails it after the work is done and
60
+ committed, which is also why there is no `--max-turns`. Use the warning to go and
61
+ look at which runs cost most and why; the levers are below.
26
62
 
27
63
  ## GitHub Actions minutes
28
64
 
@@ -57,5 +93,5 @@ on your machine when you ask it to, and the machinery is vendored into the tenan
57
93
  being raised is a session that has stopped fitting its job.
58
94
  - **Give a mention workflow a shorter ceiling than a daily one.** A focused task that runs for
59
95
  an hour has gone wrong, and the ceiling is the only thing that stops it.
60
- - **Check the ratio, not the last run.** `roster doctor` reports how many of the last ten runs
96
+ - **Check the ratio, not the last run.** Health reports how many of the last ten runs
61
97
  succeeded. One bad run is noise; four is a bill.
@@ -34,10 +34,8 @@ test/ one file per area
34
34
  module per screen under `js/views/`. `roster portal` serves them from `/assets`, reading each
35
35
  file per request. Editing a stylesheet and reloading the page is the whole edit loop.
36
36
 
37
- It was one 2,100-line HTML file until the CSS and the seven screens grew past the point where
38
- any of them could be found in it. Splitting it needed no bundler, because the browser resolves
39
- the module graph itself, and a bundler would have been a build step in a tool whose selling
40
- point is that it does not have one.
37
+ No bundler, because the browser resolves the module graph itself, and a bundler would be a
38
+ build step in a tool whose selling point is that it does not have one.
41
39
 
42
40
  ```
43
41
  templates/portal/
@@ -64,13 +62,12 @@ registers into at boot.
64
62
 
65
63
  ## The rule that matters
66
64
 
67
- **Never fix a generated file in a tenant.** `compose.mjs`, `agents.mjs`, `runner-plan.mjs` and
68
- `session.yaml` live in `templates/ops/`. Fix them there and run `roster upgrade`.
65
+ **Never fix a generated file in a tenant.** `compose.mjs`, `agents.mjs`, `runner-plan.mjs`,
66
+ `inflight.mjs`, `run-record.mjs` and `session.yaml` live in `templates/ops/`. Fix them there and run `roster upgrade`.
69
67
 
70
- This has gone wrong once already. A fix went into `roster-ops/.github/workflows/session.yaml`
71
- instead of the template and nothing noticed, because the framework had not touched that file
72
- yet. `roster upgrade` now reports an edit to a framework-owned file whether or not anything has
73
- collided, and `roster upgrade --check` fails on it.
68
+ A fix made in the tenant's copy goes unnoticed until the framework next touches that file, and
69
+ then it is a conflict. So `roster upgrade` reports an edit to a framework-owned file whether or
70
+ not anything has collided, and `roster upgrade --check` fails on it.
74
71
 
75
72
  ## Template classes
76
73
 
@@ -101,21 +98,19 @@ marker, or the leftover line is stranded.
101
98
 
102
99
  ## How the tests are meant to work
103
100
 
104
- Three habits, each of which came from a test that was passing vacuously.
101
+ Three habits, each of which guards against a test that passes without testing anything.
105
102
 
106
- **Test the harness, not just the code.** The portal tests set a property on a dead object for
107
- several rounds, because the state they were driving was a top-level `let` in a classic script
108
- and not reachable as a global. The state is now an exported object, so the tests import the
109
- real modules under a DOM shim and there is nothing to get wrong.
103
+ **Test the harness, not just the code.** The portal's state is an exported object, so the tests
104
+ import the real modules under a DOM shim and drive the same state the page does. A test that
105
+ sets a property on something the code never reads cannot fail.
110
106
 
111
- **Run it against reality.** The portal and doctor tests build from the live workspace rather
112
- than a fixture, so they break when real data grows a shape the code cannot handle. That is how
113
- the brain-comparison bug was found: every live workflow reported as absent, because filenames
114
- are templated and the comparison did not render them.
107
+ **Run it against reality when you can.** The suite builds a temporary tenant by default, so it
108
+ runs for anybody who clones the repo. Set `ROSTER_TEST_WORKSPACE=<dir>` to run the portal and
109
+ doctor tests against a real workspace instead, which is how you catch real data growing a shape
110
+ the code cannot handle: templated filenames, for instance, that a comparison forgot to render.
115
111
 
116
112
  **Mutation-test the invariants.** For anything asserting "this behaviour must not regress",
117
- break it deliberately and check the test fails. The workflow-template tests were verified this
118
- way, one mutation each.
113
+ break it deliberately and check the test fails. If it does not, the assertion is decoration.
119
114
 
120
115
  ## Adding a command
121
116
 
@@ -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,12 +25,21 @@ 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` has no `human.github`. The mention callers gate on that login, so nothing can wake an agent. |
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
- | `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). |
36
+ | `priorities` | No `org/priorities.md`. Nothing breaks; each staff member just picks its own direction. See [concepts](concepts.md#priorities). |
37
+ | `priorities.stub` | `org/priorities.md` is still the stub `roster init` wrote. |
38
+ | `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#what-roster-does-for-you). |
39
+ | `workflows` | No workflow in the repo is failing run after run. Reported for the ops repo here, and for each brain repo under its staff member. |
40
+ | `workflows.failing` | **fail.** A workflow that is not one of roster's callers has failed at least its last two runs, with the date it started. Skipped and cancelled runs are stepped over. This is the canary that goes red and stays red, because whatever would have said so broke with it. |
41
+ | `budget` | Trailing 30-day spend against a `budget` in `org.yaml`, for the org here and for a staff member under their name. A warning when it is past, never a failure, and only read when a budget is set. Cost comes from each run's record, so it says how many runs it could price. See [cost](cost.md#budgets). |
42
+ | `review-gate` | Off unless `review_gate: true` is in `org.yaml`, and then a note that it is off. When on, one per product repo. **fail** when nothing requires a pull request on its default branch, or a staff App can bypass the rule; a warning when a PR is required with no approving review, when the repo is private on GitHub Free (which can't enforce the rule), or when the settings cannot be read. See [security](security.md#the-review-gate). |
31
43
  | `upgrade` | The tenant is in sync with the framework. |
32
44
  | `upgrade.stale` | Generated files are behind. `roster upgrade --apply`. |
33
45
  | `upgrade.owned` | **fail.** A framework-owned file was edited in the tenant. Move the change upstream or the next upgrade reverts it. |
@@ -49,12 +61,12 @@ ran at all.
49
61
  | `callers.uses` | **fail.** A caller references no reusable workflow, or one in a different organisation. A private reusable workflow is only callable inside its own org. |
50
62
  | `callers.target` | **fail.** A caller points at a workflow file that is not in the ops repo. Fails at run time as "workflow not found". |
51
63
  | `surfaces` | A surface declared in `staff.yaml` is not on disk. The portal renders nothing for it. |
52
- | `secrets` | Every secret the callers reference exists on the brain repo. Derived from the callers themselves, not a fixed list. |
64
+ | `secrets` | Every secret the callers reference exists on the brain repo, or is an organisation secret shared with it. Derived from the callers themselves, not a fixed list. |
53
65
  | `labels` | Every label declared in `staff.yaml` exists. An agent applying a label that does not exist gets an API error mid-run. |
54
66
  | `peer-labels` | The `from-<handle>` label exists on the *peer's* tracker, which is where this staff member's asks land. |
55
67
  | `status-issue` | The declared status issue is actually pinned. If not, the place you look is not the place the agent maintains. |
56
68
  | `runs` | A window of recent runs. See below. |
57
- | `runs.timeout` | **fail.** Runs were killed at a ceiling. |
69
+ | `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
70
  | `runs.cancelled` | Runs were cancelled short of any ceiling, with their durations. |
59
71
 
60
72
  ## Reading `runs`
@@ -63,8 +75,11 @@ This is the only check that proves the whole chain works, so it is worth underst
63
75
 
64
76
  - **"has never run"** is a warning, not an `ok`. Nothing has exercised the App grant or the
65
77
  secrets, so nothing is known.
66
- - **"all gated out before doing anything"** means every recent trigger was `skipped`. That is
67
- normal for a mention workflow, but it means the credentials are still unproven.
78
+ - **"all gated out"** means every recent trigger was `skipped`, which is a mention workflow's
79
+ normal state: every comment on the tracker fires it and the gate drops all but the real ones.
80
+ It is only a warning when *nothing* in that repo has finished a run, because the App grant
81
+ belongs to the repository rather than to the workflow, so one finished run proves it for all
82
+ of them.
68
83
  - **"ran to a Nm ceiling and were killed"** is a timeout. GitHub reports those as `cancelled`,
69
84
  so doctor identifies them by duration. If the ceiling it names differs from the one the
70
85
  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 `human` block from `org.yaml` |
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
 
package/docs/extending.md CHANGED
@@ -12,6 +12,11 @@ Four seams, in the order you are likely to reach for them.
12
12
 
13
13
  Edit `org/*.md` in the ops repo. It reaches every staff member on their next run.
14
14
 
15
+ The [Org screen](portal.md#org) is exactly this seam: every one of these files listed off disk
16
+ with a line saying what it is for, an Edit button, and a save that commits and pushes. The list
17
+ comes off disk rather than being written into the page, so a file you add yourself is editable
18
+ there too.
19
+
15
20
  | File | For |
16
21
  |---|---|
17
22
  | `business.md` | what the business is. The one everything else is downstream of. |
@@ -19,8 +24,8 @@ Edit `org/*.md` in the ops repo. It reaches every staff member on their next run
19
24
  | `guardrails.md` | non-negotiables. What nobody may do, regardless of charter. |
20
25
  | `operating.md` | the autonomy contract: the boot ritual, the hand-off, decision rights. |
21
26
 
22
- This is the seam that pays. A concision rule here used to mean editing twelve files across two
23
- repositories; now it is one file, and the next morning everybody has it.
27
+ This is the seam that pays. A rule written here is one file, and the next morning everybody
28
+ has it.
24
29
 
25
30
  Keep the split honest. If a rule would be true of every staff member you will ever hire, it
26
31
  belongs here. If it is about one role, it belongs in that role's charter.
@@ -34,7 +39,6 @@ _identity.md who you are posting as, and where
34
39
  _paths.md where things are in the runner checkout
35
40
  daily.md the scheduled session
36
41
  mention.md a focused task from a comment
37
- pr-mention.md a review comment forwarded from the product repo
38
42
  ```
39
43
 
40
44
  The syntax is small on purpose: `{{ path.to.value }}`, `{{> partial.md }}`,
@@ -54,7 +58,12 @@ A staff member can override a fragment for themselves. `{{>? staff:prompts/work.
54
58
  `daily.md` renders `prompts/work.md` from their own brain repo if it exists, and nothing if it
55
59
  does not. That is how one role gets a different working ritual without changing anybody else's.
56
60
 
57
- See what you actually built:
61
+ See what you actually built on the [Prompt screen](portal.md#prompt), which walks the includes
62
+ rather than listing them from memory, so a fragment you just added shows up on it, marked with
63
+ the repo it came from. An optional fragment a role does not have is shown as absent rather than
64
+ hidden. The layers are editable there too, subject to the writable list on that page.
65
+
66
+ From a terminal:
58
67
 
59
68
  ```bash
60
69
  roster prompt cto --kind daily