Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
ed4c832
wip(app): extension host, sdk, and desktop main host
Hona Sep 30, 2026
10a3678
wip(app): generic side, dock, and mobile regions
Hona Sep 30, 2026
665e7c2
wip(app): shell bottom slot, channel status, builtin manifests
Hona Sep 30, 2026
c61a994
wip: browser contract, system open for file urls
Hona Sep 30, 2026
b31f579
feat(app): move usage, btw, and debug into gui extensions
Hona Sep 30, 2026
96d5666
feat(app): move the terminal into a gui extension
Hona Sep 30, 2026
55a196e
chore: lint gui extension boundaries and module state
Hona Sep 30, 2026
ce2585b
feat(desktop): move the browser pane into a gui extension
Hona Sep 30, 2026
8bbd280
feat(desktop): move the updater and pairing into gui extensions
Hona Sep 30, 2026
6eb9d3f
feat(app): move the file browser and review into gui extensions
Hona Sep 30, 2026
4fc739f
feat(desktop): move ssh and wsl servers into gui extensions
Hona Sep 30, 2026
49f1837
feat(app): host renderers for settings, status, servers, surfaces, an…
Hona Sep 30, 2026
f5bc705
fix(app): tab selection guard, host project picker, early main extens…
Hona Sep 30, 2026
adf0423
feat(app): move the session summary into a gui extension
Hona Sep 30, 2026
7c2235e
refactor(app): move extension translations into extension folders
Hona Sep 30, 2026
ff4a8e6
fix(app): keep the app mounted when an extension is enabled
Hona Sep 30, 2026
78f878c
fix(app): import extension storage from the default storage too
Hona Sep 30, 2026
ab14739
fix(app): close extension dialogs by id and stop hidden summary loads
Hona Sep 30, 2026
6154dc8
test(desktop): prune the desktop test surface
Hona Sep 30, 2026
8b7f10b
fix(ui): dispose each closing dialog on its own timer
Hona Sep 30, 2026
2c8a70c
fix(ui): guard dialog closes and derive layers from the stack
Hona Sep 30, 2026
8dcbae7
test(desktop): restore contracts the prune dropped
Hona Sep 30, 2026
94bb7f2
fix(ui): harden dialog teardown, errors and focus during exits
Hona Sep 30, 2026
35b14b3
fix(ui): tell replaced dialogs they closed; polyfill Promise.try
Hona Sep 30, 2026
3b0481e
refactor(app): leave app and desktop clean after the extension move
Hona Sep 30, 2026
33da15a
test(app): shared e2e harness for the test campaign
Hona Sep 30, 2026
ed33675
docs: test ownership rules for app, desktop and gui extensions
Hona Sep 30, 2026
cc95fd3
fix(app): normalize stored panel tab ids through the owning extension
Hona Sep 30, 2026
da7759d
fix(app): remap stored tabs, selection and preview together
Hona Sep 30, 2026
3978a5e
test(session-ui): run component tests in CI and bring them current
Hona Sep 30, 2026
37456ad
fix(app): keep a tab permanent when a preview collapses into it
Hona Sep 30, 2026
3033748
test(app): tighten the e2e harness mocks
Hona Sep 30, 2026
7da3c91
test(app): prune the composer and command test surface
Hona Sep 30, 2026
8dba9ae
fix(app): name the session's server in the summary when several exist
Hona Sep 30, 2026
b07e633
test(app): prune the gui extension test surface
Hona Sep 30, 2026
a8d4d1e
test(app): scope mock PTYs by workspace and issue unique tickets
Hona Sep 30, 2026
a242629
test(app): restore composer contracts the prune dropped
Hona Sep 30, 2026
85ba40f
test(app): restore extension contracts the prune dropped
Hona Sep 30, 2026
fd2537f
test(app): prune the shell, runtime and settings test surface
Hona Sep 30, 2026
04f8481
test(app): prune the session timeline test surface
Hona Sep 30, 2026
8a37e31
test(app): restore shell contracts the prune dropped
Hona Sep 30, 2026
f478d67
test(app): restore timeline contracts the prune dropped
Hona Sep 30, 2026
86cd53d
test(app): finish the shared e2e harness
Hona Sep 30, 2026
e649c08
test(app): answer the review pane benchmark in the server's diff shape
Hona Sep 30, 2026
c82606b
perf(app): compile extension chunks while the app idles
Hona Sep 30, 2026
2f8dfa1
perf(review): reuse the model's renderable diffs and kinds in the panel
Hona Sep 30, 2026
3ec96fb
perf(review): keep the tree still when a session switch reopens revie…
Hona Sep 30, 2026
6bca5b3
test(app): fail unmocked API requests and tighten harness waits
Hona Sep 30, 2026
6cf58d9
test(app): page mock shell output like the server and acknowledge fin…
Hona Sep 30, 2026
acc9415
refactor(util): share file URL encoding and file kinds
Hona Sep 30, 2026
2fe6172
Merge upstream/v2 into gui-extensions
Hona Sep 30, 2026
6ce809a
chore(app): merge duplicate util path imports
Hona Sep 30, 2026
84e0bc0
fix(app): keep browser comments from pre-extension builds
Hona Sep 30, 2026
afddc7c
test(browser): restore the browser pane's tests
Hona Sep 30, 2026
da3a28c
fix(browser): keep element refs unique across reloads and notes editable
Hona Sep 30, 2026
c84e9b4
test(browser): deliver the stale event in the suspension case
Hona Sep 30, 2026
7ab3087
fix(storybook): restore every designer story with its id
Hona Sep 30, 2026
120f801
fix(storybook): run the real SSH renderer in its stories
Hona Sep 30, 2026
0cee67e
fix(app): stop activating an extension after the host unmounts
Hona Sep 30, 2026
8726611
fix(app): run cleanups an extension registers after it is disposed
Hona Sep 30, 2026
d0f249e
style: format files the extension move added
Hona Sep 30, 2026
5eff053
chore(lint): vendor anti-slop Oxlint rules for the GUI packages
Hona Sep 30, 2026
e2c5866
fix(desktop): refuse the extension manager in packaged builds
Hona Sep 30, 2026
4a27b0b
docs(desktop): mark archive installs as groundwork for .ocdx extensions
Hona Sep 30, 2026
7b8f423
fix(review): keep each session's review state across the upgrade
Hona Sep 30, 2026
4942243
fix(extensions): make reload, disable and dialogs safe mid-activation
Hona Sep 30, 2026
fc1ac2a
fix(extensions): finish teardown before a replacement activates
Hona Oct 1, 2026
a4649b5
fix(app): follow a restarted server's live controller
Hona Oct 1, 2026
64f533f
refactor(desktop): serialize each extension's lifecycle
Hona Oct 1, 2026
85118c8
fix(app): remount a server's routes when sign-in replaces its controller
Hona Oct 1, 2026
b0e8f7b
fix(browser): re-register panes when the browser remote returns
Hona Oct 1, 2026
f0a9e22
fix(app): keep a disposed extension's queued dialog closed
Hona Oct 1, 2026
46409aa
fix(browser): wait for every page to close before the pane settles
Hona Oct 1, 2026
f630927
fix(app): let remote events win over older snapshots
Hona Oct 1, 2026
f3bc1e3
test(app): let the extension host fixture disable and reload
Hona Oct 1, 2026
9e34a23
fix(browser): retry a rejected pane registration
Hona Oct 1, 2026
c37c70d
fix(app): keep the app mounted through extension server reloads
Hona Oct 1, 2026
85924c5
fix(wsl): stop starting servers on teardown
Hona Oct 1, 2026
74555b9
fix(app): keep extension servers' tabs through an outage
Hona Oct 1, 2026
e24dc88
fix(summary): show the global-sync project like v2
Hona Oct 1, 2026
5ae5ded
fix(app): close the details drawer from its changes row like v2
Hona Oct 1, 2026
9dd0186
fix(app): restore v2's Context tab order and saved tree tab
Hona Oct 1, 2026
39910ec
fix(app): realign native desktop labels in hr, hu, is and lt
Hona Oct 1, 2026
fdd5cc3
fix(app): restore v2's side region opener and mobile view reset
Hona Oct 1, 2026
62a8129
fix(app): restore v2's mobile changes, browser pruning and file heading
Hona Oct 1, 2026
dab184a
fix(btw): drop a /btw tab saved before extensions
Hona Oct 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
docs: test ownership rules for app, desktop and gui extensions
Rules drawn from the test audit: one contract per test at the strongest boundary, keeper suites instead of a spec per bug, the shared e2e harness instead of per-spec setup, no test-only production seams, tests that can fail, tables instead of per-theme or per-channel repeats, and no e2e copies of component tests. gui-extensions gets its own AGENTS.md with the structure, host boundary, performance and localization rules.
  • Loading branch information
