@nanocollective/roster 0.1.0-alpha.1
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/LICENSE +21 -0
- package/README.md +129 -0
- package/dist/cli.js +5679 -0
- package/docs/README.md +99 -0
- package/docs/agents.md +163 -0
- package/docs/architecture.md +121 -0
- package/docs/commands.md +223 -0
- package/docs/concepts.md +112 -0
- package/docs/cost.md +61 -0
- package/docs/developing.md +147 -0
- package/docs/doctor-codes.md +74 -0
- package/docs/export.md +113 -0
- package/docs/extending.md +97 -0
- package/docs/getting-started.md +134 -0
- package/docs/hosting.md +72 -0
- package/docs/manual-steps.md +163 -0
- package/docs/memory.md +71 -0
- package/docs/org-yaml.md +143 -0
- package/docs/portal.md +342 -0
- package/docs/prompts.md +133 -0
- package/docs/security.md +122 -0
- package/docs/session-workflow.md +112 -0
- package/docs/staff-yaml.md +163 -0
- package/docs/troubleshooting.md +189 -0
- package/docs/upgrading.md +83 -0
- package/docs/writing-a-charter.md +83 -0
- package/package.json +60 -0
- package/templates/brain/.github/workflows/%%STAFF%%-daily.yaml +33 -0
- package/templates/brain/.github/workflows/%%STAFF%%-mention.yaml +65 -0
- package/templates/brain/.github/workflows/%%STAFF%%-pr-mention.yaml +50 -0
- package/templates/brain/CHARTER.md +49 -0
- package/templates/brain/README.md +18 -0
- package/templates/brain/drafts/README.md +7 -0
- package/templates/brain/log/decisions.md +6 -0
- package/templates/brain/memory/INDEX.md +28 -0
- package/templates/brain/staff.yaml +44 -0
- package/templates/brain/strategy/README.md +7 -0
- package/templates/briefs/amend.md +60 -0
- package/templates/briefs/charter.md +47 -0
- package/templates/briefs/discover.md +61 -0
- package/templates/briefs/voice.md +53 -0
- package/templates/ops/.github/workflows/session.yaml +333 -0
- package/templates/ops/agents.mjs +143 -0
- package/templates/ops/compose.mjs +333 -0
- package/templates/ops/org/guardrails.md +14 -0
- package/templates/ops/org/operating.md +82 -0
- package/templates/ops/org/voice.md +40 -0
- package/templates/ops/prompts/_identity.md +14 -0
- package/templates/ops/prompts/_paths.md +15 -0
- package/templates/ops/prompts/daily.md +82 -0
- package/templates/ops/prompts/mention.md +53 -0
- package/templates/ops/prompts/pr-mention.md +57 -0
- package/templates/ops/runner-plan.mjs +65 -0
- package/templates/portal/css/base.css +104 -0
- package/templates/portal/css/brain.css +106 -0
- package/templates/portal/css/diff.css +28 -0
- package/templates/portal/css/graph.css +34 -0
- package/templates/portal/css/health.css +41 -0
- package/templates/portal/css/inbox.css +79 -0
- package/templates/portal/css/layout.css +98 -0
- package/templates/portal/css/markdown.css +54 -0
- package/templates/portal/css/setup.css +106 -0
- package/templates/portal/index.html +55 -0
- package/templates/portal/js/api.js +74 -0
- package/templates/portal/js/app.js +282 -0
- package/templates/portal/js/dialog.js +70 -0
- package/templates/portal/js/dom.js +106 -0
- package/templates/portal/js/icons.js +94 -0
- package/templates/portal/js/md.js +386 -0
- package/templates/portal/js/refresh.js +59 -0
- package/templates/portal/js/router.js +20 -0
- package/templates/portal/js/state.js +160 -0
- package/templates/portal/js/textdiff.js +96 -0
- package/templates/portal/js/views/app.js +128 -0
- package/templates/portal/js/views/brain.js +260 -0
- package/templates/portal/js/views/changed.js +157 -0
- package/templates/portal/js/views/checklist.js +87 -0
- package/templates/portal/js/views/docs.js +84 -0
- package/templates/portal/js/views/files.js +95 -0
- package/templates/portal/js/views/graph.js +436 -0
- package/templates/portal/js/views/health.js +158 -0
- package/templates/portal/js/views/inbox.js +549 -0
- package/templates/portal/js/views/memory.js +135 -0
- package/templates/portal/js/views/org.js +175 -0
- package/templates/portal/js/views/paste.js +142 -0
- package/templates/portal/js/views/prompt.js +412 -0
- package/templates/portal/js/views/repos.js +92 -0
- package/templates/portal/js/views/setup.js +344 -0
- package/templates/portal/js/views/staff.js +290 -0
package/docs/concepts.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Concepts"
|
|
3
|
+
description: "The ops repo, the brain, charters, manifests, surfaces and peers."
|
|
4
|
+
sidebar_order: 3
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Concepts
|
|
8
|
+
|
|
9
|
+
## The ops repo
|
|
10
|
+
|
|
11
|
+
`<org>/roster-ops` holds two different kinds of thing, and the split matters.
|
|
12
|
+
|
|
13
|
+
**`org/` is yours.** `business.md`, `voice.md`, `guardrails.md`, `operating.md`. This is the
|
|
14
|
+
business truth and the shared half of every staff member's instructions. Edit it freely. A
|
|
15
|
+
change here reaches everybody on their next run, which is the point: a concision rule that used
|
|
16
|
+
to mean editing twelve files is now one file.
|
|
17
|
+
|
|
18
|
+
**Everything else is machinery** and belongs to the framework: `compose.mjs`, `agents.mjs`,
|
|
19
|
+
`runner-plan.mjs`, `.github/workflows/session.yaml`. Editing these works right up until the
|
|
20
|
+
framework changes the same file, at which point your change is a conflict at best and silently
|
|
21
|
+
reverted at worst. Fix machinery in the framework, then `roster upgrade`.
|
|
22
|
+
|
|
23
|
+
`roster upgrade` enforces this distinction. It reports an edit to a framework-owned file even
|
|
24
|
+
when nothing has collided yet, because "not broken yet" is the state a lost fix sits in.
|
|
25
|
+
|
|
26
|
+
## The brain
|
|
27
|
+
|
|
28
|
+
A staff member's repository *is* their memory. There is no database.
|
|
29
|
+
|
|
30
|
+
| | |
|
|
31
|
+
|---|---|
|
|
32
|
+
| `CHARTER.md` | the personality. Hand written. Decides everything else. |
|
|
33
|
+
| `staff.yaml` | the machine-readable half: handle, schedule, identities, peers, surfaces |
|
|
34
|
+
| `memory/INDEX.md` | one line per fact, read in full at every boot |
|
|
35
|
+
| `memory/notes/` | the argument behind a fact, read only when that fact is in play |
|
|
36
|
+
| `log/decisions.md` | why things were decided. Not boot context. |
|
|
37
|
+
| `.github/workflows/` | three callers, about forty lines each |
|
|
38
|
+
|
|
39
|
+
## Charter and manifest
|
|
40
|
+
|
|
41
|
+
Two halves of one thing. The charter is prose for the agent; the manifest is fields for the
|
|
42
|
+
machinery. `roster lint` fails if they disagree.
|
|
43
|
+
|
|
44
|
+
The charter is the only file roster refuses to generate. A generated charter produces a generic
|
|
45
|
+
agent, and a generic agent produces work that is plausible, competent-looking and about nothing
|
|
46
|
+
in particular.
|
|
47
|
+
|
|
48
|
+
## Surfaces
|
|
49
|
+
|
|
50
|
+
`staff.yaml` declares what a staff member keeps and how to render it:
|
|
51
|
+
|
|
52
|
+
```yaml
|
|
53
|
+
surfaces:
|
|
54
|
+
- { path: memory/, render: memory }
|
|
55
|
+
- { path: assets/, render: gallery }
|
|
56
|
+
- { path: data/, render: table }
|
|
57
|
+
- { path: strategy/, render: doc }
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
This is how the portal renders a brain it has never seen. A CMO with brand assets and analytics
|
|
61
|
+
exports declares `gallery` and `table`; nothing in the portal knows what a CMO is.
|
|
62
|
+
|
|
63
|
+
## The composed prompt
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
org/operating.md + org/guardrails.md + org/voice.md + org/business.md
|
|
67
|
+
+ <staff>/CHARTER.md + prompts/<kind>.md
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Built at run time by `compose.mjs` in the tenant's own repo. See it for yourself:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
roster prompt cto --kind daily
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`compose.mjs` is vendored into the tenant rather than imported from the framework. A run at
|
|
77
|
+
07:00 must not depend on npm, on a network fetch, or on an organisation the tenant does not
|
|
78
|
+
control.
|
|
79
|
+
|
|
80
|
+
## Kinds of run
|
|
81
|
+
|
|
82
|
+
| | |
|
|
83
|
+
|---|---|
|
|
84
|
+
| `daily` | the scheduled session. Boot, work, hand off. |
|
|
85
|
+
| `mention` | `@handle` in a comment or a new issue body. A task, not a session. |
|
|
86
|
+
| `pr-mention` | a review comment on the public product repo, forwarded in. |
|
|
87
|
+
|
|
88
|
+
`mention` and `pr-mention` prompts refuse to compose without trigger context, because they are
|
|
89
|
+
written for the comment that woke them. That is correct behaviour, not a bug.
|
|
90
|
+
|
|
91
|
+
## Identities
|
|
92
|
+
|
|
93
|
+
A staff member posts as a GitHub App, not as you. Usually two:
|
|
94
|
+
|
|
95
|
+
- a **private** identity for their own trackers, unique to them
|
|
96
|
+
- a **public** identity shared by everyone, for the product repo, deliberately anonymous
|
|
97
|
+
|
|
98
|
+
The public one is shared on purpose. A bot opening a pull request on a public repo is
|
|
99
|
+
unremarkable; a bot signing itself with a job title is a tell.
|
|
100
|
+
|
|
101
|
+
## Peers
|
|
102
|
+
|
|
103
|
+
Staff members write to each other. A peer entry carries the label *this* staff member uses when
|
|
104
|
+
filing on *that* tracker, so it is not symmetric:
|
|
105
|
+
|
|
106
|
+
```yaml
|
|
107
|
+
# technology/staff.yaml
|
|
108
|
+
peers:
|
|
109
|
+
- { handle: cmo, brain: acme/marketing, label: from-cto }
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`from-cto` lives on `acme/marketing`, because that is where the CTO's asks land.
|
package/docs/cost.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Cost"
|
|
3
|
+
description: "What an agent-run org spends, and on what."
|
|
4
|
+
sidebar_order: 12
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Cost
|
|
8
|
+
|
|
9
|
+
Three separate bills, and they behave differently.
|
|
10
|
+
|
|
11
|
+
## The coding agent
|
|
12
|
+
|
|
13
|
+
The largest by far, and the one that scales with how much work you ask for.
|
|
14
|
+
|
|
15
|
+
A session's cost is roughly its length. Ours run 11 to 55 minutes of wall clock, and a longer
|
|
16
|
+
session is a bigger bill as well as a slower one. The lever that matters is not the model
|
|
17
|
+
setting, it is how much you ask a staff member to do each morning and how much context it has
|
|
18
|
+
to read to start.
|
|
19
|
+
|
|
20
|
+
That is why the memory system is shaped the way it is. Boot context here went from about 52,000
|
|
21
|
+
words to about 6,000 by moving from a narrative status file to one line per fact. That is a
|
|
22
|
+
direct, repeated saving on every run of every staff member.
|
|
23
|
+
|
|
24
|
+
**Watch for sessions growing into their ceiling.** A run that gets killed at
|
|
25
|
+
`timeout_minutes` has been paid for and produced nothing. `roster doctor` reports the ratio.
|
|
26
|
+
|
|
27
|
+
## GitHub Actions minutes
|
|
28
|
+
|
|
29
|
+
Real on private repositories, and easy to forget because it is metered per minute of runner
|
|
30
|
+
time and every scheduled run consumes it whether or not the run was useful.
|
|
31
|
+
|
|
32
|
+
Three things spend it more than you would expect:
|
|
33
|
+
|
|
34
|
+
- **Timeouts.** A run killed at 90 minutes bills 90 minutes.
|
|
35
|
+
- **Mention workflows.** Every comment on a tracker triggers a run, even ones the condition
|
|
36
|
+
gates out. Those are seconds each, but they are not free and they are numerous.
|
|
37
|
+
- **`fetch-depth: 0`** on the brain checkout, which is deliberate (the agent reads its own
|
|
38
|
+
history) but grows with the repository.
|
|
39
|
+
|
|
40
|
+
Public repositories are free, which is why the product-repo lane costs nothing.
|
|
41
|
+
|
|
42
|
+
## GitHub Apps
|
|
43
|
+
|
|
44
|
+
Free. There is no per-App or per-installation charge. The only cost here is the human minute
|
|
45
|
+
it takes to install one.
|
|
46
|
+
|
|
47
|
+
## What roster itself costs
|
|
48
|
+
|
|
49
|
+
Nothing at run time. The framework is not a dependency of anything a tenant runs: the CLI runs
|
|
50
|
+
on your machine when you ask it to, and the machinery is vendored into the tenant's own repo.
|
|
51
|
+
|
|
52
|
+
## Keeping it down
|
|
53
|
+
|
|
54
|
+
- **Prune memory.** Deleting is the maintenance. Every line in `memory/INDEX.md` is read at
|
|
55
|
+
every boot, by every run, forever.
|
|
56
|
+
- **Do not raise a timeout to fix a slow session.** Find out why it grew. A ceiling that keeps
|
|
57
|
+
being raised is a session that has stopped fitting its job.
|
|
58
|
+
- **Give a mention workflow a shorter ceiling than a daily one.** A focused task that runs for
|
|
59
|
+
an hour has gone wrong, and the ceiling is the only thing that stops it.
|
|
60
|
+
- **Check the ratio, not the last run.** `roster doctor` reports how many of the last ten runs
|
|
61
|
+
succeeded. One bad run is noise; four is a bill.
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Working on roster itself"
|
|
3
|
+
description: "Layout, template classes, and the rules that matter."
|
|
4
|
+
sidebar_order: 22
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Working on roster itself
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
git clone <the framework> && cd roster && npm install
|
|
11
|
+
npm test # the whole suite
|
|
12
|
+
npx tsc --noEmit # typecheck
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Tests are `node:test` run through `tsx`. There is no build step for development, and the portal
|
|
16
|
+
has no build step at all.
|
|
17
|
+
|
|
18
|
+
## Layout
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
src/cli.ts command table and help
|
|
22
|
+
src/commands/ one file per command
|
|
23
|
+
src/lib/ shared: workspace, memory, render, merge, gh, templates, docs
|
|
24
|
+
templates/ops/ what a tenant's ops repo is generated from
|
|
25
|
+
templates/brain/ what a staff member's repo is generated from
|
|
26
|
+
templates/portal/ the portal: a shell, css/, and js/ as ES modules
|
|
27
|
+
docs/ these pages
|
|
28
|
+
test/ one file per area
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## The portal has no build step, on purpose
|
|
32
|
+
|
|
33
|
+
`templates/portal/` is a plain `index.html`, one stylesheet per area under `css/`, and one ES
|
|
34
|
+
module per screen under `js/views/`. `roster portal` serves them from `/assets`, reading each
|
|
35
|
+
file per request. Editing a stylesheet and reloading the page is the whole edit loop.
|
|
36
|
+
|
|
37
|
+
It was one 2,100-line HTML file until the CSS and the seven screens grew past the point where
|
|
38
|
+
any of them could be found in it. Splitting it needed no bundler, because the browser resolves
|
|
39
|
+
the module graph itself, and a bundler would have been a build step in a tool whose selling
|
|
40
|
+
point is that it does not have one.
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
templates/portal/
|
|
44
|
+
index.html the shell: <link>s, the sidebar's bones, one <script type="module">
|
|
45
|
+
css/base.css tokens, reset, buttons, inputs, chips
|
|
46
|
+
css/layout.css the frame: sidebar, main, tree-beside-viewer
|
|
47
|
+
css/<area>.css markdown, brain, inbox, graph, diff, health
|
|
48
|
+
js/state.js every screen's state in one object, and the URL that mirrors it
|
|
49
|
+
js/router.js one indirection so a view can repaint without importing the shell
|
|
50
|
+
js/dom.js api.js md.js refresh.js icons.js
|
|
51
|
+
js/app.js boot, the sidebar, dispatching a view into <main>
|
|
52
|
+
js/views/*.js one per screen
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`js/icons.js` holds two dozen [Lucide](https://lucide.dev) glyphs as inlined path data, ISC
|
|
56
|
+
licensed. Vendored rather than fetched, because the portal is offline-first and an icon that
|
|
57
|
+
needs the network is an icon that is sometimes a blank square. They take colour from
|
|
58
|
+
`currentColor` and size from a `--ic` custom property, so a component styles them in CSS and
|
|
59
|
+
never touches the SVG. Nothing in the UI should draw with a text character.
|
|
60
|
+
|
|
61
|
+
Two rules keep the module graph a tree rather than a ring: **state lives in `state.js`**, and
|
|
62
|
+
**a view never imports `app.js`**. It calls `render()` from `router.js`, which the shell
|
|
63
|
+
registers into at boot.
|
|
64
|
+
|
|
65
|
+
## The rule that matters
|
|
66
|
+
|
|
67
|
+
**Never fix a generated file in a tenant.** `compose.mjs`, `agents.mjs`, `runner-plan.mjs` and
|
|
68
|
+
`session.yaml` live in `templates/ops/`. Fix them there and run `roster upgrade`.
|
|
69
|
+
|
|
70
|
+
This has gone wrong once already. A fix went into `roster-ops/.github/workflows/session.yaml`
|
|
71
|
+
instead of the template and nothing noticed, because the framework had not touched that file
|
|
72
|
+
yet. `roster upgrade` now reports an edit to a framework-owned file whether or not anything has
|
|
73
|
+
collided, and `roster upgrade --check` fails on it.
|
|
74
|
+
|
|
75
|
+
## Template classes
|
|
76
|
+
|
|
77
|
+
Ownership decides what `roster upgrade` does, and getting it wrong is how you either lose
|
|
78
|
+
somebody's work or never ship an improvement.
|
|
79
|
+
|
|
80
|
+
| Where | Class | Upgrade behaviour |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| `templates/ops/org/`, `prompts/` | `seeded` | three-way merge |
|
|
83
|
+
| `templates/ops/` everything else | `managed` | reported even when nothing collided |
|
|
84
|
+
| `templates/brain/.github/workflows/` | `generated` | regenerated wholesale |
|
|
85
|
+
| `templates/brain/` everything else | `scaffold` | added when new, never rewritten |
|
|
86
|
+
|
|
87
|
+
`scaffold` is the important one. A working agent rewrites `CHARTER.md` and `memory/INDEX.md`
|
|
88
|
+
beyond recognition, and merging a template into that would be vandalism.
|
|
89
|
+
|
|
90
|
+
## Tokens in brain templates
|
|
91
|
+
|
|
92
|
+
`templates/brain/` is rendered with `%%TOKEN%%` substitution, **including filenames**, which is
|
|
93
|
+
why the callers are `%%STAFF%%-daily.yaml`.
|
|
94
|
+
|
|
95
|
+
An unfilled token throws rather than being left on the page. A workflow containing a literal
|
|
96
|
+
`%%SCHEDULE%%` is a file GitHub accepts and never runs.
|
|
97
|
+
|
|
98
|
+
A line containing `%%TOKENS%%` is a note to whoever reads the template and is dropped on
|
|
99
|
+
render, along with the `#` separator above it. Every line of a multi-line note needs the
|
|
100
|
+
marker, or the leftover line is stranded.
|
|
101
|
+
|
|
102
|
+
## How the tests are meant to work
|
|
103
|
+
|
|
104
|
+
Three habits, each of which came from a test that was passing vacuously.
|
|
105
|
+
|
|
106
|
+
**Test the harness, not just the code.** The portal tests set a property on a dead object for
|
|
107
|
+
several rounds, because the state they were driving was a top-level `let` in a classic script
|
|
108
|
+
and not reachable as a global. The state is now an exported object, so the tests import the
|
|
109
|
+
real modules under a DOM shim and there is nothing to get wrong.
|
|
110
|
+
|
|
111
|
+
**Run it against reality.** The portal and doctor tests build from the live workspace rather
|
|
112
|
+
than a fixture, so they break when real data grows a shape the code cannot handle. That is how
|
|
113
|
+
the brain-comparison bug was found: every live workflow reported as absent, because filenames
|
|
114
|
+
are templated and the comparison did not render them.
|
|
115
|
+
|
|
116
|
+
**Mutation-test the invariants.** For anything asserting "this behaviour must not regress",
|
|
117
|
+
break it deliberately and check the test fails. The workflow-template tests were verified this
|
|
118
|
+
way, one mutation each.
|
|
119
|
+
|
|
120
|
+
## Adding a command
|
|
121
|
+
|
|
122
|
+
1. `src/commands/<name>.ts`, exporting `<name>Command` and `<name>Help`.
|
|
123
|
+
2. Add both to the tables in `src/cli.ts`.
|
|
124
|
+
3. Document it in `docs/commands.md`. **A test fails if you do not**, and another fails if the
|
|
125
|
+
flags you document are not ones the parser accepts.
|
|
126
|
+
|
|
127
|
+
Prefer plan-then-apply. Every command that changes anything prints what it would do and needs
|
|
128
|
+
`--apply`, and that is the convention that makes them safe to run in someone else's org.
|
|
129
|
+
|
|
130
|
+
## Adding an agent preset
|
|
131
|
+
|
|
132
|
+
`templates/ops/agents.mjs`. Three fields: `install`, `run`, `token_env`. The run command reads
|
|
133
|
+
the prompt from `$AGENT_PROMPT_FILE`.
|
|
134
|
+
|
|
135
|
+
**Read the flags off the tool's own help or docs.** Every preset that ships was verified that
|
|
136
|
+
way, and the tests say which flag exists for which reason. `docs/agents.md` has to quote the
|
|
137
|
+
same install line, and a test checks it does.
|
|
138
|
+
|
|
139
|
+
## Before pushing anything that touches a live org
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
roster prompt cto --kind daily # composed prompt, byte for byte
|
|
143
|
+
roster upgrade # what would change, and where
|
|
144
|
+
roster doctor # is it still wired up
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
A prompt change with no visible diff in the composed output is the only kind that is safe.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "doctor codes"
|
|
3
|
+
description: "Every finding roster doctor can emit, what it means, and what to do."
|
|
4
|
+
sidebar_order: 19
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# doctor codes
|
|
8
|
+
|
|
9
|
+
Every finding `roster doctor` can emit. Each carries a stable `id`, which is what
|
|
10
|
+
`--json` reports and what to quote in an issue.
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
roster doctor --json | jq '.findings[] | select(.level != "ok")'
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Levels: `fail` sets exit code 1, `warn` does not, `ok` is reported so you can see the check
|
|
17
|
+
ran at all.
|
|
18
|
+
|
|
19
|
+
## Workspace
|
|
20
|
+
|
|
21
|
+
| id | Means |
|
|
22
|
+
|---|---|
|
|
23
|
+
| `gh` | Whether `gh` is installed and authenticated. A warning here means every network check was skipped, not that anything is wrong. |
|
|
24
|
+
| `org.yaml` | The org manifest parsed, and how much it declares. |
|
|
25
|
+
| `human` | **fail.** `org.yaml` has no `human.github`. The mention callers gate on that login, so nothing can wake an agent. |
|
|
26
|
+
| `repo` | Every repo in `org.yaml` is reachable. A failure means it does not exist or your `gh` cannot see it. |
|
|
27
|
+
| `repo.visibility` | A repo's real visibility disagrees with what `org.yaml` records. Cosmetic, but the posture it records is then fiction. |
|
|
28
|
+
| `business` | **fail** if `org/business.md` is missing. Every prompt is composed on top of it. |
|
|
29
|
+
| `business.stub` | `org/business.md` is still the questions it shipped with. Nothing errors; the agents just write competent work about a business that does not exist. |
|
|
30
|
+
| `actions-access` | **fail** unless the ops repo is callable from the whole organisation. This is the "workflow not found" trap. See [manual steps](manual-steps.md#1-allow-the-ops-repos-workflow-to-be-called). |
|
|
31
|
+
| `upgrade` | The tenant is in sync with the framework. |
|
|
32
|
+
| `upgrade.stale` | Generated files are behind. `roster upgrade --apply`. |
|
|
33
|
+
| `upgrade.owned` | **fail.** A framework-owned file was edited in the tenant. Move the change upstream or the next upgrade reverts it. |
|
|
34
|
+
| `upgrade.blocked` | A file cannot be merged: either a conflict to resolve, or no recorded base. See [upgrading](upgrading.md). |
|
|
35
|
+
|
|
36
|
+
## Per staff member
|
|
37
|
+
|
|
38
|
+
| id | Means |
|
|
39
|
+
|---|---|
|
|
40
|
+
| `checkout` | **fail.** Their directory is not checked out beside the ops repo, so nothing else could be checked. |
|
|
41
|
+
| `manifest` | **fail.** No `staff.yaml`, or it does not parse. `compose.mjs` reads a small strict YAML subset. |
|
|
42
|
+
| `manifest.handle` | **fail.** `staff.yaml` and `org.yaml` disagree about the handle. The composer looks them up by the `org.yaml` one. |
|
|
43
|
+
| `manifest.brain` | **fail.** No brain repo declared, so no secrets, labels or runs can be checked. |
|
|
44
|
+
| `charter` | **fail** if `CHARTER.md` is absent. Note that a stub counts as present: this checks the file exists, not that it says anything. |
|
|
45
|
+
| `charter.stub` | `CHARTER.md` exists but is still the scaffold. `charter` above only checks the file is there, which a stub is. |
|
|
46
|
+
| `memory` | `memory/INDEX.md` parses, and how many facts and notes it holds. |
|
|
47
|
+
| `compose` | All three prompts compose. A failure names the kind and the placeholder. |
|
|
48
|
+
| `callers` | Three caller workflows exist. |
|
|
49
|
+
| `callers.uses` | **fail.** A caller references no reusable workflow, or one in a different organisation. A private reusable workflow is only callable inside its own org. |
|
|
50
|
+
| `callers.target` | **fail.** A caller points at a workflow file that is not in the ops repo. Fails at run time as "workflow not found". |
|
|
51
|
+
| `surfaces` | A surface declared in `staff.yaml` is not on disk. The portal renders nothing for it. |
|
|
52
|
+
| `secrets` | Every secret the callers reference exists on the brain repo. Derived from the callers themselves, not a fixed list. |
|
|
53
|
+
| `labels` | Every label declared in `staff.yaml` exists. An agent applying a label that does not exist gets an API error mid-run. |
|
|
54
|
+
| `peer-labels` | The `from-<handle>` label exists on the *peer's* tracker, which is where this staff member's asks land. |
|
|
55
|
+
| `status-issue` | The declared status issue is actually pinned. If not, the place you look is not the place the agent maintains. |
|
|
56
|
+
| `runs` | A window of recent runs. See below. |
|
|
57
|
+
| `runs.timeout` | **fail.** Runs were killed at a ceiling. |
|
|
58
|
+
| `runs.cancelled` | Runs were cancelled short of any ceiling, with their durations. |
|
|
59
|
+
|
|
60
|
+
## Reading `runs`
|
|
61
|
+
|
|
62
|
+
This is the only check that proves the whole chain works, so it is worth understanding.
|
|
63
|
+
|
|
64
|
+
- **"has never run"** is a warning, not an `ok`. Nothing has exercised the App grant or the
|
|
65
|
+
secrets, so nothing is known.
|
|
66
|
+
- **"all gated out before doing anything"** means every recent trigger was `skipped`. That is
|
|
67
|
+
normal for a mention workflow, but it means the credentials are still unproven.
|
|
68
|
+
- **"ran to a Nm ceiling and were killed"** is a timeout. GitHub reports those as `cancelled`,
|
|
69
|
+
so doctor identifies them by duration. If the ceiling it names differs from the one the
|
|
70
|
+
caller sets today, it says so: those runs happened under the old setting.
|
|
71
|
+
- **`N/M recent runs succeeded`** is the healthy form.
|
|
72
|
+
|
|
73
|
+
A skipped run is never counted as a failure. Most of a mention workflow's history is skipped
|
|
74
|
+
runs, and counting them would bury the real ones.
|
package/docs/export.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "roster export reference"
|
|
3
|
+
description: "The JSON shape, field by field."
|
|
4
|
+
sidebar_order: 18
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# `roster export` reference
|
|
8
|
+
|
|
9
|
+
The whole org as one JSON document.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
roster export --out org.json
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Rendering is decoupled from parsing on purpose: the portal reads this shape, so anything else
|
|
16
|
+
that reads it gets the same view without reimplementing the memory grammar.
|
|
17
|
+
|
|
18
|
+
## Top level
|
|
19
|
+
|
|
20
|
+
| Field | |
|
|
21
|
+
|---|---|
|
|
22
|
+
| `org` | the GitHub organisation |
|
|
23
|
+
| `name` | the business name |
|
|
24
|
+
| `opsName` | the ops repo's directory, so a consumer can address `org.yaml` and `org/*.md` by path |
|
|
25
|
+
| `human` | the `human` block from `org.yaml` |
|
|
26
|
+
| `generatedAt` | ISO timestamp |
|
|
27
|
+
| `staff[]` | one entry per staff member |
|
|
28
|
+
|
|
29
|
+
## A staff member
|
|
30
|
+
|
|
31
|
+
| Field | |
|
|
32
|
+
|---|---|
|
|
33
|
+
| `handle`, `name`, `dir`, `brain` | identity |
|
|
34
|
+
| `statusIssue` | pinned issue number |
|
|
35
|
+
| `schedule` | cron |
|
|
36
|
+
| `mention` | what wakes them |
|
|
37
|
+
| `bots[]` | every App login, `[bot]` suffix stripped |
|
|
38
|
+
| `soloBots[]` | identities unique to this staff member |
|
|
39
|
+
| `sharedBots[]` | identities shared with others |
|
|
40
|
+
| `worksIn[]` | repos contributed to but not owned |
|
|
41
|
+
| `peers[]` | `{ handle, brain, label }` |
|
|
42
|
+
| `facts[]` | parsed memory |
|
|
43
|
+
| `sections[]` | memory section names, in file order |
|
|
44
|
+
| `notes[]` | filenames under `memory/notes/` |
|
|
45
|
+
| `links[]` | the graph |
|
|
46
|
+
| `problems[]` | lint findings |
|
|
47
|
+
| `surfaces[]` | declared surfaces and their files |
|
|
48
|
+
| `recentCommits[]` | last 25 |
|
|
49
|
+
| `factsChanged[]` | facts added and removed recently |
|
|
50
|
+
| `rig` | scaffolding health |
|
|
51
|
+
|
|
52
|
+
The split between `soloBots` and `sharedBots` matters: a solo identity names one staff member,
|
|
53
|
+
a shared one names only "one of them". Attributing work by author needs both.
|
|
54
|
+
|
|
55
|
+
## `facts[]`
|
|
56
|
+
|
|
57
|
+
| Field | |
|
|
58
|
+
|---|---|
|
|
59
|
+
| `slug` | the identifier, unique within a staff member |
|
|
60
|
+
| `section` | the heading it sits under |
|
|
61
|
+
| `statement` | the fact |
|
|
62
|
+
| `consequence` | the `So:` clause |
|
|
63
|
+
| `line` | line number in `INDEX.md` |
|
|
64
|
+
| `raw` | the source line |
|
|
65
|
+
|
|
66
|
+
## `links[]`
|
|
67
|
+
|
|
68
|
+
| Field | |
|
|
69
|
+
|---|---|
|
|
70
|
+
| `from`, `to` | slug, note filename, issue reference or path |
|
|
71
|
+
| `kind` | `note`, `wikilink`, `mention`, `issue` or `path` |
|
|
72
|
+
| `inferred` | false when authored explicitly, true when derived |
|
|
73
|
+
|
|
74
|
+
## `surfaces[]`
|
|
75
|
+
|
|
76
|
+
| Field | |
|
|
77
|
+
|---|---|
|
|
78
|
+
| `path` | as declared in `staff.yaml` |
|
|
79
|
+
| `render` | `memory`, `doc`, `gallery`, `table` or `code` |
|
|
80
|
+
| `files[]` | `{ path, bytes, modified, ext }` |
|
|
81
|
+
|
|
82
|
+
Only surfaces that exist on disk appear. A declared surface that is missing shows up in
|
|
83
|
+
`rig.missingSurfaces` instead.
|
|
84
|
+
|
|
85
|
+
## `problems[]`
|
|
86
|
+
|
|
87
|
+
| Field | |
|
|
88
|
+
|---|---|
|
|
89
|
+
| `level` | `error` or `warning` |
|
|
90
|
+
| `rule` | see [memory](memory.md) |
|
|
91
|
+
| `message` | |
|
|
92
|
+
| `line` | in `INDEX.md`, when there is one |
|
|
93
|
+
| `slug` | the fact, when there is one |
|
|
94
|
+
|
|
95
|
+
## `rig`
|
|
96
|
+
|
|
97
|
+
| Field | |
|
|
98
|
+
|---|---|
|
|
99
|
+
| `workflows[]` | filenames under `.github/workflows/` |
|
|
100
|
+
| `hasCharter`, `hasManifest` | |
|
|
101
|
+
| `missingSurfaces[]` | declared, not on disk |
|
|
102
|
+
| `memoryBytes` | size of `INDEX.md` |
|
|
103
|
+
| `notesBytes` | total size of `memory/notes/` |
|
|
104
|
+
| `lastCommit` | `{ sha, date, subject, author }` |
|
|
105
|
+
| `lastMemoryCommit` | the last commit touching `memory/` |
|
|
106
|
+
|
|
107
|
+
`lastMemoryCommit` is the one worth watching. A staff member committing daily but not touching
|
|
108
|
+
memory in a fortnight has stopped learning anything.
|
|
109
|
+
|
|
110
|
+
## Stability
|
|
111
|
+
|
|
112
|
+
This shape is not versioned and will change as the portal does. It is a convenience for reading
|
|
113
|
+
your own org, not an API to build a product on.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Extending it"
|
|
3
|
+
description: "The four seams, and which one to reach for."
|
|
4
|
+
sidebar_order: 6
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Extending it for your business
|
|
8
|
+
|
|
9
|
+
Four seams, in the order you are likely to reach for them.
|
|
10
|
+
|
|
11
|
+
## 1. Change how everyone works
|
|
12
|
+
|
|
13
|
+
Edit `org/*.md` in the ops repo. It reaches every staff member on their next run.
|
|
14
|
+
|
|
15
|
+
| File | For |
|
|
16
|
+
|---|---|
|
|
17
|
+
| `business.md` | what the business is. The one everything else is downstream of. |
|
|
18
|
+
| `voice.md` | house style. How anything anyone writes should read. |
|
|
19
|
+
| `guardrails.md` | non-negotiables. What nobody may do, regardless of charter. |
|
|
20
|
+
| `operating.md` | the autonomy contract: the boot ritual, the hand-off, decision rights. |
|
|
21
|
+
|
|
22
|
+
This is the seam that pays. A concision rule here used to mean editing twelve files across two
|
|
23
|
+
repositories; now it is one file, and the next morning everybody has it.
|
|
24
|
+
|
|
25
|
+
Keep the split honest. If a rule would be true of every staff member you will ever hire, it
|
|
26
|
+
belongs here. If it is about one role, it belongs in that role's charter.
|
|
27
|
+
|
|
28
|
+
## 2. Change what a staff member is sent
|
|
29
|
+
|
|
30
|
+
`prompts/` holds the fragments `compose.mjs` assembles.
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
_identity.md who you are posting as, and where
|
|
34
|
+
_paths.md where things are in the runner checkout
|
|
35
|
+
daily.md the scheduled session
|
|
36
|
+
mention.md a focused task from a comment
|
|
37
|
+
pr-mention.md a review comment forwarded from the product repo
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The syntax is small on purpose: `{{ path.to.value }}`, `{{> partial.md }}`,
|
|
41
|
+
`{{>? optional.md }}` and `{{#if path}}...{{/if}}`. Conditionals do not nest.
|
|
42
|
+
|
|
43
|
+
**Guard anything that assumes a manifest field.** `{{staff.product.repo}}` is empty for a staff
|
|
44
|
+
member whose `works_in` is empty, and an unresolved placeholder is a hard error rather than a
|
|
45
|
+
blank. Wrap it:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
{{#if staff.product}}
|
|
49
|
+
... anything that mentions the product repo ...
|
|
50
|
+
{{/if}}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
A staff member can override a fragment for themselves. `{{>? staff:prompts/work.md}}` in
|
|
54
|
+
`daily.md` renders `prompts/work.md` from their own brain repo if it exists, and nothing if it
|
|
55
|
+
does not. That is how one role gets a different working ritual without changing anybody else's.
|
|
56
|
+
|
|
57
|
+
See what you actually built:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
roster prompt cto --kind daily
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## 3. Give a role something new to keep
|
|
64
|
+
|
|
65
|
+
Declare a surface in `staff.yaml`:
|
|
66
|
+
|
|
67
|
+
```yaml
|
|
68
|
+
surfaces:
|
|
69
|
+
- { path: assets/, render: gallery }
|
|
70
|
+
- { path: data/, render: table }
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The portal renders it without knowing what the role is. `render` is one of `memory`, `doc`,
|
|
74
|
+
`gallery`, `table` or `code`.
|
|
75
|
+
|
|
76
|
+
A surface is also a promise: `roster doctor` warns when one is declared and not on disk.
|
|
77
|
+
|
|
78
|
+
## 4. Change what a new hire starts with
|
|
79
|
+
|
|
80
|
+
`templates/brain/` in the framework is what `roster hire` renders. Editing it changes every
|
|
81
|
+
future hire and nothing that already exists.
|
|
82
|
+
|
|
83
|
+
Filenames are rendered too, which is why the callers are `%%STAFF%%-daily.yaml`.
|
|
84
|
+
|
|
85
|
+
Two classes of file, and the difference decides what `roster upgrade` does later:
|
|
86
|
+
|
|
87
|
+
- **The caller workflows** are generated. They track the template forever and are regenerated
|
|
88
|
+
wholesale.
|
|
89
|
+
- **Everything else** is scaffold. It belongs to the staff member from the moment it is
|
|
90
|
+
created, and upgrade only ever adds a new one, never rewrites an existing one. That is why a
|
|
91
|
+
working agent's `CHARTER.md` and `memory/INDEX.md` are safe from you.
|
|
92
|
+
|
|
93
|
+
## What not to extend
|
|
94
|
+
|
|
95
|
+
`compose.mjs`, `agents.mjs`, `runner-plan.mjs` and `session.yaml` are the framework's. Change
|
|
96
|
+
them in the framework and run `roster upgrade`. Editing them in the tenant works until the
|
|
97
|
+
framework touches the same file, and `roster upgrade --check` fails on it for that reason.
|