Skip to content

feat(opencode-usage): add OpenCode Go usage status extension - #89

Merged
patextreme merged 2 commits into
mainfrom
oc-go
Oct 24, 2026
Merged

patextreme merged 2 commits into
mainfrom
oc-go

Conversation

@patextreme

@patextreme patextreme commented Oct 23, 2026 •

Copy link
Copy Markdown
Owner

Closes #88.

Adds the opencode-usage extension: a footer status slot showing OpenCode Go consumed allowance percentages as opencode-go: <rolling>% / <weekly>% / <monthly>%.

Screenshot

pi status bar showing opencode-go: 5% / 2% / 1%

Live capture from the tu terminal with the extension loaded and opencode-go/deepseek-v4.1-flash selected.

What changed

  • extensions/opencode-usage/index.ts — refreshes on session_start (when the model is opencode-go), model_select to opencode-go, and /opencode-usage-refresh; clears the slot for other providers and invalidates in-flight fetches. Reuses ctx.modelRegistry.getApiKeyForProvider("opencode-go") and sends a non-generic User-Agent. No polling, no timer, no new configuration.
  • extensions/opencode-usage/parse.ts — pure parseUsage / formatUsage / isUnknownUsage helpers for the undocumented GET https://opencode.ai/inference/go/v1/usage response (consumed 0..100 percentages, clamped; non-ok meters become ?). Reset timestamps are deliberately ignored.
  • extensions/opencode-usage/parse.test.ts — 27 parser/formatter tests, no network.
  • Failure semantics mirror ollama-usage: missing key clears the slot silently; HTTP/network/parse failures render the dim opencode-go: ? / ? / ?; partial results render known values; automatic refreshes stay silent and only /opencode-usage-refresh reports failures.
  • nix/modules/pi-package.nix — pi-opencode-usage package in both packages and checks.
  • Docs: README package list/table/section, AGENTS.md Repo Layout bullet, GLOSSARY.md usage-reporting terms, and docs/adr/0002-opencode-usage-reads-undocumented-inference-meters.md.
  • .github/assets/opencode-usage.png — the screenshot above, used by this PR description.

Verification

  • npm run format, npm run lint, npm run typecheck, npm test (438 pass, 1 skipped, 0 fail), nix flake check — all pass.
  • Live check in a tu terminal with the extension loaded:
    • session_start on opencode-go/deepseek-v4.1-flash rendered opencode-go: 5% / 2% / 1%.
    • Ctrl+P to ollama-cloud cleared the slot; cycling back restored it.
    • /opencode-usage-refresh was silent on success.
    • With an invalid key: slot showed opencode-go: ? / ? / ? and the manual command reported Failed to fetch OpenCode usage (HTTP 401 Unauthorized).

@patextreme

Copy link
Copy Markdown
Owner Author

Review complete — no repair-worthy findings

Both axes reviewed the full PR diff at 83efb84. 0 blockers · 0 significant bugs · 5 report-only · 3 refuted · 0 unresolved investigations.

