Skip to content

fix(harness): stop telling a token-billed operator to wait for a subscription - #11

Merged
dibenkobit merged 10 commits into
mainfrom
worktree-harness-naming-audit
Aug 7, 2026
Merged

dibenkobit merged 10 commits into
mainfrom
worktree-harness-naming-audit

Conversation

@dibenkobit

@dibenkobit dibenkobit commented Aug 7, 2026 •

Copy link
Copy Markdown
Member

The harness layer was built when every harness ran on a subscription, and it kept that vocabulary
after DeepSeek and Muse Code arrived on token billing. The names were the visible half —
SubscriptionCliHarness is the base class all five extend, and HarnessCapabilityGaps.SUBSCRIPTION
is what DeepSeek raises to say it holds no API key — but the sentences underneath had gone wrong
with them, and three of those are shown to the operator.

A DeepSeek operator whose wallet ran dry was told to wait for an allowance that never resets, to
raise a plan that does not exist, and that "usage-based API billing is forbidden" — which is what
they are running. A failed preflight asked every harness for "an active product subscription login".
Blocked dispatch asked for "a responsive Codex, Claude or GLM CLI" against a roster of five. All
three reach the capability card verbatim.

What changed

Each of these is its own commit, in this reading order:

  1. The spent-allowance refusal branches on HARNESS_BILLING — a subscription is waited out, a
    wallet is topped up. The forbidden-billing clause is gone from both places it appeared.
  2. A watchdog test fixture rejected its process promise a macrotask before the run awaited it, so
    pnpm test failed intermittently on a suite where every test passed. Pre-existing on main;
    fixed here because it is what pre-push runs.
  3. The preflight refusal names only the CLI, which is what that gap is about. Which credential is
    missing is already each harness's own error to raise, in its own words.
  4. The harness layer is renamed — CliAgentHarness, harness-preflight, harness-environment,
    HarnessCapabilityGaps.CREDENTIAL, HarnessErrorCodes.CAPABILITY_REQUIRED. The readiness state
    that gap maps to has always been NOT_SIGNED_IN, so the surrounding code already disagreed with
    the old name.
  5. A wallet is reported as a balance, not as a plan tier. DeepSeek's money and Muse's "Meta
    publishes no balance" were both written into plan, then run through the helper that title-cases
    a plan name and rendered in the badge a bought subscription gets.
  6. The allowance domain is renamed across four packages, /api/subscriptions becomes
    /api/allowances, and the settings panel is Allowances.
  7. The landing page reads billing from @openlab/protocol instead of keeping a second
    HARNESS_BILLING table of its own.

Allowances panel, before and after

Shot from a lab actually running on this machine, OPENLAB_HOME pointed at a disposable directory.
Muse Code is the visible case: its sentence used to sit in the plan badge, title-cased into
"Metered — Meta Publishes No Balance" beside Codex's "Plus" and Claude's "Max".

Before After
Settings page, Subscriptions tab. Muse Code carries a plan badge reading Metered — Meta Publishes No Balance. Settings page, Allowances tab. Muse Code carries a balance badge reading Meta publishes no balance.

The same lab answers GET /api/allowances with the plan and the balance split apart:

codex     state=exhausted   plan=plus   balance=null
claude    state=available   plan=max    balance=null
glm       state=available   plan=pro    balance=null
deepseek  state=unreadable  plan=null   balance=null
muse      state=available   plan=null   balance=Meta publishes no balance

Breaking

  • GET /api/subscriptions is now GET /api/allowances. The daemon and dashboard ship together and
    no third party pins it.
  • Four @openlab/harness subpaths moved. The package is private: true and is never published;
    only this repository imports it.

Not done here

  • ~40 remaining mentions of "subscription" were read one by one and left alone — spend caps (only a
    windowed subscription can carry one), AgentHarnessBilling.SUBSCRIPTION itself, and the roster
    notes about Codex, Claude and GLM. They are about actual subscriptions.
  • No screenshot of the capability card. Its two corrected sentences are covered by tests, but
    reproducing them on screen needs a spent wallet, which this machine has no key for.

Verified with pnpm check (biome, typecheck, 733 tests, build) and against a running daemon.
Written by Claude Opus 5 through the Claude Code harness.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith with what you need. Autofix is disabled.

Note

Stop telling token-billed operators to wait for a subscription when their allowance is spent

  • Renames SubscriptionCliHarness → CliAgentHarness, runSubscriptionPreflight → runHarnessPreflight, and all related types/constants throughout the harness and daemon packages, removing subscription-specific wording from user-facing messages.
  • Adds a spentAllowanceError path that distinguishes subscription-billed harnesses (advise waiting for reset/raising plan) from token-billed wallet harnesses (advise adding balance), using HarnessCapabilityGaps.ALLOWANCE instead of the old SUBSCRIPTION gap.
  • Adds readDeepseekAllowance and readMuseAllowance for token-billed harnesses, and a readHarnessAllowance dispatcher covering all supported harness kinds; allowance readings now carry explicit balance and spent fields.
  • Renames HarnessCapabilityGaps.SUBSCRIPTION → CREDENTIAL and HarnessErrorCodes.SUBSCRIPTION_AUTH_REQUIRED → CAPABILITY_REQUIRED; the /api/subscriptions endpoint moves to /api/allowances.
  • Risk: the API route change (/api/subscriptions → /api/allowances) and the renamed error codes/gap identifiers are breaking changes for any client consuming those values directly.

Macroscope summarized 1a36458.

dibenkobit and others added 8 commits August 7, 2026 19:55
A spent allowance was reported as a subscription usage limit whatever paid for
the harness. DeepSeek and Muse Code are billed by the token, so an operator whose
wallet ran dry was told to wait for an allowance that never resets, to raise a
plan that does not exist, and that usage-based API billing is forbidden — which
is what they are running. All three sentences reach the operator through the
capability card.

