omp-conductor 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +549 -234
- package/package.json +8 -5
- package/schema/config.schema.json +609 -0
- package/src/availability.ts +165 -0
- package/src/board.ts +19 -32
- package/src/brief-upgrade.ts +1 -1
- package/src/briefs/orchestrator.md +72 -31
- package/src/briefs/policy.md +48 -36
- package/src/briefs/probes/gates.md +51 -0
- package/src/briefs/probes/project-context.md +59 -0
- package/src/briefs/probes/release-procedure.md +81 -0
- package/src/cli.ts +356 -212
- package/src/config-schema.ts +352 -0
- package/src/config.ts +1037 -679
- package/src/confinement.ts +54 -0
- package/src/daemon.ts +644 -390
- package/src/diff-flags.ts +73 -4
- package/src/digest-schedule.ts +92 -24
- package/src/escalate.ts +89 -22
- package/src/fleet.ts +351 -46
- package/src/generate-schema.ts +21 -0
- package/src/graph.ts +3 -3
- package/src/host.ts +16 -0
- package/src/omp.ts +21 -1
- package/src/orchestrator-tick.ts +732 -56
- package/src/privileged.ts +264 -0
- package/src/reports.ts +203 -6
- package/src/session-host.ts +3 -0
- package/src/setup-host.ts +209 -24
- package/src/setup-install.ts +320 -0
- package/src/setup-probe.ts +412 -0
- package/src/setup-wizard.ts +1946 -0
- package/src/setup.ts +457 -53
- package/src/store.ts +610 -98
- package/src/tracker/github.ts +43 -5
- package/src/types.ts +153 -14
- package/src/upgrade.ts +44 -10
- package/src/verbs/actions.ts +131 -13
- package/src/verbs/server.ts +40 -18
- package/src/wizard-ui.ts +249 -0
- package/src/worker.ts +24 -7
- package/skills/conductor-onboarding/SKILL.md +0 -748
- package/skills/conductor-update/SKILL.md +0 -51
- package/src/plugin.ts +0 -1495
|
@@ -1,748 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: conductor-onboarding
|
|
3
|
-
description: Interview-driven onboarding for omp-conductor. Use when the user wants to set up conductor, onboard a new fleet or project, configure the fleet, asks for conductor setup help, asks what belongs in POLICY.md / ORCHESTRATOR.md, or wants an agent's release and merge authority scoped and written down. Interviews the operator on release policy, escalation taste and reporting scope, reads each routing repo's CI to propose the real pre-push gates, learns the product and roadmap the fleet will groom, scaffolds the release procedure from the repo's own release workflows rather than from the operator's memory, tailors POLICY.md (and the composed ORCHESTRATOR.md view) from the shipped templates, verifies the worker brief's assumptions against the actual repos, finishes through the deterministic /conductor setup wizard, then builds the code-graph indexes workers query instead of grepping.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Onboarding a conductor fleet
|
|
7
|
-
|
|
8
|
-
Onboarding has two layers, and they are not the same job.
|
|
9
|
-
|
|
10
|
-
- **`/conductor setup` is the mechanical layer.** It is deterministic, tested, and
|
|
11
|
-
the **only** thing that writes `config.json`. It asks closed questions, plans
|
|
12
|
-
labels, prints a dry run, and mutates nothing until the operator confirms.
|
|
13
|
-
- **You are the judgement layer.** The wizard cannot ask "where should this fleet
|
|
14
|
-
stop?" and get a useful answer from a text prompt, and it cannot read a repo to
|
|
15
|
-
find out what CI actually runs. That is the part that decides whether an
|
|
16
|
-
unattended fleet is safe, and it is yours.
|
|
17
|
-
|
|
18
|
-
So: **you interview and investigate; the wizard writes.** Never hand-edit
|
|
19
|
-
`config.json`, never construct it and ask for a blessing, never skip the wizard's
|
|
20
|
-
dry run. Its consent gate is the safety property; going around it removes the one
|
|
21
|
-
step where the operator sees what is about to change.
|
|
22
|
-
|
|
23
|
-
Two more ground rules before you start.
|
|
24
|
-
|
|
25
|
-
**Do not recite this file.** Nothing here is a script to read aloud. Every
|
|
26
|
-
question below exists to extract one decision; ask it in your own words, in the
|
|
27
|
-
operator's vocabulary, and skip the ones the repo already answers. An interview
|
|
28
|
-
where you ask and they answer is worth more than a checklist you both step
|
|
29
|
-
through.
|
|
30
|
-
|
|
31
|
-
**Investigate before you ask.** Anything discoverable — default branches, CI
|
|
32
|
-
jobs, gate commands, whether a repo has a `Makefile` — you read, then propose.
|
|
33
|
-
Asking an operator to recite their own CI config is how gates end up wrong: they
|
|
34
|
-
tell you what they *think* runs, and the fleet pushes on it at 03:00.
|
|
35
|
-
|
|
36
|
-
---
|
|
37
|
-
|
|
38
|
-
## Step 0 — orient
|
|
39
|
-
|
|
40
|
-
Before the first question, find out where you are:
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
gh auth status # repo + project scopes, and who the token is
|
|
44
|
-
cat ~/.omp/conductor/config.json # honours $OMP_CONDUCTOR_HOME
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
- **Config already there?** This is a re-run or a second project. The wizard
|
|
48
|
-
pre-fills from the existing project and replaces it in place, keeping its
|
|
49
|
-
neighbours — so the interview is now about *what changed*, not everything.
|
|
50
|
-
Read the existing project out loud and ask what is wrong with it.
|
|
51
|
-
- **`gh` missing `repo` or `project`?** Say so now. The wizard warns at the
|
|
52
|
-
confirm, but a token that cannot label issues means every claim fails, and it
|
|
53
|
-
is cheaper to fix before the interview than after.
|
|
54
|
-
- **A `POLICY.md` or `ORCHESTRATOR.md` already exists** under `<workspaceRoot>/`
|
|
55
|
-
(default `~/.omp/conductor/worktrees/`)? Read `POLICY.md` first when present —
|
|
56
|
-
that is the editable fleet policy. Otherwise read `ORCHESTRATOR.md`. It is the
|
|
57
|
-
operator's accumulated policy, and it outranks the shipped template. Your job
|
|
58
|
-
becomes amending it, and you must warn that a wizard re-run offers to overwrite
|
|
59
|
-
it.
|
|
60
|
-
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
## Step 1 — the interview
|
|
64
|
-
|
|
65
|
-
Ask for decisions, not for values you could look up. Each question below carries
|
|
66
|
-
the reason it matters; when you ask it, lead with that reason. An operator who
|
|
67
|
-
understands why a question is being asked gives a usable answer.
|
|
68
|
-
|
|
69
|
-
### Project and tracker
|
|
70
|
-
|
|
71
|
-
**"What is this fleet called, and which one repo holds the queue?"**
|
|
72
|
-
|
|
73
|
-
*Why:* the project name is the handle for everything afterwards
|
|
74
|
-
(`/conductor status <name>`, `--project <name>`), and the tracker is the **one**
|
|
75
|
-
`owner/repo` whose open-issue list *is* the queue. Nothing else is ever read.
|
|
76
|
-
|
|
77
|
-
Say the part that surprises people: **the tracker repo does not have to contain
|
|
78
|
-
any code.** A planning repo whose only content is issues is the normal shape, and
|
|
79
|
-
it is often the better one — the queue then has its own label namespace and its
|
|
80
|
-
own permissions, separate from the code it dispatches into.
|
|
81
|
-
|
|
82
|
-
Then: **"What label means a human has signed this off?"** Default
|
|
83
|
-
`ready-for-agent`. *Why:* this label is the entire consent mechanism for
|
|
84
|
-
unattended work. The conductor never adds it. Everything the fleet ever does
|
|
85
|
-
starts with a human putting that label on an issue, so it should read like a
|
|
86
|
-
decision, not like a status.
|
|
87
|
-
|
|
88
|
-
The three state labels (`agent:in-progress`, `agent:blocked`, `agent:failed`) are
|
|
89
|
-
written *by* the conductor so the tracker alone shows live state. Only ask about
|
|
90
|
-
renaming them if the repo already has a colliding label convention — the
|
|
91
|
-
namespaced defaults are right almost always, and the wizard asks with one confirm.
|
|
92
|
-
|
|
93
|
-
### Routing — what lands where
|
|
94
|
-
|
|
95
|
-
**"Which code repos does this queue dispatch into, and how would you tell them
|
|
96
|
-
apart on an issue?"**
|
|
97
|
-
|
|
98
|
-
*Why:* an issue must carry **exactly one** `repo:<name>` label naming a repo in
|
|
99
|
-
`routing.repos`. Zero or two is reported unroutable and skipped — never guessed.
|
|
100
|
-
Routing is the fan-out: one tracker, any number of code repos.
|
|
101
|
-
|
|
102
|
-
For each repo you need the clone URL and the default branch, but **look those up**
|
|
103
|
-
rather than asking (Step 2). What you actually need from the operator is the
|
|
104
|
-
routing *key* — the word that goes after the prefix — and it should be the word
|
|
105
|
-
they already use in conversation about that repo, because they are the one who
|
|
106
|
-
will be typing it onto issues at 2am.
|
|
107
|
-
|
|
108
|
-
Prefer an SSH clone URL, or an https URL backed by a credential helper. A URL
|
|
109
|
-
with a credential in it gets persisted into the mirror's git config exactly as it
|
|
110
|
-
would for a hand-run clone; say that out loud if you see one.
|
|
111
|
-
|
|
112
|
-
### Release policy — the question that actually matters
|
|
113
|
-
|
|
114
|
-
This is the decision the shipped brief exists to protect, so give it room.
|
|
115
|
-
|
|
116
|
-
First, settle who is even a candidate, because operators assume it is the workers
|
|
117
|
-
and it never is. A worker is scoped to one issue: it cannot judge whether a release
|
|
118
|
-
is worth cutting, and it stops at a green PR permanently. **The only two candidates
|
|
119
|
-
are the orchestrator and a human.** The orchestrator is the right agent for it if
|
|
120
|
-
any agent is: it is long-lived, it can see everything that merged since the last
|
|
121
|
-
release, and batching is exactly the judgement a per-issue session cannot make.
|
|
122
|
-
|
|
123
|
-
So frame it as **three** options, in this order, and name the default. Each one
|
|
124
|
-
maps to two independent wizard answers:
|
|
125
|
-
|
|
126
|
-
- `authority.release` says **who owns the release decision**;
|
|
127
|
-
- `releasePolicy` is the mechanical tool gate that says whether release-shaped
|
|
128
|
-
commands may run at all.
|
|
129
|
-
|
|
130
|
-
The interview decides both answers, and `/conductor setup` records them. Never
|
|
131
|
-
write either value into the brief or hand-edit `config.json`: config is the
|
|
132
|
-
single source, and the wizard's dry run is where the operator sees the combined
|
|
133
|
-
grant.
|
|
134
|
-
|
|
135
|
-
1. **Humans release.** *(default, and what the package ships)* Work ends at a
|
|
136
|
-
green PR. Merging is a separate decision; releasing is a separate human
|
|
137
|
-
action. Neither a worker nor the orchestrator tags, pins, publishes, or
|
|
138
|
-
deploys. "This needs releasing" becomes something the orchestrator reports.
|
|
139
|
-
→ `authority.release=human`, `releasePolicy=none`; answer **no** to both
|
|
140
|
-
release confirms.
|
|
141
|
-
2. **The orchestrator releases up to a named boundary.** Delegate release
|
|
142
|
-
authority only when its leg includes the first release action (for example,
|
|
143
|
-
creating a tag or dispatching a release workflow). → usually
|
|
144
|
-
`authority.release=orchestrator`, `releasePolicy=operator-brief`, with the
|
|
145
|
-
boundary in the procedure rather than in the grant. If its leg ends before
|
|
146
|
-
that action, use `authority.release=human`, `releasePolicy=none`.
|
|
147
|
-
3. **The orchestrator releases fully.** →
|
|
148
|
-
`authority.release=orchestrator`, `releasePolicy=operator-brief`.
|
|
149
|
-
|
|
150
|
-
Merge authority is its own confirm in every option. Also say what opening the
|
|
151
|
-
tool gate means: it is project-wide and is passed to worker sessions as well as
|
|
152
|
-
the orchestrator. It does not grant workers release authority — their brief
|
|
153
|
-
still stops them at a green PR — but it removes the mechanical block. Never open
|
|
154
|
-
it until the operator has reviewed the release procedure and stopping boundary.
|
|
155
|
-
|
|
156
|
-
Then ask the question that makes option 2 real:
|
|
157
|
-
|
|
158
|
-
> **"Where does the orchestrator's leg END?"**
|
|
159
|
-
|
|
160
|
-
Not "can it release" — *where does its leg stop*. A boundary you cannot state in
|
|
161
|
-
one sentence is not a boundary, and an orchestrator with a vague release mandate
|
|
162
|
-
is one that eventually publishes something at 03:00.
|
|
163
|
-
|
|
164
|
-
Give them a worked answer so they can calibrate. One real deployment's answer:
|
|
165
|
-
**the agent's leg ends at the merged version pin.** It may open and land the PR that
|
|
166
|
-
bumps the module image tags, because that artefact is reviewable, reversible by a
|
|
167
|
-
revert, and its correctness is checkable by a named CI check. Deploying that pin
|
|
168
|
-
to prod is **operator territory** — it needs a person who can watch it, and who
|
|
169
|
-
owns the rollback. That is a leg with an end: a merge commit.
|
|
170
|
-
|
|
171
|
-
If they choose option 2, capture all five of these, because the brief needs them
|
|
172
|
-
and a missing one is a hole:
|
|
173
|
-
|
|
174
|
-
- **what** may be released — which packages or images, from which branch;
|
|
175
|
-
- **when** — batched how, after which *named* checks are green;
|
|
176
|
-
- **what proof** must be held first — check results actually read, not an
|
|
177
|
-
impression;
|
|
178
|
-
- **what must still be asked, every time**;
|
|
179
|
-
- **what stays permanently forbidden** — force-push, secrets, production data.
|
|
180
|
-
|
|
181
|
-
If they choose option 3, do not just write it down. Warn, concretely:
|
|
182
|
-
|
|
183
|
-
- **Credentials.** Full release means the session holds publish tokens, registry
|
|
184
|
-
credentials, or deploy keys. Those live in the session's environment, exposed to
|
|
185
|
-
every turn, including a turn that went wrong. Ask whether they are willing to
|
|
186
|
-
put that credential in a process that runs unattended for weeks.
|
|
187
|
-
- **Rollback ownership.** An agent that can release owns the 03:00 rollback too,
|
|
188
|
-
and a rollback is a judgement call under time pressure with partial
|
|
189
|
-
information — the exact thing agents are worst at. If the answer to "who rolls
|
|
190
|
-
this back" is a person, then that person is already the release owner, and
|
|
191
|
-
option 2 with a named boundary is the honest configuration.
|
|
192
|
-
|
|
193
|
-
Push back once if the answer is option 3 and the reasoning is "it'll be fine".
|
|
194
|
-
Then record what they decide. It is their fleet.
|
|
195
|
-
|
|
196
|
-
**Then stop, and do not write the section yet.** What you have is the operator's
|
|
197
|
-
*intent*: how much they want to delegate, and where they want the line. The
|
|
198
|
-
*steps* on their side of that line come from the repo's own release machinery, not
|
|
199
|
-
from this conversation. Step 4 reads that machinery and turns the intent into a
|
|
200
|
-
procedure. Writing release steps from an interview answer is how a brief ends up
|
|
201
|
-
prescribing a hand-rolled release the repo's own policy forbids.
|
|
202
|
-
|
|
203
|
-
### Escalation taste
|
|
204
|
-
|
|
205
|
-
**"What should wake you up, and what can wait for the digest?"**
|
|
206
|
-
|
|
207
|
-
*Why:* there are two tiers, and the split is not adjustable — tier 1 is a worker
|
|
208
|
-
that stopped and asked a question (the orchestrator answers it), tier 2 is
|
|
209
|
-
"nobody can proceed without a human". What *is* adjustable is how tier 2 reaches
|
|
210
|
-
them and how much else comes along with it. An operator who gets paged for
|
|
211
|
-
everything stops reading the pages, which is functionally the same as having no
|
|
212
|
-
escalation channel at all.
|
|
213
|
-
|
|
214
|
-
Concretely, decide:
|
|
215
|
-
|
|
216
|
-
- **Channel.** If `omp-telegram` is installed and paired, the wizard offers that
|
|
217
|
-
chat. If not, tier 2 degrades to an issue comment — a documented fallback, not
|
|
218
|
-
an error, but say plainly that a comment on an issue nobody is watching is a
|
|
219
|
-
page that lands nowhere.
|
|
220
|
-
- **Belt and braces.** Also comment on the issue when a run escalates?
|
|
221
|
-
Recommended yes: a chat message you miss is a run nobody sees, and the comment
|
|
222
|
-
is the durable copy.
|
|
223
|
-
- **Attempts before escalating** (`maxAttemptsPerIssue`, default 3). *Why:* an
|
|
224
|
-
issue that failed three times is not a retry candidate — it is a diagnosis
|
|
225
|
-
task. Setting this high converts a bad issue into a spend line.
|
|
226
|
-
|
|
227
|
-
### Reporting scope
|
|
228
|
-
|
|
229
|
-
**"Do you want to hear about progress, or only about problems?"**
|
|
230
|
-
|
|
231
|
-
*Why:* this is the one half of the brief that the *config* also knows about,
|
|
232
|
-
because every orchestrator tick appends the matching constraint line to its
|
|
233
|
-
prompt, re-read from the config each tick. It maps onto exactly two values:
|
|
234
|
-
|
|
235
|
-
| Answer | `reporting.scope` | What that means |
|
|
236
|
-
| --- | --- | --- |
|
|
237
|
-
| "Tell me when things happen." | `material` *(default)* | Escalations, plus every material event as it happens: a run reaching a green PR (with link), a run that failed twice, an issue pulled off the queue, a cap that stopped the fleet. |
|
|
238
|
-
| "Only bother me when I'm needed." | `escalations` | Every tier-2 escalation immediately, plus one daily digest. Every other tick silent. |
|
|
239
|
-
|
|
240
|
-
Two honesty notes to pass on:
|
|
241
|
-
|
|
242
|
-
- **Neither scope is an outbound filter.** Nothing inspects the orchestrator's
|
|
243
|
-
messages and drops the ones the scope did not ask for. It is a constraint handed
|
|
244
|
-
to the model each turn, not a gate it is held to.
|
|
245
|
-
- **Changing the scope later does not rewrite an existing `POLICY.md`.** The
|
|
246
|
-
tick line changes; the brief does not. Whoever changes it must edit the brief's
|
|
247
|
-
Reporting section too, or the session is carrying two versions of the policy.
|
|
248
|
-
|
|
249
|
-
---
|
|
250
|
-
|
|
251
|
-
## Step 2 — read the repos, then propose the gates
|
|
252
|
-
|
|
253
|
-
**Do this before you ask anything about gates.** The wizard's gate prompt is a
|
|
254
|
-
free-text field; whatever goes in it is what an unattended worker runs before
|
|
255
|
-
every push. It has to match what CI actually runs, and the operator is not the
|
|
256
|
-
best source for that. Their CI config is.
|
|
257
|
-
|
|
258
|
-
For each routing repo, mirror it read-only if you do not have it locally, then
|
|
259
|
-
read:
|
|
260
|
-
|
|
261
|
-
```bash
|
|
262
|
-
gh api repos/<owner>/<repo> --jq '.default_branch'
|
|
263
|
-
gh api repos/<owner>/<repo>/contents/.github/workflows --jq '.[].name'
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
and then, in the checkout:
|
|
267
|
-
|
|
268
|
-
- `.github/workflows/*.yml` — the PR-triggered jobs and their exact `run:` lines,
|
|
269
|
-
including their `working-directory`;
|
|
270
|
-
- `package.json` — the `scripts` block (`lint`, `check`, `typecheck`, `test`);
|
|
271
|
-
- `Makefile` / `justfile` / `Taskfile.yml` — many repos put the real gate here and
|
|
272
|
-
have CI call `make check`;
|
|
273
|
-
- `pyproject.toml`, `Cargo.toml`, `go.mod` — for the same reason, in other
|
|
274
|
-
ecosystems;
|
|
275
|
-
- any `CONTRIBUTING.md` / `AGENTS.md` line that names the pre-push command.
|
|
276
|
-
|
|
277
|
-
Then **propose**, do not ask. Show your reading and the exact commands, with the
|
|
278
|
-
`cwd` each runs from, in the format the wizard takes (`cmd`, or `cmd @ cwd`,
|
|
279
|
-
comma-separated; `cwd` defaults to `.`):
|
|
280
|
-
|
|
281
|
-
```text
|
|
282
|
-
repo:api — from .github/workflows/ci.yml (on: pull_request), jobs lint + test:
|
|
283
|
-
bun run lint @ . # CI: `bun run lint` at repo root, whole tree
|
|
284
|
-
bun run check @ . # CI: tsc --noEmit
|
|
285
|
-
bun test @ . # CI: bun test
|
|
286
|
-
|
|
287
|
-
Is that the set, and is anything missing that CI would catch?
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
Three warnings to carry, every time:
|
|
291
|
-
|
|
292
|
-
1. **Whole tree, not the subdirectory.** CI lints everything. A gate that lints
|
|
293
|
-
only `src/` is how an error in a migration, a config file, a script or a test
|
|
294
|
-
fixture reaches the runners. If CI runs `bun run lint` at the root, the gate is
|
|
295
|
-
`bun run lint` at the root — not `bun run lint src`.
|
|
296
|
-
2. **Cheap gates only.** Do not put docker builds, image builds, production
|
|
297
|
-
builds, browser/e2e suites, or a full integration suite in here. The host is
|
|
298
|
-
shared; CI owns the heavy gates. If a repo's only meaningful check is heavy,
|
|
299
|
-
say so — that repo's workers will lean on CI, and that is a known cost, not
|
|
300
|
-
something to paper over with a fake gate.
|
|
301
|
-
3. **No gates at all is a real answer, and a loud one.** The wizard warns. Repeat
|
|
302
|
-
the warning in plain terms: a repo with no gates means every unattended push
|
|
303
|
-
is an experiment run on the shared runners.
|
|
304
|
-
|
|
305
|
-
---
|
|
306
|
-
|
|
307
|
-
## Step 3 — learn the product
|
|
308
|
-
|
|
309
|
-
The duties assume an orchestrator that understands what it is grooming. Duty 2
|
|
310
|
-
asks it to judge whether acceptance criteria are readable and whether an issue is
|
|
311
|
-
worth claiming at all; neither is answerable by a session that knows only the repo
|
|
312
|
-
names. So learn the product now, and write what you learn into the brief.
|
|
313
|
-
|
|
314
|
-
Read, per routing repo:
|
|
315
|
-
|
|
316
|
-
- the `README` — what the thing is for, and who uses it;
|
|
317
|
-
- whatever top-level architecture doc exists (`docs/`, `ARCHITECTURE.md`, an ADR
|
|
318
|
-
directory), enough to say which repo owns which concern;
|
|
319
|
-
- `AGENTS.md` / `CONTRIBUTING.md` for the repo's own rules, which outrank the
|
|
320
|
-
brief.
|
|
321
|
-
|
|
322
|
-
Then read the tracker as a roadmap rather than as a queue:
|
|
323
|
-
|
|
324
|
-
```bash
|
|
325
|
-
gh issue list --repo <tracker> --state open --limit 100 --json number,title,labels,milestone
|
|
326
|
-
gh api repos/<tracker>/milestones --jq '.[] | "\(.title) — \(.open_issues) open"'
|
|
327
|
-
gh label list --repo <tracker>
|
|
328
|
-
```
|
|
329
|
-
|
|
330
|
-
Milestones and the label taxonomy are what separate a theme from a one-off. Epics
|
|
331
|
-
usually surface as one or the other.
|
|
332
|
-
|
|
333
|
-
Then ask the operator exactly one question, because it is the one thing none of
|
|
334
|
-
that reading answers:
|
|
335
|
-
|
|
336
|
-
> **"Where does the roadmap live, and what is the current priority?"**
|
|
337
|
-
|
|
338
|
-
*Why:* a tracker shows what is open, never what matters. An orchestrator that
|
|
339
|
-
cannot rank work grooms by recency, and that is how a stale issue outranks the
|
|
340
|
-
thing the operator is actually shipping this month.
|
|
341
|
-
|
|
342
|
-
**Write the findings into the brief** as a `## Project context` section, in the
|
|
343
|
-
editable half beside Releases — the template ships a stub for it. Keep it under 40
|
|
344
|
-
lines: it is read on every tick, and a brief nobody finishes reading is a brief
|
|
345
|
-
that gets skimmed. It needs four things.
|
|
346
|
-
|
|
347
|
-
1. **The product in one paragraph.** What it does, for whom. Not a feature list.
|
|
348
|
-
2. **A repo map** — one line per routing key, naming what that repo owns in the
|
|
349
|
-
operator's vocabulary. This is what turns a routing label into a judgement.
|
|
350
|
-
3. **Grooming guidance for Duty 2.** Which repos ship together, so a change in one
|
|
351
|
-
is known to need a matching PR in the other. And which kinds of issue touch the
|
|
352
|
-
same files: those must not be queued concurrently, because two workers editing
|
|
353
|
-
one file produce two PRs that cannot both merge.
|
|
354
|
-
4. **Where the roadmap lives, and how to judge priority against it** — the pointer
|
|
355
|
-
the operator just gave you, in one line.
|
|
356
|
-
|
|
357
|
-
Same order of operations as Step 5: draft it now, and apply it to the brief after
|
|
358
|
-
the wizard has written the file.
|
|
359
|
-
|
|
360
|
-
---
|
|
361
|
-
|
|
362
|
-
## Step 4 — scaffold the release process from the repo, not from memory
|
|
363
|
-
|
|
364
|
-
The interview gave you a boundary. This step turns it into steps the operator's
|
|
365
|
-
repos will actually accept, for the same reason Step 2 reads CI instead of asking
|
|
366
|
-
about gates: an operator describes the release they *remember*, and a release is
|
|
367
|
-
the one procedure where being approximately right is worst.
|
|
368
|
-
|
|
369
|
-
Skip this step only for option 1 (humans release). There is nothing to scaffold:
|
|
370
|
-
the rendered paragraph already says humans hold both, and there is no procedure
|
|
371
|
-
under it to write.
|
|
372
|
-
|
|
373
|
-
### Find the release authority
|
|
374
|
-
|
|
375
|
-
Per repo that can be released, read:
|
|
376
|
-
|
|
377
|
-
```bash
|
|
378
|
-
gh api repos/<owner>/<repo>/contents/.github/workflows --jq '.[].name'
|
|
379
|
-
gh release list --repo <owner>/<repo> --limit 10
|
|
380
|
-
git tag --list --sort=-v:refname | head
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
and then, in the checkout:
|
|
384
|
-
|
|
385
|
-
- **every workflow that publishes anything** — a release, a tag, an image, a
|
|
386
|
-
package, a deployment. For each, the **trigger** is the fact that matters:
|
|
387
|
-
- `workflow_dispatch` → the release is *dispatched*. That workflow is the
|
|
388
|
-
authority, and the correct instruction is "dispatch it with the planned
|
|
389
|
-
version", never "do what it does".
|
|
390
|
-
- `on: push: tags:` → a pushed tag is the trigger, so tagging *is* releasing.
|
|
391
|
-
- `on: release: published` → the GitHub Release is the trigger.
|
|
392
|
-
- **what the workflow enforces.** Many reject a tag unless several version files
|
|
393
|
-
agree. That constraint belongs in the brief, quoted, because it is the failure
|
|
394
|
-
the agent will otherwise hit at 03:00.
|
|
395
|
-
- **`AGENTS.md`, `CONTRIBUTING.md`, and any release runbook.** An explicit policy
|
|
396
|
-
outranks anything you infer from a workflow, and it is usually where the
|
|
397
|
-
*forbidden* paths are named.
|
|
398
|
-
- **whether a release needs a human by construction.** An `npm publish` behind
|
|
399
|
-
interactive 2FA cannot be delegated to an unattended session at all, whatever the
|
|
400
|
-
operator would like. Say so rather than writing a step that cannot run.
|
|
401
|
-
|
|
402
|
-
### Then write three things, and one of them is the forbidden list
|
|
403
|
-
|
|
404
|
-
Present this back as a proposal, in the repo's own commands, before it goes in the
|
|
405
|
-
brief:
|
|
406
|
-
|
|
407
|
-
1. **The authority, named.** One sentence: which workflow or command ships this
|
|
408
|
-
repo, and how it is invoked. If it is a protected workflow, the brief says
|
|
409
|
-
*dispatch it and verify the run* and stops there.
|
|
410
|
-
2. **The steps on the agent's side of the boundary**, as commands, in order, each
|
|
411
|
-
with the check that proves it worked. A step whose success cannot be read from
|
|
412
|
-
a named check is not a step the agent can own.
|
|
413
|
-
3. **What is forbidden, and why, with the citation.** This is the part that decays
|
|
414
|
-
silently, so it is the part to write down hardest. Cite the file and the line:
|
|
415
|
-
`never mutate the deployment directly or reimplement the release by hand
|
|
416
|
-
(repos/<repo>/AGENTS.md)`. A forbidden path with a source attached survives a
|
|
417
|
-
future session's improvisation; "be careful with releases" does not.
|
|
418
|
-
|
|
419
|
-
### Ask two things the repo cannot tell you
|
|
420
|
-
|
|
421
|
-
- **"What is a release worth cutting?"** The batching unit, in the repo's own
|
|
422
|
-
vocabulary: a sprint, an epic's children all closed, N merged issues waiting, N
|
|
423
|
-
days elapsed, or urgency. Without this the orchestrator either releases per merge
|
|
424
|
-
(a stream of meaningless versions burning shared runners) or never releases at
|
|
425
|
-
all.
|
|
426
|
-
- **"Who owns the rollback?"** If the answer is a person, that person owns the
|
|
427
|
-
release, and the boundary belongs before the irreversible step regardless of
|
|
428
|
-
what option 2 sounded like in the interview.
|
|
429
|
-
|
|
430
|
-
### And make it self-correcting
|
|
431
|
-
|
|
432
|
-
A release process is the section most likely to go stale: workflows get replaced,
|
|
433
|
-
and a brief describing the old one still reads plausible. So tell the operator
|
|
434
|
-
plainly, and make sure the brief's own **Learning loop** covers it: when the
|
|
435
|
-
release workflow changes, the brief contradicts repo reality, which is exactly an
|
|
436
|
-
amendment trigger. The session proposes the corrected steps and they approve with
|
|
437
|
-
a yes.
|
|
438
|
-
|
|
439
|
-
---
|
|
440
|
-
|
|
441
|
-
## Step 5 — tailor the brief, and show the diff first
|
|
442
|
-
|
|
443
|
-
`src/briefs/orchestrator.md` in this package is the **floor**, not the deliverable.
|
|
444
|
-
It ships deliberately conservative so that an operator who never edits it still
|
|
445
|
-
has a safe fleet. Your job is to raise it to *this* fleet.
|
|
446
|
-
|
|
447
|
-
The file has a hard line in it — an HTML comment banner reading
|
|
448
|
-
`YOURS TO EDIT`. Respect it in both directions:
|
|
449
|
-
|
|
450
|
-
**Above the banner — leave it alone.** The three duties — drain (unstick what is
|
|
451
|
-
stuck), groom (keep the queue worth draining), report — the
|
|
452
|
-
escalation-tier table, and the hard boundaries describe how the package already
|
|
453
|
-
behaves. Rewriting them makes the brief disagree with the code, and the code wins.
|
|
454
|
-
The evidence rule in particular is not negotiable: *every claim cites evidence — a
|
|
455
|
-
PR URL, an issue number, or a named check actually read.* "Should be fine",
|
|
456
|
-
"looks green" and "probably passing" are not evidence.
|
|
457
|
-
|
|
458
|
-
Know what is *not* up there, though, because operators expect it to be: the
|
|
459
|
-
orchestrator's merge and release authority is **not** a hard boundary. It comes
|
|
460
|
-
from the two `authority` answers in config. Release execution has a second,
|
|
461
|
-
independent config control: `releasePolicy=none` mechanically blocks tags,
|
|
462
|
-
publishing, releases, and deploy commands even when
|
|
463
|
-
`authority.release=orchestrator`; `operator-brief` opens that gate for the
|
|
464
|
-
reviewed procedure. What is fixed above the banner is that a *worker* never
|
|
465
|
-
merges or releases, and that PRs land one at a time with a freshness re-check.
|
|
466
|
-
Changing either release control needs no negotiation with the shipped half, but
|
|
467
|
-
both must agree with the procedure below the banner.
|
|
468
|
-
|
|
469
|
-
**Below the banner — rewrite from the interview.**
|
|
470
|
-
|
|
471
|
-
- **Releases.** The section's opening paragraph is rendered from the `authority`
|
|
472
|
-
answers, and Duty 1's "the PR is green" branch is rendered from the same
|
|
473
|
-
place. Leave both alone: they are the config speaking, and re-running
|
|
474
|
-
`/conductor setup` is how they change. What you write is everything under that
|
|
475
|
-
paragraph — the procedure you scaffolded in Step 4. If humans-release, there is
|
|
476
|
-
nothing to scaffold and you stop. If they chose a boundary, write the boundary
|
|
477
|
-
as a sentence with an end — *"your leg ends at the merged pin PR; you never
|
|
478
|
-
deploy it"* — then the scaffolded steps, then the forbidden list with its
|
|
479
|
-
citations. Do not restate the merge grant in your own words: a second spelling
|
|
480
|
-
of it is exactly the disagreement the rendered paragraph exists to prevent.
|
|
481
|
-
- **Reporting.** Rewrite it as the one scope they chose, in the second person,
|
|
482
|
-
concretely. Delete the description of the scope they did not choose: it is
|
|
483
|
-
useful in a template and noise in a live prompt. Keep the closing constraint
|
|
484
|
-
verbatim in substance — no narration, no progress updates, no restating the
|
|
485
|
-
brief back. Evidence, or silence.
|
|
486
|
-
|
|
487
|
-
**Then add what the template cannot know: this fleet's own hard boundaries.** Ask
|
|
488
|
-
for them directly:
|
|
489
|
-
|
|
490
|
-
> **"Is there anywhere in these repos an agent must never go, even when an issue
|
|
491
|
-
> says to?"**
|
|
492
|
-
|
|
493
|
-
Typical answers, worth prompting for by name: infrastructure directories
|
|
494
|
-
(`terraform/`, `puppet/`, `ansible/`, `charts/`, `.github/workflows/`), anything
|
|
495
|
-
holding secrets or environment files, migration directories, a vendored or
|
|
496
|
-
generated tree, and **whole repos** that are in the org but off-limits. Write each
|
|
497
|
-
one as a path or repo name, not as a category — "no infra changes" is advice, and
|
|
498
|
-
`never edit terraform/ or .github/workflows/; escalate instead` is a boundary.
|
|
499
|
-
|
|
500
|
-
**Show the diff before writing anything.** Present the tailored sections against
|
|
501
|
-
the shipped template — the sections you rewrote, the boundaries you added, and an
|
|
502
|
-
explicit statement that everything above the banner is untouched. Get an
|
|
503
|
-
acknowledgement. This file becomes the standing prompt for a session that runs
|
|
504
|
-
unattended for weeks; the operator reading it once, now, is the cheapest review it
|
|
505
|
-
will ever get.
|
|
506
|
-
|
|
507
|
-
**Order of operations matters here.** The wizard writes the *template* (with
|
|
508
|
-
coordinates substituted) at the path it owns. So: draft and agree the tailored
|
|
509
|
-
sections now, let the wizard write the rendered floor in Step 7, and apply the
|
|
510
|
-
agreed edits to that file immediately afterwards. Do not pre-write the file to a
|
|
511
|
-
path you guessed, and do not skip the wizard's brief-writing step — you would
|
|
512
|
-
lose the substituted coordinates and the overwrite confirmation.
|
|
513
|
-
|
|
514
|
-
---
|
|
515
|
-
|
|
516
|
-
## Step 6 — check the worker brief's assumptions against reality
|
|
517
|
-
|
|
518
|
-
The worker brief makes concrete claims to a session that has no other context. If
|
|
519
|
-
a claim is wrong, the worker cannot tell — it just fails in a confusing way. Check
|
|
520
|
-
each one against the actual repos, and report what you found.
|
|
521
|
-
|
|
522
|
-
1. **Default branches.** The brief says the worktree is cut from the repo's
|
|
523
|
-
default branch and the PR targets it. Confirm per repo with
|
|
524
|
-
`gh api repos/<owner>/<repo> --jq .default_branch`. A repo on `master`, `develop`
|
|
525
|
-
or `trunk` configured as `main` produces a run that fails at worktree creation.
|
|
526
|
-
2. **Branch naming.** The conductor cuts `<type>/<slug>`, where type is `fix` when
|
|
527
|
-
an issue label's last segment is `bug` and `feat` otherwise. Check that against
|
|
528
|
-
the repo's convention and its branch protection: a ruleset that only permits
|
|
529
|
-
`feature/*`, or requires a ticket prefix, will reject every push the fleet
|
|
530
|
-
makes. `gh api repos/<owner>/<repo>/rulesets` and the branch-protection settings
|
|
531
|
-
are the place to look.
|
|
532
|
-
3. **The gates actually run, and exit 0 on a clean checkout.** This is the one
|
|
533
|
-
worth spending real time on. In a clean mirror or worktree, run each proposed
|
|
534
|
-
gate read-only and record the exit code:
|
|
535
|
-
- Safe to run: lint, typecheck, unit tests, formatter `--check` modes.
|
|
536
|
-
- Do **not** run: anything that writes to the working tree (a formatter without
|
|
537
|
-
`--check`), anything that hits the network beyond a package install, docker
|
|
538
|
-
builds, deploys, or a suite that needs live services. If a gate cannot be
|
|
539
|
-
verified safely, say which one and why, rather than asserting it works.
|
|
540
|
-
- A gate that fails on a *clean* checkout is a fleet that can never push. That
|
|
541
|
-
is a finding to report before arming, not after — the operator either fixes
|
|
542
|
-
the repo or drops the gate, and both are their call.
|
|
543
|
-
4. **Does `pull_request` actually fire?** The whole loop ends with a worker
|
|
544
|
-
watching `gh pr checks --watch` to a verdict. A workflow triggered only on
|
|
545
|
-
`push` to a branch pattern, or gated behind `if: github.actor != ...`, or one
|
|
546
|
-
that requires approval for a first-time contributor, gives a PR with **no
|
|
547
|
-
checks** — and a worker that waits forever on a verdict that never comes. Read
|
|
548
|
-
the `on:` block of each workflow and confirm `pull_request` is there and not
|
|
549
|
-
path-filtered away from the paths this fleet will touch.
|
|
550
|
-
|
|
551
|
-
Report these as findings with evidence, not as reassurance. "I read
|
|
552
|
-
`.github/workflows/ci.yml`; `on: pull_request` is present with no path filter" is a
|
|
553
|
-
finding. "CI should trigger" is not.
|
|
554
|
-
|
|
555
|
-
---
|
|
556
|
-
|
|
557
|
-
## Step 7 — finish through the wizard
|
|
558
|
-
|
|
559
|
-
Now hand the collected answers to the deterministic path:
|
|
560
|
-
|
|
561
|
-
```text
|
|
562
|
-
/conductor setup
|
|
563
|
-
```
|
|
564
|
-
|
|
565
|
-
You have the answers ready, so this is fast — and it stays the wizard's decision
|
|
566
|
-
to write, not yours. It asks, in this order: project name; tracker repo; queue
|
|
567
|
-
label; whether to rename the state labels; routing label prefix; then per repo the
|
|
568
|
-
routing key, clone URL, default branch and **pre-push gates** (your Step 2
|
|
569
|
-
proposal, in `cmd @ cwd` form); whether to add another repo; whether to set up
|
|
570
|
-
**code-graph discovery** and the root its clones live under (Step 8); caps; the
|
|
571
|
-
merge and release authority confirms; the independent **Release tool gate**
|
|
572
|
-
confirm (`none` or `operator-brief`); the worker model; the Telegram chat id for
|
|
573
|
-
tier 2; the escalation fallback; whether an orchestrator session already runs
|
|
574
|
-
elsewhere; the report scope; and finally whether to write `ORCHESTRATOR.md` +
|
|
575
|
-
`POLICY.md`.
|
|
576
|
-
|
|
577
|
-
The end of the wizard is load-bearing:
|
|
578
|
-
|
|
579
|
-
- **The dry run is the point.** Before consent, setup reads the tracker with the
|
|
580
|
-
daemon's routing code. It shows each routable and unroutable issue. Walk the
|
|
581
|
-
operator through that output. An unroutable issue means that the labels and
|
|
582
|
-
routing configuration disagree.
|
|
583
|
-
- **Nothing changes before consent.** Setup creates no label, file, database, or
|
|
584
|
-
arm marker before the operator agrees. Never answer that consent prompt for
|
|
585
|
-
the operator.
|
|
586
|
-
- **Review authority and gate together.** If the procedure assigns tags,
|
|
587
|
-
publishing, release creation, or deploy commands to the orchestrator, the dry
|
|
588
|
-
run must show both `release=orchestrator` and
|
|
589
|
-
`releasePolicy=operator-brief`. `release=orchestrator` with
|
|
590
|
-
`releasePolicy=none` assigns the job while mechanically preventing it. A
|
|
591
|
-
human-owned release should normally remain `releasePolicy=none`.
|
|
592
|
-
- **The host plan is part of consent.** For an external orchestrator, setup names
|
|
593
|
-
the heartbeat file and both safety gates. It also names the staged systemd
|
|
594
|
-
unit. An invalid existing heartbeat stops setup before any write.
|
|
595
|
-
- **The smoke is automatic and paused.** After consent, setup holds dispatch,
|
|
596
|
-
runs one daemon tick, proves `/healthz`, reads stored status, and stops the
|
|
597
|
-
temporary daemon. A running daemon is health-checked and safely restarted only
|
|
598
|
-
when no worker is live.
|
|
599
|
-
- **External arming proves the real channel.** Every setup run sends the inbound
|
|
600
|
-
Telegram challenge, even when an arm marker exists. A failed proof leaves
|
|
601
|
-
dispatch paused and prints the recovery commands.
|
|
602
|
-
|
|
603
|
-
Ask the operator to approve the `ORCHESTRATOR.md` and `POLICY.md` write. After
|
|
604
|
-
the wizard completes, apply the edits from Steps 3, 4, and 5 to `POLICY.md`.
|
|
605
|
-
Tell the operator its path. A later wizard run asks before it replaces these files.
|
|
606
|
-
|
|
607
|
-
### Then explain hold, pause, and disarm
|
|
608
|
-
|
|
609
|
-
These controls have different effects:
|
|
610
|
-
|
|
611
|
-
- **Setup readiness** means that the paused smoke passed. For external
|
|
612
|
-
orchestration, the Telegram arm proof also passed. Setup then clears the
|
|
613
|
-
dispatch pause. It does not install the staged systemd unit. Run the printed
|
|
614
|
-
install commands on systemd, or run `omp-conductor start` on another host.
|
|
615
|
-
- **Hold** (`omp-conductor hold`) pauses claims and disarms ticks. The daemon and
|
|
616
|
-
pane stay active. Use this command for maintenance or an overnight stop.
|
|
617
|
-
- **Pause** (`omp-conductor pause`) stops new claims only. Active workers finish,
|
|
618
|
-
and an armed heartbeat continues its triage and reporting duties.
|
|
619
|
-
- **Disarm** (`omp-conductor disarm`) removes the marker named by
|
|
620
|
-
`.conductor-tick.json`. The heartbeat stops, but the daemon can still claim
|
|
621
|
-
work. Use `hold`, not `disarm`, when no orchestrator can supervise dispatch.
|
|
622
|
-
- **The channel gate can stop ticks.** Every fleet heartbeat config names the
|
|
623
|
-
Telegram `access.json`. Each tick requires `enabled: true` and exactly one
|
|
624
|
-
paired owner. Missing, invalid, disabled, empty, or ambiguous access stops the
|
|
625
|
-
heartbeat.
|
|
626
|
-
|
|
627
|
-
---
|
|
628
|
-
|
|
629
|
-
## Step 8 — build the code graph, if they said yes to it
|
|
630
|
-
|
|
631
|
-
Only if the wizard's code-graph question was answered yes. It is optional, and a
|
|
632
|
-
fleet without it works exactly as it did before — but it is the cheapest single
|
|
633
|
-
improvement to how far a worker gets, so lead with the number: **workers spend
|
|
634
|
-
most of a run finding code, not changing it.** Measured on the reference fleet, a
|
|
635
|
-
run typically spends 30–62 `read` and 32–69 `bash` calls against 9–24 edits, and
|
|
636
|
-
the runs that hit the turns cap hit it with the work unfinished. A graph answers
|
|
637
|
-
"who calls this" in one call instead of twenty greps.
|
|
638
|
-
|
|
639
|
-
Say the thing operators get wrong before you run anything: **the indexed
|
|
640
|
-
directories are conductor's, not theirs.** Three candidates and only one works.
|
|
641
|
-
|
|
642
|
-
- A worker's **worktree** cannot be indexed usefully — an index is keyed by the
|
|
643
|
-
realpath it was built from, so a throwaway `worktrees/<issue>` path is always an
|
|
644
|
-
empty project. That is why the brief hands the worker an absolute path instead.
|
|
645
|
-
- Their **own checkout** must not be indexed. Refreshing means resetting to the
|
|
646
|
-
default branch, which in a directory they work in either destroys uncommitted
|
|
647
|
-
work or indexes the feature branch they left checked out.
|
|
648
|
-
- Conductor's **mirrors** are bare. No working tree, nothing to index.
|
|
649
|
-
|
|
650
|
-
So each `graphProject` is a fourth thing: a disposable clone that exists only to
|
|
651
|
-
be indexed, pinned to the repo's default branch, never edited by a human. Say that
|
|
652
|
-
out loud, because an operator who points it at `~/projects/<repo>` to "save disk"
|
|
653
|
-
has armed something that will one day `git reset --hard` over their work.
|
|
654
|
-
|
|
655
|
-
Two host prerequisites come before any of that, and neither is conductor's to
|
|
656
|
-
install. `graph-setup` reports both as step 0, so run it first and read that
|
|
657
|
-
block before running anything else.
|
|
658
|
-
|
|
659
|
-
- **The indexer must be on PATH.** `codebase-memory-mcp` is a separate project
|
|
660
|
-
([source](https://github.com/DeusData/codebase-memory-mcp)); the package never
|
|
661
|
-
installs, spawns or depends on it. A host without it gets command-not-found
|
|
662
|
-
partway down the plan.
|
|
663
|
-
- **It must be mounted as an MCP server for sessions**, in `~/.omp/agent/mcp.json`
|
|
664
|
-
on the account the daemon runs as. This is the one that bites, because it fails
|
|
665
|
-
*silently*: indexing succeeds, the databases are real and correct, and worker
|
|
666
|
-
sessions have no graph tools at all — so every worker quietly greps and the
|
|
667
|
-
whole thing looks like it simply did not help. `graph-setup` prints the exact
|
|
668
|
-
entry to paste, pointed at the binary it found.
|
|
669
|
-
|
|
670
|
-
Check the mount on the daemon's account, not yours — a per-user config that is
|
|
671
|
-
present for the operator and absent for the service account looks fine from the
|
|
672
|
-
shell they are typing in.
|
|
673
|
-
|
|
674
|
-
Then, on the host that runs the daemon:
|
|
675
|
-
|
|
676
|
-
```bash
|
|
677
|
-
omp-conductor graph-setup # read-only: prints the whole plan
|
|
678
|
-
```
|
|
679
|
-
|
|
680
|
-
Walk them through what it printed rather than pasting it silently. It has three
|
|
681
|
-
parts, and each one is a decision they can still refuse: a `git clone` per missing
|
|
682
|
-
clone, an index command per repo (minutes each — run them now, or the first worker
|
|
683
|
-
queries an empty graph), and a `cbm-reindex.service` + `cbm-reindex.timer` pair
|
|
684
|
-
derived from their own repos and branches. Then:
|
|
685
|
-
|
|
686
|
-
```bash
|
|
687
|
-
omp-conductor graph-setup --write # stages the script and the two units (no root)
|
|
688
|
-
```
|
|
689
|
-
|
|
690
|
-
**Have them run this as the account the fleet runs as, not under `sudo`.** The
|
|
691
|
-
command refuses sudo outright, and that refusal is the whole point: config path,
|
|
692
|
-
state directory, `~/.cache` and the unit's own `User=` all resolve per-account,
|
|
693
|
-
so a root run stages a timer that goes green while writing indexes into
|
|
694
|
-
`/root/.cache` where no worker session looks. It is silent, and it looks exactly
|
|
695
|
-
like the feature not helping.
|
|
696
|
-
|
|
697
|
-
`--write` stages all three files in the state directory and prints the two `sudo`
|
|
698
|
-
lines that install and enable them — installing units is the only privileged
|
|
699
|
-
step, and it never runs `systemctl` itself. Have them start the service once by
|
|
700
|
-
hand and read the result: a first real run is where a wrong branch or a missing
|
|
701
|
-
clone shows up, and the unit is written to fail loudly rather than index a stale
|
|
702
|
-
tree.
|
|
703
|
-
|
|
704
|
-
Two things to leave them with:
|
|
705
|
-
|
|
706
|
-
- **A timer, not the server's own watcher.** That watcher lives inside a connected
|
|
707
|
-
MCP session and dies with it, so nothing a worker mounts keeps anything fresh.
|
|
708
|
-
If the timer is not enabled, the graph decays and no one is told.
|
|
709
|
-
- **The graph is a snapshot, and the brief says so.** Workers are told to orient
|
|
710
|
-
with it and then read the real file before editing, because the index is the
|
|
711
|
-
default branch at the last reindex — not their branch, and not their edits.
|
|
712
|
-
|
|
713
|
-
Verify before moving on: `codebase-memory-mcp cli list_projects` must show one
|
|
714
|
-
entry per repo whose `root_path` is exactly the configured `graphProject`. That
|
|
715
|
-
match is the whole contract — the worker brief tells the session to find its
|
|
716
|
-
project by that path, so a mismatch means a silent fallback to grep.
|
|
717
|
-
|
|
718
|
-
---
|
|
719
|
-
|
|
720
|
-
## Step 9 — hand over the learning loop
|
|
721
|
-
|
|
722
|
-
Finish by telling the operator the truth about what they just wrote:
|
|
723
|
-
|
|
724
|
-
**A brief converges from operation, not from an interview.** What you produced
|
|
725
|
-
today is a good first draft, and it is wrong in ways neither of you can see yet.
|
|
726
|
-
Real deployments diverge from the template exactly where the operator learns
|
|
727
|
-
something: a reporting scope that turned out too loud, a release boundary drawn in
|
|
728
|
-
the wrong place, an escalation that should have been a digest line, a hard
|
|
729
|
-
boundary nobody thought to name until an agent walked into it.
|
|
730
|
-
|
|
731
|
-
The brief closes that gap itself now. Point them at its **Learning loop** section
|
|
732
|
-
and say what it means in practice:
|
|
733
|
-
|
|
734
|
-
- **Their corrections are the trigger.** When they tell the orchestrator to work
|
|
735
|
-
differently — mid-flight, in a reply to an escalation, anywhere — it drafts the
|
|
736
|
-
matching edit to its own brief instead of just complying once.
|
|
737
|
-
- **Approval is a Telegram yes/no.** The proposal arrives as one question carrying
|
|
738
|
-
the exact diff: the current lines, then the replacement. Yes applies it. No, or
|
|
739
|
-
no answer at all, drops it, and it does not raise that amendment again.
|
|
740
|
-
- **Two things it will never do:** propose relaxing **Hard boundaries** (that
|
|
741
|
-
section changes only when they hand-edit it), or interrupt a tick's duties to
|
|
742
|
-
ask.
|
|
743
|
-
- **Every applied amendment is logged** as one line under **Amendments** at the
|
|
744
|
-
bottom of the brief — date, trigger, summary. That list is the honest record of
|
|
745
|
-
where this interview was wrong, and it accumulates without anyone scheduling it.
|
|
746
|
-
|
|
747
|
-
That is the whole handover. Convergence is now the loop's job rather than a habit
|
|
748
|
-
the operator has to keep.
|