Report-only (recorded in audit): F-001 manual /opencode-usage-refresh has no provider guard (issue 88's Decisions and Behavior table conflict); F-004 committed screenshot carries a home-relative path, model id and live percentages; F-005 new prose says "status bar", which this PR's glossary marks _Avoid_; F-006 ~140 lines duplicated from ollama-usage; F-007 narrow Notification return plumbing.

Validation (fresh at 83efb84): npm run check, npm run typecheck, npm test (438 pass / 1 skip), nix flake check -L, and nix build .#pi-opencode-usage all passed; PR CI check CI passed at the same SHA.

Scope: no live API request was made (no real provider key); index.ts lifecycle has no test (matches the ollama-usage precedent); the screenshot was inspected visually, not pixel-diffed.

Repairs: none · Attempts: 0/10 · Human input: none

Full review reports and audit details

Standards (complete report, verbatim)

Verification run: tsc --noEmit clean, biome check extensions/opencode-usage clean, npm test 438 pass / 1 skip. Flake output, packages+checks wiring, README table row + section, AGENTS Repo Layout bullet, ADR format, default-export extension shape, and test placement all conform.

Documented-standard breaches:

  1. GLOSSARY avoided synonym violated by the same commit. GLOSSARY.md:92-95 (added here) defines Status slot with _Avoid_: Status bar, badge, and docs/agents/domain.md:15 says "Respect explicitly avoided synonyms." Yet new prose says "status bar": README.md:10, README.md:65, README.md:175, extensions/opencode-usage/index.ts:2 and the command description at index.ts:196. Hard violation, and self-inconsistent inside one change (GLOSSARY.md:89 itself says "status slot"). Pre-existing README.md:9/64/165 share the drift, but the new text should read "status slot"/"footer".

  2. Orphaned binary asset. .github/assets/opencode-usage.png (56KB, commit 83efb84) is referenced nowhere: grep -rn opencode-usage.png over the tree returns nothing, and README.md has no .github/assets/ links. AGENTS.md "Removing an extension mirrors adding one" treats doc artifacts as a referenced set; this is dead weight. Judgement-leaning but concrete.

Note (not a breach): AGENTS.md:47's example filename extensions/<name>/<name>.test.ts doesn't match extensions/opencode-usage/parse.test.ts; the authoritative bullet (AGENTS.md:48, extensions/**/*.test.ts) and the ollama precedent endorse it.

Baseline smells (judgement calls):

  1. Duplicated Code — extensions/opencode-usage/index.ts:73-201 is a near-verbatim clone of extensions/ollama-usage/index.ts:72-182: FetchResult, Notification, the timeout/AbortController fetch, applyResult, refresh, and even the comment "Bumped on every trigger so an in-flight fetch from a previous selection / can never clobber a newer state". Same for parseUsage envelope unwrapping (parse.ts:47-96 vs ollama parse.ts:59-96). The repo's own packaging (nix/modules/pi-package.nix:41-51 copies explicit per-extension files) argues against a shared module, so this may be endorsed — but ~90 duplicated lines will drift, and no test pins the shared shape.

  2. Speculative Generality / Middle Man — Notification plumbing (index.ts:63,127,158) exists for one consumer: both event handlers discard it via void refresh(ctx).catch(() => {}) (index.ts:178,190), and fetchUsage is documented "Never throws" (index.ts:66). The return plumbing plus those catches guard a path only registerCommand (index.ts:195) reads.

Orchestrator verification of line refs: README.md:10,65,175 and index.ts:2,196 do contain "status bar" (all added lines); GLOSSARY.md:89 says "status slot", GLOSSARY.md:92-95 is the status-slot entry; no file references opencode-usage.png (the PR body outside the repo does). Ground-truth handler/test/shape line numbers in the Spec report below were corrected by the orchestrator.

Spec (complete report, verbatim, with corrected line numbers)

(a) Missing or partial: Nothing in Implementation scope or the Behavior/Failure tables is unimplemented. All artifacts landed: nix/modules/pi-package.nix:41-51 derivation + packages/checks (:145,149), README (package list :34, table row :66, feature bullet :10, ## OpenCode Usage Extension :173-181), AGENTS.md:39, GLOSSARY.md:85-99 (usage meter, rolling meter, status slot), docs/adr/0002-....md. Behavior rows verified: no work at load (no module-level fetch/timer); session_start gated on ctx.model?.provider and clears otherwise (index.ts:171-179); model_select both directions (:183-191); manual command (:195-204); hasUI guards in all three paths (:172,184,198) matching "Non-UI mode | Do no network or status work." Failure matrix rows all map: no key → cleared + "provider is not configured" warning (:134-141); non-2xx → HTTP <status> <statusText> (:88-93); malformed/all-unknown → "malformed response body" (:95-96); partial → renders, silent (:129-132). Timers not parsed; 8s timeout (:44); UA pi-opencode-usage/1.0 (:41); percent consumed, clamped (parse.ts:96). npm test passes (438/439, 1 skipped).

(b) Not asked for:

  • .github/assets/opencode-usage.png (56 KB, commit 83efb84) is referenced nowhere in the repository — git grep assets/ in README/docs returns no hit. Spec's README scope lists only "Nix package list, extensions table row, feature bullet, ## OpenCode Usage Extension section"; a dead screenshot is creep. (Later refuted: the PR description references it.)
  • Minor: rounding fractional percents (parse.ts:96); spec says "the API's consumed integer percentage, clamped to 0..100".

(c) Implemented but wrong:

  1. /opencode-usage-refresh has no provider guard (index.ts:195-204). Run with a non-opencode-go model selected it fetches and re-renders the slot, contradicting "Clear the slot when the selected model is not opencode-go" and the row "model_select to anything else | Clear the slot"; the slot then stays populated until the next selection.
  2. Key resolution (index.ts:71) sits outside the try/catch (try at :79), so a getApiKeyForProvider rejection escapes the documented "Never throws" contract. Automatic paths swallow it (:178,190); the manual command's .catch(() => null) (:199) then reports nothing, so an actual failure produces neither the "Error with the failure message" nor the not-configured warning. Inherited from ollama-usage/index.ts:64, hence low severity, but it contradicts "the manual command reports failures only".

Orchestrator-verified line numbers (ground truth): session_start 171-179; model_select 183-191; registerCommand 195-204; hasUI guards 172,184,198; no-key branch 134-141; HTTP error 88-93; malformed 95-96; partial 129-132; getApiKeyForProvider at 71 outside try at 79; void refresh(ctx).catch(() => {}) at 178,190; .catch(() => null) at 199; FETCH_TIMEOUT_MS 8000 at 44; UA at 41; parse clamp+round at 96. Nix derivation 41-51, packages 145, checks 149 confirmed. Screenshot is a Pi TUI capture showing footer opencode-go: 5% / 2% / 1%, model deepseek-v4.1-flash, cwd path /…/Desktop/workspace/personal/agent-mod (oc-go).

Judge ledger and classifications (complete)

Prior actionable IDs: none — fresh run, 0 issue comments, 0 review comments, 0 reviews, no run markers; round 1, repairs used 0/10. No reconciliation needed.

ID Source axis Category Disposition Reviewed SHA
F-001 Spec (c)1 report-only (spec ambiguity) open — no repair authorized 83efb84
F-002 Spec (c)2 refuted refuted (SDK never rejects) 83efb84
F-003 Standards #2 / Spec (b) refuted refuted (PR body references asset) 83efb84
F-004 Standards #2 residual / Spec (b) report-only open 83efb84
F-005 Standards #1 report-only open 83efb84
F-006 Standards #3 report-only open 83efb84
F-007 Standards #4 report-only open 83efb84
F-008 Spec (b) minor refuted refuted 83efb84
F-009 Judge independent pass refuted (no deviation) closed 83efb84
— Prior actionable IDs — none (fresh run) —

F-001 — report-only (spec ambiguity). Provenance: Spec (c)1. Evidence: extensions/opencode-usage/index.ts:195-204 handler guards only ctx.hasUI. Issue 88 Behavior table: "/opencode-usage-refresh | Fetch and render; notify failures only" (no provider condition), acceptance criterion 3 requires refresh to report failures; the competing line is Decisions "Clear the slot when the selected model is not opencode-go", and criterion 1 scopes the positive case to a Go model. model_select-to-other clearing and in-flight invalidation are implemented (:183-191), so criterion 2 is unaffected. extensions/ollama-usage/index.ts:182-191 has the same shape, the precedent the issue says to mirror. Impact: with a non-Go model selected the command renders accurate plan-level usage until the next selection/session_start; no crash, no data/credential effect. Correction if a guard is wanted: check ctx.model?.provider !== PROVIDER_ID and clear/warn (+ test) — but that is a behavior change not specified by the command row, so it needs human intent. Disposition: open, report-only.

F-002 — refuted. Provenance: Spec (c)2. Evidence: SDK node_modules/@earendil-works/pi-coding-agent/dist/core/model-registry.js:105-112: async getApiKeyForProvider(provider) { try { return (await this.runtime.getAuth(provider))?.auth.apiKey; } catch { return undefined; } } — it cannot reject; return type Promise<string | undefined> (model-registry.d.ts:52). opencode-go is an API-key provider (OPENCODE_API_KEY mapping), so the call resolves rather than throwing. The .catch(() => null) at index.ts:199 and .catch(() => {}) at :178,190 are unreachable defensive code; no failure is silently swallowed. Inherited from ollama-usage/index.ts:64. Impact: none; no supported trigger produces the claimed outcome. Correction: none (moving the call inside the try is speculative hardening only).

F-003 — refuted. Provenance: Standards #2 / Spec (b). Evidence: PR 89 body contains ## Screenshot with ![pi status bar showing opencode-go: 5% / 2% / 1%](https://raw.githubusercontent.com/patextreme/agent-mod/83efb848599adfa87b2dfa96daf2515ec57b4502/.github/assets/opencode-usage.png) and the bullet ".github/assets/opencode-usage.png — the screenshot above, used by this PR description." The axis git grep covered only repository files, not the PR body, so "referenced nowhere" is false. Impact: none as claimed.

F-004 — report-only. Provenance: Standards #2 residual / Spec (b). Evidence: .github/assets/opencode-usage.png (56,305 bytes, commit 83efb84) shows footer opencode-go: 5% / 2% / 1%, bottom line ~/Desktop/workspace/personal/agent-mod (oc-go), (opencode-go) deepseek-v4.1-flash · high, Pi v1.4.0, [Extensions] opencode-usage. Issue 88 Implementation scope lists only README artifacts — no asset. Impact: a public repo permanently carries a home-relative directory path, local branch name, model id, and a live allowance snapshot; no username, hostname, token, or credential; referenced by the PR description; no runtime/delivery consequence. Correction: optional delete or upload the image to the PR body instead; human preference, zero risk. Disposition: open, report-only.

F-005 — report-only. Provenance: Standards #1. Evidence: GLOSSARY.md:96-98 added here: "Status slot: … _Avoid_: Status bar, badge"; docs/agents/domain.md:15: "Respect explicitly avoided synonyms." New prose uses "status bar" at README.md:10,65,175 and extensions/opencode-usage/index.ts:2,196. Counter-evidence on significance: pre-existing README.md:9,64,157,165 also say "status bar"; the SDK's own ui.setStatus doc says "footer/status bar" (types.d.ts:83); issue 88's Summary itself says "in the pi status bar"; the PR body uses both. Impact: terminology inconsistency, self-inconsistent inside one change; no runtime, contract, or delivery consequence. Policy: naming preference → report-only. Correction: terminology pass over README + both usage extensions and the glossary scope as a separate docs cleanup; no product-intent question, not material to PR 89. Disposition: open, report-only.

F-006 — report-only. Provenance: Standards #3. Evidence: diff of extensions/opencode-usage/index.ts:52-204 vs extensions/ollama-usage/index.ts:45-187 shows identical FetchResult/Notification, timeout/AbortController fetchUsage, applyResult, refresh, both event handlers, and identical comments ("Bumped on every trigger so an in-flight fetch from a previous selection / can never clobber a newer state"); parseUsage envelope unwrapping is also duplicated (parse.ts:47-96 vs ollama parse.ts:59-96). nix/modules/pi-package.nix:31-51 copies explicit per-extension files, so a shared module requires build-layout changes. Impact: drift risk only; no current defect, no test pins the shared shape. Correction: extracting a shared usage module changes packaging and extension layout — an architecture decision requiring human input; not authorized here. Disposition: open, report-only.

F-007 — report-only. Provenance: Standards #4. Evidence: extensions/opencode-usage/index.ts:63 type, returned by applyResult/refresh; event handlers discard it (:178,:190); only registerCommand reads it (:195-204); fetchUsage documented "Never throws" (:66). Impact: slightly wider internal API than needed; the value is used by the command, so not dead code; no behavior consequence. Correction: none standalone; any cleanup rides on F-006, not authorized. Disposition: open, report-only.

F-008 — refuted. Provenance: Spec (b) minor. Evidence: parse.ts:96 Math.round(Math.min(100, Math.max(0, percent))); test parse.test.ts:83-91 ("rounds a fractional percentage to the nearest integer", 3.6→4, 0.4→0) plus clamp tests :65-81. Issue 88 describes the API percentage as an integer and requires clamping, implemented exactly; for out-of-contract fractional input the spec gives no rule. Impact: none in supported scenarios; display is always an integer 0..100 as specified. Correction: none.

F-009 — judge independent requirement-deviation pass found nothing. Verified at 83efb84: no module-level fetch/timer (index.ts:38-49); session_start (:171-179) hasUI + provider gate, otherwise clear + bump generation; model_select (:183-191) both directions with generation bump preventing stale apply; registerCommand (:195-204) hasUI guard, failures only, silent success; failure matrix no key → clear + warning (:134-141), non-2xx → HTTP <code> <text> (:88-93), malformed/all-unknown → "malformed response body" (:95-96), partial → render known + silent (:129-132), network/timeout → caught message (:101-106, 8s at :44); identity STATUS_KEY "opencode-usage" (:38), label opencode-go: (parse.ts:27), UA pi-opencode-usage/1.0 (:45), endpoint hardcoded (:41), provider exact match; wiring pi-opencode-usage derivation nix/modules/pi-package.nix:41-51, packages :145, checks :149; README package list :34, table row :65, bullet :10, section :173-181; AGENTS.md :39; GLOSSARY entry; ADR 0002 with Status/alternatives/consequences; test placement extensions/opencode-usage/parse.test.ts matches extensions/**/*.test.ts discovery and the ollama precedent; tsconfig includes extensions/**/*.ts; npm test re-run 439 tests, 438 pass, 1 skip, 0 fail.

Audit record

  • Run identity: pr-review-repair run=83efb848599adfa87b2dfa96daf2515ec57b4502, round 1, kind=summary (this comment). PR feat(opencode-usage): add OpenCode Go usage status extension #89.
  • Pinned base / merge-base: 9ee039c5208712a122727267ce670fed3cdada9c (= current main tip). Head branch oc-go, head repo patextreme/agent-mod (not a fork).
  • Reviewed head: 83efb848599adfa87b2dfa96daf2515ec57b4502 (local HEAD == origin/oc-go before and after review; tree clean). Delivered head: unchanged (no repairs).
  • Requirements source: GitHub issue Add opencode-usage status extension for OpenCode Go #88 (authoritative detailed spec; not OpenSpec-routed, no openspec label, zero openspec/** paths in the diff). Standards sources: AGENTS.md, README.md, GLOSSARY.md, docs/agents/domain.md, docs/adr/0001, docs/adr/0002.
  • Review contract: full diff git diff 9ee039c...83efb84 — 9 files, +622/−4, one binary asset. Two fresh parallel read-only axis agents; neither saw prior history or the other axis.
  • Checks, freshly executed at the reviewed head by the review orchestrator: nix develop -c npm run check exit 0 (biome 2.4.13, 49 files, no fixes); npm run typecheck exit 0; npm test exit 0 (438 pass / 1 skip / 439 tests; 19 discovered suites); nix flake check -L exit 0 (inner node-tests 429 pass / 10 git-dependent sandbox skips); nix build .#pi-opencode-usage exit 0 (store output = index.ts + parse.ts). Judge re-ran npm test (438 pass / 1 skip) and read the SDK source. PR CI check CI (flake-check) = pass at the reviewed SHA. No cached-only required check; no failed, pending, or unavailable required check.
  • Skipped checks and reasons: npm run format (mutating; equivalently verified by biome check, which CI also runs); no live end-to-end API request (no real opencode-go key); no pixel-diff/privacy scan of the screenshot beyond visual inspection; no test exercises index.ts lifecycle (matches the ollama-usage precedent and issue 88's implementation scope).
  • Repairs: none. Repairs used: 0/10. No repair-start or scope-amendment records (none needed). No commits, no push. Delivery state: no code delivery; PR head unchanged at 83efb84.
  • Judge outcome: 0 actionable (blocker/significant bug), 5 report-only (F-001, F-004, F-005, F-006, F-007), 3 refuted (F-002, F-003, F-008), 1 no-deviation pass (F-009), 0 unresolved investigations.
  • Escalation: none required for merge. Optional future work if desired: enforce a provider guard on the manual refresh (settle the issue 88 intent conflict), terminology alignment, screenshot hygiene, shared usage-fetch module.
  • PR state at posting: OPEN, not draft, mergeStateStatus CLEAN, no branch protection (no required reviews/checks), author patextreme.

@patextreme
patextreme merged commit a4f6e19 into main Oct 24, 2026
1 check passed
@patextreme
patextreme deleted the oc-go branch October 24, 2026 04:05
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.

Add opencode-usage status extension for OpenCode Go

1 participant