Hona committed Sep 30, 2026
commit ed33675c38c199dd55f229ea85ece336af91e45b
12 changes: 12 additions & 0 deletions packages/app/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,18 @@
- In translation review notes, name the corpora used and call out uncertain or region-specific terminology so native speakers can focus review where it matters.
- Also use the relevant language authority or official dictionary for the locale (for example RAE/Fundéu, FranceTerme, Duden, TDK, Kotus/Kielitoimiston sanakirja, Språkrådet/Bokmålsordboka, Rada Języka Polskiego/PWN, the Russian and Arabic language academies, the Ukrainian Orthography, Taiwan MOE dictionaries, or the Royal Society of Thailand). Treat the English dictionary as the semantic source of truth and preserve placeholders, code identifiers, product names, and keyboard labels.

## Tests

A test must pay for its upkeep. Before adding one, answer three questions: which observable contract does it protect, which credible regression makes it fail, and why does no existing test already catch that regression. If any answer is missing, do not add the test.

- One contract, one owner. Test each behavior once, at the strongest boundary that observes it: the area's e2e keeper suite, or the owning module's unit test. Extend an existing table or keeper case before creating a file.
- Do not unit-test what an e2e keeper already proves, what a dependency does (for example `@pierre/trees`), config that passes values through, or source text (import or string greps). Source text is fair game only when that text is the contract, such as a persisted key.
- Never add production seams for tests: no test-only exports, parameters such as `now`, `delay` or `budget`, flags, globals, or `data-*` attributes. Control time with `setSystemTime` from `bun:test` and drive the real entry point.
- A test must be able to fail. Do not assert values the code under test produced, mock the behavior you assert, use soft assertions for the order the test guards, or leave `rejects`/`resolves` un-awaited. A regression test must fail on the code before the fix.
- Do not repeat a case per theme, text direction, viewport or channel. Use one table, and only when the behavior differs.
- When you delete or move coverage, name the test that still proves the contract. When you move a contract, mutate its owner once and confirm the new test fails.
- A contract proven by a `packages/session-ui/component-tests` test does not also get an app e2e copy.

