ghostex-fable-56-orchestration
Plan → implement → verify → fix pipeline over Ghostex panes:
- Planner: the invoking session (you), inline. No planner pane.
- Workers: Codex
gpt-5.6panes, one per phase. - Verifier: one Fable (
claude-fable-5) pane, reused across re-verification rounds. - Fixers: additional Codex panes, one per independent verifier finding.
Use $ghostex-cli conventions for all CLI interaction; run
ghostex --help when unsure about command shapes.
Step 0: Efforts and context
Resolve efforts. If the skill args contain
fable=<effort>and/orcodex=<effort>(also accept5.6=<effort>), use them. Otherwise ask the user two questions before doing anything else:- Fable effort for the verifier:
low|medium|high(defaulthigh) - Codex effort for workers/fixers:
low|medium|high|xhigh(defaulthigh)
- Fable effort for the verifier:
Get placement so panes land in the right project regardless of UI focus:
ghostex state ghostex sessions --jsonRecord
projectIdandgroupIdand pass them explicitly to everycreate-sessioncall.
Step 1: Plan inline, write the plan file
You are the planner. Explore the repo as needed, then write the plan to
docs/<today YYYY-MM-DD>/fable-plan-<slug>.md. Workers start with zero
context, so every phase must be self-contained. Required structure per phase:
## Phase <n>: <title>
- depends_on: [<phase numbers>] # empty list = can start immediately
- parallel_ok: true|false # true only if files are disjoint from every phase it could overlap with in time
- goal: <one paragraph, concrete>
- files: <exact files/dirs to touch>
- do_not_touch: <files/areas that other phases own>
- approach: <enough detail that a fresh agent will not re-derive the design>
- acceptance_criteria:
- <verifiable statement, ideally with the command that proves it>
Also include a short header section with the overall goal and any repo rules the workers must follow (e.g. no tests in the macOS app or gpui trees, no fallback-instead-of-fix). Show the user a one-line-per-phase summary of the plan before launching workers.
Step 2: Launch Codex workers per phase
Respect the dependency graph: launch a phase only when everything in its
depends_on has printed its COMPLETE sentinel. Launch phases concurrently
only when every currently-running phase involved is marked parallel_ok: true
with disjoint files. When in doubt, run sequentially.
Pane launch sequence (use this exact shape for workers, fixers, and the verifier)
Do NOT rely on create-session --input to run anything: gxserver creates
session rows lazily and startup text never types on its own. Always pass
--start (materializes the live terminal immediately) and then send the agent
command as text plus a separate Enter:
ghostex create-session "P<n>: <short title>" --project-id <projectId> --group-id <groupId> --start --json
# record .session.sessionId from the output as <sessionId>
ghostex send-text <sessionId> "codex --model gpt-5.6 -c model_reasoning_effort=<codexEffort> --dangerously-bypass-approvals-and-sandbox 'Read Phase <n> in <plan-file path> and implement exactly that phase. Do not touch anything listed in do_not_touch. When every acceptance criterion passes, print a 5-line summary of what you changed and then print PHASE <n> COMPLETE as the very last line of your final message. If you cannot proceed, print the details and then PHASE <n> BLOCKED: reason as the very last line, and stop.'"
sleep 1
ghostex send-enter <sessionId>
The sentinel must be the LAST line the agent prints. Anything the agent outputs after the sentinel (traces, summaries, tips) pushes it up the scrollback and out of the monitor's tail window — never ask for "SENTINEL followed by details".
Why separate sends: a trailing Enter inside the same send burst gets swallowed
as pasted text by bracketed-paste TUIs (Claude Code especially), leaving the
message staged but never submitted. send-text + send-enter are independent
zmx sends and work on every server version. (send-message performs both, but
always verify submission when you use it.)
Verify the launch/submission before moving on — this applies to every message you send into any pane, including the verifier:
sleep 8
ghostex read-text <sessionId> --lines 15
If the pane still shows your typed command/message sitting unsubmitted on the
composer/prompt line, run ghostex send-enter <sessionId> again and re-check.
If the pane shows only a shell prompt with no typed text, the send targeted a
dead terminal — re-run the launch sequence from send-text.
Rename the agent session once submission is confirmed, for every pane you start (workers, fixers, verifier), so the agent thread is findable and resumable later if the pane is closed or the run is interrupted:
ghostex send-text <sessionId> "/rename P<n>: <short title>"
sleep 1
ghostex send-enter <sessionId>
Use the same title as the pane. Slash commands are safe to send while the agent is working.
Keep the inline prompt on one line and free of apostrophes/backslashes so shell quoting survives. Details that must be long belong in the plan file, not the launch prompt.
For sequential phases, include a 2-3 line summary of what the previous phase
actually did (from its COMPLETE summary) by appending it to a
## Handoff notes section of the plan file before launching the next worker.
Monitoring
Use the CLI's built-in sentinel monitor — do not hand-roll read-text polling loops:
ghostex wait-for-text <sessionId> "^\s*(. )?PHASE <n> (COMPLETE|BLOCKED)" --timeout-seconds 2700
wait-for-textpolls the pane, matches the regex per scrollback line, cross-checks session liveness every poll, and exits0printing the matched line, or1with a reason on timeout / dead pane. Add--jsonfor structured output,--interval-seconds n(default 20) and--lines n(default 200) to tune.- ALWAYS anchor sentinel patterns to the line start (
^\s*plus an optional bullet prefix): agents stream their reasoning into the pane, and an unanchored pattern will false-match a sentinel mentioned mid-sentence ("...then I should print PHASE 1 BLOCKED"). Note the bullet Codex prefixes final messages with is a non-ASCII•— match it with.(any char) or(• )?, never assume plain ASCII. - NEVER shrink
--linesto dodge a stale sentinel from an earlier round.wait-for-textmatches only the last N lines of scrollback; if the agent prints anything after the sentinel (or the TUI adds tips/usage lines), a small window will structurally never contain it and the monitor blinds itself. This exact failure has happened: a worker printed• PHASE 3 COMPLETEfollowed by a 30-line trace, a--lines 15monitor polled the tail forever, and the miss was misdiagnosed as a dead-pane race. Keep the default 200-line window; disambiguate rounds with a new token instead (next bullet). - On
BLOCKED: read the reason, fix the plan or unblock it yourself if trivial (answer via the send-text/send-enter sequence), otherwise stop and ask the user. When you unblock, the oldPHASE <n> BLOCKEDline is still in scrollback and will false-match — change the token, not the window: tell the worker to end withPHASE <n> COMPLETE ROUND 2(orPHASE <n> BLOCKED ROUND 2: reason) as the last line, and re-arm the monitor on"^\s*(. )?PHASE <n> (COMPLETE|BLOCKED) ROUND 2". - On exit 1, diagnose before acting:
ghostex read-text <sessionId> --lines 100for the scrollback andghostex sessions --jsonfor lifecycle state. If the worker is genuinely stuck, report to the user rather than killing blindly. - Exit 1 with
session "<zmx>" does not existdoes not necessarily mean the worker died mid-task: the agent may have finished and the user closed the pane before your next poll. Checkghostex sessions --jsonfor a restored or renamed session (the /rename step makes it findable) and report what you know to the user instead of assuming failure. - Leave completed worker panes open. Do not kill or sleep them; the user inspects and cleans up manually.
Step 3: Fable verification
When all phases are COMPLETE, launch one Fable verifier pane using the same
pane launch sequence from Step 2 (create-session --start, send-text,
send-enter, verify submission), with this pane title and prompt:
Title: "Verify: <slug>"
Prompt: claude --model claude-fable-5 --effort <fableEffort> --dangerously-skip-permissions 'Read <plan-file path>. Independently verify every acceptance criterion in every phase against the actual working tree: read the code, run the proving commands, do not trust the handoff notes. If anything fails, first print numbered findings, one per line, formatted as FINDING <k>: phase=<n> criterion=<which> evidence=<file:line or command output> fix=<what a fixer agent must do>. Then end your reply with exactly one final line: VERIFICATION PASSED or VERIFICATION FAILED. The verdict must be the very last line you print.'
Then monitor:
ghostex wait-for-text <verifierSessionId> "^\s*VERIFICATION (PASSED|FAILED)" --timeout-seconds 3600
Step 4: Fix loop (max 3 rounds)
On VERIFICATION FAILED:
Parse the findings. Group findings that touch the same files into one fixer; independent findings get their own fixer panes (parallel only if files are disjoint).
Launch each fixer with the Step 2 pane launch sequence, title
"Fix <k>: <short>", prompt:codex --model gpt-5.6 -c model_reasoning_effort=<codexEffort> --dangerously-bypass-approvals-and-sandbox 'Read <plan-file path>. The verifier found this issue: <finding text>. Fix the root cause properly, no fallbacks or workarounds. When the relevant acceptance criteria pass, print a short summary and then FIX <k> COMPLETE as the very last line; if blocked print FIX <k> BLOCKED: reason as the very last line.'Monitor each with
ghostex wait-for-text <fixerSessionId> "^\s*(. )?FIX <k> (COMPLETE|BLOCKED)" --timeout-seconds 2700.When all fixers are COMPLETE, reuse the existing verifier pane so it keeps its context — do not launch a new one. Send the re-verify request with the separate-Enter sequence and confirm it actually submitted (this is the send most prone to the staged-but-unsubmitted failure, because the verifier is an idle Claude Code composer):
ghostex send-text <verifierSessionId> "Fixes applied for findings <list>. Re-verify only those findings, not the full plan. Print findings first if any, then end with exactly one final line: ROUND <r> VERIFICATION PASSED or ROUND <r> VERIFICATION FAILED." sleep 1 ghostex send-enter <verifierSessionId> sleep 8 ghostex read-text <verifierSessionId> --lines 10 # must show the agent working, not the staged messageThen wait for the round verdict. The old verdict is still in scrollback, which is why each re-verification round gets its own token — monitor with
ghostex wait-for-text <verifierSessionId> "^\s*(. )?ROUND <r> VERIFICATION (PASSED|FAILED)" --timeout-seconds 3600Do not try to dodge the stale verdict by shrinking
--lines— a small tail window can miss the new verdict entirely if anything prints after it. Keep the default window; the round token is the disambiguator. Sanity-check the verdict against the visible scrollback before acting.Repeat at most 3 rounds total. If findings survive round 3, stop and report the remaining findings to the user with the verifier pane id so they can inspect.
Step 5: Final report
Tell the user: plan file path, per-phase outcome, verifier verdict, fix rounds used, and the list of open panes (workers, fixers, verifier) they may want to review or close.
Boundaries
- All pane control goes through the Ghostex CLI, never raw zmx/tmux.
- Never restart the Ghostex app or run
bun run start. - Model/launch strings are pinned: workers/fixers
codex --model gpt-5.6, verifierclaude --model claude-fable-5. If a launch fails on a model or effort value, surface the exact error to the user instead of guessing an alternative model.