@nanocollective/roster 0.1.0-alpha.2 → 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 +122 -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 +33 -7
- 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/setup.js +37 -13
- 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/portal.md
CHANGED
|
@@ -16,6 +16,19 @@ authentication and no API quota, and works offline. It can write to GitHub throu
|
|
|
16
16
|
|
|
17
17
|
Keep the repos checked out beside each other, in the same shape the runner uses.
|
|
18
18
|
|
|
19
|
+
## The sidebar
|
|
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.
|
|
25
|
+
|
|
26
|
+
While the first answer is outstanding the badge is a placeholder rather than blank, because an
|
|
27
|
+
empty badge reads as zero and zero is a different claim from "still counting". The screens
|
|
28
|
+
themselves wait the same way: the inbox draws rows and the thread pane draws a page, in
|
|
29
|
+
placeholder shapes, for the seconds GitHub takes. Anything you start writing in that pane
|
|
30
|
+
survives the answer landing under it.
|
|
31
|
+
|
|
19
32
|
## Setup
|
|
20
33
|
|
|
21
34
|
**With no tenant where you started it, the portal is the setup screen instead.** That is not an
|
|
@@ -62,7 +75,38 @@ down with the list, so opening a thread is a render rather than a request.
|
|
|
62
75
|
a shorter timeline than open work because it is there to be read rather than triaged. A
|
|
63
76
|
closed row is dimmed and marked; the sidebar badge keeps counting only what is open.
|
|
64
77
|
- **Reply, close, reopen, open an issue.** All as you, through your own `gh`, so they are
|
|
65
|
-
indistinguishable from doing it on the site. Closing asks for confirmation.
|
|
78
|
+
indistinguishable from doing it on the site. Closing asks for confirmation. A half-written
|
|
79
|
+
reply survives a repaint, and pressing Comment with an empty box says so rather than
|
|
80
|
+
silently doing nothing.
|
|
81
|
+
- **A new issue asks one question: who is it for.** One dropdown, over the staff. It goes to
|
|
82
|
+
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
|
|
86
|
+
something.
|
|
87
|
+
- **Labels are the repo's own, as toggles.** They are the labels that exist on the recipient's
|
|
88
|
+
repository, fetched from GitHub and cached. A text box was a spelling test: `from-cmo` and
|
|
89
|
+
`from-CMO` are different labels and only one of them exists.
|
|
90
|
+
- **Typing `@` offers the org.** A mention is the mechanism, not decoration: a staff member's
|
|
91
|
+
workflow gates on their handle, so a misspelled one is a message nobody is woken by. The list
|
|
92
|
+
is the staff and the humans, and nothing else. The Apps are deliberately absent: `@acme-cto`
|
|
93
|
+
is a login rather than an inbox, GitHub delivers nothing for mentioning one, and an agent
|
|
94
|
+
wakes on its own handle and not on the identity it posts as, so offering them is offering
|
|
95
|
+
entries that do nothing. Each row says which it is, the list appears under the caret, arrow
|
|
96
|
+
keys and Enter pick, Escape dismisses the list rather than the dialog around it, and an email
|
|
97
|
+
address is not a mention.
|
|
98
|
+
- **Attachments.** Drop a file on the box, pick one, or paste one. Paste is the one that
|
|
99
|
+
matters, because a screenshot is on the clipboard and never on disk. GitHub's own drag-and-drop
|
|
100
|
+
attachments are minted by its web app and cannot be made with `gh`, so the file is committed
|
|
101
|
+
to `attachments/<date>-<name>` in the repo the issue lives in and pushed. The body gets the
|
|
102
|
+
link for you and the repo-relative path for the agent, which reads it off its own checkout
|
|
103
|
+
rather than needing a token for a private repo. 25MB a file; a failed push is reported rather
|
|
104
|
+
than hidden.
|
|
105
|
+
- **Reactions.** The 👀 an agent leaves when it picks something up, under the comment it is on,
|
|
106
|
+
with who left it. Without it you post a comment, see nothing change, and have to open GitHub
|
|
107
|
+
to find out it landed.
|
|
108
|
+
- **How much conversation** is on each row, which is most of what tells a live thread from
|
|
109
|
+
something filed and never answered.
|
|
66
110
|
- Issue and PR references in a body become chips you can click through, and `@handle`
|
|
67
111
|
mentions become chips too. A mention of somebody on this roster goes to their repository
|
|
68
112
|
rather than to a GitHub profile of that name, which for `@cto` is a stranger.
|
|
@@ -79,17 +123,105 @@ down with the list, so opening a thread is a render rather than a request.
|
|
|
79
123
|
disclosure. An item a cross-reference points at opens in the portal when the inbox already
|
|
80
124
|
holds it, and on GitHub when it does not.
|
|
81
125
|
|
|
126
|
+
## Pending work
|
|
127
|
+
|
|
128
|
+
The same screen, scoped to pull requests, in its own place in the sidebar. An inbox is what is
|
|
129
|
+
waiting on you; a pull request is work that is finished and waiting on a merge, and the count
|
|
130
|
+
that matters is not how many are open but how many are green and still sitting there.
|
|
131
|
+
|
|
132
|
+
It is called **Pending work** rather than "Pull requests" because that is what is behind it: a
|
|
133
|
+
piece of work a staff member finished and cannot land alone. A pull request is how it arrives,
|
|
134
|
+
not what it is.
|
|
135
|
+
|
|
136
|
+
A pull request's thread has three tabs.
|
|
137
|
+
|
|
138
|
+
- **Conversation** is the thread: body, timeline, reactions, reply box, same as an issue.
|
|
139
|
+
- **Commits**, with subject, author and sha, each linking to GitHub.
|
|
140
|
+
- **Files**: the full patch, rendered as a diff with line numbers, the same renderer *What
|
|
141
|
+
changed* uses. A file with no patch is binary or too large for the API to send one, and says
|
|
142
|
+
so rather than showing nothing.
|
|
143
|
+
|
|
144
|
+
Both are fetched when you open the tab, not carried by the inbox. A diff is the biggest thing
|
|
145
|
+
on this screen by an order of magnitude, and paying for every open PR's diff on every refresh
|
|
146
|
+
of every repo to show one of them is the wrong trade.
|
|
147
|
+
|
|
148
|
+
**Merge** sits beside Close and asks before it goes. It never deletes the branch: that is a
|
|
149
|
+
second decision and not this button's to make. It runs `gh pr merge` as you, so a protected
|
|
150
|
+
branch, a failing required check or a merge queue behaves exactly as it would on the site.
|
|
151
|
+
|
|
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
|
|
154
|
+
has already answered it in its own settings, and on any given repository most of the answers are
|
|
155
|
+
wrong. So the repository is asked instead: squash where it is allowed, then a merge commit, then
|
|
156
|
+
rebase.
|
|
157
|
+
|
|
158
|
+
### Asking for a change
|
|
159
|
+
|
|
160
|
+
**`@cto` on a pull request wakes nobody.** A staff member's caller workflow lives in their own
|
|
161
|
+
brain repo and gates on their handle appearing *there*; on a product repo the same mention
|
|
162
|
+
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.
|
|
165
|
+
|
|
166
|
+
**Reply is the one box, and it handles this.** Name somebody in a reply where a comment will
|
|
167
|
+
not reach them and the offer appears under the box, ticked: *open it on their tracker too*. One
|
|
168
|
+
press of Comment posts your words on the thread and opens the request on their tracker, which
|
|
169
|
+
carries
|
|
170
|
+
|
|
171
|
+
- the pull request, its link and its branch
|
|
172
|
+
- their `@handle`, which is what actually wakes them
|
|
173
|
+
- an instruction to **answer on the pull request**, because `prompts/mention.md` otherwise tells
|
|
174
|
+
them to answer where the request came from, which here is the tracker, leaving the diff silent
|
|
175
|
+
and you watching the wrong page
|
|
176
|
+
- the diff for the file you asked from, if you started from one, clipped: it is there to say
|
|
177
|
+
*which part*, not to be a copy of the diff that goes stale on the next push
|
|
178
|
+
|
|
179
|
+
Untick it and the reply is just a comment. Who gets asked comes off the text you actually sent,
|
|
180
|
+
so a handle you typed and then deleted is not asked.
|
|
181
|
+
|
|
182
|
+
On the **Files** tab each file's heading has its own Reply, which opens the same box about that
|
|
183
|
+
one file. "This bit is wrong" is what you want to say while looking at a diff, and the
|
|
184
|
+
alternative is describing in prose which of thirty files you meant.
|
|
185
|
+
|
|
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
|
+
Both writes go through your own `gh`, as you. Nothing is dispatched between repositories and no
|
|
192
|
+
credential is put on a public repo. The tracker issue goes first, because it is the half that
|
|
193
|
+
reaches anybody; if the copy on the pull request then fails you are told, rather than being
|
|
194
|
+
shown an error that invites you to ask the same person the same thing twice.
|
|
195
|
+
|
|
82
196
|
## Org
|
|
83
197
|
|
|
84
|
-
The layer every staff member inherits, in one place: `org.yaml` and the
|
|
85
|
-
each with a line saying what it is for, because
|
|
86
|
-
open.
|
|
198
|
+
The layer every staff member inherits, in one place: `org.yaml`, every `org/*.md`, and the
|
|
199
|
+
prompt files in `prompts/`, each with a line saying what it is for, because a filename tells
|
|
200
|
+
you nothing about which to open. Anything roster does not ship falls back to its own first
|
|
201
|
+
heading.
|
|
202
|
+
|
|
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
|
|
206
|
+
portal may actually write are listed: an editor that offers a file it cannot save is a trap.
|
|
207
|
+
|
|
208
|
+
Every one of them is editable from the screen it is read on: an **Edit** button on the file,
|
|
209
|
+
⌘S to save, and saving commits and pushes. Only `org.yaml` asks for confirmation first; a
|
|
210
|
+
prose edit is one commit to revert, and asking every time teaches people to click through the
|
|
211
|
+
question.
|
|
87
212
|
|
|
88
213
|
`org.yaml` is the exception to the write allowlist. It belongs to the person rather than the
|
|
89
214
|
agent, so it is writable. But it is the one file here that stops every prompt composing when
|
|
90
215
|
it is wrong, so the server parses it with the tenant's own `compose.mjs` first and refuses
|
|
91
|
-
anything that is not YAML it can read, or that has lost `org`, `name`,
|
|
92
|
-
entry.
|
|
216
|
+
anything that is not YAML it can read, or that has lost `org`, `name`, a handle on a staff
|
|
217
|
+
entry, or a github login on an entry in [`humans`](org-yaml.md#human-and-humans).
|
|
218
|
+
|
|
219
|
+
YAML is coloured, here and anywhere else the portal shows it: comments, keys, strings, numbers
|
|
220
|
+
and the three keywords. `org.yaml` and `staff.yaml` are the two files anybody reads closely,
|
|
221
|
+
and finding a key in a flat grey wall means reading every line.
|
|
222
|
+
|
|
223
|
+
The card at the top names **everyone** the staff answer to, not the first of them. An org can
|
|
224
|
+
have more than one human, and a card that names one of two reads as the only one who counts.
|
|
93
225
|
|
|
94
226
|
## Staff
|
|
95
227
|
|
|
@@ -156,7 +288,7 @@ rather than going nowhere.
|
|
|
156
288
|
`roster prompt <handle> --kind daily` in a terminal. Composed on the server by the tenant's own
|
|
157
289
|
`compose.mjs`, so there is no second implementation to drift.
|
|
158
290
|
|
|
159
|
-
Pick the kind: `daily
|
|
291
|
+
Pick the kind: `daily` or `mention`. A mention prompt is written for the comment
|
|
160
292
|
that woke it, so a preview fills in obviously-fake context.
|
|
161
293
|
|
|
162
294
|
Beneath the composed text, the files it was made of, in two groups.
|
|
@@ -172,25 +304,9 @@ tells the agent to open these; it does not contain them. Editing a charter chang
|
|
|
172
304
|
agent does without changing a byte of the composed prompt, and that distinction is easy to
|
|
173
305
|
miss.
|
|
174
306
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
can be sure about, because a linter you stop believing is worse than no linter.
|
|
179
|
-
|
|
180
|
-
| Check | Why it matters |
|
|
181
|
-
|---|---|
|
|
182
|
-
| a file is still the scaffold | `org/business.md` is composed into every run. Leaving it as questions is invisible: the run works and the output is just generic |
|
|
183
|
-
| a placeholder never resolved | `{{staff.product.repo}}` is null for a staff member who contributes to no other repo, and the agent reads the braces literally |
|
|
184
|
-
| the same line in two layers | the layers are inherited, so a rule a charter repeats from `org/` is duplication nobody reading either file can see |
|
|
185
|
-
| the prompt is long | it is read in full on every run, forever |
|
|
186
|
-
| it names a file that is not there | an instruction to read something absent is a quiet no-op inside a run nobody watches |
|
|
187
|
-
| an included layer is empty | it contributes nothing and costs a line of includes |
|
|
188
|
-
|
|
189
|
-
**Every finding carries the fix.** "Copy a prompt to fix this" builds a brief containing the
|
|
190
|
-
finding, the composed prompt, and every layer, and puts it on your clipboard. Paste it into
|
|
191
|
-
whatever agent you use. Knowing there is a problem is the hard part; writing the paragraph is
|
|
192
|
-
not. The box comes pre-filled with what the finding worked out, so you can add to it rather
|
|
193
|
-
than retype it.
|
|
307
|
+
What is *wrong* with any of this is on [Health](#health). This screen answers "what is sent";
|
|
308
|
+
whether what is sent is any good is a different question, and a tree that was half prompt and
|
|
309
|
+
half complaints answered neither well.
|
|
194
310
|
|
|
195
311
|
### Getting help changing it
|
|
196
312
|
|
|
@@ -284,7 +400,11 @@ opening the diff that did it: one block per file, coloured, with line numbers.
|
|
|
284
400
|
|
|
285
401
|
## Health
|
|
286
402
|
|
|
287
|
-
|
|
403
|
+
Four parts: the rig, the org, the prompts and the memory. The rig is about this staff member,
|
|
404
|
+
so it is first; the rest widens out from there.
|
|
405
|
+
|
|
406
|
+
**The rig**: schedule in words, workflows present, last commit, last commit touching `memory/`,
|
|
407
|
+
index and notes size, charter, status issue, missing surfaces.
|
|
288
408
|
|
|
289
409
|
**The org, from `roster doctor`.** Every finding that is not `ok`, with what to do about it, and
|
|
290
410
|
a button that turns the lot into one brief for a coding agent. That is `roster fix`: `doctor`,
|
|
@@ -300,20 +420,66 @@ an agent that has started editing has stopped reading.
|
|
|
300
420
|
|
|
301
421
|
Paste it into whatever edits files here, then press *Check again*. The ids should be gone.
|
|
302
422
|
|
|
303
|
-
**
|
|
423
|
+
**Prompt problems.** The audit, over all three kinds of run at once. Nothing here judges prose:
|
|
424
|
+
every check is something a machine can be sure about, because a linter you stop believing is
|
|
425
|
+
worse than no linter. A finding true of more than one prompt is one row, and it says which.
|
|
304
426
|
|
|
305
|
-
|
|
427
|
+
| Check | Why it matters |
|
|
428
|
+
|---|---|
|
|
429
|
+
| a file is still the scaffold | `org/business.md` is composed into every run. Leaving it as questions is invisible: the run works and the output is just generic |
|
|
430
|
+
| a placeholder never resolved | `{{staff.product.repo}}` is null for a staff member who contributes to no other repo, and the agent reads the braces literally |
|
|
431
|
+
| the same line in two layers | the layers are inherited, so a rule a charter repeats from `org/` is duplication nobody reading either file can see |
|
|
432
|
+
| the prompt is long | it is read in full on every run, forever |
|
|
433
|
+
| it names a file that is not there | an instruction to read something absent is a quiet no-op inside a run nobody watches |
|
|
434
|
+
| an included layer is empty | it contributes nothing and costs a line of includes |
|
|
435
|
+
|
|
436
|
+
**Every finding carries the fix.** "Copy a prompt to fix this" builds a brief containing the
|
|
437
|
+
finding, the composed prompt, and every layer, and puts it on your clipboard. Paste it into
|
|
438
|
+
whatever agent you use. Knowing there is a problem is the hard part; writing the paragraph is
|
|
439
|
+
not. The box comes pre-filled with what the finding worked out, so you can add to it rather
|
|
440
|
+
than retype it. "Open the file" takes you to that layer on the Prompt screen.
|
|
441
|
+
|
|
442
|
+
**Memory problems** are the same checks `roster lint` runs. Each one has a button that opens an
|
|
306
443
|
issue in that staff member's own repo asking them to fix it, which is usually right, because
|
|
307
444
|
they wrote it.
|
|
308
445
|
|
|
309
|
-
|
|
310
|
-
|
|
446
|
+
## Any image opens
|
|
447
|
+
|
|
448
|
+
**Click a picture and it opens over the page, fitted to the window.** Bottom right there is a
|
|
449
|
+
`-`, the current zoom, and a `+`. The percentage is a button too: it refits. Scrolling zooms,
|
|
450
|
+
dragging moves, clicking the picture goes between fitted and actual size, and `+`, `-`, `0` and
|
|
451
|
+
Escape do the same from the keyboard. Zooming is anchored on the pointer, so the thing you
|
|
452
|
+
aimed at stays where you aimed.
|
|
453
|
+
|
|
454
|
+
The percentage is of **actual size**, not of fitted, so 100% means one image pixel per screen
|
|
455
|
+
pixel. How far in it will go depends on the picture: a scaled image is a composited layer and
|
|
456
|
+
the browser allocates it at the rendered size, so the ceiling is whatever keeps that within
|
|
457
|
+
budget. Actual size is always reachable, however large the original is.
|
|
458
|
+
|
|
459
|
+
This is every image the portal renders as content: a screenshot on a doc page, a picture inside
|
|
460
|
+
a brain document, a file open in the Brain screen's viewer. A screenshot laid out for the column
|
|
461
|
+
it sits in is unreadable exactly when it matters, which is when it is a picture of an interface
|
|
462
|
+
and the part you need is the small print.
|
|
463
|
+
|
|
464
|
+
Gallery tiles are the exception, and deliberately: their click already means "open this file",
|
|
465
|
+
and the file's own view is one of the things that does open.
|
|
466
|
+
|
|
467
|
+
Fitted, the picture is inset from the window edges and framed. That is not decoration. Most of
|
|
468
|
+
these are screenshots *of this interface*, and one fitted edge to edge reads as the app having
|
|
469
|
+
navigated rather than as a picture of it.
|
|
311
470
|
|
|
312
471
|
## Docs
|
|
313
472
|
|
|
314
473
|
The framework's own documentation, rendered where you already are. Links between pages navigate
|
|
315
474
|
the portal.
|
|
316
475
|
|
|
476
|
+
**Search reads the pages, not their titles.** Twenty-odd pages is too many to scan by eye and
|
|
477
|
+
few enough for the server to read in full on every keystroke, and what you are looking for
|
|
478
|
+
("which page explains the mention gate") is a sentence in a paragraph rather than a word in a
|
|
479
|
+
heading. A page has to contain every word you typed; results rank a title hit over a heading
|
|
480
|
+
hit over sheer frequency, carry the lines the words were found in, and the first one opens as
|
|
481
|
+
you type. The query is in the URL, so a search is a link.
|
|
482
|
+
|
|
317
483
|
## After upgrading roster
|
|
318
484
|
|
|
319
485
|
**Restart the portal.** Its stylesheets and modules are read per request, so editing one and
|
package/docs/prompts.md
CHANGED
|
@@ -8,7 +8,13 @@ sidebar_order: 15
|
|
|
8
8
|
|
|
9
9
|
What a staff member is actually sent, and how to change it.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
**Look at it before changing anything.** The portal's [Prompt screen](portal.md#prompt) is the
|
|
12
|
+
composed text and, beneath it, every file it was made of: which are inlined and in what order,
|
|
13
|
+
which are only named, and which repo each came from. That last column is the one that matters,
|
|
14
|
+
because a change to `roster-ops/org/voice.md` reaches every staff member and a change to a
|
|
15
|
+
brain's own `prompts/work.md` reaches one.
|
|
16
|
+
|
|
17
|
+
From a terminal, the same text:
|
|
12
18
|
|
|
13
19
|
```bash
|
|
14
20
|
roster prompt cto --kind daily
|
|
@@ -35,16 +41,30 @@ does not control.
|
|
|
35
41
|
|---|---|---|
|
|
36
42
|
| `daily` | cron | no |
|
|
37
43
|
| `mention` | `@handle` in a comment, or in a new issue body | yes |
|
|
38
|
-
| `pr-mention` | a review comment on the product repo, forwarded in | yes |
|
|
39
44
|
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
A `mention` refuses to compose without context, because it is written for the comment that woke
|
|
46
|
+
it. That is correct behaviour rather than a bug.
|
|
47
|
+
|
|
48
|
+
The Prompt screen's kind picker handles that for you: pick **Mention** and it fills in
|
|
49
|
+
obviously-fake trigger context so there is something to look at. From a terminal you supply it
|
|
50
|
+
yourself:
|
|
42
51
|
|
|
43
52
|
```bash
|
|
44
|
-
ROSTER_CONTEXT='{"issue_number":"1","comment_id":"1","
|
|
53
|
+
ROSTER_CONTEXT='{"issue_number":"1","comment_id":"1","repo":"o/r"}' \
|
|
45
54
|
roster prompt cto --kind mention
|
|
46
55
|
```
|
|
47
56
|
|
|
57
|
+
**There are two routes in, and the prompt is not the same on both.** A comment carries a
|
|
58
|
+
`comment_id`; a mention typed into the body of a *new* issue does not, and there is no comment
|
|
59
|
+
for the agent to fetch. So `compose.mjs` derives `event.no_comment` from the absence, and
|
|
60
|
+
`prompts/mention.md` branches on it: one side points at the comment, the other at the issue
|
|
61
|
+
body. Without that, the first instruction in the prompt was a `gh api .../issues/comments/`
|
|
62
|
+
call with no id on the end, which 404s.
|
|
63
|
+
|
|
64
|
+
The second route is the busier one now. It is what the portal's [Ask a staff
|
|
65
|
+
member](portal.md#asking-for-a-change) produces, and those issues often carry a pull request
|
|
66
|
+
that lives somewhere else and say to answer there instead.
|
|
67
|
+
|
|
48
68
|
## Syntax
|
|
49
69
|
|
|
50
70
|
Four forms, and nothing else.
|
|
@@ -71,8 +91,11 @@ than a hang.
|
|
|
71
91
|
|---|---|
|
|
72
92
|
| `org` | the whole of `org.yaml` |
|
|
73
93
|
| `org.name`, `org.org` | the business name, the GitHub org |
|
|
74
|
-
| `human` | the
|
|
94
|
+
| `human` | the primary human: the first of `humans`, or the singular `human` block |
|
|
75
95
|
| `human.name`, `human.github`, `human.marker` | |
|
|
96
|
+
| `humans` | everyone the staff answer to, in order. See [org.yaml](org-yaml.md#human-and-humans) |
|
|
97
|
+
| `human_list` | all of them as a sentence: "Will (@will-lamerton) and Sam (@sam-x)" |
|
|
98
|
+
| `humans_extra` | the same, minus the primary. **Empty when there is only one**, which is what makes `{{#if humans_extra}}` the way to mention the others |
|
|
76
99
|
| `ops.dir` | ops repo directory in the checkout |
|
|
77
100
|
| `staff` | the whole of this staff member's `staff.yaml` |
|
|
78
101
|
| `staff.dir` | where their brain lands in the checkout |
|
|
@@ -81,9 +104,10 @@ than a hang.
|
|
|
81
104
|
| `peers` | list of the other staff members |
|
|
82
105
|
| `peer` | the first peer, or null |
|
|
83
106
|
| `peer_list` | peers pre-rendered as a markdown list |
|
|
84
|
-
| `kind` | `daily
|
|
107
|
+
| `kind` | `daily` or `mention` |
|
|
85
108
|
| `event` | trigger context, from `ROSTER_CONTEXT` |
|
|
86
|
-
| `event.issue_number`, `event.comment_id`, `event.
|
|
109
|
+
| `event.issue_number`, `event.comment_id`, `event.repo`, `event.actor` | |
|
|
110
|
+
| `event.no_comment` | true when the ask is the issue body rather than a comment. There is no `{{#unless}}`, so the absence is a value |
|
|
87
111
|
|
|
88
112
|
Anything else in a manifest is reachable under `staff.`, so `staff.status_issue` and
|
|
89
113
|
`staff.public_token_env` work without being listed here.
|
|
@@ -105,6 +129,9 @@ This is not hypothetical. The shipped prompts referred to `{{staff.product.repo}
|
|
|
105
129
|
which nobody noticed because every existing staff member had one. The first staff member of any
|
|
106
130
|
new org could not compose a prompt at all.
|
|
107
131
|
|
|
132
|
+
[Health](portal.md#health) checks for exactly this ("a placeholder never resolved"), per staff
|
|
133
|
+
member, which is the only way to catch it before 07:00 rather than in a run nobody watched.
|
|
134
|
+
|
|
108
135
|
## Overriding a fragment for one staff member
|
|
109
136
|
|
|
110
137
|
```
|
|
@@ -120,7 +147,17 @@ else's. `daily.md` already carries this hook.
|
|
|
120
147
|
`prompts/` is `seeded` class: yours to edit, and `roster upgrade` gives you a real three-way
|
|
121
148
|
merge. Changes reach every staff member on their next run.
|
|
122
149
|
|
|
123
|
-
|
|
150
|
+
**Edit them where you read them.** Every layer on the Prompt screen has an Edit button; saving
|
|
151
|
+
writes that one file, commits it and pushes, and the confirmation names the repository and who
|
|
152
|
+
picks it up. The org's own layers (`org/*.md`, `<ops>/prompts/*.md`) are the same edit on the
|
|
153
|
+
[Org screen](portal.md#org). Not everything is writable: `compose.mjs`, `staff.yaml`, the
|
|
154
|
+
workflows and `memory/INDEX.md` are readable and not editable, because a wrong one of those
|
|
155
|
+
stops every prompt composing or overwrites what the next run is about to write.
|
|
156
|
+
|
|
157
|
+
**The thing to check is what the edit did to the composed prompt, not to the file.** Those are
|
|
158
|
+
different questions: a line added to one fragment can land three times or not at all. Saving
|
|
159
|
+
from the Prompt screen shows you the first. From a terminal, the same check is two composes and
|
|
160
|
+
a diff:
|
|
124
161
|
|
|
125
162
|
```bash
|
|
126
163
|
roster prompt cto --kind daily > before.txt
|
|
@@ -129,5 +166,7 @@ roster prompt cto --kind daily > after.txt
|
|
|
129
166
|
diff before.txt after.txt
|
|
130
167
|
```
|
|
131
168
|
|
|
132
|
-
|
|
133
|
-
differently is not caught by anything else.
|
|
169
|
+
Either way, that is the only test there is for a prompt change. One that composes fine and
|
|
170
|
+
reads differently is not caught by anything else. [Health](portal.md#health) runs a prompt audit
|
|
171
|
+
over all the kinds at once, but everything it checks is something a machine can be sure about,
|
|
172
|
+
which never includes whether the prose is any good.
|
package/docs/security.md
CHANGED
|
@@ -42,7 +42,8 @@ workflows that constrain them.
|
|
|
42
42
|
misconfiguration and it fails at run time. Over-granting is the risk: an App installed on the
|
|
43
43
|
whole organisation can reach anything in it.
|
|
44
44
|
|
|
45
|
-
Install narrowly.
|
|
45
|
+
Install narrowly. The Staff card's **GitHub App** panel prints the list it actually needs, and
|
|
46
|
+
so does `roster app`.
|
|
46
47
|
|
|
47
48
|
## Permissions a new App asks for
|
|
48
49
|
|
|
@@ -86,6 +87,8 @@ constrained rather than sanitised:
|
|
|
86
87
|
- `/api/file` resolves the path and refuses anything outside the workspace root.
|
|
87
88
|
- `/api/diff` requires the directory to be a known staff repo and the sha to look like a sha.
|
|
88
89
|
- `/api/doc` requires the page to be one the listing offered.
|
|
90
|
+
- `/api/docasset` requires the screenshot to be one sitting in `docs/images/`, matched by name
|
|
91
|
+
against that listing. `..` is not a case to get wrong; it is simply a name not on it.
|
|
89
92
|
|
|
90
93
|
`--host` overrides the bind address and prints a warning. It exists for people who know what
|
|
91
94
|
they are doing on a network they control. See [hosting the portal](hosting.md).
|
|
@@ -96,12 +99,21 @@ Everything composed into a prompt is content you or your agents wrote: `org/`, t
|
|
|
96
99
|
memory index. A `mention` run additionally carries the text of a comment.
|
|
97
100
|
|
|
98
101
|
**On a private tracker that is you.** On the public product repo it is not, which is exactly
|
|
99
|
-
why
|
|
100
|
-
|
|
101
|
-
charter and a chain of reasoning
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
why **nothing in a product repo wakes an agent at all**. There is no caller workflow there, by
|
|
103
|
+
design: anyone can comment on a public pull request, a run started from a comment executes with
|
|
104
|
+
repository secrets, and a run that prints a charter and a chain of reasoning would print it
|
|
105
|
+
into a world-readable log.
|
|
106
|
+
|
|
107
|
+
So a mention on a public pull request is decoration. It posts, it renders as a chip, and
|
|
108
|
+
nothing happens, which is safe and also invisible. The portal is what makes it visible and what
|
|
109
|
+
gets you out of it: replying with an `@handle` where nothing listens says so, and **Ask a staff
|
|
110
|
+
member** opens the request on that person's own private tracker instead, carrying the pull
|
|
111
|
+
request and the hunk. It writes through your own `gh`, as you, so no credential lives on the
|
|
112
|
+
public repo and nothing is dispatched across a boundary.
|
|
113
|
+
|
|
114
|
+
There was once a forwarder in the product repo that bridged this automatically. It was removed;
|
|
115
|
+
see [concepts](concepts.md#kinds-of-run). If you reinstate one, its author gate is
|
|
116
|
+
load-bearing. Do not relax it.
|
|
105
117
|
|
|
106
118
|
## The loop guard
|
|
107
119
|
|
package/docs/session-workflow.md
CHANGED
|
@@ -26,14 +26,13 @@ repositories in this organisation**. Without it, callers fail with "workflow not
|
|
|
26
26
|
| Input | Type | Default | Means |
|
|
27
27
|
|---|---|---|---|
|
|
28
28
|
| `staff` | string | required | Handle, as in `org.yaml`. |
|
|
29
|
-
| `kind` | string | `daily` | `daily
|
|
29
|
+
| `kind` | string | `daily` | `daily` or `mention`. |
|
|
30
30
|
| `ops_repo` | string | required | `owner/name` of the ops repo. |
|
|
31
31
|
| `model` | string | `claude-opus-5` | Passed to the agent, unless the agent resolves its own. |
|
|
32
|
-
| `timeout_minutes` | number | `
|
|
32
|
+
| `timeout_minutes` | number | `90` | Job ceiling. |
|
|
33
33
|
| `allowed_tools` | string | `Bash,Read,Write,Edit,Glob,Grep,WebFetch,WebSearch` | Tool permissions, for agents that take them. |
|
|
34
34
|
| `issue_number` | string | `""` | Trigger context. |
|
|
35
35
|
| `comment_id` | string | `""` | Trigger context. |
|
|
36
|
-
| `pr_number` | string | `""` | Trigger context. |
|
|
37
36
|
|
|
38
37
|
## Secrets
|
|
39
38
|
|
|
@@ -62,14 +61,13 @@ authentication error forty lines into a log.
|
|
|
62
61
|
6. **Check out the brain**, full history. The agent reads its own past.
|
|
63
62
|
7. **Check out peers and product repos**, per the plan.
|
|
64
63
|
8. **Set git identity** to the App.
|
|
65
|
-
9. **
|
|
66
|
-
10. **
|
|
67
|
-
11. **
|
|
68
|
-
12. **
|
|
69
|
-
13. **
|
|
70
|
-
14. **Run the session**, by one of two steps: the Action-based reference runner, or the generic
|
|
64
|
+
9. **Set up Node and pnpm**, if the plan found a `package.json`.
|
|
65
|
+
10. **Compose the prompt**, to a step output and to `.roster-prompt.txt`.
|
|
66
|
+
11. **Check the agent has a credential.**
|
|
67
|
+
12. **Work out which agent runs this**, by running `agents.mjs`.
|
|
68
|
+
13. **Run the session**, by one of two steps: the Action-based reference runner, or the generic
|
|
71
69
|
CLI one. See [choosing a coding agent](agents.md).
|
|
72
|
-
|
|
70
|
+
14. **Say so if the run did not finish.**
|
|
73
71
|
|
|
74
72
|
## What `runner-plan.mjs` emits
|
|
75
73
|
|
package/docs/staff-yaml.md
CHANGED
|
@@ -24,8 +24,7 @@ status_issue: 15
|
|
|
24
24
|
schedule: "0 7 * * 1-5"
|
|
25
25
|
model: claude-opus-5
|
|
26
26
|
timeout_minutes: 90
|
|
27
|
-
mention_timeout_minutes:
|
|
28
|
-
pr_mention_timeout_minutes: 60
|
|
27
|
+
mention_timeout_minutes: 90
|
|
29
28
|
|
|
30
29
|
bot: acme-cto[bot]
|
|
31
30
|
public_bot: acme-robot[bot]
|
|
@@ -69,9 +68,8 @@ labels:
|
|
|
69
68
|
|---|---|---|
|
|
70
69
|
| `schedule` | none | Cron for the daily run. Rendered into the caller. |
|
|
71
70
|
| `model` | org default | Model id. |
|
|
72
|
-
| `timeout_minutes` |
|
|
73
|
-
| `mention_timeout_minutes` |
|
|
74
|
-
| `pr_mention_timeout_minutes` | 60 | Ceiling on a PR-amendment run. |
|
|
71
|
+
| `timeout_minutes` | 90 | Ceiling on the daily session. |
|
|
72
|
+
| `mention_timeout_minutes` | 90 | Ceiling on a mention run. |
|
|
75
73
|
|
|
76
74
|
The three ceilings are separate on purpose. Raising the daily one because sessions have grown
|
|
77
75
|
should not double the budget for a PR amendment. A job killed by a ceiling is reported by
|
package/docs/troubleshooting.md
CHANGED
|
@@ -9,8 +9,10 @@ sidebar_order: 10
|
|
|
9
9
|
Every trap on this page has actually been hit. Most of them fail in a way that points somewhere
|
|
10
10
|
else, which is why they are worth writing down.
|
|
11
11
|
|
|
12
|
-
Start with
|
|
13
|
-
what to do about it
|
|
12
|
+
Start with the portal's [Health](portal.md#health) screen. It groups by staff member, every
|
|
13
|
+
finding that is not `ok` says what to do about it, and a button turns the lot into one brief for
|
|
14
|
+
a coding agent, split into what an agent can fix and what only a person can. `roster doctor` and
|
|
15
|
+
`roster fix` are the same two things from a terminal.
|
|
14
16
|
|
|
15
17
|
---
|
|
16
18
|
|
|
@@ -20,8 +22,8 @@ what to do about it.
|
|
|
20
22
|
|
|
21
23
|
**Is:** the ops repo's Actions access is not set to organisation-wide.
|
|
22
24
|
|
|
23
|
-
Settings -> Actions -> General on `roster-ops`.
|
|
24
|
-
|
|
25
|
+
Settings -> Actions -> General on `roster-ops`. The setup screen deep-links that exact page,
|
|
26
|
+
which is the fastest way to fix it; Health and `roster doctor` both check it explicitly.
|
|
25
27
|
|
|
26
28
|
---
|
|
27
29
|
|
|
@@ -31,7 +33,7 @@ portal's setup screen links straight to the page.
|
|
|
31
33
|
`timeout-minutes` as `cancelled`, which reads as though somebody pressed a button.
|
|
32
34
|
|
|
33
35
|
Tell them apart by duration. Several cancelled runs all stopping at the same minute is a
|
|
34
|
-
ceiling, not a coincidence.
|
|
36
|
+
ceiling, not a coincidence. Health does this for you and names the number:
|
|
35
37
|
|
|
36
38
|
```
|
|
37
39
|
✗ cto-daily.yaml: 5 of the last 10 ran to a 60m ceiling and were killed
|
|
@@ -49,8 +51,8 @@ so old timeouts keep being reported as timeouts.
|
|
|
49
51
|
gated run still appears in the list with conclusion `skipped`. Most of a mention workflow's
|
|
50
52
|
history is skipped runs.
|
|
51
53
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
Health ignores them. A workflow whose runs are *all* skipped is reported differently, because
|
|
55
|
+
nothing has exercised the credentials.
|
|
54
56
|
|
|
55
57
|
---
|
|
56
58
|
|
|
@@ -63,11 +65,12 @@ been **installed** on the repository in question, or which repositories the inst
|
|
|
63
65
|
granted. The two are reported separately and they disagree exactly when you care.
|
|
64
66
|
|
|
65
67
|
Do not verify an installation by reading the API. The only proof of the whole chain is a run
|
|
66
|
-
that finished.
|
|
67
|
-
|
|
68
|
+
that finished. Health reads recent runs for this reason and calls a workflow that has never run
|
|
69
|
+
**unproven** rather than fine.
|
|
68
70
|
|
|
69
71
|
Fix: open the App's installation settings and check the repository list includes every tracker
|
|
70
|
-
the staff member writes to, not just its own.
|
|
72
|
+
the staff member writes to, not just its own. The **GitHub App** panel on that staff member's
|
|
73
|
+
card says so loudly, and prints the list.
|
|
71
74
|
|
|
72
75
|
---
|
|
73
76
|
|
|
@@ -107,8 +110,9 @@ first entry in `works_in`, and a staff member who contributes to no other reposi
|
|
|
107
110
|
The shipped prompts guard these. A prompt fragment you have written yourself needs
|
|
108
111
|
`{{#if staff.product}}` around anything that assumes one. Conditionals do not nest.
|
|
109
112
|
|
|
110
|
-
Similarly `{{staff.status_issue}}` is empty until
|
|
111
|
-
issue.
|
|
113
|
+
Similarly `{{staff.status_issue}}` is empty until hiring has actually been applied and opened
|
|
114
|
+
the pinned issue. Health's prompt audit has this check ("a placeholder never resolved"), and the
|
|
115
|
+
[Prompt screen](portal.md#prompt) shows you the composed text with the braces still in it.
|
|
112
116
|
|
|
113
117
|
---
|
|
114
118
|
|
|
@@ -119,8 +123,7 @@ seconds. If it does not:
|
|
|
119
123
|
|
|
120
124
|
- The reaction is `continue-on-error`. A missing reaction never costs the answer, so check
|
|
121
125
|
whether the run itself started at all.
|
|
122
|
-
- It is scoped to `kind == 'mention'`. A
|
|
123
|
-
public repo instead, so that it gets one reaction rather than two.
|
|
126
|
+
- It is scoped to `kind == 'mention'`. A daily run has nothing to react to.
|
|
124
127
|
- On the `issues` route (a mention typed into a new issue body) the eyes go on the issue, not
|
|
125
128
|
on a comment, because that payload has no comment.
|
|
126
129
|
|