@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.
Files changed (89) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +129 -0
  3. package/dist/cli.js +5679 -0
  4. package/docs/README.md +99 -0
  5. package/docs/agents.md +163 -0
  6. package/docs/architecture.md +121 -0
  7. package/docs/commands.md +223 -0
  8. package/docs/concepts.md +112 -0
  9. package/docs/cost.md +61 -0
  10. package/docs/developing.md +147 -0
  11. package/docs/doctor-codes.md +74 -0
  12. package/docs/export.md +113 -0
  13. package/docs/extending.md +97 -0
  14. package/docs/getting-started.md +134 -0
  15. package/docs/hosting.md +72 -0
  16. package/docs/manual-steps.md +163 -0
  17. package/docs/memory.md +71 -0
  18. package/docs/org-yaml.md +143 -0
  19. package/docs/portal.md +342 -0
  20. package/docs/prompts.md +133 -0
  21. package/docs/security.md +122 -0
  22. package/docs/session-workflow.md +112 -0
  23. package/docs/staff-yaml.md +163 -0
  24. package/docs/troubleshooting.md +189 -0
  25. package/docs/upgrading.md +83 -0
  26. package/docs/writing-a-charter.md +83 -0
  27. package/package.json +60 -0
  28. package/templates/brain/.github/workflows/%%STAFF%%-daily.yaml +33 -0
  29. package/templates/brain/.github/workflows/%%STAFF%%-mention.yaml +65 -0
  30. package/templates/brain/.github/workflows/%%STAFF%%-pr-mention.yaml +50 -0
  31. package/templates/brain/CHARTER.md +49 -0
  32. package/templates/brain/README.md +18 -0
  33. package/templates/brain/drafts/README.md +7 -0
  34. package/templates/brain/log/decisions.md +6 -0
  35. package/templates/brain/memory/INDEX.md +28 -0
  36. package/templates/brain/staff.yaml +44 -0
  37. package/templates/brain/strategy/README.md +7 -0
  38. package/templates/briefs/amend.md +60 -0
  39. package/templates/briefs/charter.md +47 -0
  40. package/templates/briefs/discover.md +61 -0
  41. package/templates/briefs/voice.md +53 -0
  42. package/templates/ops/.github/workflows/session.yaml +333 -0
  43. package/templates/ops/agents.mjs +143 -0
  44. package/templates/ops/compose.mjs +333 -0
  45. package/templates/ops/org/guardrails.md +14 -0
  46. package/templates/ops/org/operating.md +82 -0
  47. package/templates/ops/org/voice.md +40 -0
  48. package/templates/ops/prompts/_identity.md +14 -0
  49. package/templates/ops/prompts/_paths.md +15 -0
  50. package/templates/ops/prompts/daily.md +82 -0
  51. package/templates/ops/prompts/mention.md +53 -0
  52. package/templates/ops/prompts/pr-mention.md +57 -0
  53. package/templates/ops/runner-plan.mjs +65 -0
  54. package/templates/portal/css/base.css +104 -0
  55. package/templates/portal/css/brain.css +106 -0
  56. package/templates/portal/css/diff.css +28 -0
  57. package/templates/portal/css/graph.css +34 -0
  58. package/templates/portal/css/health.css +41 -0
  59. package/templates/portal/css/inbox.css +79 -0
  60. package/templates/portal/css/layout.css +98 -0
  61. package/templates/portal/css/markdown.css +54 -0
  62. package/templates/portal/css/setup.css +106 -0
  63. package/templates/portal/index.html +55 -0
  64. package/templates/portal/js/api.js +74 -0
  65. package/templates/portal/js/app.js +282 -0
  66. package/templates/portal/js/dialog.js +70 -0
  67. package/templates/portal/js/dom.js +106 -0
  68. package/templates/portal/js/icons.js +94 -0
  69. package/templates/portal/js/md.js +386 -0
  70. package/templates/portal/js/refresh.js +59 -0
  71. package/templates/portal/js/router.js +20 -0
  72. package/templates/portal/js/state.js +160 -0
  73. package/templates/portal/js/textdiff.js +96 -0
  74. package/templates/portal/js/views/app.js +128 -0
  75. package/templates/portal/js/views/brain.js +260 -0
  76. package/templates/portal/js/views/changed.js +157 -0
  77. package/templates/portal/js/views/checklist.js +87 -0
  78. package/templates/portal/js/views/docs.js +84 -0
  79. package/templates/portal/js/views/files.js +95 -0
  80. package/templates/portal/js/views/graph.js +436 -0
  81. package/templates/portal/js/views/health.js +158 -0
  82. package/templates/portal/js/views/inbox.js +549 -0
  83. package/templates/portal/js/views/memory.js +135 -0
  84. package/templates/portal/js/views/org.js +175 -0
  85. package/templates/portal/js/views/paste.js +142 -0
  86. package/templates/portal/js/views/prompt.js +412 -0
  87. package/templates/portal/js/views/repos.js +92 -0
  88. package/templates/portal/js/views/setup.js +344 -0
  89. package/templates/portal/js/views/staff.js +290 -0
@@ -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.