The old hook was silent about why it did or didn't launch an agent —
when nothing fired, it was impossible to tell whether the event was
gated out, a pending chunk was served, or spawn_agent silently
returned None. Logs now cover each of those cases:
hook_event=... surface_hooks=[...] gated_in=true/false
serving pending chunk (N bytes); skipping agent cycles
surface-observe: spawned pid 501605 (log: ...)
surface-observe: spawn_agent returned None
surface-observe: skipped — pid 501605 in phase "surface"
Rename surface_phase_busy to surface_phase_blocker and have it
return the pid + phase that's blocking, so stale-pid scenarios are
diagnosable from the log instead of needing a live ps walk.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
The surface_observe_cycle check was gated on \`self.agent_running("surface-observe")\`,
which returns true as long as any surface-observe agent has a pid in
\`self.agents\` — regardless of which phase that agent is in. So even
after the bail script learned to allow "one in surface + one in
post-surface" concurrency, the orchestrator still refused to spawn a
second agent for the whole cycle duration.
Replace the in-memory gate with an on-disk phase check that matches
the bail script's view of the world: scan the state dir for live
\`pid-*\` files (same ones the bail script reads and keeps fresh),
and only skip spawning if some live agent is currently in the
\`surface\` phase. If the existing agent has moved on to
\`organize-search\` / \`organize-new\` / \`observe\`, the new surface
agent can start while the old one finishes its tail.
The older agent's child handle gets dropped when \`self.agents[surface-observe]\`
is overwritten on spawn — OS reaps when it exits. That's fine: the
hook is short-lived, and cross-invocation liveness is checked via
\`libc::kill(pid, 0)\` on restored pid state, not via held child handles.
Also drop the "agent is N KB behind, sleep 5s" block. That loop only
made sense when spawns were serialized — its purpose was to give a
running agent a chance to complete before the current hook returned.
With parallelism, there's nothing to wait for: either a new surface
agent just started (no point waiting on it), or the existing agent
is in the surface phase and will finish when it finishes.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Now connects to ~/.consciousness/mcp.sock and forwards tool calls to
the consciousness daemon instead of calling tool handlers directly.
Requires the consciousness daemon to be running with MCP server.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>