## Tool Calling

- ALWAYS USE PARALLEL TOOLS WHEN APPLICABLE.
Expand Down
8 changes: 8 additions & 0 deletions packages/app/e2e/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,11 @@
- Do not retry state-changing actions. Retry idempotent readiness checks, then perform the action once and assert its outcome.
- Keep action and assertion timeouts adaptive. Do not use short timeouts as readiness probes or rely on retries to hide flakes.
- Assert exact outcomes and identities so stale state, duplicate rendering, and interactions with the wrong element cannot pass.

## Test Ownership

- Each product area has one keeper suite in `regression/` (for example tabs, settings, review, terminal, timeline history). A new regression is a new case in that suite, not a new spec file.
- Build pages with the shared harness in `utils/` (`app.ts`, `workspace.ts`, `mock-server.ts` with `mockServers` and the mock PTY). Do not hand-write routing, storage seeding, or fixtures inside a spec. If the harness lacks something, add it to `utils/` once rather than copying setup.
- Files in `utils/` must not end in `.spec.ts` or `.test.ts`, or Playwright runs them as tests.
- Assert what the user sees and can do. Assert exact pixels or CSS values only when that measurement is the contract, and then only once, in the owning keeper.
- `performance/` holds benchmarks that CI does not run. Never prove correctness with a benchmark, and never copy benchmark code into a regression spec; import it from `utils/`.
12 changes: 12 additions & 0 deletions packages/desktop/AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Desktop package notes

