Young Rulings 2026 10 10 Four
Consolidated. This note's findings now live in
../MIN-STUDIES.md§1. The original is kept for its measurements; do not re-derive from it.
Young's four rulings
Young, 2026-10-10: the four questions that were blocking the most, answered. These are decisions, not proposals. Each is applied to its stage below.
| # | Question | Ruling | Unblocks |
|---|---|---|---|
| 1 | Stage 5's owner (memory) | Min owns it — Sol proves it | Stage 5 entirely |
| 2 | a2a-sdk 1.2.2, or Hermes' plugin? | The official A2A SDK | Stage 2 |
| 3 | Which stuck number? | 3600 s — refine when we have more data | Stage 3.3's proof |
| 4 | GO $60 hard or soft, and GO-before-ZEN? | GO exhaustion rolls over to ZEN | Stage 6's guard |
Ruling 1 — Stage 5 (memory) is Min's, proof by Sol
Applied. Min owns the memory model; Sol owns the proof of the second capture mechanism. The two-owner rule holds — the builder does not prove their own control.
Why the pairing works: what an agent remembers is identity-adjacent (the registry lane), and a second capture mechanism is a control, which is proven by someone other than its author.
Ruling 2 — the official A2A SDK (a2a-sdk 1.2.2)
Applied. Stage 2 builds on the official SDK, not on Hermes' plugin.
This overrides my earlier lean and I am recording that plainly. I recommended the plugin because it was live, correct and free; Young ruled the official implementation, and the reasons are sound:
| Why the SDK wins | Evidence |
|---|---|
| It is the spec's own implementation | a2aproject/a2a-python — the reference |
| Three bindings, not one | JSON-RPC · HTTP+JSON/REST · gRPC |
| Multi-replica cluster mode | added in 1.2.0 — a fleet can scale |
| The Inspector tests against it | the conformance tool and the SDK are the same project |
| Migration is a flag | src/a2a/compat/v0_3/ still ships at the 1.2.2 tag |
Version, pinned — and the distinction that matters:a2a-sdk = 1.2.2 (2026-10-05, verified on PyPI) · A2A Protocol = 1.0.
Two different numbers. V2's defect was declaring 1.0.0 while serving v0.3
names — so both are pinned, and neither is inferred from the other.
Cost recorded, per Mira's lane: the SDK adds 26 packages. That is the price of the ruling and it is accepted.
What Hermes' plugin becomes: a reference implementation we diff against, not our transport. It stays useful — it is a working v1.0 server we can compare our conformance to.
Ruling 3 — CORRECTED by Young: the stuck rule is 300 s
Young, 2026-10-10 (correction): "Thanks Sol for point out. Ruling 3 — 300 s (will refine when we have more data)."
A task is stuck when it has produced no reply for 300 s.
Source: Hermes' A2A orphan watchdog — plugins/platforms/a2a/adapter.py:38,
_MIN_ORPHAN_TIMEOUT = 300. Sol was right to point at it and I was wrong to
prefer 3600.
Why 300 s is the better rule — and it corrects my reasoning, not just my number:
| 300 s ✅ chosen | 3600 s (my earlier pick) | |
|---|---|---|
| Condition | no reply produced at all | a claimed task whose worker stopped heartbeating |
| Detects | the task is not progressing | the worker process is gone |
| Fails | early — a silent agent is caught in 5 min | late — an hour of nothing |
| Best for | Brain following up on Heart — Young's actual requirement | long-running batch work |
The distinction I got wrong: I argued 300 s measured "a different condition", and treated that as a reason to prefer 3600 s. But the different condition is the one Young asked for. His requirement is "brain Unit can follow up if agent got stuck or block" — and a Brain cannot follow up on a task it cannot tell is stuck. 300 s is the earlier signal, and earlier is what makes follow-up possible.
"Refine it when we have more data" is a rule, not a deferral — so it is a declared constant with its source cited, never a magic value:
| Signal | Action |
|---|---|
tasks hitting stuck at 300 s that later complete | too short — raise it |
| tasks that stop and are not noticed for much longer | too long — lower it |
| a task class with a different natural duration | a per-class override, not a global change |
The other two numbers are references, not our rule:
| Number | What it measures | Our use |
|---|---|---|
| 60 s | the watchdog's sweep period | adopt as the check interval — 300 s is the threshold, 60 s is how often we look |
| 3600 s | kanban's stale worker — the process is gone | a second, coarser condition: the worker died vs the task went quiet. Both are recorded; 300 s is the primary |
Both conditions are real and distinct: a task can have a live worker producing no reply, or a reply and a dead worker. Stage 3.3 records both, and 300 s is the one the follow-up loop fires on.
Ruling 4 — GO exhaustion rolls over to ZEN
Applied. When GO is exhausted, the request falls through to ZEN. This is not our invention — it is Opencode's own documented behaviour, and the ruling adopts it rather than fighting it.
"If you also have credits on your Zen balance, you can enable the Use balance option in the console. When enabled, Go will fall back to your Zen balance after you've reached your usage limits instead of blocking requests." —
opencode.ai/docs/go
🔴 The answer to "how do we trace and track GO exhaustion?" — CORRECTED AND MEASURED
Young, 2026-10-10: "Currently can only check on the opencode console. Via opencode cli
opencode stats --days 7."
Young's method works, and I found two more surfaces that also work. All three were probed on this host. The honest correction: it is not console-only.
| # | Surface | Probe | Result |
|---|---|---|---|
| 1 | GO plan windows | GET https://opencode.ai/zen/go/v1/usage | HTTP 200 — rolling/weekly/monthly |
| 2 | Workspace budgets | GET https://opencode.ai/console/api/v1/budgets/members | HTTP 200 — limit, spent, exceeded, resets |
| 3 | Local cost + tokens | opencode stats --days 7 --cost --json | works — per-model cost, offline |
All three are keyed by the same oc_sk_… key we already hold. No console login
required.
Surface 1 — the GO plan windows
{ "usage": {
"rolling": { "status": "ok", "percent": 0, "resetsAt": "2026-10-10T13:32:00.000Z" },
"weekly": { "status": "ok", "percent": 0, "resetsAt": "2026-10-12T00:00:00.000Z" },
"monthly": { "status": "ok", "percent": 0, "resetsAt": "2026-11-09T02:42:52.000Z" } } }
This IS our window_state axis, supplied by the provider. status: "ok" | "rate-limited" maps to ok | blocked.
Surface 2 — the Budgets API (from Young's docs link) — measured
GET https://opencode.ai/console/api/v1/budgets/members
Authorization: Bearer <key> # needs All permissions, not inference-only
Live response from our workspace:
youngframeworks@gmail.com: limit=$40.00 spent=$0.00 exceeded=False source=custom resets=2026-11-01
⚠️ CORRECTION — Young caught this, and I had it wrong. That $40.00 is a
monthly spending CAP, not the ZEN credit balance.
| Concept | What it is | Readable by API? | Failure |
|---|---|---|---|
| CREDITS — the ZEN PAYG float | prepaid balance, whole workspace | NO — console only | HTTP 402 Insufficient account funds |
| BUDGETS | a monthly spending cap, resets 1st of month | YES — this endpoint | HTTP 429 |
| GO WINDOWS | the $10 subscription's limits | YES — /zen/go/v1/usage | status: rate-limited |
source: custom + resets_at: 2026-11-01 are budget semantics — the docs say
"Budgets reset on the 1st of each month at 00:00 UTC." A credit balance does not
reset monthly; it depletes. So this endpoint answers "what is the cap?", not
"what is left in ZEN?"
The docs agree: "The Credits card on Overview shows the balance for owners and admins." The balance is console-only — the GitHub issue I first cited, then wrongly retracted, was right.
Fields: limit_micro_cents · spent_micro_cents · exceeded · resets_at ·
source. 100,000,000 microcents = $1.
Surface 3 — opencode stats — Young's method, and it does more than I knew
opencode stats --days 7 --cost --json
Measured: "cost": 19.489407278400066 over 7 days, with a full token
breakdown and per-tool reliability. And per-model:
model tokens steps cost
opencode-go/longcat-2.5-preview-f… 2.8b 55.7k $0.00
opencode/space-bunny-free#default 2b 71.9k $0.00
opencode-go/qwen3.8-flash#default 309.3m 7.5k $11.17
opencode-go/deepseek-v4-flash#def… 263.5m 3.6k $6.97
This is the per-model split our ledger needs, and it is available offline —
source: /home/lab/.local/share/opencode/opencode.db (2.7 GB, local SQLite).
Useful flags: --days N · --year · --all · --models · --tools ·
--cost · --full · --project · --json.
The revised trap table — trap 1 is corrected
| # | Trap | Revised finding |
|---|---|---|
| 1 | ❌ CORRECTED — it IS, via the Budgets API. limit_micro_cents + spent_micro_cents give the $40 float directly. The earlier claim came from a GitHub issue (a feature request), not from a probe — and I repeated it without testing the console API. The inference-only key note was also wrong for our key: it returns 200 | |
| 2 | percent is floored, disagrees with the dashboard ~1pt | stands — read it as a floor |
| 3 | resetsAt is computed client-side from resetInSec | stands — a projection, not a fact |
| 4 | The fallback is a CONSOLE SETTING, not a per-request flag | stands — record the setting, don't infer the path |
| 5 | No X-RateLimit-* headers | stands — poll before the call, not after the 429 |
The lesson I am recording against myself — two errors, same root cause:
First I cited a GitHub issue as fact ("ZEN's balance is not readable"). Then,
correcting that, I read the Budgets endpoint and reported its $40.00 as the
ZEN credit balance. Both times I took a plausible-looking number and assigned it
a meaning without checking the semantics.
The endpoint's own fields said "budget": its name is budgets, its source is
custom, and its resets_at is the 1st of the month — and the docs say "Budgets
reset on the 1st of each month." A credit balance does not reset monthly.
So the first instinct was right and the retraction was wrong. The honest position is the one supported by the docs and by eight 404 probes:
| Question | Answer |
|---|---|
| GO window usage readable? | YES — /zen/go/v1/usage |
| The monthly spending cap readable? | YES — /console/api/v1/budgets/members |
| The ZEN credit balance readable? | NO — console only |
The rule: read the field names and the reset semantics before naming a number.limit ≠ balance; a monthly reset means a cap, not a float.
The three surfaces, and which to use for what
| Need | Use | Why |
|---|---|---|
| before a paid call — is GO blocked? | /zen/go/v1/usage | cheapest, read-only, provider-authoritative, costs nothing |
| what is the monthly spending cap, and how much of it is used? | /console/api/v1/budgets/members | the cap, per member |
| what did we spend, per model? | opencode stats --cost --json | offline, per-model, survives an outage |
| how much ZEN credit is left? | the console Credits card — no API | cost_state: unknown in the ledger |
The ledger reads three of the four, and says unknown for the fourth — which is
the honest answer, not a failure.
What is now unblocked
| Stage | Was blocked by | Now |
|---|---|---|
| 2 | the SDK-vs-plugin decision | unblocked — build on a2a-sdk 1.2.2 |
| 3 | the stuck number | unblocked — 3600 s, declared and refinable |
| 5 | no owner | unblocked — Min, proof by Sol |
| 6 | the GO/ZEN guard | unblocked — rollover confirmed, and the trace surface is measured |
Still open and genuinely needing Young: none of the four above. The
remaining gates are OPEN-DECISIONS.md D1–D5 and the four operation gaps in
what-we-havent-touched.md §6 — observability, backup/DR, onboarding, and
.solos having no remote.
Not established
- The usage endpoint was read, not load-tested.
percentwas 0 in all three windows at the time of measurement, so therate-limitedstatus has not been observed — it is read from the endpoint's own source. - The GO→ZEN rollover was not exercised. The docs state it and the console setting controls it; no call has been observed rolling over.
- ZEN's endpoint was probed and 404s (
/zen/v1/usage,/zen/usage) — the balance API does not exist, consistent with issue #44189. x-opencode-sessionwas tested and is required — without it the request is rejectedMissingSessionID.

