@nanocollective/roster 0.1.0-alpha.5 → 0.1.0-alpha.7

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 (74) hide show
  1. package/README.md +65 -84
  2. package/dist/cli.js +4153 -2708
  3. package/docs/README.md +9 -6
  4. package/docs/agents.md +24 -20
  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 +81 -5
  10. package/docs/concepts.md +48 -14
  11. package/docs/cost.md +36 -1
  12. package/docs/developing.md +16 -21
  13. package/docs/doctor-codes.md +8 -2
  14. package/docs/extending.md +2 -2
  15. package/docs/getting-started.md +100 -77
  16. package/docs/images/brain.jpg +0 -0
  17. package/docs/images/org.jpg +0 -0
  18. package/docs/images/prompt.jpg +0 -0
  19. package/docs/images/setup-org.jpg +0 -0
  20. package/docs/images/setup-plan.jpg +0 -0
  21. package/docs/images/staff.jpg +0 -0
  22. package/docs/manual-steps.md +93 -123
  23. package/docs/memory.md +21 -3
  24. package/docs/org-yaml.md +37 -2
  25. package/docs/portal.md +59 -33
  26. package/docs/prompts.md +25 -4
  27. package/docs/security.md +29 -5
  28. package/docs/session-workflow.md +49 -17
  29. package/docs/staff-yaml.md +13 -2
  30. package/docs/troubleshooting.md +8 -8
  31. package/docs/upgrading.md +6 -0
  32. package/docs/writing-a-charter.md +15 -0
  33. package/package.json +1 -1
  34. package/templates/brain/.github/workflows/%%STAFF%%-daily.yaml +6 -0
  35. package/templates/brain/.github/workflows/%%STAFF%%-mention.yaml +6 -0
  36. package/templates/brain/CHARTER.md +3 -3
  37. package/templates/brain/README.md +1 -0
  38. package/templates/brain/log/decisions.md +3 -0
  39. package/templates/brain/strategy/ideas.md +7 -0
  40. package/templates/ops/.github/workflows/session.yaml +108 -14
  41. package/templates/ops/agents.mjs +7 -3
  42. package/templates/ops/compose.mjs +16 -3
  43. package/templates/ops/inflight.mjs +157 -0
  44. package/templates/ops/org/operating.md +21 -1
  45. package/templates/ops/org/voice.md +9 -0
  46. package/templates/ops/prompts/_inflight.md +14 -0
  47. package/templates/ops/prompts/_paths.md +2 -1
  48. package/templates/ops/prompts/daily.md +16 -7
  49. package/templates/ops/prompts/mention.md +2 -0
  50. package/templates/ops/run-record.mjs +144 -0
  51. package/templates/portal/css/base.css +133 -73
  52. package/templates/portal/css/brain.css +23 -20
  53. package/templates/portal/css/diff.css +10 -9
  54. package/templates/portal/css/graph.css +12 -7
  55. package/templates/portal/css/health.css +12 -10
  56. package/templates/portal/css/inbox.css +24 -21
  57. package/templates/portal/css/layout.css +64 -46
  58. package/templates/portal/css/markdown.css +17 -14
  59. package/templates/portal/css/runs.css +13 -0
  60. package/templates/portal/css/setup.css +38 -32
  61. package/templates/portal/index.html +5 -1
  62. package/templates/portal/js/api.js +29 -3
  63. package/templates/portal/js/app.js +4 -1
  64. package/templates/portal/js/state.js +3 -1
  65. package/templates/portal/js/views/app.js +23 -5
  66. package/templates/portal/js/views/credential.js +93 -0
  67. package/templates/portal/js/views/graph.js +1 -1
  68. package/templates/portal/js/views/health.js +15 -3
  69. package/templates/portal/js/views/org.js +2 -0
  70. package/templates/portal/js/views/paste.js +29 -0
  71. package/templates/portal/js/views/runonce.js +88 -0
  72. package/templates/portal/js/views/runs.js +165 -0
  73. package/templates/portal/js/views/setup.js +67 -26
  74. package/templates/portal/js/views/staff.js +47 -10
@@ -1,186 +1,156 @@
1
1
  ---
2
2
  title: "Manual steps"
3
- description: "Every human action, why it cannot be automated, and what breaks if you skip it."
3
+ description: "What only a person can do, why, and what breaks if it is skipped."
4
4
  sidebar_order: 2
5
5
  ---
6
6
 
7
7
  # Manual steps