## Tests

- Follow the Tests section of `packages/app/AGENTS.md`: every test protects a named contract with a credible regression, has one owner, and can fail.
- Packaging, installer, IPC validation, credential, and SQLite migration and storage tests are contracts. Write them as tables: one table across channels, not one test per channel.
- Never add test-only parameters or exports to production modules (`now`, `delay`, `budget`). Use `setSystemTime` from `bun:test`.
- Test IPC through the raw message format the renderer sends, not through a mock client.
- No source-grep or tombstone tests that assert a file, export or feature is gone.
- Main-process code that belongs to a GUI extension is tested in `packages/gui-extensions`, not here.
- `bun typecheck` does not cover test files. Run `bun test <file>` for every test file you touch.

## Code

- Follow Solid best practices, leave a comment when violating this: https://www.brenelz.com/posts/solid-js-best-practices/
- Renderer process should only reach the main process through `window.electron` (exposed by `src/preload`) and the MessagePort RPC client in `src/renderer/api.ts`.
- Main process should define IPC handlers in `src/main/ipc-handlers/*` and compose them in `src/main/ipc.ts`.
Expand Down
33 changes: 33 additions & 0 deletions packages/gui-extensions/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# GUI extensions

Built-in features of the desktop and web app, each behind the SDK in `src/sdk/`. The renderer host lives in `packages/app/src/runtime/extension/`, the main-process host in `packages/desktop/src/main/extension/`.

## Structure

- One folder per extension: `index.ts` (`Extension.define({ id, os?, i18n })`), `contract.ts`, `renderer.tsx`, optional `main.ts`, and `i18n/<locale>.ts`.
- `src/renderer.ts` and `src/main.ts` are the only files that list the built-ins. Main never imports renderer code.
- Another extension may import only your `contract.ts` (tokens and schemas, no runtime code). Every consumer must still work when the provider is disabled: `ctx.use(Service)` is an accessor that can return `undefined`.
- Never import `@opencode/app`, `@opencode/desktop`, or `@/` paths. Import CSS with `?inline` and contribute it through `ctx.add(Style, css)`. No module-level state: keep state inside `setup`. `bun run lint` enforces these rules.

## Host boundary

- Host code must not name an extension or know its internals: no extension ids, command ids, DOM selectors, or stored key formats. When the host needs something from extensions, add a generic, documented field to the SDK (for example `PanelTab.file`, `Command.featured`, `Command.section`).
- Accepted exceptions: the built-in lists, the keybind rename map in `packages/app/src/settings/keybinds/migration.ts`, and the crash page's use of the updater contract.

## Performance

- Keep `renderer.tsx` small and put heavy UI behind `lazy()`. Load chunks with `preload(sessions, …)` or `onIdle(…)`, never at startup.
- Return stable objects from `Panel.list` and reactive contributions, so the host never remounts a trigger or a panel.
- Never add work to timeline rows. The session header slot is the only timeline surface.

## Localization

- Each extension owns its copy in `i18n/en.ts` with short keys. `ctx.t` falls back to the app's keys only for shared vocabulary such as `common.*`; feature copy belongs to the extension.
- When moving copy, keep the English byte-for-byte and carry every locale's translation. The localization rules in `packages/app/AGENTS.md` apply.

## Tests

- Follow the Tests section of `packages/app/AGENTS.md`.
- Unit-test pure logic that carries a contract: path and security checks, storage migration, protocol parsing, archive validation.
- UI behavior is proven by the app e2e keeper suites. Do not add unit tests that repeat them.
- Test a main-process entry through its `Remote` contract with real inputs, not through Electron mocks.