Version: v0.2
Date: 2026-05-30
Phase: 66 (confirmed — 65 left for credit purchase; see §8)
Type: Scoping note (orientation-first)
Author surface: Claude.ai
Repos affected: loomworks (Operator Layer frontend) primary; loomworks-engine (substrate) for the pause preference and async pause; loomworks-record (this document)
Status: Draft for Operator review
Supersedes: v0.1 (phase renumbered 65→66; cost-display decision added per CC finding F; CC pre-flight findings folded into §6)
What this document does. It scopes the Operator-facing UX for credit spend that Phase 64 left out. Phase 64 built the spend mechanism — the gate, the metering, the FIFO draw-down — as substrate only, with no Operator-facing surface. A pipeline run today spends silently after a one-time up-front authorization. This note scopes what the Operator sees and approves at spend time.
What changed from v0.1. Three things. (1) Phase number is 66, not 65 — 65 is left for credit purchase. (2) The approval card shows the spend cap ("up to N credits"), not the exact cost — because CC confirmed the true cost cannot be known until after the model runs (finding F). (3) CC's pre-flight findings are folded into §6, including a load-bearing one: the async job park/resume state must be built nearly from scratch (finding B).
What it settles. Two things ship together in Phase A: (1) spend visibility on every metered room, and (2) a spend-pause preference, defaulted on, that surfaces an approval card before async spend debits — with an off-switch built into the card itself. The card shows the cost cap before approval; the visibility surface shows the actual cost after the run. A new Operator is taught the cost model by approving spend; once they understand it, they turn the pause off from inside the card. The destination — pausing synchronous Manifestation spend too — is queued as Phase B because it requires a substrate re-architecture that the Phase A design deliberately routes around.
What Operator decision is needed. Approve this scope, or redirect. The design arc that produced it is recorded in §7 so the trajectory is walkable. One sub-decision was made by Claude and is flagged for override in §4.4 (the off-switch governs all rooms with one preference, not per-room).
In scope: visibility (all rooms), pause preference (default on), async approval card showing the cost cap, in-card off-switch, re-enable affordance. Out of scope (Phase B): pausing synchronous Manifestation spend; the funding-source / commitment-keyed gate; anything depending on credit purchase (Phase 65 purchase work).
This note is written against the live substrate as inspected, not against the Phase 47/48 plan documents (which predate Phase 64 and describe a different surface inventory). The orientation facts below come from Claude Code read-only inspection of loomworks-engine and loomworks-record on 2026-05-30.
CR-2026-095, tag phase-64-pipeline-spend-credit-gate) is substrate only. The CR names "No Operator Layer frontend" in §1, §3.1, and §23. It is fully landed (Checkpoint A closed; there is no Checkpoint B). Manifest is at v0.45; latest closed phase is 64.RoomSpendRefusedError — it does not wait. (CC-verified signatures and call sites in §6.)/me/credits reports aggregate per-asset balances (one total per credit type), frontend-readable. It does not report per-lot or per-grant breakdowns.So the gap is exactly the Operator-facing surface, and the substrate already emits everything a visibility surface needs to read.
Every metered room run shows its spend to the Operator: the per-run cost and the resulting balance movement. The actual cost is read from the consumption flow the writer records after the run; balance from /me/credits. No substrate change is required for visibility — Phase 64 emits the flows; this surfaces them.
Visibility ships regardless of the pause preference's state. Pause off still shows spend; pause is an additional approval moment on top of visibility, not a replacement for it.
A person-level spend-pause preference, modeled on the existing persons.exhaustion_preference field and its settings surface (ExhaustionPreferenceSection.tsx). It is defaulted on — which requires one deviation from the mirror pattern: a server-side default (the existing field has none; see §6-C).
When on: async Shaping and Rendering spend parks before debiting and surfaces an approval card. The Operator approves or declines. This leans on the async job model plus the Phase 45 ApprovalCard dispatcher — but with a substrate caveat (§6-B): the park/resume state largely does not exist yet and is the build's largest piece.
When off: spend proceeds and is shown, not paused.
Synchronous Manifestation does not pause in Phase A — see §3.
The true cost of a run is not knowable before the run. CC confirmed (finding F) that cost is computed from input and output tokens, and output tokens only exist after the model produces them. At card-surface time, the actual figure does not yet exist.
Decision: the card shows the cap — "this run will cost up to N credits." N is the maximum possible spend, derived from input tokens plus the max_tokens ceiling. This is the only number that is true before the run: the spend can never exceed it. The Operator approves a real boundary, not a guess.
The visibility surface (§2.1) then shows the actual cost after the run lands — which is typically lower than the cap. This pairing teaches the cost model honestly: over a few runs the Operator learns both what a run can cost at most and what it usually costs, without the card ever asserting a precision the substrate cannot deliver.
Rejected alternatives: an estimate ("about N") — closer-sounding but can be wrong, and a teaching surface that teaches a wrong number is worse than one that teaches a true ceiling. Approve-blind (no number, show cost after) — defeats the card's purpose, which is cost at decision time.
Every approval card carries three actions: approve, decline, and "stop pausing — just show me spend from now on." The third flips the pause preference off in place, at the moment of friction, without a trip to settings.
This is the load-bearing piece of the design. Default-on serves teaching (every new Operator is taught costs by approving spend); the off-switch costs established Operators nothing (they graduate themselves the moment they decide they understand). It is also what makes async-only Phase A sufficient — Operators graduate past pause within a few runs, so the Manifestation gap (§3) is observable only during a short on-ramp.
Substrate note (§6-E): a literal third button is net-new on the ApprovalCard, but the dispatcher already passes action_params into the approve handler, so a "remember preference" write can ride the approve action without a new dispatch mechanism. CR drafting decides between a true third button and a remember-on-approve checkbox.
Turning pause off is not a one-way door. The spend-visibility surface carries an easy way to turn pause back on. (Off-switch reach is single-preference, all-rooms — see §4.4.)
CC confirmed the pause-and-await mechanism is architecturally available for async rooms and not for synchronous Manifestation:
Phase A pauses what can be paused now and routes around the re-architecture. Phase B is exactly the Manifestation synchronous-to-job re-architecture, isolated to where it is genuinely required. By the time Phase B lands, most Operators have turned pause off, so it is a completeness item, not a blocker. The Phase A pause-preference help text states the gap plainly: pause applies to rendering and shaping; Manifestation shows spend and proceeds until Phase B.
Earlier iterations tried to make the gate infer who wants approval (new vs. ongoing; committed vs. testing; funding source). Every inference required a substrate signal the gate could read at spend time, and inspection showed those signals either do not exist yet or are not reachable on the hot path (§7). An Operator-set preference sidesteps all of it: the Operator declares the behavior; the gate obeys.
Default-off teaches nothing — spend flows past a new Operator who does not yet know to turn pause on. The people who most need the approval moment are the ones who do not know to enable it. Default-on teaches the cost model by asking for approval, then lets the Operator graduate when they decide they understand.
The seed (loomworks-candidate-seed-v0_9) names: "Automatic state transitions on artifacts the Operator has authority over are a category error. … the system surfaces and signals; the Operator approves." Default-on makes the system's out-of-box stance "the Operator approves spend" — the seed's position as the default. The in-card off-switch makes relaxing it an exercise of Operator authority. "Only show what is available" holds: once pause is off, the cards stop appearing. The seed is silent on credits specifically; this design extends an existing Operator-preference pattern (exhaustion_preference) rather than inventing a new one.
Decision: the off-switch governs all rooms with one preference, with the re-enable affordance on the visibility surface (§2.5). Reasoning: per-room granularity adds preference fields and card-copy branches for a distinction a new Operator will not feel during the short window before they graduate. One toggle matches the exhaustion_preference single-field shape. Override: one word redirects this to per-room.
In scope (Phase A): - Visibility surface — actual per-run cost + balance movement, all metered rooms. - Spend-pause preference — person-level field (exhaustion_preference mold + server-default), default on. - Async park-and-surface — Shaping and Rendering park before debit; the park/resume state is net-new substrate (§6-B). - Approval card — shows the cost cap; carries approve / decline / stop-pausing. - Re-enable affordance on the visibility surface.
Out of scope — Phase B (queued): - Pausing synchronous Manifestation spend (requires the synchronous-to-job re-architecture).
Out of scope — depends on future phases: - Funding-source / commitment-keyed gate behavior (requires lot provenance on the hot path AND credit purchase, which is Phase 65; see §7). Recorded as a rejected-for-now path. - Per-lot / per-grant-kind balance readouts ("free credits remaining"). Not feasible frontend-only; needs a new substrate query and still misses referral-funded balance.
CC verified the substrate names and behaviors this scoping assumes. Folded in so CR drafting builds against confirmed facts.
A. Gate seam and insertion point. gate_room_spend(*, person_id, engagement_id, actor_kind, key_source, db) -> GateDecision (credit/gate.py). gated_room_complete(*, ctx, room, fallback_llm, system_prompt, user_prompt, max_tokens=4096, temperature=None, db) (credit/room_gate.py). Three call sites: agents/shaping.py, agents/render_specialist.py, engagement/manifestation_organization.py. The pre-debit park point sits inside gated_room_complete, between the gate check and the model call. A park branch raises a new pause signal caught in the agent ahead of the existing except handlers.
B. Async job park/resume — LOAD-BEARING, large. shaping_jobs has four states (queued/dispatched/completed/failed); no parked state, no resume — both built from scratch. render_jobs has a fifth state awaiting_external (Phase 34) — a park precedent, but its resume is poll-driven (external completion re-dispatches), not approval-driven. So Phase A needs: (1) a new pending-approval state on shaping_jobs and on render_jobs (distinct from awaiting_external); (2) an approval-driven resume that re-enters the agent at the spend point after the card is approved. The awaiting_external precedent re-dispatches a fresh continuation rather than resuming mid-function, so resume must decide: re-run gate+model after approval (re-cost) vs. cache pre-approval work. This is the largest piece of the CR and should carry its own checkpoint/halt threshold.
C. Preference field. persons.exhaustion_preference is TEXT NULL with a CHECK constraint, added in migration 0064, no server_default (default NULL). To default the spend-pause field ON, mirror the add_column + CHECK pattern but add a server_default — the one deviation from the mirror.
D. Settings surface. loomworks/src/app/settings/components/ExhaustionPreferenceSection.tsx — a client component reading current value via AuthProvider's /me, rendering an option list, PATCHing the engine on select. Clean template for a single-toggle spend-pause section.
E. ApprovalCard third action. The dispatcher registry is binary per action_type (approve handler + optional on_decline). No native third button. But dispatch_action(*, action_params: dict, …) passes the card's action_params (JSONB) into the handler — so "remember this preference" can ride in action_params written by the approve handler, or the frontend fires a separate PATCH /me/settings alongside approve. A literal third button is net-new; a preference-write on approve is already expressible.
F. Per-run cost before debit — NOT recoverable. Cost = tokens→credit conversion over input and output tokens; output tokens exist only after the model runs. At card-surface time only an estimate (input + max_tokens cap) is available. The true figure exists only as the post-model consumption flow. This is why §2.3 shows the cap, not the exact cost.
G. Phase number. No phase-65 is formally reserved; the Phase 64 CR's "purchase is Phase 65+" is an anticipation, not a reservation. Highest built/tagged phase is 64. Operator decision (this revision): this work takes 66; 65 is left for purchase.
Recorded so a Discovery record can reconstruct the path, not just the destination.
grant_kind) is a join away and absent for referral balance; the gate reads aggregate balance and never touches lots. Decisively: no committed credit exists yet — purchase is Phase 65 — so every metered Operator is on free credits today. The distinction the gate would key on does not exist.The signature of the convergence: each move made the build smaller and the seed fit tighter at the same time.
Resolved: this work is Phase 66. Phase 65 is left for credit purchase. CR drafting stamps all artifacts 66 and lands them under loomworks-record/phases/phase-66-spend-visibility-and-pause/.
loomworks-record/phases/phase-66-spend-visibility-and-pause/ (the per-phase convention CC confirmed; the earlier cr-drafting-handoffs/ path was wrong and is dropped). Ask Claude Code to create the phase directory and commit both files there. Downloads is staging only.loomworks-phase-66-cr-drafting-handoff-v0_2). Most of §6 is already verified, so the drafting pass focuses on the build-step structure and the finding-B park/resume design, which carries its own checkpoint.