Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions docs/goals-system-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,19 @@ rotation) we're ahead of both. Imported the rest as persona edits (`SKILL/SPEC/I

---

## Live-org fixes (2026-07-24)

Two failures found driving a real org (canora-sync, goals `gp6`/`gp7`) — both in the plumbing
around the org, not the org itself:

| Symptom | Root cause | Fix |
|---|---|---|
| Reports reach the orchestrator's inbox but it never acts on them | The bus is **pull-only**: `POST /panes/:id/messages` stores + pings WS clients, nothing writes to the pane. An interactive `claude` orchestrator blocks on stdin the moment its turn ends, so mail sat unread (88 messages deep) until a human typed. | `control/nudge.rs` + `arm_inbox_nudge` in `routes.rs`: when mail lands for an opted-in pane (`meta.role` in `goals-orch`/`spec`, or `hp.nudge=on`), wait for the pane to leave `Busy`, then type ONE coalesced, rate-limited (`60s`) line naming the read cursor. `HYPERPANES_MSG_NUDGE=0` disables. |
| Replies to a spec agent 404 (`no such pane`) | **Two pane-id spellings**: app-created panes are `pane-<uuid>`, control-created ones bare `<uuid>`. An agent handed one and reconstructing the other addressed a queue nobody reads. | `ReadModel::resolve_pane_id` canonicalizes either spelling (and a session uid); `find_pane_scoped` returns the canonical id, so post and read land on the same inbox. |
| Impl-agent work is invisible: no chat, and the pane is gone afterwards | `spawn_workers` put **N agents in one pane**; `claude -p` prints nothing until it exits; the runner exits on drain and the pane auto-closes with its scrollback. | `hyperpanes worker` gains `--stream` (render Claude `stream-json` events as progress), `--log-dir` (per-task raw transcript that outlives the pane) and `--linger`; `spawn_workers` defaults to `layout:"pane-per-worker"` (one pane per agent) and exposes `stream`/`logDir`/`lingerSecs`. Personas pass `--output-format stream-json --verbose`. |

---

## Build order

