Candidate manifest 8b4ab31014a124ec0e3abba44238976d6f4c9af48cbdb36c2c77d18f0137f888 from Darkwing's round 2 packet (0a4076e8e), on BASE1223a0f37. No manifest path changed between BASE and the landing target. Filbert approved round 2 in 28468, closing C1 and D1 from round 1 (28464). Sage reran the full gate at the landing target with the candidate applied. The replay acceptance follows ruling sage-0204. Co-Authored-By: Claude Opus 5.5 <[email protected]>
86 lines
3.8 KiB
Markdown
86 lines
3.8 KiB
Markdown
# Conductor protocol — poor-man orchestration loop
|
|
|
|
How the stack orchestrates headless pi workers to do work on itself.
|
|
|
|
## Role contracts
|
|
|
|
Role authority is declared in role contracts, one file per role, under
|
|
`roles/` (e.g. `roles/conductor-policy.json`). The repository root holds
|
|
only first-class, bootstrap-required configuration; role contracts are
|
|
tracked, versioned files whose changes arrive as reviewed commits.
|
|
|
|
## Roles
|
|
|
|
| Role | Runs where | Powers | Never has |
|
|
|---|---|---|---|
|
|
| **Conductor** | host (assistant or owner) | git (clone/commit/push), task dispatch, review, verification suites, Gitea | nothing new |
|
|
| **Worker** | container (headless pi via `scripts/run-task.sh`) | read/write/edit/bash inside its workspace; persistent session on request | git credentials, docker socket, host filesystem |
|
|
|
|
## The loop
|
|
|
|
1. **Decompose**: conductor turns a goal into worker tasks small enough to
|
|
specify completely in one prompt (file paths, acceptance criteria, style
|
|
constraints, verification the worker can run itself, e.g. `node --check`).
|
|
2. **Mirror**: conductor maintains the repo clone at
|
|
`<dataRoot>/workspaces/stack-repo` (host-side git; workers see it read-write
|
|
through their workspace mount).
|
|
3. **Dispatch**: `scripts/run-task.sh run <worker-task.json>` — worker edits the
|
|
clone. Session name `worker-<n>` keeps continuity across refinement rounds.
|
|
4. **Extract**: `git -C <workspace> diff > patch` — the worker's entire output
|
|
is a reviewable diff. Run record (result.json, stderr.txt) is the receipt.
|
|
5. **Review**: conductor reads the diff line by line. Bad output → refine the
|
|
prompt, re-dispatch (same session: "your patch had these problems…").
|
|
6. **Integrate**: conductor applies the patch to the real repo, runs the full
|
|
suites, commits and pushes. Suites failing → revert apply, back to step 5.
|
|
7. **Record**: update CURRENT.md, BUILD-LOG, close the Gitea issue.
|
|
|
|
## Guardrails
|
|
|
|
- Workers never receive credentials; they never run git; they never leave the
|
|
workspace (container is the boundary; tools allowlist is the gate).
|
|
- Every worker diff is reviewed by the conductor before integration. No
|
|
auto-apply. (Auto-apply would be a capability-policy decision for later.)
|
|
- Verification is mechanical: suites + `node --check` / `bash -n` gates.
|
|
- Recursive decomposition = "fail → smaller task", never "hope."
|
|
|
|
## Landing approved work
|
|
|
|
A stack coder pushes `work/vikunja-<p>-<t>` to the working repository, the
|
|
reviewer approves a commit on it, and the PM raises a `git.merge.protected`
|
|
decision for the human. After the human resolves it with the merge option,
|
|
the conductor runs:
|
|
|
|
```bash
|
|
scripts/land-work.sh <decision-id> --dry-run
|
|
scripts/land-work.sh <decision-id>
|
|
```
|
|
|
|
The tool binds the commit to what was reviewed and decided. It reads the
|
|
decision, its raise and the latest verdict from the bus database
|
|
read-only, requires the work ref to be at the approved commit, squashes
|
|
the reviewed diff onto `refactor` in a detached worktree, checks the
|
|
patch-id, runs the full gate and commits once, locally. It refuses work
|
|
that adds a file at the repository root, in a new top-level directory, or
|
|
under `src/` or `v1/`. Each
|
|
refusal names its reason (`docs/TOOLS.md`). Don't work around one; fix the
|
|
cause or go back to the PM.
|
|
|
|
The conductor then reviews the commit, pushes it as a separate act and
|
|
sends the PM the line the tool printed. Only `refactor` lands this way.
|
|
Merges to `next` or `main`, re-pins and restarts keep their own authority,
|
|
and no stack role runs this tool.
|
|
|
|
## Worker task template
|
|
|
|
```json
|
|
{
|
|
"taskVersion": 1,
|
|
"id": "t-worker-<name>",
|
|
"prompt": "<full spec: goal, files, constraints, acceptance, self-checks>",
|
|
"workspace": "stack-repo",
|
|
"capabilities": { "tools": ["read", "write", "edit", "bash"] },
|
|
"session": "worker-1",
|
|
"timeoutSeconds": 600
|
|
}
|
|
```
|