fix: validated ticket sweep — tier 1 + tier 2 bugs #538

Merged
dries merged 11 commits from fix/validated-ticket-sweep into main 2026-08-20 23:12:38 +02:00
Owner

One commit per validated ticket, each with a failing-first (red/green) test where testable. All fixes were verified against current main before implementation (see the validation comments on each ticket).

Tier 1

  • #529 — sends fired during the navigation stale window targeted the previous session. handleSend/handleRetrySend now fail closed when the session prop lags the route id. (useSessionActions.staleRoute.test.ts, red on main)
  • #490 — the global SSE hub silently dropped ocman.session.idle / ocman.session.changed on a full subscriber buffer. Both are identity-keyed, so they now coalesce last-write-wins per (event, session) like queue.updated; remaining drops log at Debug. (TestBroadcastHubCoalescesSessionEventsOnFullBuffer, red on main)
  • #462 — autoapprove tee buffer was unbounded and re-parsed from offset 0 per write. Now incremental parsing (cost per Write ∝ new bytes), 4 MB pending cap, resync at the next event terminator on overflow. (tee_buffer_test.go, red on main)
  • #532 — MCP splits and scheduled prompts ensured against the raw worktree dir, launching a second opencode instance per worktree. Both paths now fold to the project root like the queue path. (host_ensure_fold_test.go, red on main)

Tier 2

  • #456 — the ensure/restart singleflight body ran under the winning caller's ctx; a cancelled winner failed every coalesced waiter. The body now runs detached (2 min bound); callers wait on their own ctx. (TestEnsureProjectOpencode_WinnerCancelDoesNotFailWaiters, red on main)
  • #458 — ChildResultBroker.wait discarded an already-received result on a cancel race. A result in hand now always wins. (TestWaitReturnsBufferedResultOverCancellation, red on main)
  • #459 — composer retried a downed backend every 1s forever with the composer locked. Now 5 retries with exponential backoff, then unlock with the draft intact. (Composer.queue test, red on main)
  • #460 — the session cache mirror wrote a full Map clone on every streaming token. Now debounced 500 ms with an unmount/session-switch flush. (useSession write-count test, red on main)
  • #533 — remote create-session ran EnsureProjectOpencode before platform validation; an unknown platform launched a managed instance on the remote. Registry check now precedes the ensure. Local half N/A on main (PR #523 unmerged). (TestCreateSessionValidatesPlatformBeforeEnsure, red on main)
  • #530 — architecture diagram now shows the production port :8228, with the dev split noted below it.

Verification

  • make lint clean (guards included)
  • make test: backend + frontend fully green (1855 frontend tests)
  • go test -race on the touched concurrency packages (hostsvc/local)

Closes #529, closes #490, closes #462, closes #532, closes #456, closes #458, closes #459, closes #460, closes #533, closes #530.

One commit per validated ticket, each with a failing-first (red/green) test where testable. All fixes were verified against current main before implementation (see the validation comments on each ticket). ## Tier 1 - **#529** — sends fired during the navigation stale window targeted the previous session. handleSend/handleRetrySend now fail closed when the session prop lags the route id. (useSessionActions.staleRoute.test.ts, red on main) - **#490** — the global SSE hub silently dropped ocman.session.idle / ocman.session.changed on a full subscriber buffer. Both are identity-keyed, so they now coalesce last-write-wins per (event, session) like queue.updated; remaining drops log at Debug. (TestBroadcastHubCoalescesSessionEventsOnFullBuffer, red on main) - **#462** — autoapprove tee buffer was unbounded and re-parsed from offset 0 per write. Now incremental parsing (cost per Write ∝ new bytes), 4 MB pending cap, resync at the next event terminator on overflow. (tee_buffer_test.go, red on main) - **#532** — MCP splits and scheduled prompts ensured against the raw worktree dir, launching a second opencode instance per worktree. Both paths now fold to the project root like the queue path. (host_ensure_fold_test.go, red on main) ## Tier 2 - **#456** — the ensure/restart singleflight body ran under the winning caller's ctx; a cancelled winner failed every coalesced waiter. The body now runs detached (2 min bound); callers wait on their own ctx. (TestEnsureProjectOpencode_WinnerCancelDoesNotFailWaiters, red on main) - **#458** — ChildResultBroker.wait discarded an already-received result on a cancel race. A result in hand now always wins. (TestWaitReturnsBufferedResultOverCancellation, red on main) - **#459** — composer retried a downed backend every 1s forever with the composer locked. Now 5 retries with exponential backoff, then unlock with the draft intact. (Composer.queue test, red on main) - **#460** — the session cache mirror wrote a full Map clone on every streaming token. Now debounced 500 ms with an unmount/session-switch flush. (useSession write-count test, red on main) - **#533** — remote create-session ran EnsureProjectOpencode before platform validation; an unknown platform launched a managed instance on the remote. Registry check now precedes the ensure. Local half N/A on main (PR #523 unmerged). (TestCreateSessionValidatesPlatformBeforeEnsure, red on main) - **#530** — architecture diagram now shows the production port :8228, with the dev split noted below it. ## Verification - make lint clean (guards included) - make test: backend + frontend fully green (1855 frontend tests) - go test -race on the touched concurrency packages (hostsvc/local) Closes #529, closes #490, closes #462, closes #532, closes #456, closes #458, closes #459, closes #460, closes #533, closes #530.
dries force-pushed fix/validated-ticket-sweep from b2c7fac942
Some checks failed
CI / Frontend (pull_request) Successful in 8m15s
CI / Playwright E2E (pull_request) Successful in 9m20s
CI / Backend (pull_request) Failing after 9m28s
CI / Coverage Results (pull_request) Failing after 19s
CI / Build (pull_request) Successful in 3m17s
CI / Semantic Tag (pull_request) Has been skipped
to b3aa0f34aa
Some checks failed
CI / Frontend (pull_request) Successful in 8m52s
CI / Playwright E2E (pull_request) Successful in 9m51s
CI / Build (pull_request) Successful in 3m33s
CI / Backend (pull_request) Failing after 3m21s
CI / Semantic Tag (pull_request) Has been skipped
CI / Coverage Results (pull_request) Failing after 14s
2026-08-20 20:57:54 +02:00
Compare
Author
Owner

