Skip to content

PRODUCT.md — Open Question (OQ) Escalation Channel

PRODUCT.md — Open Question (OQ) Escalation Channel

Section titled “PRODUCT.md — Open Question (OQ) Escalation Channel”

A formal, durable channel by which a cmux sub-worker (running workflow-orchestration mid-Task) can escalate an Open Question (OQ) to its parent orchestrator session, and by which the parent’s decision can flow back to the worker. The channel supports both blocking OQs (worker pauses until the parent decides) and non-blocking OQs (worker continues; parent processes asynchronously). The channel exists so that the orchestrator-of-orchestrators pattern scales beyond ad-hoc OQ embedding in final-turn assistant text or out-of-band disk writes.

S61’s orchestrator-of-orchestrators pattern dispatches multiple parallel cmux sub-workers, each running the full workflow-orchestration skill on its own ID-N Task. Workers proceed independently — but a worker mid-Task can hit questions only the parent can resolve:

  • Spec ambiguity at the Planning phase (PRODUCT.md / TECH.md inconsistency).
  • Scope renegotiation request from a worker’s Executor.
  • Checker FAIL pattern requiring parent-level judgment.
  • A cross-Task dependency surfacing that breaks the sibling-only constraint.

Today, a worker has no formal way to ask. It can embed OQs in the final assistant turn before stop, or scribble to disk; either way the parent must poll or read post-hoc. This serialises an inherently parallel workflow and forces the parent to either over-poll or accept arbitrary latency.

  • Workers can emit OQs at any point in the Task lifecycle, durably, without losing state or terminating.
  • The parent can consume OQs in a deterministic order and reply with decisions that workers reliably receive.
  • Blocking and non-blocking semantics are both supported and explicit on emission.
  • Worker and parent both survive restarts mid-OQ without losing or duplicating outcomes.
  • Transport choice. Whether OQs travel via JSONL append, SQLite, named pipes, cmux send, or something else is a TECH.md concern. This spec describes only the semantics the transport must satisfy.
  • OQ classification. Deciding which OQs are “good” vs “premature” vs resolvable-locally is a workflow-orchestration concern, not a channel concern.
  • Parent-side UI. Any UI for the parent to browse, triage, or batch OQs is a future enhancement; this spec covers the wire-level behaviour only.
  • Multi-parent fan-in / OQ routing across orchestrator generations. A worker has exactly one parent and OQs go to that parent. Deeper trees are out of scope.

OQs are the surface. “User” here means two consumers of the surface:

  • Worker — the sub-Claude session emitting OQs and receiving decisions.
  • Parent — the orchestrator session reading OQs and writing decisions.