8
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.
9
+ What roster cannot do for you, why, and what it looks like when one is skipped. The
10
+ [getting started](getting-started.md) path walks through each of these in order; this page is
11
+ what to read when one of them bites.
11
12
 
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
- The portal's **Health** screen checks most of these, per staff member and for the org, with
18
- every finding carrying the sentence that fixes it. `roster doctor` prints the same from a
19
- terminal. Look after each step.
13
+ **Health**, or `roster doctor`, checks each one it can, and every finding carries the sentence
14
+ that fixes it.
20
15
 
21
16
  ---
22
17
 
23
- ## 1. Allow the ops repo's workflow to be called
24
-
25
- **Do:** `<org>/roster-ops` -> Settings -> Actions -> General -> *Access* -> **Accessible from
26
- repositories in the organisation**. The setup screen links straight to that page.
18
+ ## 1. Create the organisation
27
19
 
28
- **Why not automated:** it is an organisation permission on a repository, and the API for it
29
- needs admin rights that a token created for a different purpose should not have. roster reads
30
- it and tells you, but setting it is one click and it is yours.
20
+ **Do:** make it on github.com, if you do not have one.
31
21
 
32
- **If you skip it:** every caller fails with **"workflow not found"**. That reads like a typo in
33
- a path, or a missing file, or a bad branch reference. You will check all three. It is none of
34
- them, it is this.
35
-
36
- **Check:** Health, or `roster doctor`, reports `roster-ops is callable from the whole org`.
22
+ **Why not automated:** GitHub has no API for creating an organisation.
37
23
 
38
24
  ---
39
25
 
40
- ## 2. Create the GitHub App
26
+ ## 2. Confirm the GitHub App
41
27
 
42
- **Do:** the **GitHub App** button on a staff card in the portal, or `roster app <handle>` in a
43
- terminal. Either opens a browser, GitHub asks you to confirm, and you come back. Credentials go
44
- straight into the repository's secrets.
28
+ **Do:** press **GitHub App** on a staff card, or run `roster app <handle> --apply`. A tab
29
+ opens; confirm on GitHub. The App's id and private key go straight into the brain repo's
30
+ secrets.
45
31
 
46
- **Why not fully automated:** there is no API that creates a GitHub App. The only route is the
47
- App Manifest flow: POST a manifest to a settings page, a human confirms, GitHub returns a
48
- one-time code. roster does everything either side of that confirmation.
32
+ **Why not automated:** there is no API that creates a GitHub App. The only route is the App
33
+ Manifest flow, where a person confirms on a GitHub page. roster does everything either side of
34
+ that.
49
35
 
50
- **If you skip it:** the run fails at the token-minting step with a message about the app not
51
- existing.
36
+ **If you skip it:** the run fails at the token-minting step, saying the App does not exist.
52
37
 
53
- **Note:** the private key is handed to `gh` on standard input. It is never written to a file,
54
- never passed on a command line, and never appears in the process table. If the secret write
55
- fails after the App is created, the key is gone: generate a new one from the App's settings
56
- page and set the secret by hand. roster tells you this if it happens.
38
+ The private key is handed to `gh` on standard input. It is never written to a file, never on a
39
+ command line, and never in the process table. If the secret write fails after the App is
40
+ created, the key is gone: generate a new one from the App's settings page and set the secret by
41
+ hand. roster says so if it happens.
57
42
 
58
43
  ---
59
44
 
60
- ## 3. Install the App, and grant it the right repositories
61
-
62
- **Do:** open the URL the portal shows, or that `roster app` prints. Choose repositories.
45
+ ## 3. Confirm the App's install
63
46
 
64
- **Why not automated:** installing is a grant of access to specific repositories, and GitHub
65
- requires a human to choose them. This is the correct behaviour and should not be worked around.
47
+ **Do:** press **Install it** in the portal, or open the link `roster app` prints. The page opens
48
+ with the organisation and the repos this staff member needs already selected: its brain, each
49
+ peer's tracker, and the product repos. Check the list and confirm.
66
50
 
67
- **Grant it on every tracker the staff member writes to**, not just their own. The token is
68
- minted organisation-wide, and a peer's board is where a brief lands. Both the portal and
69
- `roster app` say so.
51
+ **Why not automated:** installing is a grant of access to specific repositories, and GitHub asks
52
+ a person to confirm it. That is correct and should not be worked around.
70
53
 
71
- **If you skip it, or under-grant it:** this is the trap that costs the most time, because of
72
- how it fails.
54
+ The pre-selection uses `suggested_target_id` and `repository_ids[]` on the install page. GitHub's
55
+ own links use them, but they are not in GitHub's documentation. If the ids cannot be read, or
56
+ GitHub stops honouring them, the link is the plain install page and you tick the repos yourself;
57
+ the portal and `roster app` both list which.
73
58
 
