docs: PM-mode discipline + multi-agent relay coordination protocol
All checks were successful
All checks were successful
AGENTS.md +292 lines: the relay (model-agnostic MCP message-bus at localhost:7110), PM session launch checklist (poller, watch.sh, kitty/tmux spawner), session handoff (inbox.log / poller.log / relay-log.jsonl), PM turn-start discipline (drain inbox, list_pending, git log, surface proactively), multi-dev parallelism (5-rule checklist with the slice-3 anti-pattern), when-the-PM-is-blocked protocol. Plus the slice-4 PM kickoff prompt under docs/superpowers/kickoffs/. Load-bearing for the multi-agent workflow executing slice-4 (and beyond): session discipline (turn-start polling, no standby mode, proactive surfacing) was the prior failure mode; the prompt + these sections bake the correction into AGENTS.md so a fresh PM agent session inherits it.
This commit is contained in:
295
AGENTS.md
295
AGENTS.md
@@ -219,10 +219,28 @@ and "why isn't X here?" questions. Consult it before adding anything.
|
|||||||
|
|
||||||
- **Trunk-based development** (target, once branch protection is in place):
|
- **Trunk-based development** (target, once branch protection is in place):
|
||||||
1. Branch from `main` for any change.
|
1. Branch from `main` for any change.
|
||||||
2. Open a PR targeting `main`.
|
2. Push the branch to origin (`git push -u origin <branch>`).
|
||||||
3. CI gates: `cargo fmt --check`, `cargo clippy -- -D warnings`, `cargo test --all`,
|
3. Open a PR targeting `main` via **`tea`** (Gitea CLI, not `gh`):
|
||||||
|
```bash
|
||||||
|
tea pulls create \
|
||||||
|
--head <branch> \
|
||||||
|
--base main \
|
||||||
|
--title "<imperative subject ≤72 chars>" \
|
||||||
|
--description "<body — what, why, merge instructions, reviews>"
|
||||||
|
```
|
||||||
|
The `tea` login is `alee` (default), against `https://git.adlee.work`. Verify with
|
||||||
|
`tea login list` if auth fails. `tea pulls list` shows open PRs; `tea pulls merge <index>`
|
||||||
|
merges from CLI (honors the merge strategy passed via `--style`).
|
||||||
|
4. CI gates: `cargo fmt --check`, `cargo clippy -- -D warnings`, `cargo test --all`,
|
||||||
`cargo deny check`. All must pass before merge.
|
`cargo deny check`. All must pass before merge.
|
||||||
4. Squash-merge to keep `main` linear.
|
5. **Merge strategy — default squash, carve-out for stacked branches:**
|
||||||
|
- **Default:** squash-merge to keep `main` linear. One commit per PR, clean history.
|
||||||
|
- **Rebase-merge carve-out:** when a PR's commit SHAs are carried by downstream branches
|
||||||
|
(the "stacked branches" topology — pivot/strategic-doc PRs that depend on each other),
|
||||||
|
use **rebase-merge** instead. This preserves the original commit SHAs so downstream
|
||||||
|
branches see the pivot as "already in main" with zero rebase pain. Squash would orphan
|
||||||
|
the originals and force conflict-prone rebases across every dependent branch. State
|
||||||
|
"merge via rebase-merge, not squash" explicitly in the PR description when this applies.
|
||||||
- **Never push directly to `main`.** Branch protection (planned) will enforce; until then,
|
- **Never push directly to `main`.** Branch protection (planned) will enforce; until then,
|
||||||
self-discipline.
|
self-discipline.
|
||||||
- **Commit messages:** imperative mood, subject ≤ 72 chars, body wraps at 72, blank line
|
- **Commit messages:** imperative mood, subject ≤ 72 chars, body wraps at 72, blank line
|
||||||
@@ -232,6 +250,277 @@ and "why isn't X here?" questions. Consult it before adding anything.
|
|||||||
each land as separate commits when practical. Don't bundle unrelated work.
|
each land as separate commits when practical. Don't bundle unrelated work.
|
||||||
- **Never commit secrets.** The `.gitignore` already covers `.env*`, `*.pem`, `*.key`. If
|
- **Never commit secrets.** The `.gitignore` already covers `.env*`, `*.pem`, `*.key`. If
|
||||||
a new secret pattern appears, extend `.gitignore` in the same commit.
|
a new secret pattern appears, extend `.gitignore` in the same commit.
|
||||||
|
- **PR description template** (for non-trivial PRs):
|
||||||
|
```
|
||||||
|
## What lands
|
||||||
|
- <bullet per logical change>
|
||||||
|
|
||||||
|
## Merge instructions (if non-default)
|
||||||
|
- <e.g. "rebase-merge, not squash — see AGENTS.md Git workflow">
|
||||||
|
|
||||||
|
## Reviews (if applicable)
|
||||||
|
- <companion review docs / ADRs / specs>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Multi-agent coordination — the relay (cross-model)
|
||||||
|
|
||||||
|
When more than one agent session works this repo in parallel (the PM / senior-dev "lift"
|
||||||
|
paradigm), they coordinate through a small MCP message-bus — the **relay** at
|
||||||
|
[`~/Sources/relay`](file:///home/alee/Sources/relay). One server, many terminals: each
|
||||||
|
session posts and drains messages **by role** instead of the user copy-pasting blocks
|
||||||
|
between windows.
|
||||||
|
|
||||||
|
**The relay is model-agnostic.** Nothing about it is Claude-specific — it speaks plain
|
||||||
|
MCP / JSON-RPC over HTTP. A **GLM-backed agent and a Claude agent share the same bus** and
|
||||||
|
exchange messages transparently, as long as both point at the same running relay and use
|
||||||
|
the same role/kind vocabulary below.
|
||||||
|
|
||||||
|
- **Currently running on `localhost:7110`.** The port is set by `RELAY_PORT` (default
|
||||||
|
`7331` if unset). The server binds `127.0.0.1`, so **all agents must be on this host**
|
||||||
|
(a GLM agent in a separate container/VM can't reach it over localhost).
|
||||||
|
- **Roles are slots, not models.** The relay knows `pm, dev-a, dev-b, dev-c, dev-d,
|
||||||
|
dev-e, dev-f` — that's it. A GLM agent claims a free role (say `dev-c`) exactly like a
|
||||||
|
Claude agent claims `dev-a`; the relay neither knows nor cares which model drives a role.
|
||||||
|
- **Start the relay before opening sessions** if you want MCP auto-registration. The shell
|
||||||
|
shim (below) works at any time.
|
||||||
|
|
||||||
|
### Port assignments (per project)
|
||||||
|
|
||||||
|
Each project gets its own relay port so multiple coordinated lifts can run side by side
|
||||||
|
without colliding. Set `RELAY_PORT` to the project's port when launching the relay and in
|
||||||
|
every agent/shim that talks to it.
|
||||||
|
|
||||||
|
| Port | Project | Notes |
|
||||||
|
|---|---|---|
|
||||||
|
| `7110` | [`~/Sources/rutster`](file:///home/alee/Sources/rutster) | **this repo** |
|
||||||
|
| `7331` | [`~/Sources/relicario`](file:///home/alee/Sources/relicario) | also the relay's built-in default |
|
||||||
|
|
||||||
|
Adding a project? Pick an unused port, launch with
|
||||||
|
`~/Sources/relay/start.sh --repo <path> --port <n>`, and add the row here.
|
||||||
|
|
||||||
|
### Two ways for an agent to connect
|
||||||
|
|
||||||
|
**1. MCP-native** — for any harness that speaks MCP over SSE (this includes Claude Code
|
||||||
|
pointed at a GLM model). Register the relay as an SSE server, then call `post_message` /
|
||||||
|
`read_messages` / `list_pending` as native tools. Config shape:
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
// .mcp.json (or your harness's MCP config)
|
||||||
|
{ "mcpServers": { "relay": { "type": "sse", "url": "http://localhost:7110/sse" } } }
|
||||||
|
```
|
||||||
|
For Claude Code specifically: `claude mcp add --transport sse relay http://localhost:7110/sse`.
|
||||||
|
|
||||||
|
**2. Shell shim** — for any agent that can run a shell command (model- and harness-agnostic;
|
||||||
|
the fallback when MCP isn't registered). The shims read `RELAY_PORT`, so export it once:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export RELAY_PORT=7110 # match the running relay
|
||||||
|
python3 ~/Sources/relay/call.py list_pending '{"for":"dev-c"}'
|
||||||
|
python3 ~/Sources/relay/call.py read_messages '{"for":"dev-c"}' # drains the inbox
|
||||||
|
python3 ~/Sources/relay/call.py post_message \
|
||||||
|
'{"from":"dev-c","to":"pm","kind":"status","body":"Slice-1 media codec boundary DONE. Tests green."}'
|
||||||
|
# call.ts is the TypeScript equivalent: npx tsx ~/Sources/relay/call.ts <tool> '<json>'
|
||||||
|
```
|
||||||
|
|
||||||
|
### The three tools
|
||||||
|
|
||||||
|
| Tool | Args | Behavior |
|
||||||
|
|---|---|---|
|
||||||
|
| `post_message` | `from`, `to`, `kind`, `body` | Push to a role's inbox; returns `{"id": "<uuid>"}` |
|
||||||
|
| `read_messages` | `for` | **Drains** the inbox (consume-once), oldest first |
|
||||||
|
| `list_pending` | `for` | Count + kinds **without** consuming |
|
||||||
|
|
||||||
|
`kind` is one of `status` (a dev's STATUS UPDATE / PM rollup), `directive` (PM → dev:
|
||||||
|
PROCEED / HOLD / RESCOPE), `question` (dev → PM), or `free` (anything else). Keep `body`
|
||||||
|
single-line (periods between sentences, ` -- ` for stronger breaks) — some inbox monitors
|
||||||
|
use strict JSON parsers that choke on embedded newlines.
|
||||||
|
|
||||||
|
A GLM `dev-c` posting a `status` and a Claude `pm` reading it is the same roundtrip in both
|
||||||
|
directions — the bus is symmetric. See [`~/Sources/relay/README.md`](file:///home/alee/Sources/relay/README.md)
|
||||||
|
for roles, ports, and the `pm` convenience wrapper.
|
||||||
|
|
||||||
|
### Multi-dev parallelism — dispatching work without serial bottlenecks
|
||||||
|
|
||||||
|
When the PM dispatches a slice across N devs, the goal is **N devs actively
|
||||||
|
working, not N-1 blocked on one.** File-level non-overlap (no merge conflicts)
|
||||||
|
is necessary but NOT sufficient — task dependencies, not file ownership, are
|
||||||
|
what stall a dev. Apply this checklist to every multi-dev dispatch:
|
||||||
|
|
||||||
|
1. **Identify the critical-path foundation first.** Anything consumed by other
|
||||||
|
tasks (a new trait + impl others extend, an mpsc channel others wire into,
|
||||||
|
a mock others depend on) belongs to ONE dev's "foundation" sub-task that
|
||||||
|
lands FIRST. Don't parallelize across a dependency; parallelize the tail
|
||||||
|
AFTER the foundation lands.
|
||||||
|
2. **Pre-list "parallelizable-now" work explicitly, per slice, up front.**
|
||||||
|
Before dispatching, scan the spec's test section (often §7.4) and §8
|
||||||
|
done-criteria for: unit tests against already-existing types, golden JSON
|
||||||
|
fixtures, harness scaffolding, docs, `examples/`, LEARNING.md pointers,
|
||||||
|
README e2e plans, behavior-preserving refactors. These are the "filler" a
|
||||||
|
blocked dev picks up instead of stalling. List them in the dispatch so a
|
||||||
|
blocked dev knows the fallback without asking.
|
||||||
|
3. **Sequence so the blocking path is the shortest one.** If dev-a's
|
||||||
|
foundation takes 30 min and dev-b's dependent work takes 20 min, the
|
||||||
|
parallelizable tail needs to be >50 min of work or you'll have idle devs.
|
||||||
|
Either bundle more into the foundation (so less depends on it) or split
|
||||||
|
the dependent work across more devs.
|
||||||
|
4. **State dependencies explicitly in each directive.** Every directive
|
||||||
|
names: (a) files the dev owns, (b) files that are off-limits (seam-test
|
||||||
|
invariants), (c) blockers — which dev's status unblocks this dev, and
|
||||||
|
what to do in the meantime (the "filler" from rule 2).
|
||||||
|
5. **The seam test is sacred.** When a slice has a "these files stay
|
||||||
|
byte-identical to baseline" gate (e.g. slice-2/3's
|
||||||
|
`loop_driver.rs` + `rtc_session.rs`), tell EVERY dev not to touch them.
|
||||||
|
Don't rely on one dev's directive; broadcast the constraint.
|
||||||
|
|
||||||
|
**Anti-pattern (real, from slice-3):** split by file (no conflict) but one
|
||||||
|
dev's task consumed another's unwritten outputs (mpc channels + mock). File
|
||||||
|
non-overlap still produced a serial bottleneck because the task dependency
|
||||||
|
wasn't surfaced. Fix: rule 1 + 2 + 4 together.
|
||||||
|
|
||||||
|
### Background poller (`poller.py`) — for PM sessions that monitor the relay over time
|
||||||
|
|
||||||
|
A PM session that drains the inbox only when prompted will miss time-sensitive `question`s
|
||||||
|
from devs and let directives stall. **Launch the relay's `poller.py` as a background
|
||||||
|
process at the start of every PM session** (one per relay port — for rutster that's 7110):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
setsid env RELAY_PORT=7110 python3 ~/Sources/relay/poller.py \
|
||||||
|
>> /tmp/relay-poller/7110/poller.log 2>&1 < /dev/null & disown
|
||||||
|
```
|
||||||
|
|
||||||
|
What it does (every 20s by default): lists/drains the PM inbox (logging full bodies to
|
||||||
|
`/tmp/relay-poller/7110/inbox.log`), lists pending for each dev role, and posts a
|
||||||
|
`question`-kind nudge if a directive has sat undrained >120s. State files persist across
|
||||||
|
PM sessions — read `inbox.log` to catch up on what the poller drained while you were away,
|
||||||
|
and `poller.log` to confirm the poller is actually cycling.
|
||||||
|
|
||||||
|
**Verification discipline (load-bearing — this was a real failure mode):** after launching,
|
||||||
|
wait ≥60s, then confirm the log shows **at least 3 "poll cycle N complete" lines** before
|
||||||
|
claiming the poller is running. A "poller start" line + a live process is NOT sufficient —
|
||||||
|
the SSE client can launch then silently fail on the first poll. The cycle entries are the
|
||||||
|
proof of life. If you only see the start line, kill + relaunch + re-verify.
|
||||||
|
|
||||||
|
**Do NOT use `call.py` in a loop.** Each `call.py` invocation opens a fresh SSE
|
||||||
|
connection + spawns a daemon thread that doesn't clean up. Over hundreds of polls
|
||||||
|
this piles up connections/threads and eventually wedges. `poller.py` opens ONE
|
||||||
|
persistent SSE connection and reuses it — that's the whole point of the distinction.
|
||||||
|
|
||||||
|
**For dev sessions:** dev agents don't need the poller. They should drain their own
|
||||||
|
inbox at the start of every turn (or poll every ~20-30s if the harness supports
|
||||||
|
background polling). The poller exists to keep PM-side state warm and catch devs who
|
||||||
|
go quiet — devs are the *consumers* of directives, not the pollers.
|
||||||
|
|
||||||
|
### Session handoff — what to read when resuming a PM session
|
||||||
|
|
||||||
|
When a PM session resumes (new terminal, cleared context, etc.), the relay's in-memory
|
||||||
|
queues may have been drained by the poller in the previous session. Reconstruct state
|
||||||
|
from, in order:
|
||||||
|
|
||||||
|
1. `/tmp/relay-poller/<port>/inbox.log` — every message the poller drained, full bodies,
|
||||||
|
in order. **Read this first.**
|
||||||
|
2. `/tmp/relay-poller/<port>/poller.log` — poll-cycle entries, nudges posted, errors.
|
||||||
|
Skim for any nudge you sent that the dev never responded to.
|
||||||
|
3. `~/Sources/relay/relay-log.jsonl` — the relay's own append-only archive, **the durable
|
||||||
|
full-body record** of every posted message (the in-memory queues are consume-once and
|
||||||
|
vanish on relay restart; this file is the source of truth across restarts). Use this if
|
||||||
|
the poller state dir was wiped.
|
||||||
|
4. `git log --oneline --all -20` — what actually landed; reconcile against the statuses
|
||||||
|
in inbox.log.
|
||||||
|
|
||||||
|
The PM session's first action on resume is reading these — not posting directives. You
|
||||||
|
cannot direct devs whose last status you don't know.
|
||||||
|
|
||||||
|
### PM session launch checklist — reproducible setup for new sessions
|
||||||
|
|
||||||
|
When you (the user) open a fresh set of kitty/tmux terminals for a new PM + N-dev lift,
|
||||||
|
run this checklist so polling "just works" without manual relay between you and the PM
|
||||||
|
agent:
|
||||||
|
|
||||||
|
**1. Start the relay before opening agent sessions.** The PM and dev agents auto-register
|
||||||
|
their MCP tools against the relay on startup — but only if the relay is already running.
|
||||||
|
For rutster:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~/Sources/relay/start.sh --repo ~/Sources/rutster --port 7110
|
||||||
|
```
|
||||||
|
|
||||||
|
(This terminal becomes the relay log. Leave it open, or background it.)
|
||||||
|
|
||||||
|
**2. Launch the poller in the background (PM-side inbox monitoring):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
setsid env RELAY_PORT=7110 python3 ~/Sources/relay/poller.py \
|
||||||
|
>> /tmp/relay-poller/7110/poller.log 2>&1 < /dev/null & disown
|
||||||
|
```
|
||||||
|
|
||||||
|
The poller drains the `pm` inbox every 20s and logs full bodies to
|
||||||
|
`/tmp/relay-poller/7110/inbox.log`. It also nudges any dev whose directive inbox stays
|
||||||
|
undrained >120s. State files persist across PM sessions, so closing the PM terminal
|
||||||
|
doesn't lose history.
|
||||||
|
|
||||||
|
**3. Open a side kitty window for live inbox + poller tail:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# In a kitty tab alongside the PM chat:
|
||||||
|
RELAY_PORT=7110 ~/Sources/relay/watch.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This live-tails both `inbox.log` (dev → pm messages) and `poller.log` (poll cycle
|
||||||
|
markers + nudges), so you see dev activity in real time without depending on the PM
|
||||||
|
agent surfacing it. You see what the PM sees.
|
||||||
|
|
||||||
|
**4. Bring up the PM/dev sessions (manual / tmux / kitty):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~/Sources/relay/start.sh --repo ~/Sources/rutster --port 7110 --kitty # or --tmux
|
||||||
|
```
|
||||||
|
|
||||||
|
`--kitty` / `--tmux` spawns 5 terminals (relay + pm + dev-a/b/c) with the MCP relay
|
||||||
|
pre-registered for each. The generated prompts bake in `RELAY_PORT=7110` so the shims
|
||||||
|
(`call.py`) self-configure even without the env. (For `--manual`, the relaying prompts
|
||||||
|
are printed for you to paste into separate terminals.)
|
||||||
|
|
||||||
|
### PM-mode discipline (load-bearing — this was a real failure mode)
|
||||||
|
|
||||||
|
The poller and watch.sh capture state, but **the PM agent is still turn-based** — a
|
||||||
|
background process cannot interrupt the PM mid-conversation. The user's prompt is the
|
||||||
|
only thing that spins the PM up. Therefore, **at the start of every turn**, before answering
|
||||||
|
the user's question or acting on it, the PM agent MUST:
|
||||||
|
|
||||||
|
1. Drain its own inbox via `python3 ~/Sources/relay/call.py read_messages '{"for":"pm"}'`
|
||||||
|
(or read `inbox.log` if the poller has been running).
|
||||||
|
2. Poll pending for each dev role: `python3 ~/Sources/relay/call.py list_pending '{"for":"<role>"}'`
|
||||||
|
— catches directives that haven't been picked up.
|
||||||
|
3. Check `git log --oneline --all -10` for commits that landed since last seen.
|
||||||
|
4. Surface anything actionable to the user BEFORE they have to ask "did you see X?":
|
||||||
|
"`<dev>` posted `<status>` at `<ts>`; next action I'm taking is `<action>`."
|
||||||
|
|
||||||
|
If a dev posted a `question` kind that needs a user decision (architectural forks,
|
||||||
|
build-vs-reuse calls, etc.), surface it explicitly with a proposed default and ask for
|
||||||
|
confirmation — don't go silent on it and don't proceed without the user's call.
|
||||||
|
|
||||||
|
**Anti-pattern (real, from slice-3):** the poller was running and draining state
|
||||||
|
correctly, but the PM only read it when the user asked "did you see X?" — wasting the
|
||||||
|
poller's value. The turn-start discipline above is what makes the poller actually useful.
|
||||||
|
|
||||||
|
### When the PM is blocked on a user decision
|
||||||
|
|
||||||
|
Devs posting `question`-kind messages often need a user-only decision (architecture
|
||||||
|
forks, build-vs-reuse, licensing posture, etc.). The PM should NOT bike-shed these in
|
||||||
|
relay messages — surface them to the user immediately and propose a default:
|
||||||
|
|
||||||
|
```
|
||||||
|
<dev-X> asked an architectural question: <one-line summary>.
|
||||||
|
My recommendation: <default>. Confirm or redirect?
|
||||||
|
```
|
||||||
|
|
||||||
|
Devs don't wait for the PM to bike-shed; they wait for the user. The PM's job is to
|
||||||
|
telescope the decision needed to one line, propose a sound default, and let the user
|
||||||
|
confirm. If the user is away, the PM can post a `status` to the dev saying "blocked on
|
||||||
|
user decision re: <X>, you'll get a directive when they confirm" so the dev can pick up
|
||||||
|
parallel work or stand down.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
130
docs/superpowers/kickoffs/slice-4-pm-prompt.md
Normal file
130
docs/superpowers/kickoffs/slice-4-pm-prompt.md
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
# PM kickoff prompt — slice 4 (barge-in / VAD-driven playout kill)
|
||||||
|
|
||||||
|
> Drop this prompt into a fresh PM agent session to bootstrap it with full
|
||||||
|
> context. Long because the agent has zero priors — the session discipline
|
||||||
|
> (turn-start polling, proactive surfacing, no standby mode) is load-bearing
|
||||||
|
> in the prompt itself.
|
||||||
|
>
|
||||||
|
> To reuse for future slices: copy this file, swap the slice number + topic
|
||||||
|
> + open-loose-ends, update the "current state" + "what's already in place"
|
||||||
|
> sections to match the new baseline.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
```
|
||||||
|
You are the PM for the rutster project. Slice-3 just merged. Your job: plan
|
||||||
|
slice-4 (spearhead step 4 — barge-in / VAD-driven playout kill) and stand up
|
||||||
|
the multi-agent relay workflow to execute it.
|
||||||
|
|
||||||
|
## Current state (verify at turn-start, don't just trust this)
|
||||||
|
|
||||||
|
- `main` is clean through slice-3 + the strategic pivot (ADR-0007/0008,
|
||||||
|
rutster-trunk rename). All 4 PRs merged (slice-1, slice-2, pivot, slice-3).
|
||||||
|
Run `git log --oneline -15` to confirm.
|
||||||
|
- CI is green on main: fmt, clippy --all-targets -D warnings, test --all
|
||||||
|
(stable + 1.85), cargo deny check.
|
||||||
|
- Poller SHOULD be running in background (check: `pgrep -af poller.py`).
|
||||||
|
If not, relaunch per AGENTS.md "PM session launch checklist".
|
||||||
|
- Relay on localhost:7110. State at /tmp/relay-poller/7110/.
|
||||||
|
- Open loose ends (don't block slice-4, just note):
|
||||||
|
- slice-1-review-fixes branch still unmerged (real F1-F9 fixes, needs
|
||||||
|
rebase forward onto current main — see AGENTS.md parallelism rules)
|
||||||
|
- Server is memory-constrained (1.9 GB RAM) — CI matrix jobs sometimes
|
||||||
|
OOM-kill in parallel. Known infra issue, not a code defect.
|
||||||
|
- AGENTS.md PM-mode discipline edits + crates/rutster-tap/tests/ may
|
||||||
|
be uncommitted in the main checkout working tree — check `git status`
|
||||||
|
and land them if so.
|
||||||
|
|
||||||
|
## What slice-4 is
|
||||||
|
|
||||||
|
Spearhead step 4 of 6: **barge-in / VAD-driven playout kill.** The FOB reflex
|
||||||
|
loop acts on the advisory signals slice-3 pre-paved.
|
||||||
|
|
||||||
|
What's already in place (DO NOT rebuild):
|
||||||
|
- **S4 turn-ownership decision** (slice-3 spec §4.3 + §7.5 #7): OpenAI
|
||||||
|
Realtime's server-side VAD is DISABLED (`session.update` with
|
||||||
|
`turn_detection: null`). The FOB owns turn-taking. Locked by unit test.
|
||||||
|
- **`speech_started` / `speech_stopped` advisory events** (slice-3 spec §3.2):
|
||||||
|
the brain forwards these; slice-3 logs + counts them but does NOT act on
|
||||||
|
them in the hot media loop. Slice-4 wires them into the reflex loop.
|
||||||
|
- **Core-authoritative playout buffer** (slice-2 §4.1): structurally
|
||||||
|
prevents the brain from gating playout. Already in place. Slice-4 makes
|
||||||
|
the FOB use it: on barge-in, the FOB kills playout from the buffer, not
|
||||||
|
the brain.
|
||||||
|
|
||||||
|
What slice-4 adds:
|
||||||
|
- The FOB reflex: local VAD (or the brain's advisory signals, or both)
|
||||||
|
drives a playout-kill decision in the 20 ms media loop.
|
||||||
|
- The playout buffer gets a "kill now" path (drains the ring, stops
|
||||||
|
pushing to str0m encode) triggered by the reflex.
|
||||||
|
- The seam test continues: `loop_driver.rs` + `rtc_session.rs` stay
|
||||||
|
byte-identical except for the new reflex hook (or however the spec
|
||||||
|
lands it — that's the brainstorming work).
|
||||||
|
|
||||||
|
## What to read FIRST (in order)
|
||||||
|
|
||||||
|
1. **AGENTS.md** — the whole thing. Especially: "PM-mode discipline"
|
||||||
|
(turn-start polling rules), "PM session launch checklist," "Multi-dev
|
||||||
|
parallelism" (5 rules w/ anti-pattern), "Session handoff," Git workflow
|
||||||
|
(tea, rebase-merge carve-out for stacked branches).
|
||||||
|
2. **docs/superpowers/specs/2026-06-30-slice-3-realtime-brain-design.md**
|
||||||
|
§4.3 (S4 decision), §1.2 (what step 4 defers), §7.5 #7 (S4 done-criteria).
|
||||||
|
3. **docs/superpowers/specs/2026-06-28-slice-2-agent-tap-design.md** §4.1
|
||||||
|
(the core-authoritative playout buffer).
|
||||||
|
4. **docs/ARCHITECTURE.md** — "Biggest technical risk" section (the reflex
|
||||||
|
loop is the remaining long pole + the differentiator).
|
||||||
|
5. **docs/PORT_PLAN.md** §Phasing (step 4 = barge-in).
|
||||||
|
6. **docs/reviews/2026-06-29-strategic-reviews-post-pivot-rescore.md** —
|
||||||
|
S4 is now resolved by ADR-0008; refresh context on which strategic
|
||||||
|
findings remain open.
|
||||||
|
|
||||||
|
## How to run slice-4 planning
|
||||||
|
|
||||||
|
1. **Brainstorm first** — use the `brainstorming` skill before any design
|
||||||
|
doc. Explore: what triggers barge-in (local VAD vs brain advisory vs
|
||||||
|
both)? Where does the kill decision live (loop_driver? a new reflex
|
||||||
|
module?)? How does the playout buffer drain? What's the latency budget?
|
||||||
|
What does the seam test look like (which files stay byte-identical)?
|
||||||
|
Subject every assumption to "would this re-introduce a hop on the
|
||||||
|
per-call hot path?" (ADR-0002's load-bearing rule).
|
||||||
|
2. **Write the spec** — `docs/superpowers/specs/<date>-slice-4-barge-in-design.md`
|
||||||
|
using the `writing-plans` skill pattern. Include §1.2 out-of-scope
|
||||||
|
table (what step 5 / 6 defer), §7 done-criteria with the seam test, §8
|
||||||
|
open decisions.
|
||||||
|
3. **Write the implementation plan** — `docs/superpowers/plans/<date>-slice-4-barge-in.md`.
|
||||||
|
Structure it for parallelism (see AGENTS.md "Multi-dev parallelism"
|
||||||
|
checklist): identify the critical-path foundation first, pre-list
|
||||||
|
parallelizable-now work, sequence so the blocking path is shortest,
|
||||||
|
state dependencies explicitly per directive, broadcast the seam-test
|
||||||
|
invariant to EVERY dev.
|
||||||
|
4. **Use the `multi-agent-kickoff` skill** to generate the PM + dev
|
||||||
|
terminals. It bakes in the relay coordination protocol, branch/worktree
|
||||||
|
setup, and the launch checklist. Port 7110 for rutster.
|
||||||
|
5. **Launch the poller** per AGENTS.md before devs come online. Verify
|
||||||
|
≥3 "poll cycle N complete" lines in /tmp/relay-poller/7110/poller.log
|
||||||
|
before claiming it's running.
|
||||||
|
6. **Dispatch** using the 5-rule parallelism checklist. Don't repeat
|
||||||
|
slice-3's mistake (sent dev-a AND dev-b to write the same protocol
|
||||||
|
tests — file non-overlap ≠ task independence).
|
||||||
|
|
||||||
|
## PM-mode discipline (load-bearing)
|
||||||
|
|
||||||
|
You are turn-based. The poller keeps state warm but cannot interrupt you.
|
||||||
|
At the start of EVERY turn, before answering the user:
|
||||||
|
1. Drain pm inbox (or read /tmp/relay-poller/7110/inbox.log if poller ran).
|
||||||
|
2. `list_pending` for each dev role.
|
||||||
|
3. `git log --oneline --all -10`.
|
||||||
|
4. Surface anything actionable BEFORE the user asks "did you see X?"
|
||||||
|
|
||||||
|
Do NOT go on standby waiting for commands. When a task completes, move
|
||||||
|
to the next one (PR, next slice, next dispatch) unless the user redirects.
|
||||||
|
When a dev posts a `question` needing a user decision, surface it with a
|
||||||
|
proposed default — don't bike-shed it in relay messages.
|
||||||
|
|
||||||
|
## First action
|
||||||
|
|
||||||
|
Read AGENTS.md in full, then the slice-3 + slice-2 specs. Confirm poller
|
||||||
|
is alive. Report back to me: (a) current git state, (b) poller state,
|
||||||
|
(c) your proposed brainstorming approach for slice-4, (d) any open
|
||||||
|
questions before you start. Then begin.
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user