githubnext/chopinPublic

Let's compose rich plans together

Give the Projects sidebar its own decision-count socket

The second review of #378 (#345) found sidebar counts still went stale in
four cases. With no document open there was no socket at all. A watch the
server refused because GitHub was briefly unavailable was never retried. A
watch sent before the catalogue loaded never covered rows that loaded later,
so a decision answered in between stayed wrong. Deleting an archived child of
an active parent never published the lower project total.

- The sidebar now owns a socket at /ws/sidebar, admitted on the browser
  session and origin alone and bound to no room. Room sockets no longer carry
  decision counts, subscribe to repository topics, or accept watches.
- sidebar:watch is additive and bounded: 50 repositories a frame, 500 document
  IDs per entry, 200 repositories per socket. sidebar:unwatch removes the ones
  the sidebar stops showing. The server checks GitHub read access and the
  repository node ID before subscribing, shares one check across overlapping
  frames, and skips the check for a repository already watched under the same
  name. That lets the client watch again whenever rows load, so every loaded
  document gets a snapshot.
- The reply separates refused from unavailable. Unavailable subscribes
  nothing; the client watches those repositories again with every loaded
  document after a backoff from two seconds to a minute. A recheck that cannot
  reach GitHub keeps an existing subscription, and a denied one drops it.
- Permanent deletion publishes sidebar:decisions with the deleted document at
  zero and the repository total read after the commit.
- New coverage: decision-watch and client planner unit tests for limits,
  chunking, outage recovery, overlapping and stale replies; a service test
  for the deletion announcement; and browser tests for a viewer with no
  document open, a row loaded after the first snapshot, and deleting an
  archived child while a viewer stays in the parent. The reconnect test now
  drops the sidebar socket.
- docs/channels.md, docs/authentication.md and AGENTS.md describe the sidebar
  socket. The archive and restore wording now matches the client: archive
  applies the returned total, and restore reloads the active catalogue.

Assistant-model: Claude Opus 5.5 (fast)
Assistant-workflow: issue-to-pr
Assistant-duration: 18m converged, estimated 45m
Assistant-verification: bun run fix passed: dprint fmt and oxlint --fix, 10 existing warnings, 0 errors; changes inspected
Assistant-verification: bun test failed: 4077 pass, 3 skip, 29 fail, all in scripts/pr-maintenance because this host requires commit signing; GIT_CONFIG_GLOBAL=/dev/null bun test scripts/pr-maintenance passed 166/166
Assistant-verification: bun unit passed: decision-watch (frame and socket limits, outage recovery, shared checks, unwatch during a check, rename, recheck, release), sidebar-decision-watch planner (chunking, late-loaded rows, refused and unavailable, stale replies, backoff), deletion announcement
Assistant-verification: bun run types passed: every workspace package and e2e
Assistant-verification: bun run ci passed: dprint, oxlint, tokens, design contract after renewing the navigation-shell owner hash, design record, impeccable
Assistant-verification: postgres contract passed: TEST_DATABASE_URL against a disposable postgres:17 on 127.0.0.1:55346, apps/server/src/storage/postgres 74/74
Assistant-verification: playwright e2e failed: sidebar-decision-counts, smoke and document-navigation passed 52/53, including all nine sidebar-count tests; document-navigation:322 failed at 412.125 > 412, the same result the reviewer got on origin/main
Assistant-verification: playwright e2e passed: without the deletion announcement, the archived-child deletion test fails because the project total never drops
User-preference: keep sidebar counts fresh across repositories with authorized socket subscriptions
Co-authored-by: Alex Lavaee <lavaman131@github.com>
ALAlex Lavaee authored
1d25117Patch
Parentb95c7a2
24 files changed+1384-477
StackedSplit
AGENTS.md+2 โˆ’1
apps/server/src/chat/incoming.ts+13 โˆ’4
apps/server/src/main.ts+173 โˆ’100
apps/server/src/plan/decision-counts.test.ts+58 โˆ’51
apps/server/src/plan/service.ts+16 โˆ’16
apps/server/src/questions-service-durability-1.test.ts+1 โˆ’1
apps/server/src/socket/admission.ts+37 โˆ’4
apps/server/src/socket/decision-watch.test.ts+143 โˆ’88
apps/server/src/socket/decision-watch.ts+122 โˆ’52
apps/server/src/wire.ts+23 โˆ’6
apps/web/src/document-actions.tsx+6 โˆ’8
apps/web/src/navigation-shell.tsx+7 โˆ’22
apps/web/src/room-workspace.tsx+0 โˆ’15
apps/web/src/sidebar-decision-counts.test.ts+3 โˆ’4
apps/web/src/sidebar-decision-watch.test.ts+184 โˆ’0
apps/web/src/sidebar-decision-watch.ts+151 โˆ’0
apps/web/src/use-sidebar-decisions.ts+93 โˆ’0
apps/web/src/wire.ts+5 โˆ’4
docs/authentication.md+4 โˆ’1
docs/channels.md+44 โˆ’31
e2e/database.ts+13 โˆ’0
e2e/github.ts+3 โˆ’1
e2e/sidebar-decision-counts.e2e.ts+212 โˆ’17
packages/protocol/index.d.ts+71 โˆ’51