1. **Queue plumbing (B)** — `work_db()` path, disk-backed `Shared::default`, boot recovery, reaper
Expand Down
8 changes: 6 additions & 2 deletions resources/claude/goal-orchestrator/IMPL.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ done.
Fix until it passes.
3. **Commit** your work on your worktree's branch with a clear message. Leave the branch for the
spec agent to integrate — do not push, do not merge to main.
4. **Exit 0 on success**, non-zero on genuine failure. The runner acks on 0 (subtask `done`, which
4. **Narrate as you go.** Your pane renders your turn live (the runner is started with `--stream`
against `--output-format stream-json`), so a short line before each phase — what you're about
to do and why — is what the human sees. Silence for ten minutes reads as a wedged pane.
5. **Exit 0 on success**, non-zero on genuine failure. The runner acks on 0 (subtask `done`, which
unblocks any dependents) and nacks on non-zero (requeue with backoff, or dead-letter after
retries). Your printed last line is recorded — make it a one-line summary (what changed + the
branch/commit).
Expand All @@ -29,7 +32,8 @@ the spec is a higher-tier (opus/fable) model and is live. This is the "plan big,
trade — one cheap round-trip buys an opus-grade decision without taking the build off sonnet.

- Your advisor's pane id is in the payload (the spec agent stamped `advisor=<paneId>`); your own
pane id is `$HYPERPANES_PANE_ID`.
pane id is `$HYPERPANES_PANE_ID`. Pass both **verbatim** — don't add or strip a `pane-` prefix
(the API tolerates either spelling now, but only the exact id is guaranteed to be your queue).
- Ask one tight, decidable question — propose your answer, don't write an essay:
`send_message {to:"<advisor paneId>", from:"$HYPERPANES_PANE_ID", body:"<HP_TASK_ID>: <the fork,
the options, which you'd pick and why>"}`.
Expand Down
13 changes: 10 additions & 3 deletions resources/claude/goal-orchestrator/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,12 @@ For each goal you're given (free text):
Your own pane already carries this identity (the app set it); keep the scheme for everything
you spawn so a glance at the workspace reads project → task.
3. **Ingest reports.** Read spec-agent messages (`read_messages` on your pane; spec agents
`send_to_parent`). A report is one of: `progress` (incl. `spec:`/`respec:`), `blocked <reason>`,
`send_to_parent`). The bus is pull-only, so the app helps: when mail lands for your pane while
you're idle it types a one-line `[hyperpanes] inbox: N new message(s) … read_messages {paneId,
after:<seq>}` nudge into you. **Treat that line as a work order** — read from the given cursor
and act before anything else. It is coalesced (one line per burst) and rate-limited, so still
poll `read_messages` yourself on every loop pass; never assume the nudge is your only signal.
A report is one of: `progress` (incl. `spec:`/`respec:`), `blocked <reason>`,
`needs-decision <q>`, `done <evidence>`, `failed <reason>`. Act:
- `progress` — update ledger, continue.
- `needs-decision` — answer from the goal intent if you can; otherwise surface to the human
Expand Down Expand Up @@ -130,7 +135,9 @@ plan doc), so you don't babysit individual tasks — you watch goals and health.

**Impl-agent pane budget:** fan-out is soft-capped at **16 worker panes** — the spec agent sets
`count <= 16` and the queue multiplexes any overflow (competing-consumers), so more subtasks than 16
drain through the 16 panes rather than opening more. It's persona-enforced (see `SPEC.md` section 2),
drain through the 16 panes rather than opening more. `spawn_workers` now gives each worker its own
pane (`layout:"pane-per-worker"`, the default), so `count` IS the pane count — one readable agent
per pane instead of N interleaved into one. It's persona-enforced (see `SPEC.md` section 2),
not a code limit; hold the line so concurrent goals don't explode the pane count.

### MCP config on every spawned claude
Expand All @@ -143,7 +150,7 @@ the default `~/.claude.json` once `CLAUDE_CONFIG_DIR` is set — without the fla
every `mcp__hyperpanes__*` tool. The app already appends it on your own spawn; pass it down the
same way when you spawn a spec agent, and tell the spec agent to do the same in its
`spawn_workers` command, e.g.:
`spawn_workers {queue, count:N, isolation:"worktree", command:"sh -c 'claude --dangerously-skip-permissions --mcp-config <state-dir>/goals-mcp.json -p \"$HP_TASK_PAYLOAD\" --append-system-prompt-file $HP_GOAL_PERSONA_DIR/IMPL.md ${HP_GOAL_SETTINGS:+--settings $HP_GOAL_SETTINGS} --model ${HP_GOAL_IMPL_MODEL:-claude-sonnet-5[1m]}'"}`
`spawn_workers {queue, count:N, isolation:"worktree", stream:true, lingerSecs:120, command:"sh -c 'claude --dangerously-skip-permissions --mcp-config <state-dir>/goals-mcp.json -p \"$HP_TASK_PAYLOAD\" --output-format stream-json --verbose --append-system-prompt-file $HP_GOAL_PERSONA_DIR/IMPL.md ${HP_GOAL_SETTINGS:+--settings $HP_GOAL_SETTINGS} --model ${HP_GOAL_IMPL_MODEL:-claude-sonnet-5[1m]}'"}`

### If the `mcp__hyperpanes__*` tools won't load — drop to the Control API, don't reverse-engineer

Expand Down
15 changes: 12 additions & 3 deletions resources/claude/goal-orchestrator/SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,15 @@ prompt wedges the pane).
the advisor:** include `advisor=<your $HYPERPANES_PANE_ID>` in every payload so an impl agent that
hits a strategic fork can consult you mid-build instead of guessing or bouncing the whole subtask
(see IMPL.md "Consult your advisor").
- `spawn_workers {queue, count:N, isolation:"worktree", command:"sh -c 'claude --dangerously-skip-permissions --mcp-config <state-dir>/goals-mcp.json -p \"$HP_TASK_PAYLOAD\" --append-system-prompt-file $HP_GOAL_PERSONA_DIR/IMPL.md ${HP_GOAL_SETTINGS:+--settings $HP_GOAL_SETTINGS} --model ${HP_GOAL_IMPL_MODEL:-claude-sonnet-5[1m]}'"}`
— the `--mcp-config` flag is required (see `SKILL.md` "MCP config on every spawned claude");
- `spawn_workers {queue, count:N, isolation:"worktree", stream:true, lingerSecs:120, command:"sh -c 'claude --dangerously-skip-permissions --mcp-config <state-dir>/goals-mcp.json -p \"$HP_TASK_PAYLOAD\" --output-format stream-json --verbose --append-system-prompt-file $HP_GOAL_PERSONA_DIR/IMPL.md ${HP_GOAL_SETTINGS:+--settings $HP_GOAL_SETTINGS} --model ${HP_GOAL_IMPL_MODEL:-claude-sonnet-5[1m]}'"}`
— **keep the visibility trio**: `stream:true` + `--output-format stream-json --verbose` makes the
impl agent's turn readable in its pane (a bare `claude -p` prints nothing until it exits, so the
pane looks dead for the whole build), and `lingerSecs` holds the pane open after the queue drains
(the pane auto-closes when the runner exits, taking the scrollback with it). Add
`logDir:"<state-dir>/worker-logs"` when you want the raw transcript to outlive the pane.
`spawn_workers` gives each worker **its own pane** by default (`layout:"pane-per-worker"`), so
`count:N` = N readable panes; `layout:"single-pane"` multiplexes them into one if you'd rather.
The `--mcp-config` flag is required (see `SKILL.md` "MCP config on every spawned claude");
without it, account rotation hides `mcp__hyperpanes__*` tools from the impl agent.
`${HP_GOAL_SETTINGS:+--settings $HP_GOAL_SETTINGS}` likewise carries the user's statusline
(see `SKILL.md` "Statusline on every spawned claude") — harmless when the var is unset.
Expand All @@ -80,7 +87,9 @@ prompt wedges the pane).
## 3. Integrate & verify

- **Be the impl agents' advisor while the wave runs.** You're the higher-tier model that wrote the
spec, so you're on call: watch your inbox (`read_messages {paneId:<your $HYPERPANES_PANE_ID>}`)
spec, so you're on call. The app types a one-line `[hyperpanes] inbox: N new message(s)…` nudge
into your pane when mail lands while you're idle — when you see it, read and answer immediately.
Don't rely on it alone: also watch your inbox (`read_messages {paneId:<your $HYPERPANES_PANE_ID>}`)
for `<taskId>:` consults and answer fast (`send_message {to:<the `from` pane id on the message>,
from:"$HYPERPANES_PANE_ID", body:<crisp decision>}`). A 20-second answer here saves a thrown-away
subtask and a whole re-spec round-trip — this is the point of pairing your intelligence with their
Expand Down
Loading
Loading