0001 — Orchestrate the *loop runners; do not reimplement them¶
Status: accepted · Date: 2026-08-14
Context¶
Four autonomous session runners already exist and are mature in the one dimension
that is hardest to get right: claudeloop, codexloop, cursorloop, and
agyloop each classify a provider rejection into waitable rate-limit window vs
exhausted credits that only a human can fix, never block on a human, write
savepoints, and expose a mid-run control plane over a documented run directory.
Vibey needs an autonomous build phase. The obvious options are to build a fifth runner that talks to all four providers directly, or to drive the four existing ones.
Decision¶
Vibey drives the existing runners as subprocesses through a uniform
EngineAdapter. It never calls a provider API for build work. Each runner is an
engine: vibey builds its argv from a descriptor, spawns it, tails its
events.jsonl, writes its inbox/, and reads its snapshots.
Consequences¶
Good. The hardest, most vendor-specific logic — capacity classification, wait policy, never-blocking, session resumption — is inherited rather than rewritten four times. Each runner keeps improving independently. A fifth vendor is a new descriptor plus an adapter, not a new provider integration.
Bad. Vibey depends on four pre-1.0 projects that will drift. Their CLI surfaces already diverge (different effort vocabularies, different session verbs, different sandbox flags — see rotation-and-engines.md §1).
Mitigation, and it is the load-bearing part of this decision: an executable
conformance suite. Every descriptor claim — flags, state directory, run-dir
shape, snapshot schema, capacity mapping, done marker, control-plane behavior — is
asserted against the installed binary by vibey doctor --conformance. A failing
check marks that engine ineligible for rotation, not fatal. Vibey degrades to
three engines rather than crashing mid-cycle.
Alternatives rejected¶
- A fifth unified runner. Would duplicate the credits-vs-window logic four
times, and every provider change would be vibey's problem. The
*loopfamily exists precisely because that logic is subtle enough to deserve its own project. - Import the runners as libraries. Their public surface is a CLI; their Python internals are explicitly not a stable API, and importing four packages with conflicting vendor SDK dependencies into one process is a dependency-resolution problem with no good answer.
- A hosted LLM gateway (LiteLLM/OpenRouter) instead of the runners. A gateway routes model calls. It does not run an agentic coding session, manage a workspace, or resume across a five-hour rate-limit window. Wrong altitude.