@nanocollective/roster 0.1.0-alpha.5 → 0.1.0-alpha.51
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 +5161 -3012
- package/docs/README.md +9 -6
- package/docs/agents.md +24 -20
- package/docs/architecture.md +13 -5
- package/docs/charters/analyst.md +65 -0
- package/docs/charters/cmo.md +69 -0
- package/docs/charters/community.md +63 -0
- package/docs/charters/cto.md +71 -0
- package/docs/charters/designer.md +65 -0
- package/docs/charters/devops.md +65 -0
- package/docs/charters/pm.md +70 -0
- package/docs/charters/qa.md +65 -0
- package/docs/charters/support.md +60 -0
- package/docs/charters/writer.md +63 -0
- package/docs/commands.md +93 -7
- package/docs/concepts.md +61 -14
- package/docs/cost.md +36 -1
- package/docs/developing.md +16 -21
- package/docs/doctor-codes.md +10 -2
- package/docs/export.md +2 -0
- package/docs/extending.md +2 -2
- package/docs/getting-started.md +128 -78
- 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 -123
- package/docs/memory.md +21 -3
- package/docs/org-yaml.md +40 -2
- package/docs/portal.md +177 -58
- package/docs/prompts.md +31 -4
- package/docs/security.md +37 -5
- package/docs/session-workflow.md +63 -17
- package/docs/staff-yaml.md +30 -3
- package/docs/troubleshooting.md +8 -8
- package/docs/upgrading.md +9 -3
- package/docs/writing-a-charter.md +28 -0
- package/package.json +18 -20
- package/templates/brain/.github/workflows/%%STAFF%%-daily.yaml +26 -1
- package/templates/brain/.github/workflows/%%STAFF%%-mention.yaml +59 -13
- 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 +4 -1
- package/templates/brain/strategy/ideas.md +7 -0
- package/templates/briefs/amend.md +4 -3
- package/templates/briefs/priorities.md +46 -0
- package/templates/ops/.github/workflows/session.yaml +236 -15
- package/templates/ops/agents.mjs +7 -3
- package/templates/ops/compose.mjs +31 -3
- package/templates/ops/inflight.mjs +157 -0
- package/templates/ops/org/operating.md +43 -4
- package/templates/ops/org/voice.md +9 -0
- package/templates/ops/prompts/_inflight.md +14 -0
- package/templates/ops/prompts/_paths.md +2 -1
- package/templates/ops/prompts/daily.md +37 -9
- package/templates/ops/prompts/mention.md +21 -0
- package/templates/ops/run-record.mjs +146 -0
- package/templates/portal/css/base.css +167 -73
- package/templates/portal/css/brain.css +23 -20
- package/templates/portal/css/diff.css +10 -9
- package/templates/portal/css/graph.css +12 -7
- package/templates/portal/css/health.css +26 -11
- package/templates/portal/css/home.css +95 -0
- package/templates/portal/css/inbox.css +45 -25
- package/templates/portal/css/layout.css +90 -46
- package/templates/portal/css/markdown.css +36 -14
- package/templates/portal/css/runs.css +13 -0
- package/templates/portal/css/setup.css +117 -34
- package/templates/portal/index.html +21 -9
- package/templates/portal/js/api.js +44 -4
- package/templates/portal/js/app.js +94 -9
- package/templates/portal/js/dialog.js +83 -0
- package/templates/portal/js/homesort.js +174 -0
- package/templates/portal/js/icons.js +37 -0
- package/templates/portal/js/inflight.js +18 -0
- package/templates/portal/js/md.js +5 -2
- package/templates/portal/js/mdedit.js +84 -0
- package/templates/portal/js/readiness.js +70 -0
- package/templates/portal/js/refresh.js +10 -2
- package/templates/portal/js/state.js +11 -5
- package/templates/portal/js/views/app.js +24 -7
- package/templates/portal/js/views/brain.js +1 -1
- package/templates/portal/js/views/checklist.js +10 -4
- package/templates/portal/js/views/credential.js +84 -0
- package/templates/portal/js/views/graph.js +1 -1
- package/templates/portal/js/views/health.js +27 -9
- package/templates/portal/js/views/hire.js +593 -0
- package/templates/portal/js/views/home.js +546 -0
- package/templates/portal/js/views/inbox.js +226 -70
- package/templates/portal/js/views/org.js +46 -106
- package/templates/portal/js/views/orgedit.js +234 -0
- package/templates/portal/js/views/paste.js +137 -63
- package/templates/portal/js/views/prompt.js +100 -42
- 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 +170 -0
- package/templates/portal/js/views/setup.js +261 -75
- package/templates/portal/js/views/staff.js +170 -243
- package/templates/portal/js/views/todo.js +62 -0
package/docs/staff-yaml.md
CHANGED
|
@@ -22,13 +22,14 @@ brain: acme/technology
|
|
|
22
22
|
status_issue: 15
|
|
23
23
|
|
|
24
24
|
schedule: "0 7 * * 1-5"
|
|
25
|
-
model: claude-opus-5
|
|
25
|
+
model: claude-opus-5-5
|
|
26
26
|
timeout_minutes: 90
|
|
27
27
|
mention_timeout_minutes: 90
|
|
28
|
+
max_runs_per_day: 6
|
|
28
29
|
|
|
29
30
|
bot: acme-cto[bot]
|
|
30
31
|
public_bot: acme-robot[bot]
|
|
31
|
-
public_token_env:
|
|
32
|
+
public_token_env: PUBLIC_TOKEN
|
|
32
33
|
agent_secret: CLAUDE_CODE_OAUTH_TOKEN
|
|
33
34
|
|
|
34
35
|
identities:
|
|
@@ -49,7 +50,7 @@ surfaces:
|
|
|
49
50
|
|
|
50
51
|
labels:
|
|
51
52
|
owner: [will, cto, cmo]
|
|
52
|
-
kind: [decision,
|
|
53
|
+
kind: [decision, review, chore, keep-open, build, blocked]
|
|
53
54
|
```
|
|
54
55
|
|
|
55
56
|
## Identity
|
|
@@ -59,6 +60,7 @@ labels:
|
|
|
59
60
|
| `handle` | yes | Must match `org.yaml`. The composer looks them up by the `org.yaml` one, so a mismatch composes the wrong brain. |
|
|
60
61
|
| `name` | yes | Role name in prose. |
|
|
61
62
|
| `mention` | yes | What wakes them, as in `@cto`. The caller's condition tests for this string. |
|
|
63
|
+
| `icon` | no | The icon the portal shows for them: `code`, `megaphone`, `life-buoy`, `compass`, `brush`, `bug`, `server`, `book-open`, `bar-chart`, `users` or `person`. Unset, it is picked from the role. |
|
|
62
64
|
| `brain` | yes | `owner/name` of this repo. Without it nothing can check secrets, labels or runs. |
|
|
63
65
|
| `status_issue` | yes in practice | Number of the pinned status issue. The prompts reference it, so a run cannot compose without it. `roster hire --apply` writes it. |
|
|
64
66
|
|
|
@@ -70,6 +72,7 @@ labels:
|
|
|
70
72
|
| `model` | org default | Model id. |
|
|
71
73
|
| `timeout_minutes` | 90 | Ceiling on the daily session. |
|
|
72
74
|
| `mention_timeout_minutes` | 90 | Ceiling on a mention run. |
|
|
75
|
+
| `max_runs_per_day` | 6 | Runs a UTC day that start without a person asking: a peer's ask, or a follow-on to a daily run. Mentions never count. Rendered into the callers, so change it and run `roster upgrade`. |
|
|
73
76
|
|
|
74
77
|
The three ceilings are separate on purpose. Raising the daily one because sessions have grown
|
|
75
78
|
should not double the budget for a PR amendment. A job killed by a ceiling is reported by
|
|
@@ -154,6 +157,30 @@ Labels this staff member expects to exist on its own tracker, grouped for readab
|
|
|
154
157
|
value across every group is checked by `roster doctor`. An agent applying a label that does not
|
|
155
158
|
exist gets an API error mid-run.
|
|
156
159
|
|
|
160
|
+
Four labels are roster's own and are checked on every tracker whatever this lists:
|
|
161
|
+
|
|
162
|
+
| Label | Means |
|
|
163
|
+
|---|---|
|
|
164
|
+
| `decision` | An ask for the human to rule on. It carries a default and a date; past the date, the staff member acts on the default and closes it. |
|
|
165
|
+
| `review` | An ask for the human to read or approve something. |
|
|
166
|
+
| `chore` | Something only the human can do: a setting, an account, a key. |
|
|
167
|
+
| `keep-open` | A standing thread. A sweep never closes it. `roster hire` puts it on the status issue. |
|
|
168
|
+
|
|
169
|
+
Every ask on the human carries exactly one of the first three, which is how the portal sorts
|
|
170
|
+
what needs them. Staff close any issue on their tracker once nothing is left to do on it,
|
|
171
|
+
including the human's requests, with one line saying what closed it.
|
|
172
|
+
|
|
173
|
+
## `memory`
|
|
174
|
+
|
|
175
|
+
This staff member's own memory budgets, overriding the org's. Same three fields as
|
|
176
|
+
[`memory` in org.yaml](org-yaml.md#memory); any left out fall back to the org, then to the
|
|
177
|
+
defaults.
|
|
178
|
+
|
|
179
|
+
```yaml
|
|
180
|
+
memory:
|
|
181
|
+
max_index_kb: 32
|
|
182
|
+
```
|
|
183
|
+
|
|
157
184
|
## What `roster upgrade` does to this file
|
|
158
185
|
|
|
159
186
|
Nothing. It is `scaffold` class: written once by `roster hire`, and yours from that moment.
|
package/docs/troubleshooting.md
CHANGED
|
@@ -22,8 +22,10 @@ a coding agent, split into what an agent can fix and what only a person can. `ro
|
|
|
22
22
|
|
|
23
23
|
**Is:** the ops repo's Actions access is not set to organisation-wide.
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
`roster init --apply` sets it, and so does *Set it for me* on the setup screen. If GitHub
|
|
26
|
+
refused (it needs admin on the ops repo), set it by hand: Settings -> Actions -> General ->
|
|
27
|
+
Access on `roster-ops`, "accessible from repositories in the organisation". Health and `roster
|
|
28
|
+
doctor` both check it explicitly.
|
|
27
29
|
|
|
28
30
|
---
|
|
29
31
|
|
|
@@ -90,11 +92,9 @@ constrains it is not a thing anybody wants.
|
|
|
90
92
|
**Is:** you edited a framework-owned file. `compose.mjs`, `agents.mjs`, `runner-plan.mjs` and
|
|
91
93
|
`session.yaml` are generated. The next `roster upgrade` reconciles them against the template.
|
|
92
94
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
`roster upgrade` now reports an edit to a framework-owned file whether or not anything has
|
|
97
|
-
collided, and `roster upgrade --check` fails on it. Move the change upstream.
|
|
95
|
+
Nothing notices until the framework next touches that file. `roster upgrade` reports an edit
|
|
96
|
+
to a framework-owned file whether or not anything has collided, and `roster upgrade --check`
|
|
97
|
+
fails on it. Move the change upstream.
|
|
98
98
|
|
|
99
99
|
---
|
|
100
100
|
|
|
@@ -181,7 +181,7 @@ landed on GitHub thirty seconds ago and the checkout is behind, that is what you
|
|
|
181
181
|
|
|
182
182
|
## `roster upgrade` says a file has no base
|
|
183
183
|
|
|
184
|
-
A tenant
|
|
184
|
+
A tenant with no recorded merge base has nothing to merge against. Reconstruct
|
|
185
185
|
one from the framework's history:
|
|
186
186
|
|
|
187
187
|
```bash
|
package/docs/upgrading.md
CHANGED
|
@@ -10,9 +10,9 @@ The framework writes templates out. A tenant runs its own copies. So the two dri
|
|
|
10
10
|
`roster upgrade` is what reconciles them without eating your edits.
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
|
-
roster upgrade
|
|
14
|
-
roster upgrade --apply
|
|
15
|
-
roster upgrade --check
|
|
13
|
+
npx @nanocollective/roster@latest upgrade # what would change
|
|
14
|
+
npx @nanocollective/roster@latest upgrade --apply # do it
|
|
15
|
+
npx @nanocollective/roster@latest upgrade --check # exit non-zero if anything is pending (for CI)
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
## How it decides
|
|
@@ -53,6 +53,12 @@ The diff is printed either way, so nothing goes quietly.
|
|
|
53
53
|
If you want a caller to differ, change the thing it is generated from. Timeouts, schedule,
|
|
54
54
|
model and identities all live in `staff.yaml`.
|
|
55
55
|
|
|
56
|
+
**A caller the framework no longer generates is removed.** It would still dispatch into
|
|
57
|
+
`session.yaml` with a kind that no longer composes, and fail at run time. The plan lists it.
|
|
58
|
+
The one that has gone so far is `<handle>-pr-mention.yaml`; if your tenant is old enough to
|
|
59
|
+
have one, a forwarding workflow in the product repo went with it, and that one is yours to
|
|
60
|
+
delete, because `roster upgrade` never writes into product repos.
|
|
61
|
+
|
|
56
62
|
## Conflicts
|
|
57
63
|
|
|
58
64
|
A conflict is never written into a live file. Agents read `org/voice.md` at every boot, and
|
|
@@ -28,6 +28,13 @@ layer. That is the part that matters most, because without them the model writes
|
|
|
28
28
|
of whoever it was shown. The brief then interviews you, drafts from your answers, and tells you
|
|
29
29
|
what it cut and why.
|
|
30
30
|
|
|
31
|
+
**So is a worked example, when one fits.** A staff member whose handle or role reads as one of
|
|
32
|
+
the ten roles below gets the matching [example](#worked-examples) inside the brief, labelled as a
|
|
33
|
+
model for the shape and not content to copy. The copy-a-prompt panel has a picker to choose
|
|
34
|
+
another or none; in a terminal it is `--example <handle>` or `--example none`. It is still a brief you
|
|
35
|
+
answer: the interview comes first, and nothing in the charter should come from the example
|
|
36
|
+
rather than from you.
|
|
37
|
+
|
|
31
38
|
From a terminal, the same brief:
|
|
32
39
|
|
|
33
40
|
```bash
|
|
@@ -63,6 +70,27 @@ touches. Be specific. A vague boundary is one that gets crossed at 07:00 with no
|
|
|
63
70
|
**Where the rest of it lives.** Point at `memory/INDEX.md`, `log/decisions.md`, the pinned
|
|
64
71
|
status issue, and the surfaces the manifest declares.
|
|
65
72
|
|
|
73
|
+
## Worked examples
|
|
74
|
+
|
|
75
|
+
Ten, for an invented company called Acme. The handle in brackets is the one `--example` takes.
|
|
76
|
+
|
|
77
|
+
| Example | What they do |
|
|
78
|
+
|---|---|
|
|
79
|
+
| [CTO](charters/cto.md) (`cto`) | Builds the product: fixes, features and tests, as pull requests. |
|
|
80
|
+
| [CMO](charters/cmo.md) (`cmo`) | Posts, SEO, copy and launch plans, as drafts to approve. |
|
|
81
|
+
| [Head of Support](charters/support.md) (`support`) | Answers issues, writes help docs, and turns user reports into bugs. |
|
|
82
|
+
| [Product Manager](charters/pm.md) (`pm`) | Turns ideas and user feedback into clear specs and a ranked backlog. |
|
|
83
|
+
| [Designer](charters/designer.md) (`designer`) | Improves the product's look and usability, with accessibility fixes, as pull requests. |
|
|
84
|
+
| [QA Engineer](charters/qa.md) (`qa`) | Tests the product, finds bugs, and writes clear reproductions and tests. |
|
|
85
|
+
| [DevOps Engineer](charters/devops.md) (`devops`) | Keeps CI, deploys and dependencies healthy, and patches security updates. |
|
|
86
|
+
| [Technical Writer](charters/writer.md) (`writer`) | Writes and keeps up the docs, guides and changelog. |
|
|
87
|
+
| [Data Analyst](charters/analyst.md) (`analyst`) | Reads the numbers and writes a short weekly report on what changed. |
|
|
88
|
+
| [Community Manager](charters/community.md) (`community`) | Answers discussions, welcomes contributors, and drafts release announcements. |
|
|
89
|
+
|
|
90
|
+
They are examples to adapt, not templates to fill in. Read them for what a finished charter covers and how specific it gets, then write your own
|
|
91
|
+
about your business. A charter copied from one of these describes Acme. The brief carries the matching one for
|
|
92
|
+
you; these links are for reading them first.
|
|
93
|
+
|
|
66
94
|
## Things worth being concrete about
|
|
67
95
|
|
|
68
96
|
- **Escalation.** Name the label and the mechanism, not the sentiment. "Open an issue labelled
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nanocollective/roster",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.51",
|
|
4
4
|
"description": "An agent-run org, powered by GitHub. Scaffold AI staff members whose brain is a repo.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -16,23 +16,6 @@
|
|
|
16
16
|
"engines": {
|
|
17
17
|
"node": ">=20"
|
|
18
18
|
},
|
|
19
|
-
"scripts": {
|
|
20
|
-
"build": "tsup src/cli.ts --format esm --target node20 --clean",
|
|
21
|
-
"dev": "tsx src/cli.ts",
|
|
22
|
-
"test": "tsx --test test/*.test.ts",
|
|
23
|
-
"test:all": "pnpm test:format && pnpm test:lint && pnpm test:types && pnpm test:knip && pnpm test",
|
|
24
|
-
"test:ava:coverage": "c8 --reporter=text --reporter=json-summary tsx --test test/*.test.ts",
|
|
25
|
-
"test:format": "biome check --no-errors-on-unmatched .",
|
|
26
|
-
"test:lint": "biome lint .",
|
|
27
|
-
"test:lint:fix": "biome check --write .",
|
|
28
|
-
"test:types": "tsc --noEmit",
|
|
29
|
-
"test:knip": "knip",
|
|
30
|
-
"test:audit": "pnpm audit --audit-level=high",
|
|
31
|
-
"test:security": "semgrep scan --config auto --error",
|
|
32
|
-
"typecheck": "tsc --noEmit",
|
|
33
|
-
"format": "biome check --write .",
|
|
34
|
-
"prepublishOnly": "pnpm test:all && pnpm build"
|
|
35
|
-
},
|
|
36
19
|
"devDependencies": {
|
|
37
20
|
"@biomejs/biome": "^2.5.12",
|
|
38
21
|
"@types/node": "^22.10.2",
|
|
@@ -52,9 +35,24 @@
|
|
|
52
35
|
"url": "https://github.com/Nano-Collective/roster/issues"
|
|
53
36
|
},
|
|
54
37
|
"author": "Nano Collective",
|
|
55
|
-
"packageManager": "pnpm@11.0.9",
|
|
56
38
|
"publishConfig": {
|
|
57
39
|
"access": "public",
|
|
58
40
|
"tag": "latest"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsup src/cli.ts --format esm --target node20 --clean",
|
|
44
|
+
"dev": "tsx src/cli.ts",
|
|
45
|
+
"test": "tsx --test test/*.test.ts",
|
|
46
|
+
"test:all": "pnpm test:format && pnpm test:lint && pnpm test:types && pnpm test:knip && pnpm test",
|
|
47
|
+
"test:ava:coverage": "c8 --reporter=text --reporter=json-summary tsx --test test/*.test.ts",
|
|
48
|
+
"test:format": "biome check --no-errors-on-unmatched .",
|
|
49
|
+
"test:lint": "biome lint .",
|
|
50
|
+
"test:lint:fix": "biome check --write .",
|
|
51
|
+
"test:types": "tsc --noEmit",
|
|
52
|
+
"test:knip": "knip",
|
|
53
|
+
"test:audit": "pnpm audit --audit-level=high",
|
|
54
|
+
"test:security": "semgrep scan --config auto --error",
|
|
55
|
+
"typecheck": "tsc --noEmit",
|
|
56
|
+
"format": "biome check --write ."
|
|
59
57
|
}
|
|
60
|
-
}
|
|
58
|
+
}
|
|
@@ -6,22 +6,47 @@ name: %%STAFF_UPPER%% daily run
|
|
|
6
6
|
#
|
|
7
7
|
# NOTE: %%TOKENS%% are filled by `roster hire`; GitHub's own ${{ }} are left alone.
|
|
8
8
|
|
|
9
|
+
# The trigger and the date, as the Actions list shows it. The daily limit counts follow-on runs
|
|
10
|
+
# by this name.
|
|
11
|
+
run-name: "%%STAFF%% ${{ github.event_name == 'schedule' && 'daily' || inputs.trigger || 'manual' }}"
|
|
12
|
+
|
|
9
13
|
on:
|
|
10
14
|
schedule:
|
|
11
15
|
- cron: "%%SCHEDULE%%"
|
|
16
|
+
# By hand, from Run once now, or as a follow-on: a daily run that ended with the next step
|
|
17
|
+
# ready starts one more, up to max_runs_per_day.
|
|
12
18
|
workflow_dispatch:
|
|
19
|
+
inputs:
|
|
20
|
+
trigger:
|
|
21
|
+
description: "manual | follow-on"
|
|
22
|
+
required: false
|
|
23
|
+
default: manual
|
|
24
|
+
type: string
|
|
13
25
|
|
|
14
|
-
# A second run while one is in flight would fight it over the tracker and the branch state.
|
|
26
|
+
# A second run while one is in flight would fight it over the tracker and the branch state. A
|
|
27
|
+
# follow-on is started from inside the run before it, so it waits here until that one ends.
|
|
15
28
|
concurrency:
|
|
16
29
|
group: %%STAFF%%-session
|
|
17
30
|
cancel-in-progress: false
|
|
18
31
|
|
|
19
32
|
jobs:
|
|
20
33
|
session:
|
|
34
|
+
# The ceiling on what session.yaml's job token may do: reading the checkout, and the one
|
|
35
|
+
# comment that says a run failed when the App that would normally say so is what broke.
|
|
36
|
+
# A called workflow cannot raise these, so they have to be granted here.
|
|
37
|
+
#
|
|
38
|
+
# `actions` is for the daily limit, which reads this repo's runs, and for starting a
|
|
39
|
+
# follow-on run, which the job token may do: a workflow_dispatch it sends still starts a run.
|
|
40
|
+
permissions:
|
|
41
|
+
contents: read
|
|
42
|
+
issues: write
|
|
43
|
+
actions: write
|
|
21
44
|
uses: %%OPS_REPO%%/.github/workflows/session.yaml@main
|
|
22
45
|
with:
|
|
23
46
|
staff: %%STAFF%%
|
|
24
47
|
kind: daily
|
|
48
|
+
trigger: ${{ github.event_name == 'schedule' && 'daily' || inputs.trigger || 'manual' }}
|
|
49
|
+
max_runs_per_day: %%MAX_RUNS%%
|
|
25
50
|
ops_repo: %%OPS_REPO%%
|
|
26
51
|
model: %%MODEL%%
|
|
27
52
|
timeout_minutes: %%TIMEOUT%%
|
|
@@ -3,11 +3,28 @@ name: %%STAFF_UPPER%% on a mention
|
|
|
3
3
|
# Generated by roster. The body lives in %%OPS_REPO%%/.github/workflows/session.yaml.
|
|
4
4
|
#
|
|
5
5
|
# "%%MENTION%% ..." in a comment on this tracker, or in the body of a new issue, wakes a focused
|
|
6
|
-
# run in about 30 seconds.
|
|
6
|
+
# run in about 30 seconds. So does another staff member filing an issue here with their
|
|
7
|
+
# `from-<handle>` label. It is a task, not a session: no boot ritual, no handoff, and the reply
|
|
7
8
|
# goes in the thread.
|
|
8
9
|
#
|
|
9
10
|
# NOTE: %%TOKENS%% are filled by `roster hire`. GitHub's own ${{ }} expressions are left alone.
|
|
10
11
|
|
|
12
|
+
# What the run is and what started it, as the Actions list shows it. The portal reads this back
|
|
13
|
+
# to link a live run to the request behind it, and the daily limit counts peer runs by it.
|
|
14
|
+
# Most events here are nobody asking (a comment without the mention, this staff member's own
|
|
15
|
+
# reply), and those are named `ignored`, so nothing reads one as work while it waits to be
|
|
16
|
+
# skipped.
|
|
17
|
+
run-name: >-
|
|
18
|
+
%%STAFF%% ${{
|
|
19
|
+
(contains(fromJSON('%%HUMAN_LOGINS%%'), github.event.sender.login) &&
|
|
20
|
+
(contains(github.event.comment.body, '%%MENTION%%') ||
|
|
21
|
+
(github.event_name == 'issues' && contains(github.event.issue.body, '%%MENTION%%'))))
|
|
22
|
+
&& 'mention'
|
|
23
|
+
|| ((github.event_name == 'issues' && github.event.action == 'opened' &&
|
|
24
|
+
endsWith(github.event.sender.login, '[bot]') &&
|
|
25
|
+
github.event.sender.login != '%%APP%%[bot]') && 'peer' || 'ignored')
|
|
26
|
+
}} #${{ github.event.issue.number }}
|
|
27
|
+
|
|
11
28
|
on:
|
|
12
29
|
# A mention in the body of a brand new issue, which is often the faster route: one box, rather
|
|
13
30
|
# than a "create, then comment" round trip. `edited` doubles as the way to re-ask without
|
|
@@ -22,12 +39,6 @@ on:
|
|
|
22
39
|
issue_comment:
|
|
23
40
|
types: [created, edited]
|
|
24
41
|
|
|
25
|
-
# Per issue, not global. Several comments in a row are the normal case and a shared group would
|
|
26
|
-
# silently drop all but one of them.
|
|
27
|
-
concurrency:
|
|
28
|
-
group: %%STAFF%%-mention-${{ github.event.issue.number }}
|
|
29
|
-
cancel-in-progress: false
|
|
30
|
-
|
|
31
42
|
jobs:
|
|
32
43
|
answer:
|
|
33
44
|
# Only a human this org answers to, only on a real mention. A bot quoting the phrase must
|
|
@@ -44,19 +55,54 @@ jobs:
|
|
|
44
55
|
#
|
|
45
56
|
# One spelling of the mention is enough: GitHub's `contains` is documented as not case
|
|
46
57
|
# sensitive, so this already matches an uppercase mention at the start of a sentence.
|
|
58
|
+
#
|
|
59
|
+
# The second route is a peer's ask: a new issue opened by an App, never this staff member's
|
|
60
|
+
# own, carrying a `from-<handle>` label. Only `opened`, so the peer editing its own issue
|
|
61
|
+
# later wakes nobody. It names no peer, so a later hire needs no regenerated caller: the only
|
|
62
|
+
# accounts that can open an issue on a private tracker are the people and Apps it is
|
|
63
|
+
# installed for. A peer run may not file on another peer, and session.yaml holds peer runs
|
|
64
|
+
# to the day's limit, so two staff cannot wake each other forever.
|
|
65
|
+
# Per issue, not global: several comments in a row are the normal case, and a shared group
|
|
66
|
+
# would silently drop all but one of them. On the job rather than the workflow, so the
|
|
67
|
+
# condition below is decided first. At workflow level every event queued here, including
|
|
68
|
+
# this staff member's own replies, and GitHub keeps one waiting run per group, so a reply
|
|
69
|
+
# waiting to be skipped could knock out a real mention waiting behind a run.
|
|
70
|
+
concurrency:
|
|
71
|
+
group: %%STAFF%%-mention-${{ github.event.issue.number }}
|
|
72
|
+
cancel-in-progress: false
|
|
47
73
|
if: >-
|
|
48
|
-
contains(fromJSON('%%HUMAN_LOGINS%%'), github.event.sender.login) &&
|
|
49
74
|
(
|
|
50
|
-
(github.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
75
|
+
contains(fromJSON('%%HUMAN_LOGINS%%'), github.event.sender.login) &&
|
|
76
|
+
(
|
|
77
|
+
(github.event_name == 'issue_comment' &&
|
|
78
|
+
contains(fromJSON('%%HUMAN_LOGINS%%'), github.event.comment.user.login) &&
|
|
79
|
+
contains(github.event.comment.body, '%%MENTION%%')) ||
|
|
80
|
+
(github.event_name == 'issues' &&
|
|
81
|
+
contains(github.event.issue.body, '%%MENTION%%'))
|
|
82
|
+
)
|
|
83
|
+
) || (
|
|
84
|
+
github.event_name == 'issues' && github.event.action == 'opened' &&
|
|
85
|
+
endsWith(github.event.sender.login, '[bot]') &&
|
|
86
|
+
github.event.sender.login != '%%APP%%[bot]' &&
|
|
87
|
+
contains(join(github.event.issue.labels.*.name, ','), 'from-')
|
|
55
88
|
)
|
|
89
|
+
# The ceiling on what session.yaml's job token may do: reading the checkout, and the one
|
|
90
|
+
# comment that says a run failed when the App that would normally say so is what broke.
|
|
91
|
+
# A called workflow cannot raise these, so they have to be granted here.
|
|
92
|
+
#
|
|
93
|
+
# `actions` is for the daily limit, which counts this repo's runs today. It is `write`
|
|
94
|
+
# because session.yaml's job asks for that (a daily run starts its follow-on with it), and a
|
|
95
|
+
# called job asking for more than its caller granted fails before it starts.
|
|
96
|
+
permissions:
|
|
97
|
+
contents: read
|
|
98
|
+
issues: write
|
|
99
|
+
actions: write
|
|
56
100
|
uses: %%OPS_REPO%%/.github/workflows/session.yaml@main
|
|
57
101
|
with:
|
|
58
102
|
staff: %%STAFF%%
|
|
59
103
|
kind: mention
|
|
104
|
+
trigger: ${{ contains(fromJSON('%%HUMAN_LOGINS%%'), github.event.sender.login) && 'mention' || 'peer' }}
|
|
105
|
+
max_runs_per_day: %%MAX_RUNS%%
|
|
60
106
|
ops_repo: %%OPS_REPO%%
|
|
61
107
|
model: %%MODEL%%
|
|
62
108
|
timeout_minutes: %%MENTION_TIMEOUT%%
|
|
@@ -11,10 +11,10 @@ between %%MENTION%% and everyone else.
|
|
|
11
11
|
Write it before the first unattended run. A generated charter would produce a generic agent,
|
|
12
12
|
which is the failure this whole arrangement exists to avoid.
|
|
13
13
|
|
|
14
|
-
Write it with your own AI
|
|
14
|
+
Write it with your own AI. This prints a brief to paste into whichever agent you use (in
|
|
15
|
+
Claude Code it is also /charter, from inside this repo):
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
/charter
|
|
17
|
+
roster brief charter %%STAFF%%
|
|
18
18
|
|
|
19
19
|
Or write it by hand. The headings below are the shape that has worked; the words are yours.
|
|
20
20
|
|
|
@@ -10,6 +10,7 @@ on, and has decided.
|
|
|
10
10
|
| `memory/INDEX.md` | One line per fact, read at every boot. |
|
|
11
11
|
| `memory/notes/` | The argument behind a fact, read on demand. |
|
|
12
12
|
| `log/decisions.md` | Why things were decided. Not boot context. |
|
|
13
|
+
| `strategy/` | Longer role documents, and `ideas.md`: ideas parked here rather than filed as issues. |
|
|
13
14
|
| `.github/workflows/` | Three callers. The body lives in `%%OPS_REPO%%`. |
|
|
14
15
|
|
|
15
16
|
Scheduled runs and mentions are wired up by roster. To see what this staff member is actually
|
|
@@ -4,3 +4,6 @@ Why things were decided, newest first. Not boot context: this is read when a dec
|
|
|
4
4
|
being revisited, not every morning.
|
|
5
5
|
|
|
6
6
|
One entry per decision. What was decided, why, and what would change it back.
|
|
7
|
+
|
|
8
|
+
This file holds the current month. Move anything older into `log/decisions/<YYYY-MM>.md`:
|
|
9
|
+
`roster lint` warns past 24KB.
|
|
@@ -13,6 +13,9 @@ schedule: "%%SCHEDULE%%"
|
|
|
13
13
|
model: %%MODEL%%
|
|
14
14
|
timeout_minutes: %%TIMEOUT%%
|
|
15
15
|
mention_timeout_minutes: %%MENTION_TIMEOUT%%
|
|
16
|
+
# Runs a day that start without a person asking: a peer's ask, or a follow-on to a daily run.
|
|
17
|
+
# Mentions never count against it.
|
|
18
|
+
max_runs_per_day: %%MAX_RUNS%%
|
|
16
19
|
|
|
17
20
|
bot: %%APP%%[bot]
|
|
18
21
|
public_bot: %%PUBLIC_APP%%[bot]
|
|
@@ -40,4 +43,4 @@ surfaces:
|
|
|
40
43
|
|
|
41
44
|
labels:
|
|
42
45
|
owner: [%%HUMAN_MARKER%%, %%STAFF%%]
|
|
43
|
-
kind: [decision,
|
|
46
|
+
kind: [decision, review, chore, keep-open, build, blocked]
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Ideas
|
|
2
|
+
|
|
3
|
+
Speculative ideas, one line each, parked here rather than filed as issues. An issue is for
|
|
4
|
+
something that needs a ruling; an idea that does not yet is noise on the tracker.
|
|
5
|
+
|
|
6
|
+
Promote one to an issue when it serves a priority in `org/priorities.md` and needs a ruling.
|
|
7
|
+
Delete one when it stops being interesting.
|
|
@@ -44,7 +44,8 @@ If what is wanted needs one of these, say so and stop. Do not work around it.
|
|
|
44
44
|
|
|
45
45
|
1. **Say which file, and why that one.** Prefer the narrowest file that achieves it. If the
|
|
46
46
|
change is about %%NAME%% specifically, it does not belong in `org/`.
|
|
47
|
-
2. **
|
|
47
|
+
2. **Hand back the whole file you changed**, in the block described at the end. The tool
|
|
48
|
+
%%HUMAN%% pastes your answer into shows them the diff, so they see exactly what moved.
|
|
48
49
|
3. **Do not restate.** Every layer is already in the composed text below. A rule added to
|
|
49
50
|
`org/voice.md` that `org/operating.md` already states makes the prompt longer and no
|
|
50
51
|
clearer.
|
|
@@ -56,5 +57,5 @@ If what is wanted needs one of these, say so and stop. Do not work around it.
|
|
|
56
57
|
|
|
57
58
|
## Then
|
|
58
59
|
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
Outside the block: the one-line reason for the file you chose, and what you cut. Inside it:
|
|
61
|
+
the whole changed file. Nothing is saved until %%HUMAN%% has seen the diff and pressed Save.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
Write `org/priorities.md` for %%ORG_NAME%%.
|
|
2
|
+
|
|
3
|
+
You are helping %%HUMAN%% decide what their AI staff should work on this month. Every staff
|
|
4
|
+
member reads this file at the start of every run and picks work that serves it, so a vague
|
|
5
|
+
priority produces scattered work.
|
|
6
|
+
|
|
7
|
+
## Where you are
|
|
8
|
+
|
|
9
|
+
- `%%OPS_REPO_DIR%%/org/business.md`: what the business is. **Read it first**, and don't ask
|
|
10
|
+
anything it already answers.
|
|
11
|
+
- `%%OPS_REPO_DIR%%/org/priorities.md`: the current file, which you are replacing.
|
|
12
|
+
|
|
13
|
+
If you cannot read files where you are running, ask %%HUMAN%% to paste `org/business.md`.
|
|
14
|
+
|
|
15
|
+
## Interview
|
|
16
|
+
|
|
17
|
+
Ask one question at a time. You are after:
|
|
18
|
+
|
|
19
|
+
- **The one outcome that matters most this month**, and how they will know it happened.
|
|
20
|
+
- **At most two more**, in order. Push back on a fourth: past three it is a wish list.
|
|
21
|
+
- **What is out of scope this month**: work that is tempting but not now. Naming it is what
|
|
22
|
+
stops the staff doing it.
|
|
23
|
+
|
|
24
|
+
Prefer outcomes ("a stranger pays for it") to activities ("improve the landing page"). If an
|
|
25
|
+
answer is an activity, ask what it is for.
|
|
26
|
+
|
|
27
|
+
## Write it
|
|
28
|
+
|
|
29
|
+
Use exactly this shape:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
## What matters this month
|
|
33
|
+
|
|
34
|
+
### Priorities, in order
|
|
35
|
+
|
|
36
|
+
1. The first outcome, and how you will know it happened.
|
|
37
|
+
2. The second.
|
|
38
|
+
3. The third.
|
|
39
|
+
|
|
40
|
+
### Out of scope this month
|
|
41
|
+
|
|
42
|
+
- One thing per line.
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Keep each priority to a sentence or two. Don't add anything %%HUMAN%% didn't say without
|
|
46
|
+
listing it after the file so they can check it.
|