Skip to content

0.10.0: code quality, Trust/updater on OpenCode 2, removals announced in 0.9 - #40

Merged
Codestz merged 34 commits into
mainfrom
quality/0.10
Oct 5, 2026
Merged

Codestz merged 34 commits into
mainfrom
quality/0.10

Conversation

@Codestz

@Codestz Codestz commented Oct 5, 2026

Copy link
Copy Markdown
Owner

0.10.0 — code quality, one OpenCode 2 fix, and what 0.9 said it would remove

Released together with the after-0.9 work already on main (new README, docs site at cockpit.codestz.dev, per-bay GIFs).

What changes for users

  • Fixed: Trust and the updater load on OpenCode 2 when installed alone. OpenCode 2 sets up a plugin's interface only once its agent half has loaded; these two had none, so opencode plugin add @opencode-cockpit/trust (or /updater), as the install docs say, loaded nothing. Each now publishes an empty agent half (trust/server, updater/server). The bundle was never affected.
  • Fixed: doctor checks Status's agent half too.
  • Removed: /statusline (now /status-setup only), the statusline-design skill folder, recognition of names from before 0.9 (now unknown names like a typo: a top-level one is a ! row and a doctor line; one inside a bay's section is named by /cockpit-setup), and six entry points nothing imported (review/frame — broken since added — trail/core, trust/core, updater/core, status/fixtures, client/elements).
  • Everything else behaves as before. See CHANGELOG [Unreleased].

Internals

  • core/ is pure: file, git and process access moved to io/ in each bay (CONTRIBUTING has the rule).
  • Client organised by what it talks to: daemon/, opencode/{host,server}/, settings/{keys}/, setup/{conventions}/. Every public client/* subpath unchanged.
  • Big files split, one per commit: Trust and Subagents interfaces (one closure each, 850–970 lines), Review's panel actions, Trust's explorer, Subagents' pane, Status config, Trail's dialog columns, client host/server.
  • blockWidth/measureBlock in client/sidebar (pasted in four bays). Row painters stay per bay: tones and faint differ.
  • Build clears dist/ and types/ and runs tsc -b itself — a stale file had hidden review/frame.
  • Trail's measurement and the smoke's agent turn use the shared scripts/measure-agent.ts. New scripts/golden.ts snapshots every preview. tui-smoke split into per-bay probes with install modes (bundle / all bays / one bay), and OpenCode 2 configured as the install docs say — which is how the Trust/updater bug surfaced.

Checked

  • Every commit: build, lint, typecheck, 2196 tests, 51 preview goldens identical (except the intended notice changes).
  • pack:check: every package packed; bundle and each bay installed alone; new agent halves load.
  • tui-smoke on OpenCode 1.18.32 and 2.0.18 × bundle, all bays, each of the 7 bays alone: 18/18 pass.
  • Trail measurement (real model turn) passes on both OpenCodes.
  • Public API vs main: the six removals, trust/server + updater/server added, OLD_NAMES/SETUP_COMMAND gone from client/settings, blockWidth/measureBlock added to client/sidebar.
  • Independent adversarial review: no code defects; its doc/check findings fixed in the last two commits.

After merge

Release 0.10.0; then delete media/dock.gif and media/statusline.png.

🤖 Generated with Claude Code

Codestz and others added 30 commits October 4, 2026 12:34
…roof a refactor changed nothing

51 previews across Review, Shell, Status, Subagents, Trail, Trust and the client, run under a
pseudo-terminal so they draw in colour (a tone that moved is a difference), Status on an empty config
of its own. `bun scripts/golden.ts` before, `--check` after.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eview found untested

Review: every binding runs a command and every command has a key; no key means two things; a key
goes through the guard and leaves the keys screen first, except ?, close and the numbers; and the
keys screen shows every bound key — or names it as unlisted, with where it is said instead.
Trail: each tone is its theme colour, ink falls back panel → background → text, and a chip is its
tone faded into the dialog by its TINTS amount, made once, never on a theme with nothing to tint.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… on OpenCode 1 too

SERVER_BAYS left Status out, so a Status in tui.json but not opencode.json — no /status-setup, no
skill — drew no warning. And the test now checks the other way round: every bay that publishes
./server is one doctor checks, which would have caught this.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he bundle, or one bay alone

SMOKE_INSTALL=bays (the default, as before) | bundle | <bay>. The harness, the install and the
command helpers are modules of their own; each probe owns its steps and what they assert, and the
entry only orders the stages and runs a probe when its bay is installed. A bay alone must not bring
the others' palette entries, and /cockpit-setup is offered only where an agent side ships it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…gent keeps under .claude/

Biome read the worktree's own biome.json as a second root and refused to run; bun test ran its 135
test files beside ours, and the two copies collided on the real daemons and terminals they start
(a 79-second run went past 20 minutes). Biome now follows .gitignore, and tests are found under
packages/, where every one of them lives.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… imported are gone

review/frame — broken since it was added (it named dist/core/frame.js; the file was always
dist/core/view/frame.js, 0.6 through 0.9); trail/core, trust/core and updater/core with the index
files that only served them; status/fixtures; client/elements. client/brief stays: Status's
interface imports it. status/segment stays for custom segments, shell/connect for pack-check.

Proven: every published entry point's runtime and .d.ts exports, against main, differ by these six
alone; pack-check loads the bundle and each bay alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
journal.ts appends to and reads the trail's file; it goes to src/io/, which may import core/ but is
never imported by it. paths.ts stays in core/: it only works out where the file goes. CONTRIBUTING
gains the line for io/ and says what core/ may not do: touch the filesystem, git, the network or
another process — the clock or an environment variable as a parameter's default is fine.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…es.ts splits by what it does

git/sources.ts (473 lines) asked git, read files, and decided what the answers meant. The deciding —
decide, pickBase, withCounts, nameStatus, statusChange, the caps — is core/git/changes.ts, pure; the
asking — runGit, readBlob, readWorking, worktreeChanges, branchChanges, baseCandidates — is
io/git.ts. Every line of the old file is in one of the two. The store's persistence and the system
viewer move to io/ whole; the store's paths stay in core/, where they only work out a location.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s the Disk it is handed

Core already took its filesystem as an argument; disk.ts also held the real one. nodeDisk goes to
io/disk.ts, and core/disk.ts keeps the Disk interface, the in-memory disk the tests use, and
readJson. spawn.ts, which runs opencode, moves to io/ whole.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… to io/ — core/ only decides

core/budget, core/command and core/custom read files, run commands and import modules; they now live
in io/, and core/ keeps the types and the decisions (which path a module resolves to, what a missing
one says). previewSettings no longer reads the disk itself: `as` takes a readFile from its caller.
pack-check loads custom modules from their new place.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…etup/

src/ was 25 flat files. The daemon's client, connection and spawn go to daemon/; what OpenCode hands a
plugin (host, server, surfaces, elements, sidebar, service) to opencode/; the settings and what reads
them to settings/; brief and conventions join setup/. Pure moves: the exports map points every public
subpath at its new file, so `client/host`, `client/settings`, `client/setup`, … are unchanged — the
public API snapshot is identical. service.ts finds its package one folder further up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nknown names like a typo

0.9 kept detecting the old names for one release, each a `!` row naming the new key; 0.10 drops
settings/old-names.ts and its two calls. Their values were never read and still are not. What changes
is what is said: a top-level one (`statusline`, `ui`, Status's keys at the root) is "not a setting",
with the nearest name offered, in Status's column and in doctor; one inside a bay's section
(`review.sidebarOrder`, `subagents.hideFinishedAfter`) is named by /cockpit-setup only, like any key
the bay does not read. `OLD_NAMES` and `SETUP_COMMAND` leave client/settings; the settings reference
loses its old-names table (the site's configuration page keeps it for anyone moving an old file).
Three previews showed an old-name notice; they show a wrong-kind one now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
0.9 renamed the command and kept the old name for one release, as a command of its own whose line
said the new name. It goes in 0.10, as announced. The skill no longer lists it as a trigger; the
smoke runs /status-setup with AGENT=1 instead, and looks for the old section's new notice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dist/ was cleared already; types/ is tsc -b's, and an incremental build never deletes what a moved or
removed source left behind — a stale file kept review/frame looking fine for three releases. The
build now removes types/ and the build info, then runs tsc -b, so packing and releasing (which run
the build and nothing else before npm pack) ship declarations that match the code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the shape in host/index

host.ts was 800 lines: the Host shape, the key helpers, the v1 wrapper, the v2 builder and the entry
both load. Each is its own file now; index keeps the shape, dualTui and onPaste and re-exports the
rest, so `client/host` exports exactly what it did (public API snapshot unchanged). No line of logic
changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…aces, and the entry in index

server.ts was 841 lines: what a feature answers with, the v1 hooks, the v2 context and its tools and
commands, reading a skill's folder, and the entry both OpenCodes load. Each is its own file; surfaces
moves in beside them. index re-exports exactly what `client/server` exported before (public API
snapshot unchanged). No line of logic changed; v1's contentText is exported inside the folder only,
for v2's tool text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…it, and the lines it draws

616 lines held four concerns. core/config/ now has shape.ts (the section's types and the kind of each
key), load.ts (loadStatus and the notices `cockpit_settings` lists), problems.ts (what only Status
can tell is wrong), lines.ts (presets, overrides, lines resolved). index.ts is `status/config`, which
the bundle's bin imports: it exports exactly what it did (public API snapshot unchanged). No cycles:
shape ← lines ← problems ← load. No line of logic changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the project uses

conventions.ts (403) did three things under three headings: find and write the marked section,
locate the instruction files, and scan the project (long-running commands, ticket keys, the repo).
Each heading is a file in setup/conventions/; importers name the one they use. No line changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s had it pasted

Shell, Subagents, Trail and Trust each measured their block the same five lines: the container's
width over the block's own, a quarter of the window before the first layout. `blockWidth` and
`measureBlock` in client/sidebar now; Shell keeps its narrower guess (30) as an argument, Subagents
keeps its debug line. client/sidebar gains the two exports.

The row painters stay per bay: they draw different tones, and faint differently (italic in three,
the terminal's dim in Updater) — merging them would change what the sidebar looks like.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y>.ts

catalog.ts carried every bay's keys inline (Status's alone ran 107 lines). They still live in client
— it cannot import the bays, and packages/opencode's test still holds each list to its bay's
defaults — but one file per bay, so a bay's keys are found and changed next to nothing else.
OWN_KEYS is the same object; the generated reference is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s.ts

dialog.ts laid out the whole of `/trail` and every row in it. How wide each column is at a width,
and a thing's or a touch's row in those columns, move to columns.ts; dialog.ts keeps the header, the
keys, the empty prose and the assembly. No line of logic changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s do

packages/trail/measure/agent.ts was written before scripts/measure-agent.ts and kept its own copies:
finding OpenCode, the world's config and environment, the turn, reading tool calls and text, the
retry loop. It uses the shared ones now. `measure` takes how many runs must pass (Trail asks two of
three of a free model) and stops once enough have. Run on 2.0.18 and 1.18.32: both passed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ncode run` and read it

scripts/smoke/agent.ts spawned OpenCode and parsed its JSON events itself, a third copy of what
scripts/measure-agent.ts does. It calls `turn` now, which says whether the turn finished in its five
minutes, so the smoke still fails a turn that hung.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tor, what was removed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tice a wrong kind

On OpenCode 2 every entry goes in opencode.json, which loads both halves — what `opencode plugin add`
writes — instead of splitting the halves across opencode.json and cli.json, which no user does and
which loaded a bay's interface twice. Review's section carried an old name for its `!` row; old names
are unknown names now, said by /cockpit-setup only, so it carries a value of the wrong kind.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…een that lays them out

explorer.ts was 1021 lines in four marked sections. The tree (families as folds, a row each) is
tree.ts, the card (what the selected row is and why, and its buttons) card.ts; explorer.ts keeps the
screen. Importers name the file that has what they use. No line of logic changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…er to footer

screen.ts (978) drew every item of a subagent's conversation and laid the pane out around them. A
call's row or box at each fold, its todo list, a card and the thinking move to calls.ts; screen.ts
keeps the body, the header, the footer, the keys and the scroll. No line of logic changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…edger dialog, and the wiring

tui/index.tsx was one 851-line closure. paint.ts draws the sidebar block and the dialog; requests.ts
decides and answers what OpenCode asks; ledger.tsx is `/trust` and its keys; index.tsx keeps the
ledger file and the wiring — as Trail's was split for 0.9. No line of logic changed: what the closure
held is a small shared object now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…essages, and the wiring

tui/index.tsx was one 966-line closure. paint.ts draws the sidebar block and the pane; pane.ts is the
pane and its keys (open, move, fold, stop, remove, background); messages.ts is typing to a subagent
and relaying its answer; surface.ts the pane's state they share; index.tsx follows the conversation
and wires the slots. No line of logic changed; the spinners' frame belongs to the painter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…that joins them

actions.ts (765) held every verb. Moving through the list and the diff is navigation.ts; comments,
replies, marking read or viewed, folding and submit are threads.ts, which reads the rows through
navigation; actions.ts keeps sources, the base, stats, the viewer and the keys screen, and returns
the one table keys.ts binds. No line of logic changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codestz and others added 4 commits October 4, 2026 20:49
…gets an empty agent half

OpenCode 2 sets up a plugin's interface only once its agent half has loaded; one with no `./server`
is skipped on both sides without a word in either log (measured on 2.0.18). These two had none, so
`opencode plugin add @opencode-cockpit/trust` (or `/updater`), as the install docs say, loaded
nothing. The smoke never saw it: it put interfaces in cli.json, which no install does.

Each now publishes `./server` — `dualServer(id, async () => ({}))`, which also brings `/cockpit-setup`.
Doctor lists them as EMPTY_SERVER_BAYS: on OpenCode 1 the interface alone is the whole bay, so a
tui.json-only install is not told to add an agent half. Smoke: trust, updater, bays and bundle pass on
2.0.18; trust and updater on 1.18.32.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ads the new agent halves, docs say what doctor lists

From the independent review of the branch:
- site/scripts/engine.ts mapped `@opencode-cockpit/<pkg>/<sub>` to `src/<sub>.ts`, which the client's
  new folders broke for every subpath but `.`; it reads the package's `exports` now (output unchanged).
- pack-check imports `trust/server` and `updater/server` from an install of each alone, and checks
  OpenCode 2's door is shipped — the fix this release makes had no pack-time check.
- Two site pages said doctor lists every name from before 0.9; it lists top-level ones, and
  `/cockpit-setup` names a key a bay does not read in its own section.
- A smoke comment still said Trust and the updater have no agent side.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…be in 0.10; the client preview shows a real notice

The folder was kept in 0.9 for anyone who had copied it; its own description said "removed in 0.10".
status-setup carries the same design rules. The client preview's sample settings used `shell.sidebarOrder`
to show a `!` row, which an unknown key in a section no longer draws; it uses a `sidebar` list in
Shell's section, which does. Both from the independent review.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ger hides the keys

The pane stopped at 200 files, and this release's own branch (222) was cut short. Files were read one
after another, 6 ms each; eight at a time, 200 take 390 ms instead of 1.3 s. A paint measured 4 ms at
200 files, 9 ms at 1,000 and 117 ms at 2,000, so the cap moves to 1,000, where a paint still fits in a
frame.

The footer's notice took the keys' row. "More than N files changed" stays for the whole review, so
every key — the way out included — was gone. Trouble now takes the rule's row above the keys, and the
footer is still two rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Codestz
Codestz merged commit 67df1e2 into main Oct 5, 2026
2 checks passed
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