@nanocollective/roster 0.1.0-alpha.2 → 0.1.0-alpha.21
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/README.md +70 -84
- package/dist/cli.js +4775 -2495
- package/docs/README.md +19 -11
- package/docs/agents.md +328 -13
- package/docs/architecture.md +13 -5
- package/docs/charters/cmo.md +69 -0
- package/docs/charters/cto.md +71 -0
- package/docs/charters/support.md +60 -0
- package/docs/commands.md +95 -11
- package/docs/concepts.md +64 -12
- package/docs/cost.md +39 -3
- package/docs/developing.md +16 -21
- package/docs/doctor-codes.md +21 -6
- package/docs/export.md +2 -1
- package/docs/extending.md +13 -4
- package/docs/getting-started.md +121 -80
- 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 +94 -101
- package/docs/memory.md +29 -8
- package/docs/org-yaml.md +76 -11
- package/docs/portal.md +261 -47
- package/docs/prompts.md +77 -11
- package/docs/security.md +51 -7
- package/docs/session-workflow.md +51 -21
- package/docs/staff-yaml.md +16 -7
- package/docs/troubleshooting.md +23 -20
- package/docs/upgrading.md +9 -3
- package/docs/writing-a-charter.md +33 -17
- package/package.json +1 -1
- package/templates/brain/.github/workflows/%%STAFF%%-daily.yaml +7 -0
- package/templates/brain/.github/workflows/%%STAFF%%-mention.yaml +16 -4
- package/templates/brain/CHARTER.md +3 -3
- package/templates/brain/README.md +1 -0
- package/templates/brain/log/decisions.md +3 -0
- package/templates/brain/staff.yaml +0 -1
- package/templates/brain/strategy/ideas.md +7 -0
- package/templates/ops/.github/workflows/session.yaml +117 -40
- package/templates/ops/agents.mjs +127 -8
- package/templates/ops/compose.mjs +77 -7
- package/templates/ops/inflight.mjs +157 -0
- package/templates/ops/org/operating.md +21 -7
- package/templates/ops/org/voice.md +9 -0
- package/templates/ops/prompts/_identity.md +8 -1
- package/templates/ops/prompts/_inflight.md +14 -0
- package/templates/ops/prompts/_paths.md +2 -1
- package/templates/ops/prompts/daily.md +16 -7
- package/templates/ops/prompts/mention.md +18 -2
- package/templates/ops/run-record.mjs +144 -0
- package/templates/portal/css/base.css +238 -64
- package/templates/portal/css/brain.css +30 -20
- package/templates/portal/css/diff.css +15 -10
- package/templates/portal/css/graph.css +12 -7
- package/templates/portal/css/health.css +32 -11
- package/templates/portal/css/inbox.css +117 -14
- package/templates/portal/css/layout.css +93 -41
- package/templates/portal/css/markdown.css +57 -15
- package/templates/portal/css/runs.css +13 -0
- package/templates/portal/css/setup.css +83 -39
- package/templates/portal/index.html +24 -3
- package/templates/portal/js/api.js +65 -4
- package/templates/portal/js/app.js +112 -12
- package/templates/portal/js/dialog.js +94 -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/mdedit.js +84 -0
- package/templates/portal/js/mention.js +264 -0
- package/templates/portal/js/refresh.js +136 -6
- package/templates/portal/js/state.js +55 -8
- package/templates/portal/js/views/app.js +23 -5
- package/templates/portal/js/views/checklist.js +29 -10
- package/templates/portal/js/views/credential.js +98 -0
- package/templates/portal/js/views/docs.js +94 -4
- package/templates/portal/js/views/files.js +58 -14
- package/templates/portal/js/views/graph.js +1 -1
- package/templates/portal/js/views/health.js +178 -37
- package/templates/portal/js/views/inbox.js +938 -98
- package/templates/portal/js/views/memory.js +16 -1
- package/templates/portal/js/views/org.js +124 -104
- package/templates/portal/js/views/orgedit.js +213 -0
- package/templates/portal/js/views/paste.js +33 -7
- package/templates/portal/js/views/prompt.js +61 -67
- package/templates/portal/js/views/repos.js +20 -15
- package/templates/portal/js/views/runonce.js +94 -0
- package/templates/portal/js/views/runs.js +165 -0
- package/templates/portal/js/views/setup.js +311 -83
- package/templates/portal/js/views/staff.js +139 -22
- 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/org-yaml.md
CHANGED
|
@@ -31,11 +31,15 @@ experiment_private: true
|
|
|
31
31
|
|
|
32
32
|
agent:
|
|
33
33
|
id: claude-code-action
|
|
34
|
+
permissions: full
|
|
34
35
|
|
|
35
36
|
defaults:
|
|
36
|
-
model: claude-opus-5
|
|
37
|
-
timeout_minutes:
|
|
38
|
-
|
|
37
|
+
model: claude-opus-5-5
|
|
38
|
+
timeout_minutes: 90
|
|
39
|
+
mention_timeout_minutes: 90
|
|
40
|
+
|
|
41
|
+
budget: 300
|
|
42
|
+
review_gate: true
|
|
39
43
|
|
|
40
44
|
staff:
|
|
41
45
|
- { handle: cto, dir: technology, name: Chief Technology Officer, schedule: "0 7 * * 1-5" }
|
|
@@ -57,18 +61,40 @@ repos:
|
|
|
57
61
|
| `name` | yes | What the business is called, in prose. Appears in prompts. |
|
|
58
62
|
| `ops_dir` | no | Directory name of the ops repo in the runner checkout. Defaults to `roster-ops`. |
|
|
59
63
|
| `experiment_private` | no | Whether the fact that this org is agent-run is itself private. Read by the guardrails fragment. |
|
|
64
|
+
| `budget` | no | USD over any trailing 30 days, for the whole org. See [below](#budget). |
|
|
65
|
+
| `review_gate` | no | `true` to have GitHub require a reviewed pull request before anything merges into a product repo. Off by default, because private repos need a paid plan for it. See [security](security.md#the-review-gate). |
|
|
66
|
+
|
|
67
|
+
### `human` and `humans`
|
|
60
68
|
|
|
61
|
-
|
|
69
|
+
Who the staff answer to. One person is a `human` map; more than one is a `humans` list:
|
|
62
70
|
|
|
63
|
-
|
|
71
|
+
```yaml
|
|
72
|
+
humans:
|
|
73
|
+
- { name: Will, github: will-lamerton, marker: will, role: founder }
|
|
74
|
+
- { name: Sam, github: sam-x, marker: sam, role: operations }
|
|
75
|
+
```
|
|
64
76
|
|
|
65
77
|
| Field | Required | Means |
|
|
66
78
|
|---|---|---|
|
|
67
|
-
| `github` | yes | Login. **The mention callers gate on
|
|
79
|
+
| `github` | yes | Login. **The mention callers gate on these**, so without one nothing can wake an agent. |
|
|
68
80
|
| `name` | no | What to call them in prose. Defaults to the login. |
|
|
69
|
-
| `marker` | no | Provenance tag on a fact they ruled on, as in `[will]`. Also used as a label. |
|
|
81
|
+
| `marker` | no | Provenance tag on a fact they ruled on, as in `[will]`. Also used as a label. Defaults to the first part of the name, lowercased. |
|
|
70
82
|
| `role` | no | Prose only. |
|
|
71
83
|
|
|
84
|
+
Both keys are read, and the singular is not deprecated: an org with one human should keep
|
|
85
|
+
writing `human`. When both are present, anyone in `human` who is not already in the list is
|
|
86
|
+
appended rather than dropped.
|
|
87
|
+
|
|
88
|
+
**The first entry is the primary.** Prompts are prose addressed to somebody ("*Will* is not
|
|
89
|
+
here"), and a list of two cannot go in that sentence, so the first one goes there, their
|
|
90
|
+
`marker` is what `%%HUMAN_MARKER%%` renders, and the rest are named by
|
|
91
|
+
`{{humans_extra}}` in the identity fragment. Everything that *gates* on identity reads all of
|
|
92
|
+
them: the mention caller's `if:` is `contains(fromJSON('["will-lamerton","sam-x"]'), …)`.
|
|
93
|
+
|
|
94
|
+
Adding a human changes every generated caller workflow, so it takes a `roster upgrade` to reach
|
|
95
|
+
the brain repos. Until that lands, the new person can open issues and read everything, and
|
|
96
|
+
mentioning a staff member does nothing.
|
|
97
|
+
|
|
72
98
|
### `agent`
|
|
73
99
|
|
|
74
100
|
Which coding agent runs a session. Either a string, or a map. See
|
|
@@ -81,10 +107,16 @@ Which coding agent runs a session. Either a string, or a map. See
|
|
|
81
107
|
| `run` | if `id` is unknown | Shell command that runs it, reading `$AGENT_PROMPT_FILE`. |
|
|
82
108
|
| `token_env` | if `id` is unknown | Environment variable its credential goes in. |
|
|
83
109
|
| `model` | no | Default model for this agent. A staff member's own `model` wins. |
|
|
110
|
+
| `permissions` | no | `full`, `workspace` or `read-only`. Defaults to `full`. One word, translated into each agent's own vocabulary: a tool list for Claude, a sandbox and an approval policy for Codex, a development mode for nanocoder. A staff member can set their own, and be trusted less than the org. |
|
|
111
|
+
| `options` | no | A map, in that agent's own vocabulary, spelled onto its command line untranslated. The escape hatch for anything roster does not model. |
|
|
84
112
|
|
|
85
113
|
Any field given overrides the preset's, so a preset that is right except for one flag needs
|
|
86
114
|
one line.
|
|
87
115
|
|
|
116
|
+
An agent that needs a config file of its own gets one written when `roster init` chooses it,
|
|
117
|
+
with the parts only a person can supply left as `FILL IN` blanks. `roster doctor` fails while
|
|
118
|
+
any of them are still there.
|
|
119
|
+
|
|
88
120
|
### `defaults`
|
|
89
121
|
|
|
90
122
|
Fallbacks for staff members who do not set their own.
|
|
@@ -92,8 +124,23 @@ Fallbacks for staff members who do not set their own.
|
|
|
92
124
|
| Field | Means |
|
|
93
125
|
|---|---|
|
|
94
126
|
| `model` | Model id passed to the agent. |
|
|
95
|
-
| `timeout_minutes` | Ceiling on a daily session. |
|
|
96
|
-
| `
|
|
127
|
+
| `timeout_minutes` | Ceiling on a daily session. `90` if unset. |
|
|
128
|
+
| `mention_timeout_minutes` | Ceiling on a mention run. Falls back to `timeout_minutes`, then `90`. |
|
|
129
|
+
| `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. |
|
|
130
|
+
|
|
131
|
+
### `memory`
|
|
132
|
+
|
|
133
|
+
Budgets `roster lint` holds every staff member's memory to. All optional; a staff member's own
|
|
134
|
+
[`memory:`](staff-yaml.md#memory) overrides these.
|
|
135
|
+
|
|
136
|
+
```yaml
|
|
137
|
+
memory:
|
|
138
|
+
max_fact_chars: 400 # one fact's line in memory/INDEX.md
|
|
139
|
+
max_index_kb: 24 # the whole index, read in full at every boot
|
|
140
|
+
max_decisions_kb: 24 # log/decisions.md
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Over budget is a warning naming what to cut, never an error. See [memory](memory.md#budgets).
|
|
97
144
|
|
|
98
145
|
### `staff`
|
|
99
146
|
|
|
@@ -106,10 +153,26 @@ lives in their own `staff.yaml`.
|
|
|
106
153
|
| `dir` | no | Directory and repo name. Defaults to the handle. |
|
|
107
154
|
| `name` | no | Role name in prose. |
|
|
108
155
|
| `schedule` | no | Cron. Informational here; the caller workflow is what actually schedules. |
|
|
156
|
+
| `budget` | no | USD over any trailing 30 days, for this staff member alone. |
|
|
109
157
|
|
|
110
158
|
`roster hire` appends to this list. An empty list (`staff: []`) is valid and is what a fresh
|
|
111
159
|
org has.
|
|
112
160
|
|
|
161
|
+
### `budget`
|
|
162
|
+
|
|
163
|
+
A number of dollars, on the org, on a staff entry, or both:
|
|
164
|
+
|
|
165
|
+
```yaml
|
|
166
|
+
budget: 300
|
|
167
|
+
staff:
|
|
168
|
+
- { handle: cto, dir: technology, name: Chief Technology Officer, budget: 200 }
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Spend over the trailing 30 days is added up from each run's record. Past a budget, `roster
|
|
172
|
+
doctor` warns and the portal's Runs screen marks the total. **Nothing stops a run.** A cap that
|
|
173
|
+
ends a session fails it after the work is done and committed, which is a false red rather than a
|
|
174
|
+
saved penny; `timeout_minutes` is the real bound. See [cost](cost.md#budgets).
|
|
175
|
+
|
|
113
176
|
### `repos`
|
|
114
177
|
|
|
115
178
|
Every repository the org owns, and what it is for.
|
|
@@ -127,11 +190,13 @@ Every repository the org owns, and what it is for.
|
|
|
127
190
|
|
|
128
191
|
| Reader | Uses |
|
|
129
192
|
|---|---|
|
|
130
|
-
| `compose.mjs` | `org`, `name`, `human`, `ops_dir`, `staff` |
|
|
193
|
+
| `compose.mjs` | `org`, `name`, `human`, `humans`, `ops_dir`, `staff` |
|
|
131
194
|
| `runner-plan.mjs` | `org`, `staff`, and each manifest's `works_in` and `peers` |
|
|
132
195
|
| `agents.mjs` | `agent`, `staff` |
|
|
133
196
|
| `roster hire` | all of it, plus every existing manifest |
|
|
134
|
-
| `roster doctor` | all of it |
|
|
197
|
+
| `roster doctor` | all of it, including `budget` |
|
|
198
|
+
| `roster lint` | `staff`, `memory` |
|
|
199
|
+
| `roster portal` | all of it; the Runs screen reads `budget` |
|
|
135
200
|
|
|
136
201
|
## Editing it
|
|
137
202
|
|
package/docs/portal.md
CHANGED
|
@@ -16,6 +16,21 @@ 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
|
+
Anything that asks before it acts (a merge, a push, a hire, a retire, a paid run) asks in the
|
|
22
|
+
page's own dialog, never the browser's `confirm()`, which blocks the whole tab.
|
|
23
|
+
|
|
24
|
+
**The counts are right on load.** The badges beside Inbox and Pending work are fetched once at
|
|
25
|
+
boot, and the Inbox screen shares that request rather than making a second one. A sidebar that
|
|
26
|
+
says nothing until you look at it is not a sidebar.
|
|
27
|
+
|
|
28
|
+
While the first answer is outstanding the badge is a placeholder rather than blank, because an
|
|
29
|
+
empty badge reads as zero and zero is a different claim from "still counting". The screens
|
|
30
|
+
themselves wait the same way: the inbox draws rows and the thread pane draws a page, in
|
|
31
|
+
placeholder shapes, for the seconds GitHub takes. Anything you start writing in that pane
|
|
32
|
+
survives the answer landing under it.
|
|
33
|
+
|
|
19
34
|
## Setup
|
|
20
35
|
|
|
21
36
|
**With no tenant where you started it, the portal is the setup screen instead.** That is not an
|
|
@@ -37,13 +52,31 @@ It asks GitHub which of two things this is:
|
|
|
37
52
|
nothing is written until you apply. Same `initFiles` the CLI runs, so the browser and the
|
|
38
53
|
terminal cannot disagree about what a new tenant contains.
|
|
39
54
|
|
|
40
|
-
Then
|
|
41
|
-
|
|
42
|
-
|
|
55
|
+
Then what is left. **The Actions setting**, read on arrival: if it is not set, *Set it for me*
|
|
56
|
+
asks GitHub to set it, and a refusal comes back with GitHub's reason and a link to the page to
|
|
57
|
+
click. **The agent credential**: a paste box, a line on where your agent's credential comes from,
|
|
58
|
+
and where it will be stored (one org secret shared with the brains, or each brain where an org
|
|
59
|
+
secret would not arrive, with the reason). The value goes to the local server in a POST and from
|
|
60
|
+
there to `gh` on standard input; it is never written to disk or echoed back. Until somebody is
|
|
61
|
+
hired the box says so, because nothing would read it. Then which repos the staff work in, as a
|
|
62
|
+
picker over what your `gh` can see minus what `org.yaml` already has, recorded with the
|
|
63
|
+
visibility GitHub reports. Then the two files only you can write: a prompt for writing
|
|
64
|
+
`org/business.md`, and `org/priorities.md` opened in place, stub and all, to replace with what
|
|
65
|
+
matters this month. Each says whether it is written yet.
|
|
43
66
|
|
|
44
67
|
Nothing here stores which step you are on. Setup takes days rather than minutes: an App has to be
|
|
45
68
|
installed, a credential set, a first run finished. So the page derives its state from `roster
|
|
46
|
-
doctor` every time it is drawn
|
|
69
|
+
doctor` every time it is drawn, and runs the check again after anything on it is saved. A stored
|
|
70
|
+
step counter would disagree with the world within an hour.
|
|
71
|
+
|
|
72
|
+
### Getting started
|
|
73
|
+
|
|
74
|
+
**Once the org exists, the same steps stay in the sidebar as *Getting started*** for as long as
|
|
75
|
+
anything is left: nobody hired, or `org/business.md` or `org/priorities.md` still the stub. An
|
|
76
|
+
org nobody has been hired into opens on it rather than on an empty Inbox, with *Hire your first
|
|
77
|
+
staff member* first, then the Actions setting, the credential, the repo picker, both files, and
|
|
78
|
+
what doctor still says. A link to another screen still wins. When the list is empty the entry
|
|
79
|
+
goes away; the repo picker lives on under [Org](#org).
|
|
47
80
|
|
|
48
81
|
## Inbox
|
|
49
82
|
|
|
@@ -62,7 +95,37 @@ down with the list, so opening a thread is a render rather than a request.
|
|
|
62
95
|
a shorter timeline than open work because it is there to be read rather than triaged. A
|
|
63
96
|
closed row is dimmed and marked; the sidebar badge keeps counting only what is open.
|
|
64
97
|
- **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.
|
|
98
|
+
indistinguishable from doing it on the site. Closing asks for confirmation. A half-written
|
|
99
|
+
reply survives a repaint, and pressing Comment with an empty box says so rather than
|
|
100
|
+
silently doing nothing.
|
|
101
|
+
- **A new issue asks one question: who is it for.** One dropdown, over the staff. It goes to
|
|
102
|
+
that person's brain repo and their `@handle` is written into the body for you, because those
|
|
103
|
+
are the two things that make an issue reach an agent rather than sit there. Asking for a
|
|
104
|
+
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
|
|
105
|
+
something.
|
|
106
|
+
- **Labels are the repo's own, as toggles.** They are the labels that exist on the recipient's
|
|
107
|
+
repository, fetched from GitHub and cached. A text box was a spelling test: `from-cmo` and
|
|
108
|
+
`from-CMO` are different labels and only one of them exists.
|
|
109
|
+
- **Typing `@` offers the org.** A mention is the mechanism, not decoration: a staff member's
|
|
110
|
+
workflow gates on their handle, so a misspelled one is a message nobody is woken by. The list
|
|
111
|
+
is the staff and the humans, and nothing else. The Apps are deliberately absent: `@acme-cto`
|
|
112
|
+
is a login rather than an inbox, GitHub delivers nothing for mentioning one, and an agent
|
|
113
|
+
wakes on its own handle and not on the identity it posts as, so offering them is offering
|
|
114
|
+
entries that do nothing. Each row says which it is, the list appears under the caret, arrow
|
|
115
|
+
keys and Enter pick, Escape dismisses the list rather than the dialog around it, and an email
|
|
116
|
+
address is not a mention.
|
|
117
|
+
- **Attachments.** Drop a file on the box, pick one, or paste one. Paste is the one that
|
|
118
|
+
matters, because a screenshot is on the clipboard and never on disk. GitHub's own drag-and-drop
|
|
119
|
+
attachments are minted by its web app and cannot be made with `gh`, so the file is committed
|
|
120
|
+
to `attachments/<date>-<name>` in the repo the issue lives in and pushed. The body gets the
|
|
121
|
+
link for you and the repo-relative path for the agent, which reads it off its own checkout
|
|
122
|
+
rather than needing a token for a private repo. 25MB a file; a failed push is reported rather
|
|
123
|
+
than hidden.
|
|
124
|
+
- **Reactions.** The 👀 an agent leaves when it picks something up, under the comment it is on,
|
|
125
|
+
with who left it. Without it you post a comment, see nothing change, and have to open GitHub
|
|
126
|
+
to find out it landed.
|
|
127
|
+
- **How much conversation** is on each row, which is most of what tells a live thread from
|
|
128
|
+
something filed and never answered.
|
|
66
129
|
- Issue and PR references in a body become chips you can click through, and `@handle`
|
|
67
130
|
mentions become chips too. A mention of somebody on this roster goes to their repository
|
|
68
131
|
rather than to a GitHub profile of that name, which for `@cto` is a stranger.
|
|
@@ -79,32 +142,141 @@ down with the list, so opening a thread is a render rather than a request.
|
|
|
79
142
|
disclosure. An item a cross-reference points at opens in the portal when the inbox already
|
|
80
143
|
holds it, and on GitHub when it does not.
|
|
81
144
|
|
|
145
|
+
## Pending work
|
|
146
|
+
|
|
147
|
+
The same screen, scoped to pull requests, in its own place in the sidebar. An inbox is what is
|
|
148
|
+
waiting on you; a pull request is work that is finished and waiting on a merge, and the count
|
|
149
|
+
that matters is not how many are open but how many are green and still sitting there.
|
|
150
|
+
|
|
151
|
+
It is called **Pending work** rather than "Pull requests" because that is what is behind it: a
|
|
152
|
+
piece of work a staff member finished and cannot land alone. A pull request is how it arrives,
|
|
153
|
+
not what it is.
|
|
154
|
+
|
|
155
|
+
A pull request's thread has three tabs.
|
|
156
|
+
|
|
157
|
+
- **Conversation** is the thread: body, timeline, reactions, reply box, same as an issue.
|
|
158
|
+
- **Commits**, with subject, author and sha, each linking to GitHub.
|
|
159
|
+
- **Files**: the full patch, rendered as a diff with line numbers, the same renderer *What
|
|
160
|
+
changed* uses. A file with no patch is binary or too large for the API to send one, and says
|
|
161
|
+
so rather than showing nothing.
|
|
162
|
+
|
|
163
|
+
Both are fetched when you open the tab, not carried by the inbox. A diff is the biggest thing
|
|
164
|
+
on this screen by an order of magnitude, and paying for every open PR's diff on every refresh
|
|
165
|
+
of every repo to show one of them is the wrong trade.
|
|
166
|
+
|
|
167
|
+
**Merge** sits beside Close and asks before it goes. It never deletes the branch: that is a
|
|
168
|
+
second decision and not this button's to make. It runs `gh pr merge` as you, so a protected
|
|
169
|
+
branch, a failing required check or a merge queue behaves exactly as it would on the site.
|
|
170
|
+
|
|
171
|
+
**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
|
|
172
|
+
has already answered it in its own settings, and on any given repository most of the answers are
|
|
173
|
+
wrong. So the repository is asked instead: squash where it is allowed, then a merge commit, then
|
|
174
|
+
rebase.
|
|
175
|
+
|
|
176
|
+
### Asking for a change
|
|
177
|
+
|
|
178
|
+
**`@cto` on a pull request wakes nobody.** A staff member's caller workflow lives in their own
|
|
179
|
+
brain repo and gates on their handle appearing *there*; on a product repo the same mention
|
|
180
|
+
posts, renders as a chip, and does nothing. That is deliberate, for the reasons in
|
|
181
|
+
[security](security.md#trust-in-a-prompt). Saying so out loud is the portal's job, because a
|
|
182
|
+
silent no-op is worse than the restriction itself.
|
|
183
|
+
|
|
184
|
+
**Reply is the one box, and it handles this.** Name somebody in a reply where a comment will
|
|
185
|
+
not reach them and the offer appears under the box, ticked: *open it on their tracker too*. One
|
|
186
|
+
press of Comment posts your words on the thread and opens the request on their tracker, which
|
|
187
|
+
carries
|
|
188
|
+
|
|
189
|
+
- the pull request, its link and its branch
|
|
190
|
+
- their `@handle`, which is what actually wakes them
|
|
191
|
+
- an instruction to **answer on the pull request**, because `prompts/mention.md` otherwise tells
|
|
192
|
+
them to answer where the request came from, which here is the tracker, leaving the diff silent
|
|
193
|
+
and you watching the wrong page
|
|
194
|
+
- the diff for the file you asked from, if you started from one, clipped: it is there to say
|
|
195
|
+
*which part*, not to be a copy of the diff that goes stale on the next push
|
|
196
|
+
|
|
197
|
+
Untick it and the reply is just a comment. Who gets asked comes off the text you actually sent,
|
|
198
|
+
so a handle you typed and then deleted is not asked.
|
|
199
|
+
|
|
200
|
+
On the **Files** tab each file's heading has its own Reply, which opens the same box about that
|
|
201
|
+
one file. "This bit is wrong" is what you want to say while looking at a diff, and the
|
|
202
|
+
alternative is describing in prose which of thirty files you meant.
|
|
203
|
+
|
|
204
|
+
Both writes go through your own `gh`, as you. Nothing is dispatched between repositories and no
|
|
205
|
+
credential is put on a public repo. The tracker issue goes first, because it is the half that
|
|
206
|
+
reaches anybody; if the copy on the pull request then fails you are told, rather than being
|
|
207
|
+
shown an error that invites you to ask the same person the same thing twice.
|
|
208
|
+
|
|
209
|
+
## Runs
|
|
210
|
+
|
|
211
|
+
What each staff member ran in the last 30 days: when, daily or mention, how it ended, how long
|
|
212
|
+
it took, turns and cost where known, and a link to the log. Above the tables, the 30-day total
|
|
213
|
+
for the org, and each staff member's own in their heading.
|
|
214
|
+
|
|
215
|
+
Runs are not in any repo, so this is the one screen that is only ever on GitHub. It reads the
|
|
216
|
+
run lists through your own `gh`, the same way the inbox does, and offline it says so rather
|
|
217
|
+
than drawing an empty table that reads as "nothing ran".
|
|
218
|
+
|
|
219
|
+
Cost comes from the record each run leaves behind (see [cost](cost.md#what-each-run-cost)). A
|
|
220
|
+
run from before records existed, or from an agent that does not report cost, shows a dash, and
|
|
221
|
+
a total says how many runs it could price. Each record is downloaded once and kept for as long
|
|
222
|
+
as the portal runs, so the first visit is the slow one.
|
|
223
|
+
|
|
224
|
+
A skipped mention is not a run and is not listed. A `setup-failure` is a run that failed before
|
|
225
|
+
the agent started, usually a token or a checkout. Past a [`budget`](org-yaml.md#budget), the
|
|
226
|
+
total turns amber.
|
|
227
|
+
|
|
82
228
|
## Org
|
|
83
229
|
|
|
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.
|
|
230
|
+
The layer every staff member inherits, in one place: `org.yaml`, every `org/*.md`, and the
|
|
231
|
+
prompt files in `prompts/`, each with a line saying what it is for, because a filename tells
|
|
232
|
+
you nothing about which to open. Anything roster does not ship falls back to its own first
|
|
233
|
+
heading.
|
|
234
|
+
|
|
235
|
+
**Add a product repo** opens the same picker setup uses, so a repo created later does not mean
|
|
236
|
+
typing `role: product` into `org.yaml` by hand. Adding one commits `org.yaml` and pushes. A new
|
|
237
|
+
hire picks it up; somebody already hired keeps the `works_in` in their own `staff.yaml`.
|
|
238
|
+
|
|
239
|
+
**The list comes off disk**, not out of the page, so a tenant that adds `org/pricing.md` can
|
|
240
|
+
open it like any other. Only files the
|
|
241
|
+
portal may actually write are listed: an editor that offers a file it cannot save is a trap.
|
|
242
|
+
|
|
243
|
+
Every one of them is editable from the screen it is read on: an **Edit** button on the file,
|
|
244
|
+
⌘S to save, and saving commits and pushes. Only `org.yaml` asks for confirmation first; a
|
|
245
|
+
prose edit is one commit to revert, and asking every time teaches people to click through the
|
|
246
|
+
question.
|
|
87
247
|
|
|
88
248
|
`org.yaml` is the exception to the write allowlist. It belongs to the person rather than the
|
|
89
249
|
agent, so it is writable. But it is the one file here that stops every prompt composing when
|
|
90
250
|
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.
|
|
251
|
+
anything that is not YAML it can read, or that has lost `org`, `name`, a handle on a staff
|
|
252
|
+
entry, or a github login on an entry in [`humans`](org-yaml.md#human-and-humans).
|
|
253
|
+
|
|
254
|
+
YAML is coloured, here and anywhere else the portal shows it: comments, keys, strings, numbers
|
|
255
|
+
and the three keywords. `org.yaml` and `staff.yaml` are the two files anybody reads closely,
|
|
256
|
+
and finding a key in a flat grey wall means reading every line.
|
|
257
|
+
|
|
258
|
+
The card at the top names **everyone** the staff answer to, not the first of them. An org can
|
|
259
|
+
have more than one human, and a card that names one of two reads as the only one who counts.
|
|
93
260
|
|
|
94
261
|
## Staff
|
|
95
262
|
|
|
96
|
-
Everyone on the roster, and the
|
|
263
|
+
Everyone on the roster, and the things you would otherwise do from a terminal.
|
|
97
264
|
|
|
98
265
|
**Hiring** runs the same `buildPlan` and `applyPlan` that `roster hire` does, on the server.
|
|
99
|
-
Only the handle is required; everything else is copied from whoever is already here.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
266
|
+
Only the handle is required; everything else is copied from whoever is already here. The first
|
|
267
|
+
hire has nobody to copy App names from, so its form also asks for the App's name and the shared
|
|
268
|
+
public App's, which are `--app` and `--public-app`. The public one only matters when a product
|
|
269
|
+
repo is public; on private ones the session uses the staff member's own App. You see
|
|
270
|
+
the plan first, listing every file, every label, the schedule it chose and why, the commits it
|
|
271
|
+
will make as you in repos that already exist (each peer's `staff.yaml`, `org.yaml`), whether the
|
|
272
|
+
new brain joins the credential's org secret, and what is left for you. Nothing happens until you
|
|
273
|
+
apply. What the terminal would have printed is shown when it finishes.
|
|
103
274
|
|
|
104
275
|
**Writing the charter** is the copy-a-prompt loop below, aimed at `CHARTER.md`. `hire`
|
|
105
276
|
deliberately does not write it, because a generated charter produces exactly the generic agent
|
|
106
|
-
this whole arrangement exists to avoid.
|
|
107
|
-
|
|
277
|
+
this whole arrangement exists to avoid. It is the same brief as `roster brief charter
|
|
278
|
+
<handle>`, with somewhere to put the answer, and a picker for the worked example it carries as a
|
|
279
|
+
model: matched to the role, or another, or none.
|
|
108
280
|
|
|
109
281
|
**The GitHub App** is `roster app`, on this server rather than a second one. There is no API that
|
|
110
282
|
creates an App: the only route is the manifest flow, where you post a manifest to a settings page,
|
|
@@ -114,9 +286,17 @@ one origin. The private key is still held in memory and written straight to a re
|
|
|
114
286
|
|
|
115
287
|
GitHub redirects the tab *it* opened, not the one you clicked from, so the original polls for the
|
|
116
288
|
result. What it cannot do is install the App: that is a grant of access to specific repositories
|
|
117
|
-
and GitHub asks a
|
|
118
|
-
|
|
119
|
-
|
|
289
|
+
and GitHub asks a person to confirm it, which is correct and should not be worked around. So the
|
|
290
|
+
panel's **Install it** opens the install page with the org and the repos already selected: the
|
|
291
|
+
brain, every peer tracker it writes to, and the product repos. Any whose id could not be read are
|
|
292
|
+
listed for you to tick.
|
|
293
|
+
|
|
294
|
+
**Agent credential** is the setup screen's paste box, reachable from each card, because the
|
|
295
|
+
moment you look for it is while setting somebody up. It is once for the org.
|
|
296
|
+
|
|
297
|
+
**Run once now** starts the daily workflow, follows it, and shows how it ended with the log's
|
|
298
|
+
link and, on a failure, the step it failed at. It asks first, because it is a real run. A success
|
|
299
|
+
is what turns doctor's *unproven* into proven. Health has the same button.
|
|
120
300
|
|
|
121
301
|
**Retiring** is `roster retire`, and it is deliberately not deletion. A brain repo is that
|
|
122
302
|
agent's entire memory and there is no undo, so retiring disables the workflows, unwires them
|
|
@@ -152,11 +332,11 @@ rather than going nowhere.
|
|
|
152
332
|
|
|
153
333
|
## Prompt
|
|
154
334
|
|
|
155
|
-
**What this staff member is actually sent**,
|
|
156
|
-
|
|
335
|
+
**What this staff member is actually sent**, the same as `roster prompt <handle> --kind daily`.
|
|
336
|
+
Composed on the server by the tenant's own
|
|
157
337
|
`compose.mjs`, so there is no second implementation to drift.
|
|
158
338
|
|
|
159
|
-
Pick the kind: `daily
|
|
339
|
+
Pick the kind: `daily` or `mention`. A mention prompt is written for the comment
|
|
160
340
|
that woke it, so a preview fills in obviously-fake context.
|
|
161
341
|
|
|
162
342
|
Beneath the composed text, the files it was made of, in two groups.
|
|
@@ -172,25 +352,9 @@ tells the agent to open these; it does not contain them. Editing a charter chang
|
|
|
172
352
|
agent does without changing a byte of the composed prompt, and that distinction is easy to
|
|
173
353
|
miss.
|
|
174
354
|
|
|
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.
|
|
355
|
+
What is *wrong* with any of this is on [Health](#health). This screen answers "what is sent";
|
|
356
|
+
whether what is sent is any good is a different question, and a tree that was half prompt and
|
|
357
|
+
half complaints answered neither well.
|
|
194
358
|
|
|
195
359
|
### Getting help changing it
|
|
196
360
|
|
|
@@ -284,7 +448,11 @@ opening the diff that did it: one block per file, coloured, with line numbers.
|
|
|
284
448
|
|
|
285
449
|
## Health
|
|
286
450
|
|
|
287
|
-
|
|
451
|
+
Four parts: the rig, the org, the prompts and the memory. The rig is about this staff member,
|
|
452
|
+
so it is first; the rest widens out from there.
|
|
453
|
+
|
|
454
|
+
**The rig**: schedule in words, workflows present, last commit, last commit touching `memory/`,
|
|
455
|
+
index and notes size, charter, status issue, missing surfaces.
|
|
288
456
|
|
|
289
457
|
**The org, from `roster doctor`.** Every finding that is not `ok`, with what to do about it, and
|
|
290
458
|
a button that turns the lot into one brief for a coding agent. That is `roster fix`: `doctor`,
|
|
@@ -300,20 +468,66 @@ an agent that has started editing has stopped reading.
|
|
|
300
468
|
|
|
301
469
|
Paste it into whatever edits files here, then press *Check again*. The ids should be gone.
|
|
302
470
|
|
|
303
|
-
**
|
|
471
|
+
**Prompt problems.** The audit, over all three kinds of run at once. Nothing here judges prose:
|
|
472
|
+
every check is something a machine can be sure about, because a linter you stop believing is
|
|
473
|
+
worse than no linter. A finding true of more than one prompt is one row, and it says which.
|
|
474
|
+
|
|
475
|
+
| Check | Why it matters |
|
|
476
|
+
|---|---|
|
|
477
|
+
| 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 |
|
|
478
|
+
| 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 |
|
|
479
|
+
| 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 |
|
|
480
|
+
| the prompt is long | it is read in full on every run, forever |
|
|
481
|
+
| it names a file that is not there | an instruction to read something absent is a quiet no-op inside a run nobody watches |
|
|
482
|
+
| an included layer is empty | it contributes nothing and costs a line of includes |
|
|
483
|
+
|
|
484
|
+
**Every finding carries the fix.** "Copy a prompt to fix this" builds a brief containing the
|
|
485
|
+
finding, the composed prompt, and every layer, and puts it on your clipboard. Paste it into
|
|
486
|
+
whatever agent you use. Knowing there is a problem is the hard part; writing the paragraph is
|
|
487
|
+
not. The box comes pre-filled with what the finding worked out, so you can add to it rather
|
|
488
|
+
than retype it. "Open the file" takes you to that layer on the Prompt screen.
|
|
304
489
|
|
|
305
|
-
Memory problems are the same checks `roster lint` runs. Each one has a button that opens an
|
|
490
|
+
**Memory problems** are the same checks `roster lint` runs. Each one has a button that opens an
|
|
306
491
|
issue in that staff member's own repo asking them to fix it, which is usually right, because
|
|
307
492
|
they wrote it.
|
|
308
493
|
|
|
309
|
-
|
|
310
|
-
|
|
494
|
+
## Any image opens
|
|
495
|
+
|
|
496
|
+
**Click a picture and it opens over the page, fitted to the window.** Bottom right there is a
|
|
497
|
+
`-`, the current zoom, and a `+`. The percentage is a button too: it refits. Scrolling zooms,
|
|
498
|
+
dragging moves, clicking the picture goes between fitted and actual size, and `+`, `-`, `0` and
|
|
499
|
+
Escape do the same from the keyboard. Zooming is anchored on the pointer, so the thing you
|
|
500
|
+
aimed at stays where you aimed.
|
|
501
|
+
|
|
502
|
+
The percentage is of **actual size**, not of fitted, so 100% means one image pixel per screen
|
|
503
|
+
pixel. How far in it will go depends on the picture: a scaled image is a composited layer and
|
|
504
|
+
the browser allocates it at the rendered size, so the ceiling is whatever keeps that within
|
|
505
|
+
budget. Actual size is always reachable, however large the original is.
|
|
506
|
+
|
|
507
|
+
This is every image the portal renders as content: a screenshot on a doc page, a picture inside
|
|
508
|
+
a brain document, a file open in the Brain screen's viewer. A screenshot laid out for the column
|
|
509
|
+
it sits in is unreadable exactly when it matters, which is when it is a picture of an interface
|
|
510
|
+
and the part you need is the small print.
|
|
511
|
+
|
|
512
|
+
Gallery tiles are the exception, and deliberately: their click already means "open this file",
|
|
513
|
+
and the file's own view is one of the things that does open.
|
|
514
|
+
|
|
515
|
+
Fitted, the picture is inset from the window edges and framed. That is not decoration. Most of
|
|
516
|
+
these are screenshots *of this interface*, and one fitted edge to edge reads as the app having
|
|
517
|
+
navigated rather than as a picture of it.
|
|
311
518
|
|
|
312
519
|
## Docs
|
|
313
520
|
|
|
314
521
|
The framework's own documentation, rendered where you already are. Links between pages navigate
|
|
315
522
|
the portal.
|
|
316
523
|
|
|
524
|
+
**Search reads the pages, not their titles.** Twenty-odd pages is too many to scan by eye and
|
|
525
|
+
few enough for the server to read in full on every keystroke, and what you are looking for
|
|
526
|
+
("which page explains the mention gate") is a sentence in a paragraph rather than a word in a
|
|
527
|
+
heading. A page has to contain every word you typed; results rank a title hit over a heading
|
|
528
|
+
hit over sheer frequency, carry the lines the words were found in, and the first one opens as
|
|
529
|
+
you type. The query is in the URL, so a search is a link.
|
|
530
|
+
|
|
317
531
|
## After upgrading roster
|
|
318
532
|
|
|
319
533
|
**Restart the portal.** Its stylesheets and modules are read per request, so editing one and
|