74
- > The API reports an App's **declaration** separately from an installation's **grant**.
75
- > `GET /apps/<slug>` will happily tell you the App exists and has `contents: write`. That says
76
- > nothing about whether it has been installed on the repository you care about. Two of our
77
- > Apps declare permissions they were never granted.
78
-
79
- So: **do not verify an installation by reading the API.** The only thing that proves the whole
80
- chain (App created, installed, granted, secrets right, workflow reachable) is a run that
81
- finished. `roster doctor` reads a window of recent runs for exactly this reason, and reports a
82
- workflow that has never run as **unproven** rather than as fine.
83
-
84
- **Check:** that staff member's Health screen, or `roster doctor <handle>`. Then trigger one run
85
- and look again.
59
+ **If you under-grant it:** this is the trap that costs the most time, because the API reports an
60
+ App's **declaration** separately from an installation's **grant**. `GET /apps/<slug>` will say
61
+ the App exists and has `contents: write` without saying whether it is installed on the repo you
62
+ care about. So **do not verify an installation by reading the API**. Run it once (item 5).
86
63
 
87
64
  ---
88
65
 
89
- ## 4. Set the agent's credential
66
+ ## 4. Get the agent's credential
90
67
 
91
- **Do:** put the coding agent's credential on each brain repo as a secret. The name follows the
92
- credential: `CLAUDE_CODE_OAUTH_TOKEN`, `CODEX_API_KEY`, and so on. See
93
- [choosing a coding agent](agents.md).
68
+ **Do:** get a credential for your coding agent (`claude setup-token` for Claude Code; the others
69
+ are in [choosing a coding agent](agents.md#1-the-credential-exists-and-you-have-it)) and paste it
70
+ into **Agent credential**, or pipe it to `roster credential --apply`. It is stored once, as an
71
+ organisation secret shared with the brain repos, and each hire adds its repo to it.
94
72
 
95
73
  **Why not automated:** it is your account's credential and roster has no way to obtain one.
96
74
 
75
+ Where an org secret would not arrive, it goes on each brain repo instead and says why. On GitHub
76
+ Free, org secrets do not reach private repos, and only an org owner can set one. Setting an org
77
+ secret also needs the `admin:org` scope on your `gh` token; if it is missing, the credential goes
78
+ on each repo and the output gives the command that adds the scope.
79
+
97
80
  **If you skip it:** the run fails immediately with `the caller passed no agent credential`.
98
- That check exists so it fails there rather than forty lines later inside the agent, after the
99
- checkouts have already happened.
100
81
 
101
- **Check:** Health, or `roster doctor`, lists the secrets each caller references and whether
102
- they exist.
82
+ **Check:** Health, or `roster doctor`, lists the secrets each caller references and whether they
83
+ exist, on the repo or shared from the org.
103
84
 
104
85
  ---
105
86
 
106
- ## 5. Write `org/business.md`
87
+ ## 5. Watch the first run
107
88
 
108
- **Do:** the setup screen does both halves of this. **Copy the prompt** puts a brief on your
109
- clipboard with every file it refers to inlined, so a chat window with no filesystem is as useful
110
- as an agent standing in the repo; the box beneath it takes the reply, shows you a diff, and
111
- saves only when you press the button. `roster brief discover` prints the same brief for a
112
- terminal, and you can always just answer the questions in the file by hand.
89
+ **Do:** **Run once now** on the staff card or on Health, or `roster run <handle> --apply`. It
90
+ starts the daily workflow, follows it, and reports how it ended, with the log.
113
91
 
114
- Health reports `business.stub` while it is still the questions.
92
+ **Why it is yours:** it is a real run. It does a day's work and costs what one does, so it is a
93
+ button you press rather than something setup does behind your back.
115
94
 
116
- **Why not automated:** an agent that does not know the business writes work that is plausible
117
- and generic. That is worse than no work, because it takes longer to notice. This file is
118
- composed into the top of every prompt, every run.
119
-
120
- **If you skip it:** nothing errors. That is the problem. You get competent-looking output about
121
- a business that does not exist.
95
+ **Why it matters:** the only thing that proves the whole chain (App created, installed, granted,
96
+ secrets right, ops repo callable) is a run that finished. `roster doctor` reports a workflow
97
+ that has never run as **unproven** rather than fine, and one finished run clears it.
122
98
 
123
99
  ---
124
100
 
125
- ## 6. Write each staff member's `CHARTER.md`
101
+ ## 6. Write `org/business.md` and `org/priorities.md`
126
102
 
127
- **Do:** **Write the charter** on that staff member's card, which is the same copy-a-prompt,
128
- paste-the-answer-back round trip as item 5, aimed at `CHARTER.md`. The brief it copies carries
129
- the org layer, `business.md` and **the peers' charters**, because without those the model writes
130
- a second copy of whoever it was shown. `roster brief charter <handle>` prints the same brief, and
131
- [writing a charter](writing-a-charter.md) has the shape if you would rather write it yourself.
103
+ **Do:** on the setup screen, **Copy the prompt** puts a brief on your clipboard with every file
104
+ it refers to inside it; paste the reply back and you get a diff and a save button. `roster brief
105
+ discover` prints the same brief. Then write `org/priorities.md`: a few ranked lines on what
106
+ matters this month.
132
107
 
133
- **Why not automated:** same reason, one level down. The charter is what makes a staff member
134
- different from the others.
108
+ Health reports `business.stub` and `priorities.stub` while either is still the stub.
135
109
 
136
- **If you skip it:** `charter` reports it as present, because the stub is a file. `charter.stub`
137
- is the finding that says nobody has answered it. The agent has no personality and produces
138
- whatever the shared layer implies.
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.
139
112
 
140
- ---
113
+ **If you skip it:** nothing errors. That is the problem.
141
114
 
142
- ## 7. Commit and push what roster wrote into other repos
115
+ ---
143
116
 
144
- **Do:** hiring writes a whole brain repo and pushes it, so this is mostly about `roster
145
- upgrade`, which writes into repos on disk and leaves them for you. Review, commit, push.
117
+ ## 7. Write each staff member's `CHARTER.md`
146
118
 
147
- Hiring from the Staff screen still edits the *other* staff members' manifests on disk to wire the
148
- peers both ways, and those are yours to commit.
119
+ **Do:** **Write the charter** on that staff member's card, the same round trip as item 6. The
120
+ brief carries the org layer, `business.md`, the peers' charters, and where the role matches one,
121
+ a [worked example](writing-a-charter.md#worked-examples) to model the shape on. `roster brief
122
+ charter <handle>` prints the same brief.
149
123
 
150
- **Why not automated:** roster does not commit on your behalf into repositories it did not
151
- create in that command. And **App tokens cannot push a change under `.github/workflows/` in any
152
- repository**, which is a GitHub restriction and not a configuration mistake. That is also why
153
- agents can never update their own workflows, and why upgrades are human-run by design.
124
+ **Why not automated:** the charter is what makes a staff member different from the others, and
125
+ a generated one is the generic agent this arrangement exists to avoid.
154
126
 
155
- **If you skip it:** the change exists locally and nowhere else. `roster upgrade` will report it
156
- as still pending next time, which is the intended behaviour.
127
+ **If you skip it:** `charter.stub` says nobody has answered it, and the agent produces whatever
128
+ the shared layer implies.
157
129
 
158
130
  ---
159
131
 
160
- ## Order
132
+ ## 8. Commit what `roster upgrade` wrote
133
+
134
+ **Do:** review, commit and push what `roster upgrade --apply` changed in the brain repos.
161
135
 
162
- For a new organisation:
136
+ **Why not automated:** **App tokens cannot push a change under `.github/workflows/`**, in any
137
+ repository. That is a GitHub restriction, and it is why agents never update their own workflows
138
+ and upgrades are run by a person. Hiring commits its own changes as you, and lists them first.
163
139
 
164
- In the portal it is the screen you are looking at, in this order:
140
+ **If you skip it:** the change exists locally and nowhere else, and `roster upgrade` reports it as
141
+ pending next time.
165
142
 
166
- ```
167
- the setup screen 1 is deep-linked from it, and 5 is on it
168
- Staff -> Hire someone then 7
169
- GitHub App, on the new staff card 2, then 3
170
- Write the charter, on the same card 6
171
- 4 is yours: a secret on the brain repo
172
- Health until the ids are gone
173
- ```
143
+ ---
174
144
 
175
- From a terminal:
145
+ ## What roster does for you
176
146
 
177
- ```
178
- roster init --org <org> --apply # 1 applies here
179
- roster hire <handle> --apply # then 7
180
- roster app <handle> # 2, then 3
181
- # 4, 5, 6
182
- roster doctor <handle>
183
- ```
147
+ Each of these is a step of a command, listed in its plan before it happens, and each falls back
148
+ to telling you exactly what to click if GitHub refuses.
184
149
 
185
- Then trigger one run by hand before trusting the schedule. A workflow that has never run has
186
- proved nothing.
150
+ | | |
151
+ |---|---|
152
+ | The ops repo's Actions access | `roster init --apply`, or **Set it for me** on the setup screen. Needs admin on the ops repo. Skipped, every caller fails with "workflow not found". |
153
+ | Committing peer wiring | `roster hire --apply` commits and pushes each peer's `staff.yaml` and `org.yaml` as you. A push that fails is reported and left for you. |
154
+ | Giving each new brain the credential | `roster hire --apply` adds the repo to the org secret. |
155
+ | Choosing repos on the install page | pre-selected, as above. |
156
+ | The review gate on product repos | `roster hire --apply` adds it where there is none. See [security](security.md#the-review-gate). |
package/docs/memory.md CHANGED
@@ -16,8 +16,8 @@ memory/INDEX.md one line per fact. Read in full at every boot.
16
16
  memory/notes/*.md the argument behind a fact. Read only when that fact is in play.
17
17
  ```
18
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.
19
+ That split is the whole design. Boot context here went from about 52,000 words to about 10,000
20
+ today by making it, and the saving repeats on every run forever.
21
21
 
22
22
  ## The grammar
23
23
 
@@ -48,10 +48,27 @@ rumour.
48
48
  Rule 5 is the one that gets skipped and the one that matters. Everything else degrades slowly;
49
49
  this one degrades the boot cost of every future run.
50
50
 
51
+ ## Budgets
52
+
53
+ Prose is advice, and an index nobody prunes grows past what a run can usefully read. So
54
+ `roster lint` warns past three budgets:
55
+
56
+ | Budget | Default | Rule |
57
+ |---|---|---|
58
+ | One fact's line | 400 characters | `too-long`, naming the fact |
59
+ | `memory/INDEX.md` | 24KB | `index-too-big`, naming the three longest facts |
60
+ | `log/decisions.md` | 24KB | `decisions-too-big`: roll older entries into `log/decisions/<YYYY-MM>.md` |
61
+
62
+ Change them under `memory:` in [org.yaml](org-yaml.md#memory), or per staff member in
63
+ [staff.yaml](staff-yaml.md#memory). The daily prompt tells a staff member to check its sizes at
64
+ hand-off and make pruning that run's job when it is over.
65
+
51
66
  ## What does not go in memory
52
67
 
53
68
  - **Why something was decided.** That is `log/decisions.md`, and it is not boot context.
54
69
  - **How a thing works.** That is a draft or a strategy document.
70
+ - **An idea not yet acted on.** That is one line in `strategy/ideas.md`, and it becomes an issue
71
+ only when it needs a ruling.
55
72
  - **What is outstanding.** That is the pinned status issue.
56
73
 
57
74
  Nothing is copied between them. Four places, four jobs, and a fact that appears in two of them
@@ -64,7 +81,8 @@ opens an issue in that staff member's own repository asking them to fix it, whic
64
81
  right move: they wrote it, and an issue on their tracker is a thing that wakes them.
65
82
 
66
83
  It catches: a missing `So:`, a duplicate slug, a note nothing links to, a link to a note that
67
- does not exist, an over-long line, a `[measured]` fact with no `n`, and an "updated:" chain.
84
+ does not exist, an over-long line, a `[measured]` fact with no `n`, an "updated:" chain, and an
85
+ index or decision log over its budget.
68
86
 
69
87
  The same checks, for a terminal or for CI:
70
88
 
package/docs/org-yaml.md CHANGED
@@ -34,10 +34,12 @@ agent:
34
34
  permissions: full
35
35
 
36
36
  defaults:
37
- model: claude-opus-5
37
+ model: claude-opus-5-5
38
38
  timeout_minutes: 90
39
39
  mention_timeout_minutes: 90
40
40
 
41
+ budget: 300
42
+
41
43
  staff:
42
44
  - { handle: cto, dir: technology, name: Chief Technology Officer, schedule: "0 7 * * 1-5" }
43
45
  - { handle: cmo, dir: marketing, name: Chief Marketing Officer, schedule: "40 7 * * 1-5" }
@@ -58,6 +60,7 @@ repos:
58
60
  | `name` | yes | What the business is called, in prose. Appears in prompts. |
59
61
  | `ops_dir` | no | Directory name of the ops repo in the runner checkout. Defaults to `roster-ops`. |
60
62
  | `experiment_private` | no | Whether the fact that this org is agent-run is itself private. Read by the guardrails fragment. |
63
+ | `budget` | no | USD over any trailing 30 days, for the whole org. See [below](#budget). |
61
64
 
62
65
  ### `human` and `humans`
63
66
 
@@ -123,6 +126,20 @@ Fallbacks for staff members who do not set their own.
123
126
  | `mention_timeout_minutes` | Ceiling on a mention run. Falls back to `timeout_minutes`, then `90`. |
124
127
  | `allowed_tools` | Claude's own spelling of a permission level, kept because it predates `agent.permissions` and still wins for the agents that take a tool list. Nothing translates it for the others: a list written for one agent is not a permission level for another. Prefer [`agent.permissions`](agents.md#permissions), which every agent understands. |
125
128
 
129
+ ### `memory`
130
+
131
+ Budgets `roster lint` holds every staff member's memory to. All optional; a staff member's own
132
+ [`memory:`](staff-yaml.md#memory) overrides these.
133
+
134
+ ```yaml
135
+ memory:
136
+ max_fact_chars: 400 # one fact's line in memory/INDEX.md
137
+ max_index_kb: 24 # the whole index, read in full at every boot
138
+ max_decisions_kb: 24 # log/decisions.md
139
+ ```
140
+
141
+ Over budget is a warning naming what to cut, never an error. See [memory](memory.md#budgets).
142
+
126
143
  ### `staff`
127
144
 
128
145
  The registry. One inline map per staff member. **This is the org's view of them**; the rest
@@ -134,10 +151,26 @@ lives in their own `staff.yaml`.
134
151
  | `dir` | no | Directory and repo name. Defaults to the handle. |
135
152
  | `name` | no | Role name in prose. |
136
153
  | `schedule` | no | Cron. Informational here; the caller workflow is what actually schedules. |
154
+ | `budget` | no | USD over any trailing 30 days, for this staff member alone. |
137
155
 
138
156
  `roster hire` appends to this list. An empty list (`staff: []`) is valid and is what a fresh
139
157
  org has.
140
158
 
159
+ ### `budget`
160
+
161
+ A number of dollars, on the org, on a staff entry, or both:
162
+
163
+ ```yaml
164
+ budget: 300
165
+ staff:
166
+ - { handle: cto, dir: technology, name: Chief Technology Officer, budget: 200 }
167
+ ```
168
+
169
+ Spend over the trailing 30 days is added up from each run's record. Past a budget, `roster
170
+ doctor` warns and the portal's Runs screen marks the total. **Nothing stops a run.** A cap that
171
+ ends a session fails it after the work is done and committed, which is a false red rather than a
172
+ saved penny; `timeout_minutes` is the real bound. See [cost](cost.md#budgets).
173
+
141
174
  ### `repos`
142
175
 
143
176
  Every repository the org owns, and what it is for.
@@ -159,7 +192,9 @@ Every repository the org owns, and what it is for.
159
192
  | `runner-plan.mjs` | `org`, `staff`, and each manifest's `works_in` and `peers` |
160
193
  | `agents.mjs` | `agent`, `staff` |
161
194
  | `roster hire` | all of it, plus every existing manifest |
162
- | `roster doctor` | all of it |
195
+ | `roster doctor` | all of it, including `budget` |
196
+ | `roster lint` | `staff`, `memory` |
197
+ | `roster portal` | all of it; the Runs screen reads `budget` |
163
198
 
164
199
  ## Editing it
165
200
 
package/docs/portal.md CHANGED
@@ -18,10 +18,9 @@ Keep the repos checked out beside each other, in the same shape the runner uses.
18
18
 
19
19
  ## The sidebar
20
20
 
21
- **The counts are right on load.** The badges beside Inbox and Pending work used to fill in only
22
- once something caused a render with an inbox already loaded, which in practice meant "after you
23
- visit the Inbox". A sidebar that says nothing until you look at it is not a sidebar. The page
24
- now asks once at boot, and the Inbox screen shares that request rather than making a second one.
21
+ **The counts are right on load.** The badges beside Inbox and Pending work are fetched once at
22
+ boot, and the Inbox screen shares that request rather than making a second one. A sidebar that
23
+ says nothing until you look at it is not a sidebar.
25
24
 
26
25
  While the first answer is outstanding the badge is a placeholder rather than blank, because an
27
26
  empty badge reads as zero and zero is a different claim from "still counting". The screens
@@ -50,9 +49,15 @@ It asks GitHub which of two things this is:
50
49
  nothing is written until you apply. Same `initFiles` the CLI runs, so the browser and the
51
50
  terminal cannot disagree about what a new tenant contains.
52
51
 
53
- Then: the Actions setting, deep-linked to the exact page with the failure it causes if skipped;
54
- which repos the staff work in, as a picker over what your `gh` can see minus what `org.yaml`
55
- already has; and a prompt for writing `org/business.md`.
52
+ Then what is left. **The Actions setting**, read on arrival: if it is not set, *Set it for me*
53
+ asks GitHub to set it, and a refusal comes back with GitHub's reason and a link to the page to
54
+ click. **The agent credential**: a paste box, a line on where your agent's credential comes from,
55
+ and where it will be stored (one org secret shared with the brains, or each brain where an org
56
+ secret would not arrive, with the reason). The value goes to the local server in a POST and from
57
+ there to `gh` on standard input; it is never written to disk or echoed back. Until somebody is
58
+ hired the box says so, because nothing would read it. Then which repos the staff work in, as a
59
+ picker over what your `gh` can see minus what `org.yaml` already has, and a prompt for writing
60
+ `org/business.md`.
56
61
 
57
62
  Nothing here stores which step you are on. Setup takes days rather than minutes: an App has to be
58
63
  installed, a credential set, a first run finished. So the page derives its state from `roster
@@ -80,9 +85,8 @@ down with the list, so opening a thread is a render rather than a request.
80
85
  silently doing nothing.
81
86
  - **A new issue asks one question: who is it for.** One dropdown, over the staff. It goes to
82
87
  that person's brain repo and their `@handle` is written into the body for you, because those
83
- are the two things that make an issue reach an agent rather than sit there. It used to ask
84
- for a staff member *and* a repo, which let you set the pair to a combination that woke
85
- nobody. To file in a product repo instead, use GitHub: this form is for asking the staff for
88
+ are the two things that make an issue reach an agent rather than sit there. Asking for a
89
+ repo as well would let you pick a pair that wakes nobody. To file in a product repo instead, use GitHub: this form is for asking the staff for
86
90
  something.
87
91
  - **Labels are the repo's own, as toggles.** They are the labels that exist on the recipient's
88
92
  repository, fetched from GitHub and cached. A text box was a spelling test: `from-cmo` and
@@ -149,8 +153,7 @@ of every repo to show one of them is the wrong trade.
149
153
  second decision and not this button's to make. It runs `gh pr merge` as you, so a protected
150
154
  branch, a failing required check or a merge queue behaves exactly as it would on the site.
151
155
 
152
- **It does not ask how.** There used to be a squash / merge commit / rebase dropdown beside it.
153
- That is a question about git rather than about the pull request in front of you, the repository
156
+ **It does not ask how.** Squash, merge commit or rebase is a question about git rather than about the pull request in front of you, the repository
154
157
  has already answered it in its own settings, and on any given repository most of the answers are
155
158
  wrong. So the repository is asked instead: squash where it is allowed, then a merge commit, then
156
159
  rebase.
@@ -160,8 +163,8 @@ rebase.
160
163
  **`@cto` on a pull request wakes nobody.** A staff member's caller workflow lives in their own
161
164
  brain repo and gates on their handle appearing *there*; on a product repo the same mention
162
165
  posts, renders as a chip, and does nothing. That is deliberate, for the reasons in
163
- [security](security.md#trust-in-a-prompt), and it used to be silent, which is worse than the
164
- restriction itself.
166
+ [security](security.md#trust-in-a-prompt). Saying so out loud is the portal's job, because a
167
+ silent no-op is worse than the restriction itself.
165
168
 
166
169
  **Reply is the one box, and it handles this.** Name somebody in a reply where a comment will
167
170
  not reach them and the offer appears under the box, ticked: *open it on their tracker too*. One
@@ -183,16 +186,30 @@ On the **Files** tab each file's heading has its own Reply, which opens the same
183
186
  one file. "This bit is wrong" is what you want to say while looking at a diff, and the
184
187
  alternative is describing in prose which of thirty files you meant.
185
188
 
186
- There was briefly a second button up here called *Ask a staff member*. It did almost the same
187
- thing as Reply, differing mainly in making you pick a name from a dropdown rather than typing
188
- it, and nothing on the page said which one you wanted. Two ways to do one thing is worse than
189
- either of them.
190
-
191
189
  Both writes go through your own `gh`, as you. Nothing is dispatched between repositories and no
192
190
  credential is put on a public repo. The tracker issue goes first, because it is the half that
193
191
  reaches anybody; if the copy on the pull request then fails you are told, rather than being
194
192
  shown an error that invites you to ask the same person the same thing twice.
195
193
 
194
+ ## Runs
195
+
196
+ What each staff member ran in the last 30 days: when, daily or mention, how it ended, how long
197
+ it took, turns and cost where known, and a link to the log. Above the tables, the 30-day total
198
+ for the org, and each staff member's own in their heading.
199
+
200
+ Runs are not in any repo, so this is the one screen that is only ever on GitHub. It reads the
201
+ run lists through your own `gh`, the same way the inbox does, and offline it says so rather
202
+ than drawing an empty table that reads as "nothing ran".
203
+
204
+ Cost comes from the record each run leaves behind (see [cost](cost.md#what-each-run-cost)). A
205
+ run from before records existed, or from an agent that does not report cost, shows a dash, and
206
+ a total says how many runs it could price. Each record is downloaded once and kept for as long
207
+ as the portal runs, so the first visit is the slow one.
208
+
209
+ A skipped mention is not a run and is not listed. A `setup-failure` is a run that failed before
210
+ the agent started, usually a token or a checkout. Past a [`budget`](org-yaml.md#budget), the
211
+ total turns amber.
212
+
196
213
  ## Org
197
214
 
198
215
  The layer every staff member inherits, in one place: `org.yaml`, every `org/*.md`, and the
@@ -200,9 +217,8 @@ prompt files in `prompts/`, each with a line saying what it is for, because a fi
200
217
  you nothing about which to open. Anything roster does not ship falls back to its own first
201
218
  heading.
202
219
 
203
- **The list comes off disk**, not out of the page. It used to be five paths written into the
204
- portal, so a tenant that added `org/pricing.md` could not open it at all and one that had not
205
- written `org/business.md` yet got "not found" with nothing to do about it. Only files the
220
+ **The list comes off disk**, not out of the page, so a tenant that adds `org/pricing.md` can
221
+ open it like any other. Only files the
206
222
  portal may actually write are listed: an editor that offers a file it cannot save is a trap.
207
223
 
208
224
  Every one of them is editable from the screen it is read on: an **Edit** button on the file,
@@ -225,18 +241,20 @@ have more than one human, and a card that names one of two reads as the only one
225
241
 
226
242
  ## Staff
227
243
 
228
- Everyone on the roster, and the four things you could previously only do from a terminal.
244
+ Everyone on the roster, and the things you would otherwise do from a terminal.
229
245
 
230
246
  **Hiring** runs the same `buildPlan` and `applyPlan` that `roster hire` does, on the server.
231
247
  Only the handle is required; everything else is copied from whoever is already here. You see
232
- the plan first, listing every file, every label, the schedule it chose and why, and the manual
233
- steps it cannot do for you. Nothing happens until you apply. What the terminal would have
234
- printed is shown when it finishes.
248
+ the plan first, listing every file, every label, the schedule it chose and why, the commits it
249
+ will make as you in repos that already exist (each peer's `staff.yaml`, `org.yaml`), whether the
250
+ new brain joins the credential's org secret, and what is left for you. Nothing happens until you
251
+ apply. What the terminal would have printed is shown when it finishes.
235
252
 
236
253
  **Writing the charter** is the copy-a-prompt loop below, aimed at `CHARTER.md`. `hire`
237
254
  deliberately does not write it, because a generated charter produces exactly the generic agent
238
- this whole arrangement exists to avoid. So this is the route that was previously `roster brief
239
- charter <handle>` and a terminal.
255
+ this whole arrangement exists to avoid. It is the same brief as `roster brief charter
256
+ <handle>`, with somewhere to put the answer, and a picker for the worked example it carries as a
257
+ model: matched to the role, or another, or none.
240
258
 
241
259
  **The GitHub App** is `roster app`, on this server rather than a second one. There is no API that
242
260
  creates an App: the only route is the manifest flow, where you post a manifest to a settings page,
@@ -246,9 +264,17 @@ one origin. The private key is still held in memory and written straight to a re
246
264
 
247
265
  GitHub redirects the tab *it* opened, not the one you clicked from, so the original polls for the
248
266
  result. What it cannot do is install the App: that is a grant of access to specific repositories
249
- and GitHub asks a human to choose them, which is correct and should not be worked around. The panel
250
- says so loudly, and says to grant every tracker the staff member writes to rather than only their
251
- own.
267
+ and GitHub asks a person to confirm it, which is correct and should not be worked around. So the
268
+ panel's **Install it** opens the install page with the org and the repos already selected: the
269
+ brain, every peer tracker it writes to, and the product repos. Any whose id could not be read are
270
+ listed for you to tick.
271
+
272
+ **Agent credential** is the setup screen's paste box, reachable from each card, because the
273
+ moment you look for it is while setting somebody up. It is once for the org.
274
+
275
+ **Run once now** starts the daily workflow, follows it, and shows how it ended with the log's
276
+ link and, on a failure, the step it failed at. It asks first, because it is a real run. A success
277
+ is what turns doctor's *unproven* into proven. Health has the same button.
252
278
 
253
279
  **Retiring** is `roster retire`, and it is deliberately not deletion. A brain repo is that
254
280
  agent's entire memory and there is no undo, so retiring disables the workflows, unwires them
@@ -284,8 +310,8 @@ rather than going nowhere.
284
310
 
285
311
  ## Prompt
286
312
 
287
- **What this staff member is actually sent**, which was previously only reachable through
288
- `roster prompt <handle> --kind daily` in a terminal. Composed on the server by the tenant's own
313
+ **What this staff member is actually sent**, the same as `roster prompt <handle> --kind daily`.
314
+ Composed on the server by the tenant's own
289
315
  `compose.mjs`, so there is no second implementation to drift.
290
316
 
291
317
  Pick the kind: `daily` or `mention`. A mention prompt is written for the comment