| 1 | # Performance regression checks |
| 2 | |
| 3 | The v2 transport has a focused regression command: |
| 4 | |
| 5 | ```bash |
| 6 | vp run test:perf:v2-wire |
| 7 | ``` |
| 8 | |
| 9 | It exercises the real v2 projection and client reducers. This matters because the older |
| 10 | built-app benchmark fixtures seed v1 orchestration events; running those fixtures against a v2 |
| 11 | client can produce reassuring timings while missing the active transport path. |
| 12 | |
| 13 | The v2 checks pin these invariants: |
| 14 | |
| 15 | - Client cold opens target 75 recent timeline rows and about 1 MiB of encoded data. Required |
| 16 | control records and the at-least-one-row rule can exceed the byte target; it is not a hard cap. |
| 17 | - The bounded snapshot does not retain the full duplicate conversation-message table. |
| 18 | - Older activity is fetched in bounded HTTP pages and merged without disturbing the live scroll |
| 19 | window. |
| 20 | - Clients with confirmed history paging opt into socket snapshot fallbacks that use the same |
| 21 | bounded recent-history window and carry the cursor needed to fetch older history. Older clients |
| 22 | and WebSocket-only starts retain the compatible full-snapshot fallback. |
| 23 | - Snapshot queries bound timeline reads per fork ancestor, not total rows: dependency and |
| 24 | control records still accompany the window. Smaller socket frames alone do not establish a |
| 25 | whole-server memory reduction. |
| 26 | - Resume catch-up checks the original 1 MiB persisted-payload budget before decoding, then |
| 27 | replays at most 128 thread events and 1 MiB of projected event JSON before falling back to a |
| 28 | snapshot. Raw JSON bytes are not a heap-memory cap. Raising this budget merely because output |
| 29 | projects small can increase server allocations on repeated large tool updates. |
| 30 | - Raw command output, dynamic-tool result bodies, and inline file-change bodies are omitted at |
| 31 | the wire boundary, including small outputs. Explicit failure flags and bounded result IDs |
| 32 | preserve status and grouped action counts without shipping those bodies. Persisted events |
| 33 | remain complete; the existing diff endpoints still provide file content when requested. |
| 34 | Explicit cross-environment transcript exports preserve full tool bodies so the destination |
| 35 | agent can read the context. The export rejects timeline JSON above the file attachment limit |
| 36 | before sending it; the client still applies its attachment limit to the complete file. |
| 37 | - The initial shell contains active navigation rows only. Archived rows use the dedicated archive |
| 38 | query, and transcript message bodies stay in thread detail regardless of message size. |
| 39 | - Shell resume sends deltas plus compact repository-enrichment metadata, not another full project |
| 40 | and thread snapshot. |
| 41 | - Auto, steer, and restart sends resolve delivery from authoritative state inside the |
| 42 | server's per-thread dispatch lock. Model selection and identified-checkpoint rollback also |
| 43 | dispatch without first fetching a full thread projection when the server advertises support. |
| 44 | Older servers retain projection-based validation. Explicit start sends already skipped that |
| 45 | read, so this saving does not apply to every client send path. |
| 46 | |
| 47 | When changing projection schemas, paging, shell synchronization, or thread state, run this command |
| 48 | alongside the focused package typechecks and a real-client pass on every affected surface. The |
| 49 | transport performance fixtures validate contract encoding and compare pre-compression RPC JSON |
| 50 | for the same synthetic workload, preserving historical application-object counts separately. |
| 51 | These are neither compressed WebSocket captures nor measurements of server RSS, and command |
| 52 | fixtures exclude unrelated settings calls and subscription events. Payload budgets belong in |
| 53 | these tests so regressions fail locally. |
| 54 | |
| 55 | ## Event store and startup |
| 56 | |
| 57 | Sequence cursors must seek an index rather than scan retained history. The application sequence |
| 58 | index contains project events and v2 thread events; the per-thread sequence index contains only v2 |
| 59 | thread events. Legacy-only and unknown threads must return zero without scanning other threads. |
| 60 | Catch-up queries keep sequence ranges and optional thread or command predicates indexable. |
| 61 | |
| 62 | Startup selects recovery candidates from current projection state before reading full thread |
| 63 | projections. Candidate selection includes queued runs, pending runtime requests, live provider |
| 64 | sessions, background work, and unfinished delegated deliveries. Archived threads still participate |
| 65 | where recovery requires them. Completed thread history is not loaded merely to discover that no |
| 66 | work remains. |
| 67 | |
| 68 | Projection verification decodes canonical rows in bounded pages, including shared provider records |
| 69 | and fork ancestry. Rebuild replays event pages through a fixed sequence within its transaction; |
| 70 | it must not collect the entire event log before projecting it. |
| 71 | |
| 72 | The focused regression coverage lives in `OrchestrationEventStore.sequence.test.ts`, |
| 73 | `ProjectionRecovery.test.ts`, and the provider runtime recovery tests under `apps/server/src`. |
| 74 | |
| 75 | ## Background work and navigation |
| 76 | |
| 77 | The settlement worker reads active, unpinned threads without explicit settlement overrides from |
| 78 | `ProjectionStore.getSettlementCandidates`. It reads the latest run and user-message timestamps, |
| 79 | rejects active runs and pending requests, and checks background work using the same derivation as |
| 80 | the shell. It does not load archived histories, fork ancestry, transcript counts, or provider |
| 81 | sessions. The orchestrator still validates the current thread before applying settlement. |
| 82 | |
| 83 | Read receipts use `ProjectionStore.getThread` to load only canonical thread metadata. A visit |
| 84 | advances `lastVisitedAt` monotonically without changing `updatedAt` or decoding history. Shell |
| 85 | queries use a covering thread/run index for item counts and a partial index for nonterminal |
| 86 | background items, including idle items. Latest user-message reads use an index ordered by update |
| 87 | time. |
| 88 | |
| 89 | Command palette results keep live VCS and linked pull-request queries leased to the visible |
| 90 | viewport, including a small overscan region. Cached badges remain available while leases are |
| 91 | released. Local Git status reads skip divergence calculations; callers that need ahead/behind |
| 92 | counts keep the full status path. Cached fetch failures share the existing backoff and log only |
| 93 | the actual failed attempt. Unchanged desktop environment bootstrap reads retain their array |
| 94 | identity so polling does not invalidate subscribers. |
| 95 | |
| 96 | Local trace records and provider event logs use asynchronous, serialized file writes and rotation. |
| 97 | Batching synchronous appends still stalls the server event loop on slow storage, especially on |
| 98 | Windows. While a trace write is slow, producers keep up to one trace file of records, so the spans |
| 99 | that time a stall survive it; records drop only while the disk stays stuck. Provider logging |
| 100 | retains backpressure. Shutdown drains accepted writes before closing their scope. |
| 101 | |
| 102 | Relay awareness queues one pending publication per thread and retains a follow-up when the thread |
| 103 | changes during a send. Transcript deltas and tool output do not enqueue awareness work. Run, |
| 104 | request, and relevant thread metadata events do. Configuration is checked before shell reads, and |
| 105 | connection changes invalidate published identities so relinking can publish unchanged state. |
| 106 | Failed publications retry the latest state up to five times with exponential delays. A new |
| 107 | relevant event resets the budget; successful publication, disabling, or unlinking cancels retries. |
| 108 | After the budget is exhausted, publication waits for another relevant event instead of generating |
| 109 | continuous traffic during an outage. |
| 110 | |
| 111 | Focused coverage: `ProjectionSettlement.test.ts`, `ThreadSettlementService.test.ts`, |
| 112 | `runtimeLayer.test.ts`, `AgentAwarenessRelay.test.ts`, `GitVcsDriverCore.test.ts`, |
| 113 | `ThreadStatusIndicators.subscriptions.test.tsx`, and `desktopLocal.test.ts`. |