← Back to LoopUp
architecture

What LoopUp actually does

The technical account: the run model, each stage's real contract, the approval gate, replay, and what "verified" means. Live at loopmachine.coey.dev.

Five small production services compose into one durable, resumable, human-authorized run that ends in an independently re-checkable result and a newly minted reusable tool — with no service copying another's code.

01The run is a Durable Object, not a request

A run is not a synchronous HTTP call that dies with the request. POST /api/runs creates a Durable Object (LoopupRun) with SQLite storage holding the run's identity, its stage table, and every stage's receipt. The DO advances itself with an alarm-driven stage machine: each stage runs, writes its result to SQLite, broadcasts to any watchers, then schedules the next stage via storage.setAlarm(). That's why a run survives the request, can be polled or streamed, blocks for human approval, and can be replayed later.

POST /api/runs {goal}
  -> DO LoopupRun created, SQLite row written, alarm set
       stage 0  backwards    -> receipt -> alarm
       stage 1  manyface     -> receipt -> alarm
       stage 2  temper       -> receipt -> alarm
       stage 3  deja-shared  -> receipt -> alarm
       stage 4  airdesk      -> status = awaiting_approval, HALT (no alarm)
                               [human approves / denies] -> alarm
       stage 5  approval     -> pass only if status === approved
       stage 6  mint         -> promote composite -> new tool
       stage 7  done         -> status = completed

02Composition, not absorption

Every stage calls another service's live HTTP API with fetch. LoopUp holds adapters and the run engine; it contains none of the five services' internals. If a stage's service is unreachable, that leg is recorded as a real error and marked unproven — the run does not fabricate a result.

StageLive serviceWhat it contributes
backwardsbackwards.coey.devTurns the goal into concrete checks; a failed check (verified-disproved) is surfaced honestly, not hidden.
manyfacemanyface.coey.devRaces candidate answers; a critic attacks each; returns the winner plus every loser's verdict.
tempertemper.coey.devReuses a prior proven tool if one exists for this goal shape; otherwise promotes the winning move into a new one.
deja-shareddeja-shared.coey.devInjects a prior learned instinct if one exists; records this run's episode (consented, redaction-aware).
airdeskairdesk.coey.devRuns the irreversible step to awaiting_signature, then blocks for a human decision.

03The approval gate is a real halt

At the airdesk stage the run sets status = awaiting_approval and stops scheduling alarms. Nothing proceeds until a human acts:

Approval binds to the exact artifact hash shown. Send a hash that doesn't match the run's approvalArtifactHash and you get 409 approval void. If the underlying result changed, a prior approval is meaningless — you must approve the thing you actually saw.

04What "verified" means, honestly

GET /api/runs/:id?replay=true re-fetches each upstream receipt and recomputes the evidence root. The verdict distinguishes two kinds of leg, because they are not equally re-derivable:

deterministic  backwards, manyface

Same input yields the same receipt. Their stored hash must recompute exactly. This is the tamper-evident backbone — if these don't match, something was altered.

stateful  temper mint, deja append, airdesk task

Each call creates a new artifact (a new tool, a new memory entry, a new task), so their hash is not re-derivable by anyone. They are verified as recorded from a live call, and the receipt says so plainly rather than faking a match.

verified: true means every deterministic leg re-verified exactly and every leg actually ran. This is deliberately narrower than "all hashes identical," because claiming a stateful leg is hash-stable would be dishonest.

05The recursion

After a fully-approved run, LoopUp takes its own composite evidence root and posts it to temper /api/promote, minting a loopup-orchestration tool whose provenance points at the run that proved the whole stack. A tool built by composing tools. The mint is downstream of the evidence root and is deliberately not folded back into that root — that would be circular.

06HTTP contract

POST /api/runs              {goal}          -> {runId}
GET  /api/runs/:id                          -> {run: {...stages, status, receipts}}
GET  /api/runs/:id?replay=true              -> {run, replay: {verified, evidenceRoot, legChecks}}
GET  /api/runs/:id/ws                       -> WebSocket, streams stage transitions
POST /api/runs/:id/approve  {artifactHash}  -> 200 | 409 (hash mismatch, void)
POST /api/runs/:id/dev-sign {artifactHash}  -> 200 {note: demo signer}
POST /api/runs/:id/deny     {reason?}       -> 200, run resumable
GET  /api/status                            -> {ok, service, timestamp}

Run status values: running, awaiting_approval, approved, completed, failed, denied.

07Frontend progress model

The page drives progress by polling GET /api/runs/:id every 1.5s, not by WebSocket alone. The backend reaches the approval gate in about 8 seconds — often before a socket finishes handshaking — so polling is the primary driver and the WebSocket is an optional low-latency enhancement.

08Cloudflare primitives used

09Honest limits