Numbered invariants below are testable from either side of the channel. They are grouped by dimension but share a single OQ-INV-N namespace so test ids remain stable as the spec evolves.

  1. OQ-INV-1. Each OQ is a self-contained record with at minimum: a unique oq_id stable for the lifetime of the OQ; the worker_id (or session id) of the emitter; an emitted_at timestamp (UTC, ISO-8601); a question field carrying the human-readable text the parent must answer; an urgency one of {low, normal, high}; a blocking boolean; and a context_ref pointing to enough information (e.g. file path, commit SHA, Subtask ID) for the parent to act without re-deriving the situation.
  2. OQ-INV-2. An OQ record is immutable once emitted. Corrections or refinements are expressed as new OQs (with a supersedes field referencing the original oq_id), never as in-place edits.
  3. OQ-INV-3. OQ emission is atomic from the parent’s perspective: the parent either observes the complete record or does not observe it at all. Partial records (truncated JSON, half-written rows) are never visible.
  4. OQ-INV-4. Per-worker FIFO ordering: if a worker emits OQ A before OQ B, any reader of that worker’s OQ stream observes A before B. No global cross- worker ordering is required.
  5. OQ-INV-5. OQ emission is durable: once the emitter’s emit call returns success, the record survives a worker crash, parent crash, or host reboot, and is observable by the parent on the next read.
  6. OQ-INV-6. OQ records are append-only from the worker’s perspective. A worker cannot retract an OQ; it can only emit a follow-up OQ that supersedes or cancels it (status: cancelled, with supersedes).
  1. OQ-INV-7. A worker MAY emit an OQ at any point during a Task — Planner phase, Executor phase, Checker phase, Curator phase, or between phases. The channel does not constrain when OQs are valid; workflow-orchestration does.
  2. OQ-INV-8. When a worker emits a blocking: true OQ, it MUST NOT make further progress on the Task that depends on the answer until a matching decision is observed. The worker MAY continue independent side-work (e.g. running tests, writing journal entries) that does not depend on the OQ.
  3. OQ-INV-9. When a worker emits a blocking: false OQ, it continues immediately. The OQ’s decision, when it arrives, is applied if still relevant, or discarded if the worker has already chosen a path that subsumes it.
  4. OQ-INV-10. A blocking OQ has at most one resolving decision. The decision either answers it (decision.outcome ∈ {answered, deferred, cancelled}) or directs the worker to stop the Task (outcome: abort_task). The decision schema mirrors the OQ: oq_id, decided_at, outcome, answer (free text), and an optional directive field for structured instructions.
  5. OQ-INV-11. Acknowledgement. A worker observes that its OQ has been seen when (and only when) it observes a corresponding decision record. There is no separate “seen-but-not-yet-decided” signal — absence of a decision means “no decision yet”. This keeps the protocol two-state and avoids spurious mid-state notifications.
    • Open question: is a lightweight received ack required for observability (e.g. so the parent can show “OQ open for 30 minutes” in a future UI), or does decision-or-nothing suffice?
  6. OQ-INV-12. Idempotent emission. If a worker emits an OQ, restarts before observing confirmation, and re-attempts the same OQ, the resulting state is indistinguishable from a single emission. Workers achieve this by deriving oq_id from stable inputs (Task ID, phase, content hash) so a re-emission produces the same id; the channel deduplicates by oq_id.
  7. OQ-INV-13. Cancellation. A worker MAY cancel an open OQ it emitted by writing a follow-up OQ with the same oq_id and status: cancelled. A cancellation is itself a record subject to OQ-INV-3 and OQ-INV-5. The parent SHOULD treat a cancelled OQ as resolved and not write a decision for it; if a decision arrives anyway, the worker ignores it.
  1. OQ-INV-14. Decisions are durable, atomic, and append-only with the same guarantees as OQ records (OQ-INV-3, OQ-INV-5, OQ-INV-6).
  2. OQ-INV-15. Decisions are addressed: every decision carries the oq_id it resolves. A worker’s decision-reader filters by oq_id; it never consumes a decision intended for another OQ.
  3. OQ-INV-16. At-least-once delivery. A decision written by the parent is observed by the worker at least once, provided the worker is alive long enough to read it. The worker tolerates duplicate observations of the same decision (idempotent application — applying the same decision twice yields the same worker-state as applying it once).
  4. OQ-INV-17. In-order-per-OQ. Only one decision per OQ is consumed. If duplicates appear, only the first observed decision takes effect; subsequent duplicates are no-ops. If the parent writes conflicting decisions for the same OQ (a protocol violation), the worker takes the first and surfaces the conflict via a new OQ.
  5. OQ-INV-18. Latency bound. A blocking OQ that has a corresponding decision written by the parent unblocks the worker within a configurable latency budget (default: 10 seconds wall-clock) on the worker’s next decision-polling cycle. The exact polling cadence is a TECH concern; the budget is the testable behaviour.
  6. OQ-INV-19. Decisions never carry executable code intended to be silently applied by the worker. A decision is advisory text plus an optional structured directive (e.g. {kind: 'abort_task'} or {kind: 'rewrite_spec', target: 'PRODUCT.md', section: '3.2'}); the worker decides how to act on it within its own workflow-orchestration rules. This preserves the worker’s auditability.
  1. OQ-INV-20. A worker with a blocking OQ open is in a distinct, parent-observable state — awaiting-decision — separate from working, stopped, crashed. The parent can enumerate workers by state without reading individual OQ streams.
  2. OQ-INV-21. When a worker enters awaiting-decision, it persists enough state to resume the blocked operation after the decision arrives. The worker treats the wait as a checkpoint boundary: on restart while blocked, it re-enters awaiting-decision and resumes polling, without re-executing the work that produced the OQ.
  3. OQ-INV-22. A worker with only non-blocking OQs outstanding remains in working (not awaiting-decision). Non-blocking OQs do not affect the worker’s lifecycle state.
  4. OQ-INV-23. When the parent observes a worker in awaiting-decision, it can list that worker’s open OQs deterministically (FIFO per OQ-INV-4), so the parent always knows what to answer first.
  5. OQ-INV-24. Worker stop events and OQ emissions are independent. A worker can stop (turn complete) with non-blocking OQs still open; the parent processes those OQs against the next worker turn or fleet-level review. A worker MUST NOT stop while a blocking OQ is open and undecided — it remains in awaiting-decision until resolved.
    • Open question: is there an upper bound on how long a worker may remain in awaiting-decision before the parent is required to either decide or abort? This spec defers the timeout policy to workflow-orchestration; the channel itself imposes no timeout.
  1. OQ-INV-25. No loss. No OQ that the worker considers successfully emitted is invisible to the parent on a subsequent read. Crash-then-read must yield the OQ.
  2. OQ-INV-26. No duplication, from the parent’s perspective. The parent observes each distinct OQ exactly once when iterating by oq_id. Re-emissions (OQ-INV-12) deduplicate to the same record.
  3. OQ-INV-27. No silent corruption. A record that fails an integrity check (missing required fields, malformed timestamp, unknown urgency) is surfaced as a channel error, not silently dropped. The reader fails closed: it reports the error and refuses to advance past the bad record until an operator resolves it.
  4. OQ-INV-28. Per-worker isolation. OQs emitted by worker A are not visible in worker B’s OQ stream. Cross-worker discovery, if needed, is the parent’s job (e.g. enumerating all worker directories).
  5. OQ-INV-29. Worker restart safety. A worker can crash and be relaunched with the same identity (same worker_id); on restart it can re-read its own OQ stream and the corresponding decision stream and re-derive whether each OQ is unresolved, decided, or cancelled, without parent involvement.
  6. OQ-INV-30. Parent restart safety. A parent can crash and be relaunched; on restart it can re-read every worker’s OQ stream and decision stream and re-derive the set of open OQs awaiting decision, without worker involvement. No in-memory state is required for correctness.
  7. OQ-INV-31. Observable provenance. Every OQ and every decision is traceable to its emitter via the record itself (worker_id / parent-session id) and to its temporal position (emitted_at / decided_at). The channel does not depend on filesystem metadata for provenance.
  1. OQ-INV-32. The worker’s contract: “I emit; if I see a decision for my oq_id, I act on it; if I crash and restart, I check before re-emitting; I never assume an OQ was received until I see a decision.”
  2. OQ-INV-33. The parent’s contract: “I read OQs in per-worker FIFO order; for each OQ I either write exactly one decision or leave it open; I tolerate seeing the same OQ across restarts; I never write a decision for an oq_id I have already decided.”

Collected from inline **Open question:** markers above:

  • OQ-Q1 (OQ-INV-11). Should the channel carry a received acknowledgement distinct from the decision, to support future parent-side UI (“OQ open for 30 min”)? The current spec deliberately keeps the protocol two-state to minimise surface area; revisit if observability needs grow.
  • OQ-Q2 (OQ-INV-24). Should the channel impose a maximum awaiting-decision duration before auto-abort, or is that strictly a workflow-orchestration policy concern? Current default: channel imposes none; orchestrator decides.
  • OQ-Q3. Should non-blocking OQs accumulate beyond a per-worker cap to prevent runaway emission from a confused worker? Out of scope here; revisit with the first integration that exposes the pathology.