@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.
Files changed (103) hide show
  1. package/README.md +70 -84
  2. package/dist/cli.js +5161 -3012
  3. package/docs/README.md +9 -6
  4. package/docs/agents.md +24 -20
  5. package/docs/architecture.md +13 -5
  6. package/docs/charters/analyst.md +65 -0
  7. package/docs/charters/cmo.md +69 -0
  8. package/docs/charters/community.md +63 -0
  9. package/docs/charters/cto.md +71 -0
  10. package/docs/charters/designer.md +65 -0
  11. package/docs/charters/devops.md +65 -0
  12. package/docs/charters/pm.md +70 -0
  13. package/docs/charters/qa.md +65 -0
  14. package/docs/charters/support.md +60 -0
  15. package/docs/charters/writer.md +63 -0
  16. package/docs/commands.md +93 -7
  17. package/docs/concepts.md +61 -14
  18. package/docs/cost.md +36 -1
  19. package/docs/developing.md +16 -21
  20. package/docs/doctor-codes.md +10 -2
  21. package/docs/export.md +2 -0
  22. package/docs/extending.md +2 -2
  23. package/docs/getting-started.md +128 -78
  24. package/docs/images/brain.jpg +0 -0
  25. package/docs/images/org.jpg +0 -0
  26. package/docs/images/prompt.jpg +0 -0
  27. package/docs/images/setup-org.jpg +0 -0
  28. package/docs/images/setup-plan.jpg +0 -0
  29. package/docs/images/staff.jpg +0 -0
  30. package/docs/manual-steps.md +94 -123
  31. package/docs/memory.md +21 -3
  32. package/docs/org-yaml.md +40 -2
  33. package/docs/portal.md +177 -58
  34. package/docs/prompts.md +31 -4
  35. package/docs/security.md +37 -5
  36. package/docs/session-workflow.md +63 -17
  37. package/docs/staff-yaml.md +30 -3
  38. package/docs/troubleshooting.md +8 -8
  39. package/docs/upgrading.md +9 -3
  40. package/docs/writing-a-charter.md +28 -0
  41. package/package.json +18 -20
  42. package/templates/brain/.github/workflows/%%STAFF%%-daily.yaml +26 -1
  43. package/templates/brain/.github/workflows/%%STAFF%%-mention.yaml +59 -13
  44. package/templates/brain/CHARTER.md +3 -3
  45. package/templates/brain/README.md +1 -0
  46. package/templates/brain/log/decisions.md +3 -0
  47. package/templates/brain/staff.yaml +4 -1
  48. package/templates/brain/strategy/ideas.md +7 -0
  49. package/templates/briefs/amend.md +4 -3
  50. package/templates/briefs/priorities.md +46 -0
  51. package/templates/ops/.github/workflows/session.yaml +236 -15
  52. package/templates/ops/agents.mjs +7 -3
  53. package/templates/ops/compose.mjs +31 -3
  54. package/templates/ops/inflight.mjs +157 -0
  55. package/templates/ops/org/operating.md +43 -4
  56. package/templates/ops/org/voice.md +9 -0
  57. package/templates/ops/prompts/_inflight.md +14 -0
  58. package/templates/ops/prompts/_paths.md +2 -1
  59. package/templates/ops/prompts/daily.md +37 -9
  60. package/templates/ops/prompts/mention.md +21 -0
  61. package/templates/ops/run-record.mjs +146 -0
  62. package/templates/portal/css/base.css +167 -73
  63. package/templates/portal/css/brain.css +23 -20
  64. package/templates/portal/css/diff.css +10 -9
  65. package/templates/portal/css/graph.css +12 -7
  66. package/templates/portal/css/health.css +26 -11
  67. package/templates/portal/css/home.css +95 -0
  68. package/templates/portal/css/inbox.css +45 -25
  69. package/templates/portal/css/layout.css +90 -46
  70. package/templates/portal/css/markdown.css +36 -14
  71. package/templates/portal/css/runs.css +13 -0
  72. package/templates/portal/css/setup.css +117 -34
  73. package/templates/portal/index.html +21 -9
  74. package/templates/portal/js/api.js +44 -4
  75. package/templates/portal/js/app.js +94 -9
  76. package/templates/portal/js/dialog.js +83 -0
  77. package/templates/portal/js/homesort.js +174 -0
  78. package/templates/portal/js/icons.js +37 -0
  79. package/templates/portal/js/inflight.js +18 -0
  80. package/templates/portal/js/md.js +5 -2
  81. package/templates/portal/js/mdedit.js +84 -0
  82. package/templates/portal/js/readiness.js +70 -0
  83. package/templates/portal/js/refresh.js +10 -2
  84. package/templates/portal/js/state.js +11 -5
  85. package/templates/portal/js/views/app.js +24 -7
  86. package/templates/portal/js/views/brain.js +1 -1
  87. package/templates/portal/js/views/checklist.js +10 -4
  88. package/templates/portal/js/views/credential.js +84 -0
  89. package/templates/portal/js/views/graph.js +1 -1
  90. package/templates/portal/js/views/health.js +27 -9
  91. package/templates/portal/js/views/hire.js +593 -0
  92. package/templates/portal/js/views/home.js +546 -0
  93. package/templates/portal/js/views/inbox.js +226 -70
  94. package/templates/portal/js/views/org.js +46 -106
  95. package/templates/portal/js/views/orgedit.js +234 -0
  96. package/templates/portal/js/views/paste.js +137 -63
  97. package/templates/portal/js/views/prompt.js +100 -42
  98. package/templates/portal/js/views/repos.js +20 -15
  99. package/templates/portal/js/views/runonce.js +94 -0
  100. package/templates/portal/js/views/runs.js +170 -0
  101. package/templates/portal/js/views/setup.js +261 -75
  102. package/templates/portal/js/views/staff.js +170 -243
  103. package/templates/portal/js/views/todo.js +62 -0
@@ -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: PIPWEB_TOKEN
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, setup, build, blocked]
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.
@@ -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
- 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
+ `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
- This happened here: a fix went into `roster-ops/.github/workflows/session.yaml` instead of
94
- `templates/ops/...`, and nothing noticed because the framework had not touched that file *yet*.
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 created before the merge base was recorded has nothing to merge against. Reconstruct
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 # what would change
14
- roster upgrade --apply # do it
15
- roster upgrade --check # exit non-zero if anything is pending (for CI)
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.5",
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. It is a task, not a session: no boot ritual, no handoff, and the reply
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.event_name == 'issue_comment' &&
51
- contains(fromJSON('%%HUMAN_LOGINS%%'), github.event.comment.user.login) &&
52
- contains(github.event.comment.body, '%%MENTION%%')) ||
53
- (github.event_name == 'issues' &&
54
- contains(github.event.issue.body, '%%MENTION%%'))
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
- cd %%DIR%% && claude
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, setup, build, blocked]
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. **Show a diff, not a rewritten file.** %%HUMAN%% has to be able to see exactly what moved.
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
- Give %%HUMAN%% the diff, the one-line reason for the file you chose, and what you cut. They
60
- apply it: in the portal's Prompt screen, or by editing the file and committing it.
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.