Rebased onto main after #523 landed (local pre-ensure + create-path worktree folding). Two notes: (1) the #533 platform-validation guard now covers BOTH paths — local and remote ensure — with an allowance for the empty-platform auto-pick; (2) the description's "local half N/A" note for #533 is stale post-rebase. #531 stays closed: main's local soft-fail Debug log is now justified by an explanatory comment in the handler, which was the ticket's alternative fix. Full suite re-run green after rebase (go test ./..., 1866 frontend tests, tsc, eslint).

Rebased onto main after #523 landed (local pre-ensure + create-path worktree folding). Two notes: (1) the #533 platform-validation guard now covers BOTH paths — local and remote ensure — with an allowance for the empty-platform auto-pick; (2) the description's "local half N/A" note for #533 is stale post-rebase. #531 stays closed: main's local soft-fail Debug log is now justified by an explanatory comment in the handler, which was the ticket's alternative fix. Full suite re-run green after rebase (go test ./..., 1866 frontend tests, tsc, eslint).
fix: validate the create-session platform via the session service
Some checks failed
CI / Frontend (pull_request) Successful in 8m25s
CI / Playwright E2E (pull_request) Successful in 9m31s
CI / Backend (pull_request) Successful in 10m7s
CI / Coverage Results (pull_request) Successful in 19s
CI / Build (pull_request) Successful in 3m26s
CI / Semantic Tag (pull_request) Successful in 6s
Pages / Build and publish site (push) Waiting to run
CI / Frontend (push) Has been cancelled
CI / Backend (push) Has been cancelled
CI / Playwright E2E (push) Has been cancelled
CI / Coverage Results (push) Has been cancelled
CI / Build (push) Has been cancelled
CI / Semantic Tag (push) Has been cancelled
897aa89028
Fixes the CI backend failure introduced by the #533 commit: the handler
read s.registry directly, which is nil in the owner-routing test doubles
(they stub s.sessions over their own registry), panicking in
TestHandlersAcceptLocalOwner. Worse, s.registry and the registry behind
s.sessions are not necessarily the same object, so the pre-ensure check
could disagree with the Create it was guarding.

Move the rule to sessionsvc.KnownPlatform, which answers from the same
registry Create validates against and is nil-safe. Covered by
TestKnownPlatform.

Coverage ratchet: ✅ pass

Suite Baseline This PR Δ
frontend 71.33% 71.39% +0.06 ✅
go 81.40% 81.30% -0.10 ✅

Tolerance: -0.1%. Baseline stored on gh-pages.

<!-- coverage-ratchet --> ### Coverage ratchet: ✅ pass | Suite | Baseline | This PR | Δ | | |---|---|---|---|---| | frontend | 71.33% | 71.39% | +0.06 | ✅ | | go | 81.40% | 81.30% | -0.10 | ✅ | _Tolerance: -0.1%. Baseline stored on `gh-pages`._
dries merged commit 897aa89028 into main 2026-08-20 23:12:38 +02:00
dries deleted branch fix/validated-ticket-sweep 2026-08-20 23:12:39 +02:00
Sign in to join this conversation.
No description provided.