host_mode_plan_v0¶
host_mode_plan_v0 is a public-safe host-mode selector for LoopX workflows. It
sits above the shipped LoopX Turn and
runtime connector catalog: it chooses the
user-facing host mode from intent and advertised host capabilities, then prints
the matching preview command. It is not a launcher, scheduler, permission grant,
validator, or second source of truth.
The problem it solves is operational ambiguity. A user may say "keep working when I close the visible UI", "let chat create work", or "wake from a timer". Those are different host modes, but all must preserve the same LoopX invariants: scoped agent identity, quota guard before work, no-spend quiet checks, independent validation, durable writeback, and quota spend only after validated writeback.
Boundary¶
The selector returns mode=dry_run_host_mode_selector. It must not start a
process, open a session, arm a timer, call a chat gateway, validate a result,
write LoopX state, or spend quota. For any execution path, loopx_turn_v0,
TurnEnvelope, registry, quota, todo projection, and run history remain
authoritative.
For headless execution, the selector maps to loopx turn plan and then the
existing Turn lifecycle:
That mapping is the key product value: the selector makes mode choice visible without inventing a parallel runner or a second workflow authority.
Modes¶
| mode | connector / contract | good fit | required capability |
|---|---|---|---|
visible_tui |
codex_cli_tui connector |
user wants to watch or steer each turn | visible_session |
isolated_headless_turn |
loopx_turn_v0 with generic-cli and isolated-headless |
bounded unattended work through typed host results | loopx_turn, typed_host_adapter, independent_validator |
im_gateway |
gateway/webhook connector | chat or another surface should create durable work | chat_gateway |
shell_service |
shell worker plus LoopX Turn | cron, launchd, service timer, or manual shell wakeup | service_timer, shell, loopx_turn, typed_host_adapter, independent_validator |
hybrid_handoff |
explicit transition contract | one mode should escalate or continue in another | at least two concrete modes ready |
Intent And Capability Signals¶
Public-safe user_intent signals fail closed when unknown:
watch_each_turn->visible_tui;continue_without_ui->isolated_headless_turn;intake_from_chat->im_gateway;timer_keepalive->shell_service;escalate_between_modes->hybrid_handoff.
Public-safe host_capabilities signals are:
visible_session;loopx_turn;typed_host_adapter;independent_validator;chat_gateway;service_timer;shell.
The first intent selects selected_mode. Every mode option still reports
capability_ready so an operator can see whether the selected host can actually
run the desired mode.
Visible Host Identity¶
A coarse visible_session capability cannot distinguish Codex CLI, Claude Code,
or another generic visible host such as OpenCode. The planner therefore fails
closed for visible_tui unless an explicit, catalog-registered host_identity
is supplied (--host-identity: codex-cli, claude-code, generic-cli, or
the opencode alias). With no
identity, the visible option reports connector_id=null,
host_resolution=identity_required, turn_mapping.host=null,
capability_ready=false, a blocking reason naming the missing identity, and a
stop-first next step; no Codex CLI default is fabricated. An unregistered
identity similarly yields connector_id=null with
host_resolution=unregistered_host_identity. The resolution state lives in the
separately typed host_resolution field so the connector_id field only ever
carries real runtime connector catalog ids.
With an identity, the typed mapping is used: codex-cli -> codex_cli_tui,
claude-code -> claude_code_loop, and generic-cli -> opencode_goal_loop
(the OpenCode visible goal loop runs through the generic-cli Turn host;
--host-identity opencode is accepted as an alias for the same mapping). Every
emitted connector id must exist in the runtime connector catalog; an identity
with no registered catalog connector fails closed instead of emitting a dynamic
string.
Readiness And Proofs¶
Readiness fails closed: a mode only reports capability_ready=true when every
capability listed in required_host_capabilities is advertised. Because
shell_service proves typed host results and independent validation, those
capabilities are part of its requirement; a host with only service_timer,
shell, and loopx_turn is not ready. Each mode option also reports
missing_host_capabilities, human-readable blocking_reasons, and
recommended_next_steps so an operator knows what to fill in before attempting
the mode. When a mode is not ready, the first recommended step is stop: the
operator fills the gaps and re-runs the selector before any preview command is
treated as runnable. When im_gateway is ready, its steps are intake-first:
create durable work from the chat/webhook surface, confirm LoopX state is safe
to proceed, then choose an execution mode (isolated_headless_turn for
unattended runs or visible_tui for user gates) without treating the gateway
as an executor.
Shape¶
{
"schema_version": "host_mode_plan_v0",
"mode": "dry_run_host_mode_selector",
"agent_model": "peer_v1",
"goal_id": "loopx-meta",
"agent_id": "codex-main-control",
"selected_mode": "isolated_headless_turn",
"selected_connector_id": "loopx_turn",
"selected_turn_mapping": {
"host": "generic-cli",
"execution_mode": "isolated-headless",
"scheduler_owner": "outer_controller",
"plan_command": "loopx turn plan --goal-id loopx-meta --agent-id codex-main-control --host generic-cli --execution-mode isolated-headless --scheduler-owner outer_controller"
},
"next_preview_command": "loopx turn plan ...",
"mode_options": [],
"identity_contract": {},
"no_spend_policy": {},
"turn_contract": {},
"transitions": [],
"boundary": {},
"truth_contract": {}
}
Each mode_options[] entry includes the connector id, readiness, required
host capabilities, Turn mapping when one exists, scheduler execution context,
quota guard command, and required proofs.
Functional Points¶
The selector provides four concrete functions:
- Mode choice: turn user intent into a named host mode instead of forcing users and agents to infer visible/headless/gateway/timer behavior manually.
- Turn mapping: for unattended execution, print the exact
loopx turn planpreview that preserves host, execution mode, scheduler owner, agent id, and available capabilities. - Readiness surface: report which advertised capabilities are missing before a mode can be trusted.
- Safe handoff plan: name transitions such as visible bootstrap to isolated headless Turn, headless user-gate escalation back to visible TUI, gateway intake to Turn, and shell timer to visible escalation.
Acceptance Checks¶
A fixture or implementation is acceptable when:
schema_version=host_mode_plan_v0andmode=dry_run_host_mode_selector;- the five canonical modes are present and intent selects the expected mode;
isolated_headless_turnmaps toloopx turn plan --host generic-cli --execution-mode isolated-headless --scheduler-owner outer_controller;- scoped identity flows into Turn and quota preview commands as
--agent-id; - the no-spend policy covers selector previews, Turn plan previews, quiet monitors, cadence-only changes, and final/readiness checks;
- the boundary says the selector does not execute, write, spend, infer production permission, infer credential access, or infer destructive authority;
- no selected mode reports
capability_ready=truewhile a capability-backed required proof is unavailable (includingshell_servicerequiring bothtyped_host_adapterandindependent_validator); - visible
host_identityis required whenvisible_tuiis selected, is preserved for distinct hosts such as Codex CLI and Claude Code in the connector id, Turn mapping, and preview commands, and every emitted connector id exists in the runtime connector catalog (unresolved mappings emitconnector_id=nullplus a typedhost_resolutioninstead of a non-catalog value); - handoffs preserve the selected agent id and expose target readiness; and
- unknown intent or host capability values fail closed with suggestions.