Problem / motivation
In loop-graph, when the executor closes a milestone it flips the ledger gate to
pending-audit and then keeps cheap-ticking until the supervisor's next
scheduled wake happens to notice it. The supervisor polls; it is never told.
On interval hosts this adds latency and burns idle ticks.
Idea: an event-driven wake — when the executor flips a gate to pending-audit
(or writes a terminal ledger status), it rings the supervisor directly instead of
letting it poll. Record the supervisor's address (host + session/thread id) in
ops.md at setup; the executor uses it to signal.
Design (in octopus terms)
- This is a new directed edge (executor → supervisor), not a new node. Add it
per the model.md / CONTRIBUTING edge rules.
- A doorbell on the mailbox, not a second mailbox. The wake is content-free
("go read the ledger now"). All state stays in the ledger (single writer =
executor, unchanged). On wake the supervisor does exactly what it does on a poll:
read ledger → audit the pending-audit gate. → no second scoreboard, invariants
intact.
ops.md holds the supervisor address (host + session/thread id): operational
metadata, written once at setup, config-like — not a contended edit, not the
ledger, not directives. Label it best-effort routing (it can go stale).
Host support (researched against source)
- Codex — ✅ native. First-class thread-coordination tools:
send_message_to_thread(threadId, msg) (+ wait_threads, create_thread,
handoff_thread). Executor →
send_message_to_thread(<supervisor threadId from ops.md>, "M2 pending-audit")
wakes/queues a turn on the supervisor thread. (Codex Desktop surface.)
- grok — ❌ no native cross-session wake. Read the source:
/loop → scheduler_create delivers only to its own parent_session_id
(input fields: task_id/interval/prompt/recurring/durable/foreground/fire_immediately
— no target-session field). The Notification hook fans out to external
HTTP/shell (owner ping), not into another session. session_admin = fork / rename /
delete only. The leader IPC (unix socket) is multi-view on one agent, not
agent-to-agent messaging. → keep poll/park on grok.
- Claude Code / Cursor / shell. Claude Code's adaptive self-paced re-invoke already
minimizes park latency; no cross-session inject. shell: you own both loops — a
touch + the other loop's poll is the crude equivalent.
Must be an optimization, not a replacement
Recorded session ids go stale (ephemeral sessions; grok's scratch dir is deleted
when a goal ends; Codex threads can be archived). Keep the interval-poll / adaptive
re-invoke as the guaranteed backstop; the wake only cuts latency. Same slot as the
existing grok hooks — "optional reliability upgrade, not required for the base
pattern."
Irony worth recording
The wake helps most on interval hosts (grok / Cursor / shell) where the supervisor
sleeps a fixed cadence — but the main interval host, grok, can't do it. The host
that can (Codex) is adaptive, so the win is smaller (still real: precise wake the
instant the gate flips, and the supervisor loop can sit truly idle between rings
instead of cheap-ticking).
Governance / next step (do not merge into templates yet)
Per the anti-bloat rule, no pattern enters the templates without a real consumer.
Pilot on one real Codex loop-graph run and confirm both:
- the ring actually re-activates an idle supervisor thread, and
- with the session id removed/stale, the poll/adaptive backstop still catches
the pending-audit gate (graceful degrade).
If it holds:
- add a "cross-session wake" capability row to
lib/host-dialects.md
(Codex ✅ · grok ❌ · Claude Code ➖ self-paced covers it · Cursor ❌ · shell ⚠️
you own both loops), and
- add an optional wake step to the executor + supervisor templates — host
specifics behind host-dialects.md, templates staying host-agnostic.
Problem / motivation
In loop-graph, when the executor closes a milestone it flips the ledger gate to
pending-auditand then keeps cheap-ticking until the supervisor's nextscheduled wake happens to notice it. The supervisor polls; it is never told.
On interval hosts this adds latency and burns idle ticks.
Idea: an event-driven wake — when the executor flips a gate to
pending-audit(or writes a terminal ledger status), it rings the supervisor directly instead of
letting it poll. Record the supervisor's address (host + session/thread id) in
ops.mdat setup; the executor uses it to signal.Design (in octopus terms)
per the model.md / CONTRIBUTING edge rules.
("go read the ledger now"). All state stays in the ledger (single writer =
executor, unchanged). On wake the supervisor does exactly what it does on a poll:
read ledger → audit the
pending-auditgate. → no second scoreboard, invariantsintact.
ops.mdholds the supervisor address (host + session/thread id): operationalmetadata, written once at setup, config-like — not a contended edit, not the
ledger, not directives. Label it best-effort routing (it can go stale).
Host support (researched against source)
send_message_to_thread(threadId, msg)(+wait_threads,create_thread,handoff_thread). Executor →send_message_to_thread(<supervisor threadId from ops.md>, "M2 pending-audit")wakes/queues a turn on the supervisor thread. (Codex Desktop surface.)
/loop→scheduler_createdelivers only to its ownparent_session_id(input fields:
task_id/interval/prompt/recurring/durable/foreground/fire_immediately— no target-session field). The Notification hook fans out to external
HTTP/shell (owner ping), not into another session.
session_admin= fork / rename /delete only. The leader IPC (unix socket) is multi-view on one agent, not
agent-to-agent messaging. → keep poll/park on grok.
minimizes park latency; no cross-session inject. shell: you own both loops — a
touch+ the other loop's poll is the crude equivalent.Must be an optimization, not a replacement
Recorded session ids go stale (ephemeral sessions; grok's scratch dir is deleted
when a goal ends; Codex threads can be archived). Keep the interval-poll / adaptive
re-invoke as the guaranteed backstop; the wake only cuts latency. Same slot as the
existing grok hooks — "optional reliability upgrade, not required for the base
pattern."
Irony worth recording
The wake helps most on interval hosts (grok / Cursor / shell) where the supervisor
sleeps a fixed cadence — but the main interval host, grok, can't do it. The host
that can (Codex) is adaptive, so the win is smaller (still real: precise wake the
instant the gate flips, and the supervisor loop can sit truly idle between rings
instead of cheap-ticking).
Governance / next step (do not merge into templates yet)
Per the anti-bloat rule, no pattern enters the templates without a real consumer.
Pilot on one real Codex loop-graph run and confirm both:
the
pending-auditgate (graceful degrade).If it holds:
lib/host-dialects.md(Codex ✅ · grok ❌ · Claude Code ➖ self-paced covers it · Cursor ❌ · shell
you own both loops), and
specifics behind
host-dialects.md, templates staying host-agnostic.