cloudaxe-opencode/packages/app/src
Kit Langton ab31c41fee refactor(bus): one GlobalBus emit per event, with optional sync metadata
Today every sync.run produces TWO GlobalBus events:

  1. From bus.publish itself — the projection view:
     { payload: { id, type: "session.created", properties } }

  2. From sync/index.ts — the source-of-truth envelope:
     { payload: { type: "sync", syncEvent: { type: "session.created.1", id, seq, aggregateID, data } } }

These two emits live in different files and discriminate via
`payload.type` — either a real event type or the sentinel "sync".
That collision is the source of confusion in the codebase:
consumers have to know which shape they're looking at.

The duality dates back to:
  - d88912abf0 (Dec 2025): Dax added GlobalBus.emit inside bus.publish.
  - b22add292c (#22347, Apr 2026): James consolidated sync events onto
    GlobalBus by ADDING a second emit with the source envelope.

#22347's stated intent was "consolidate events into a single stream".
Two emits with different shapes on one channel isn't quite that. One
emit per event with a unified shape is.

This PR collapses the two emits into one:

  { payload: { id, type: "session.created", properties, sync?: { name, seq, aggregateID, data } } }

- bus.publish accepts an optional `sync?: SyncMetadata` option.
- sync/index.ts passes it; its own GlobalBus.emit is removed.
- BusEvent.effectPayloads() adds an optional `sync` field to every
  event schema.
- SyncEvent.effectPayloads() is no longer included in the wire schema
  (it's now subsumed by the optional field).
- Consumers migrate from `payload.type === "sync"` filtering to
  `payload.sync != null` filtering. control-plane/workspace.ts
  reconstructs SerializedEvent from { id, sync.* } for replay.

Note: this is a BREAKING SDK wire-format change. External consumers
that read `payload.type === "sync"` or `payload.syncEvent` need to
migrate. Internal opencode consumers all migrated in this PR.

Side observation: the runtime emit was using `payload.syncEvent`
(nested) while the SDK schema declared the fields at top level under
`type: "sync"` — a silent schema drift. Collapsing to one emit also
fixes that drift by definition.

Verified:
  - bun typecheck — clean
  - bun run test test/sync/index.test.ts test/bus/bus-effect.test.ts
    test/server/httpapi-event.test.ts
    test/server/httpapi-event-diagnostics.test.ts — 29/29 green
  - bun run test test/server/httpapi-sdk.test.ts -t "streams sync-backed" — green
2026-05-18 13:23:30 -04:00
..
addons fix: clean up 49 unused variables, catch params, and stale imports (#22695) 2026-04-16 02:01:53 +00:00
components desktop: add free limit + go usage exceeded dialogs to match tui (#27677) 2026-05-18 15:20:08 +08:00
constants app: file type filter on desktop + multiple files (#18403) 2026-03-20 15:02:07 +00:00
context refactor(bus): one GlobalBus emit per event, with optional sync metadata 2026-05-18 13:23:30 -04:00
hooks fix(app): more startup perf (#19288) 2026-03-26 13:41:22 -05:00
i18n docs(app): stale reference to removed multi-edit tool (#27579) 2026-05-14 13:10:01 -05:00
pages fix(ui): fix question dock overflow and message part flex layout (#28142) 2026-05-18 17:55:27 +08:00
utils fix(app): enhance error handling by unwrapping SDK-wrapped errors in formatServerError (#27061) 2026-05-13 13:53:10 +05:30
app.tsx desktop: sentry integration (#15300) 2026-05-01 11:56:31 +08:00
custom-elements.d.ts
entry.tsx fix(app): preserve auth token credentials (#25636) 2026-05-03 21:13:42 +00:00
env.d.ts desktop: sentry integration (#15300) 2026-05-01 11:56:31 +08:00
index.css app: better loading (#23489) 2026-04-20 05:17:37 +00:00
index.ts Reapply "fix(app): startup efficiency" 2026-03-25 06:25:57 -05:00
sst-env.d.ts zen: update sst version 2026-02-18 13:54:23 -05:00
theme-preload.test.ts fix(app): remove oc-1 theme 2026-03-09 08:25:41 -05:00