The refusal now takes the advice from HARNESS_BILLING: a subscription is waited
out, a wallet is topped up. The module is named for what it detects rather than
for the billing it used to assume.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The streaming fixture rejects its process promise when the watchdog aborts, and
the run only awaits that promise after it has written the events before it to
disk. In between, the rejection has nothing watching it, so Node reported it as
unhandled and vitest failed the whole suite on a run whose every test passed —
intermittently, under the load of the full workspace test, which is what the
pre-push hook runs.

The rejection is now marked as observed where it is created. The run awaits the
same promise and still sees the same rejection.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…every harness

A CLI that cannot answer for its own version is a missing installation, and the
sentence saying so asked for "an active product subscription login" whatever the
harness authenticates with. DeepSeek is handed a key and Muse Code logs in to
Meta, so both were sent after something that does not exist — and the capability
card shows the sentence verbatim.

The refusal now names only what this gap is about, the CLI itself. Which
credential is missing is already the authentication check's to say, and each
harness says it in its own words. The README requirement is corrected the same
way: it still named two of the five harnesses.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…riptions

The base class every harness extends, the preflight it runs, the environment it
sanitizes and the gap it reports when a credential is missing were all named for
a subscription. Three harnesses hold one; DeepSeek is handed an API key and Muse
Code logs in to a Meta account, and both extended SubscriptionCliHarness and
raised HarnessCapabilityGaps.SUBSCRIPTION to say they had no key. The readiness
state that gap maps to has always been NOT_SIGNED_IN, so the surrounding code
already disagreed with the name.

SubscriptionCliHarness is CliAgentHarness, subscription-preflight is
harness-preflight, subscription-environment is harness-environment, the gap is
CREDENTIAL and the error code every capability error carries is
CAPABILITY_REQUIRED rather than SUBSCRIPTION_AUTH_REQUIRED — it is set for a
spent allowance too.

Three more operator-facing sentences went with them. Dispatch asked for "a
responsive Codex, Claude or GLM CLI with an active product subscription" and
told the operator that "API billing is forbidden" — the second copy of a clause
already removed from the spent-allowance refusal, and both are shown on the
capability card. Hibernation reported "No subscription-authenticated agent CLI
harness is available" for a roster of five.

BREAKING CHANGE: @openlab/harness subpaths ./subscription-cli-harness,
./subscription-cli-harness.types, ./subscription-environment and
./subscription-usage-limit are now ./cli-agent-harness, ./cli-agent-harness.types,
./harness-environment and ./spent-allowance. The package is not published.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
DeepSeek's remaining money and Muse Code's "Meta publishes no balance" were both
reported in `plan`, the field that carries the tier a vendor sells. The page then
ran them through the helper that title-cases a plan name and rendered them in the
badge a bought subscription gets, so a dollar amount appeared as though it were a
plan the operator holds.

An allowance now carries `plan` and `balance` separately, the same split the
preflight has always reported: a vendor that sells tiers names one and states no
balance, a vendor that sells tokens states a balance and has no tier.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…subscriptions

The domain that reads what each harness has left was called subscription-allowance
across four packages, served at /api/subscriptions, and shown to the operator
under a heading that said Subscriptions. It carries DeepSeek's wallet and Muse
Code's metered account, neither of which is a subscription, so the page named two
of its five cards wrong and the settings tab named itself wrong above them.

The domain is harness-allowance, the route is /api/allowances, the section is
Allowances, and the daemon plumbing that passed these readings around as
`subscriptions` passes them as `allowances`. What a vendor answers is a
HarnessAllowanceReading and what the lab publishes is a HarnessAllowance, which
are two different shapes that were both called the same thing.

The dashboard directory is allowance-panel rather than harness-allowance so its
`#src/<dir>/<file>` paths cannot collide with the protocol's, which the dashboard
tsconfig resolves against its own src.

BREAKING CHANGE: GET /api/subscriptions is now GET /api/allowances. The daemon
and dashboard ship together and no third party pins it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ating it

The site kept its own copy of which harness costs money — a second HARNESS_BILLING
table declared beside the roster, with the answer hand-written into each entry.
Two tables for one fact, and this is the page an operator decides what to install
from, so a drift between them would land in the worst possible place.

The roster entries now name the harness they stand for and the page reads the
billing out of @openlab/protocol, the same table the dashboard card and the
spent-allowance refusal read.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… subscriptions

The README listed the settings panel under its old heading, and the readiness
schema said a ready harness names "the subscription it is signed in to" — which
two of the five never do, and which the schema itself contradicts by carrying a
balance beside the plan.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 7, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
openlab-landing Ready Ready Preview Aug 7, 2026 4:04pm

Request Review

Comment thread packages/harness/src/harness-allowance/deepseek-allowance.ts
Exhaustion was read off the windows alone, and a wallet meters none — so a
DeepSeek wallet DeepSeek had stopped serving came back as an account with
everything still to spend. The panel showed it available beside a balance of
nought while the preflight was already refusing every run on the same verdict,
leaving an operator nothing on the page to explain why nothing dispatched.

The vendor's own verdict is carried on the reading and taken at its word, so the
panel and the dispatch decision agree and the harness is stopped a step earlier.
A spent account is only called a plan where the vendor sold a tier: a wallet
comes back when the operator pays for it, never on a renewal they could wait for.
@dibenkobit
dibenkobit merged commit b214851 into main Aug 7, 2026
7 checks passed

This branch was successfully deployed

1 active deployment
Preview — 1a364583 Deployed Aug 7, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant