Skip to content

Speed up dashboard reads, task counts, and live refreshes - #2611

Merged
integry merged 13 commits into
mainfrom
perf/staging-read-latency
Sep 29, 2026
Merged

integry merged 13 commits into
mainfrom
perf/staging-read-latency

Conversation

@integry

@integry integry commented Sep 29, 2026 •

Copy link
Copy Markdown
Owner

Dashboard completion queries blocked the API event loop for several seconds, delaying unrelated authenticated reads. This PR reduces that SQL work and moves the remaining completion projection into a dedicated read-only worker. It also reduces task-count work and replaces repeated status/usage HTTP refreshes with authorized WebSocket snapshots.

Changes:

  • Mount initial dashboard readers after the activity subscription is acknowledged, with a bounded fallback when sockets are unavailable. Target initial queue snapshots to their subscriber without invalidating other tabs.
  • Precache the canonical root document instead of following the redirected /index.html alias, and share concurrent service-worker registration attempts.
  • Keep full task JSON out of completion window sorts; share ranking between parent outcomes and earlier updates.
  • Search compact decoded titles once instead of repeating the projection for every candidate page, preserving Unicode matching and entity grouping.
  • Use indexed history existence for counts without state filters; replace the repository index with a covering repository/type/identity index.
  • Use websocket-triggered goal live-detail and context-preview reads, retaining disconnected fallback, missed-event recovery, and lifecycle ordering.
  • Push status and agent usage projections directly to connected clients, sharing one server sampler and enforcing the usage endpoint's permission boundary. Retain HTTP bootstrap, reconnect/focus recovery, old-server compatibility, and disconnected fallback.
  • Use existing server-side socket authorization revalidation instead of duplicate browser session polling; refresh browser authorization on revocation or permission-change messages. Consume pushed unread counts when supplied.
  • Keep health notifications updating server-side and prevent stale bootstrap results or old error states from overriding newer pushed health.
  • Run outcomes/narrative completion projections off the API event loop. Bound the queue and request deadline; share only concurrent identical reads, with no result TTL.

Verified live results

Twelve implementation commits through e5eb86905 are deployed on ui.gitfix.dev / api.gitfix.dev. Staging source revision 60e879582 contains its prior base plus these twelve changes; unrelated main-branch changes were not included. Original local Compose files are unchanged.

Measurement Before After
Outcomes endpoint, three sequential authenticated rounds (median) 3,197 ms 1,133 ms
Tiny authenticated read while a 50-entity outcomes request runs, three rounds (median) 1,071 ms 124 ms
Outcomes request in a live dashboard browser load (representative capture) 6,996 ms 2,440 ms

A separate three-minute authenticated dashboard trace measured the polling fix:

Browser requests, including bootstrap Before After
/api/status 20 2
/api/config/agent-tank/usage 17 2
/api/auth/user 4 1

The updated browser made no API requests after its first ten seconds during that trace, while consuming four usage snapshots and one health snapshot (plus legacy invalidations) without page errors. The captures contain different task activity, so the total request reduction (82 to 35) is not presented as a controlled throughput comparison. Derived task/dashboard projections still reconcile on relevant activity and on their five-minute missed-event safety interval; this is not a claim that all HTTP traffic is eliminated.

Standalone task-list reads measured 202 → 181 ms median. Standalone task search remained approximately 0.25 seconds (235 → 259 ms across the separate live runs); no isolated network-speed improvement is claimed for that endpoint. The request-thread fix prevents completion projections from adding seconds of queueing to those reads.

On the fixed staging snapshot, outcomes title search improved from 4,784 to 974 ms, no-match search from 4,008 to 438 ms, and warm all-task SQL counts from about 36 to 6.6 ms. Full projection/list objects matched the original implementation. Worker reads preserve result parity and subsequent-write visibility; a foreground probe during concurrent completion reads improved from 1,241 to 11 ms locally.

Startup follow-up: Live startup verification: fresh load and idle reload each made 22 API requests to 22 distinct URLs, with no duplicate API reads or page errors. Opening a second tab caused zero API reads in the original tab. The service worker activated with propr-shell-v2; no /index.html or /index alias was fetched and reload did not repeat asset precaching. A task completion during a separate reload correctly refreshed affected projections. After a forced 35-second outage, the socket rejoined activity and all 26 recovery reads returned 200, with no page errors.

Validation and rollout

  • GitHub checks passed at d7c72e72a before rollout.
  • 71 API tests passed inside the deployed image, including worker scheduling, freshness, queue bounds, deadlines, errors, shutdown, route injection, Unicode search, entity history, and index rollback.
  • 234 UI tests, server/UI typechecks, API lint, and production builds passed in focused clean-checkout validation.
  • Push follow-up: 107 focused UI tests and 64 API tests passed, including direct snapshot consumption, permission filtering, stale-response ordering, recovery, and authorization revalidation. Eight snapshot/resource-authorization tests also passed inside the API image. Server/UI typechecks, lint, and production image builds passed.
  • Startup follow-up: 76 focused UI tests and 10 API tests passed, including API tests inside the built image. Clean server/UI typechecks, targeted lint, and production image builds passed.
  • The compiled worker and an isolated full API were smoke-tested against the copied database.
  • A 5.5-minute connected trace confirmed no repeated status/usage/session reads. A forced 35-second outage exercised fallback and reconnect: all 15 recovery reads returned 200, pushed updates resumed, and no page errors occurred.
  • Post-deployment authenticated browser checks succeeded; all updated services are running with zero restarts. The narrative model response remains separate from normal read responsiveness.

Deployment was coordinated across Node services for the index migration, then API-only for the read-worker follow-up and API/UI-only for the push follow-up (no new schema changes). Staging source/default image tags match the deployment. Image mappings, current/rollback Compose overrides, and operating instructions are retained in /root/gitfix-deployments/read-perf-20260929. A consistent predeployment database backup is retained; routine rollback uses image/index rollback rather than replacing newer user data.

packages/api/PERFORMANCE.md and scripts/benchmark-dashboard-outcomes.ts document the investigation and snapshot reproduction (--worker also reports foreground timer responsiveness).

@integry integry added the AI label Sep 29, 2026
@integry

integry commented Sep 29, 2026

Copy link
Copy Markdown
Owner Author

/review

@propr-dev

propr-dev Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

✅ AI Code Review Complete requested by @integry

Posted 1 review:

View Task Details

@propr-dev

propr-dev Bot commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

🔍 AI Code Review — codex:gpt-6-astra

Overall Evaluation

Ready to merge within the reviewed scope. The PR addresses API responsiveness through smaller completion queries and a dedicated read-only worker, while replacing repeated browser reads with authorized snapshots.

✅ Bounded worker execution — Concurrent identical requests share an in-flight promise, distinct requests are capped, and worker failure rejects outstanding reads without retaining cached results.

✅ Authorized snapshot delivery — Usage snapshots enforce the HTTP endpoint’s demo-or-instance.manage_agents permission boundary and require activity-room membership.

✅ Recovery and ordering — Pushed system health invalidates older HTTP responses; subscription bootstrap has a bounded fallback; disconnected polling and reconnect recovery remain supported.

The supplied current-head checks show 23 passed, none failed or pending, and 23 neutral/skipped. This assessment uses static tracing of the supplied code; no commands or tests were run.

Merge blockers

No merge blockers.

Suggestions

These are optional follow-ups and are not sent to /fix.

S1: 🟢 Make responsiveness testing deterministic

In packages/api/test/dashboardReadService.test.ts, the responsiveness test assumes the 6,000-task projection remains unfinished after a 10 ms timer and foreground query. That assertion depends on machine speed and scheduling. A controlled worker gate would demonstrate foreground responsiveness more reliably. This is optional test hardening, not a demonstrated implementation defect.

S2: 🟢 Update usage event documentation

The unchanged comment in packages/api/routes/configRoutesAgentTank.ts still describes every client rereading the usage endpoint after an invalidation. Update it to explain negotiated snapshots and legacy-client invalidations so future changes preserve the intended compatibility behavior. This documentation cleanup does not affect merge readiness.

Score

The implementation directly addresses the objective, includes focused regression coverage, and has no verified merge blockers in the supplied code. Current-head checks are successful.

Score: 9/10


🤖 Review Details

  • Model: GPT-6 Astra
  • Time: 39s
  • Tokens: 70,961 (69,943 in / 1,018 out)
  • Cost: $0.65

View Task


💡 Next step: Comment /fix to address every F# merge blocker, or name records explicitly, as in /fix F3 S5.
F# and S# IDs increment across review comments and remain permanent, so selectors such as /fix F3 F5 stay unambiguous across cycles. S# suggestions stay optional: they are implemented only when you name them, and they never relax a merge blocker.

🤖 Review by ProPR

@integry
integry marked this pull request as ready for review September 29, 2026 09:53
@propr-dev

propr-dev Bot commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

CI failed: Full Test Suite Shard 1/4

Please investigate and fix this CI failure.

  • Check: Full Test Suite Shard 1/4
  • Result: failure
  • Commit: e5eb86905035 (e5eb869050352cdf42536f5e7e964e48b2dbab60)
  • Details: View CI failure

Failure evidence

.github:11
Process completed with exit code 1.

.github:18894
Process completed with exit code 1.

.github:2
Node.js 20 is deprecated. The following actions target Node.js 20 but are being forced to run on Node.js 24: actions/checkout@11d5960. For more information see: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/

@propr-dev

propr-dev Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

✅ Applied the requested follow-up changes in commit 3a17177

Summary of Changes

Fixed outdated Socket.IO mocks in the failing activity-delivery test and a related usage test. Added assertions for snapshot delivery, deduplication, and shutdown.

Validation: all 13 tests across three relevant files passed; targeted lint and diff checks passed.

The Node.js deprecation warning was unrelated. No visual changes or previews.


🤖 Implementation Details

  • Model: GPT-6 Astra (high)
  • Requested By: @propr-dev[bot]
  • Time: 3m 15s
  • Tokens: 586,884 (583,854 in / 3,030 out)
  • Cost: $1.18

Undo Changes • View Task Execution


💡 ProPR Slash Commands
Command Description Example
/merge Merge target branch into this PR and resolve conflicts /merge
/review Request an AI code review /review or /review claude-sonnet
/fix Implement fixes for issues found by /review /fix or /fix address the null check issue
/switch Change the AI model for this PR /switch claude-opus
/use Override the model for a single follow-up run /use claude-sonnet
/ultrafix Loop review→fix cycles until score goal is met /ultrafix or /ultrafix goal=8 max=10

🤖 Generated by ProPR
Processing comment ID: 5887919044✓

@propr-dev

propr-dev Bot commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

CI failed: Full Test Suite Shard 3/4

Please investigate and fix this CI failure.

  • Check: Full Test Suite Shard 3/4
  • Result: failure
  • Commit: e5eb86905035 (e5eb869050352cdf42536f5e7e964e48b2dbab60)
  • Details: View CI failure

Failure evidence

.github:11
Process completed with exit code 1.

.github:15416
Process completed with exit code 1.

.github:2
Node.js 20 is deprecated. The following actions target Node.js 20 but are being forced to run on Node.js 24: actions/checkout@11d5960. For more information see: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/

@propr-dev

propr-dev Bot commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

CI failed: Run Full Test Suite

Please investigate and fix this CI failure.

Failure evidence

.github:68
Process completed with exit code 1.

.github:66
Full test suite shards finished with result 'failure'.

.github:4
Node.js 20 is deprecated. The following actions target Node.js 20 but are being forced to run on Node.js 24: actions/checkout@11d5960, actions/download-artifact@018cc2c. For more information see: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/

@github-actions

Copy link
Copy Markdown

Full Test Suite Results

Validation failed during: Test shard (shard 1), Test shard (shard 3).

  • Dependency install: success
  • Workspace build: success
  • Docs validation: success
  • Shard coverage verification: success
  • Hosted native Electron units: skipped (classifier decision: false)
  • Shard 1/4: failed during Test shard in 206.5s on GitHub Actions 1000050936
    • packages/api/test/activityDelivery.test.ts: exit 1
  • Shard 2/4: passed in 357.3s on GitHub Actions 1000050937
  • Shard 3/4: failed during Test shard in 279.5s on GitHub Actions 1000050938
    • packages/api/test/agentTankUsageObservation.test.ts: exit 1
  • Shard 4/4: passed in 328.4s on GitHub Actions 1000050939
View shard 1/4 output
...(truncated)

 components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.

�[90mstderr�[2m | src/components/RepositorySettingsBar.test.tsx�[2m > �[22m�[2mRepositorySettingsBar non-blocking checks�[2m > �[22m�[2mdoes not save unchanged or malformed input
�[22m�[39mEncountered two children with the same key, `repo-1`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.

 �[32m✓�[39m src/components/RepositorySettingsBar.test.tsx �[2m(�[22m�[2m25 tests�[22m�[2m)�[22m�[33m 1072�[2mms�[22m�[39m
�[90mstdout�[2m | src/hooks/useHeaderStats.recovery.test.tsx�[2m > �[22m�[2museHeaderStats live recovery�[2m > �[22m�[2mrefreshes only queue activity and bounds identical periodic invalidations
�[22m�[39m[useHeaderStats] Received changed queue stats, scheduling stats refresh

�[90mstdout�[2m | src/hooks/useHeaderStats.recovery.test.tsx�[2m > �[22m�[2museHeaderStats live recovery�[2m > �[22m�[2mretries only a failed queue reconciliation and commits its fingerprint after recovery
�[22m�[39m[useHeaderStats] Received changed queue stats, scheduling stats refresh

�[90mstdout�[2m | src/hooks/useHeaderStats.recovery.test.tsx�[2m > �[22m�[2museHeaderStats live recovery�[2m > �[22m�[2mdefers hidden-tab churn and performs one full visible recovery
�[22m�[39m[useHeaderStats] Received changed queue stats, scheduling stats refresh

�[90mstdout�[2m | src/hooks/useHeaderStats.recovery.test.tsx�[2m > �[22m�[2museHeaderStats live recovery�[2m > �[22m�[2mrevalidates missed same-count draft and health changes after a web reconnect
�[22m�[39m[useHeaderStats] Received changed queue stats, scheduling stats refresh

�[90mstdout�[2m | src/hooks/useHeaderStats.recovery.test.tsx�[2m > �[22m�[2museHeaderStats live recovery�[2m > �[22m�[2mrevalidates missed same-count draft and health changes after a web reconnect
�[22m�[39m[useHeaderStats] Received changed queue stats, scheduling stats refresh

�[90mstderr�[2m | src/components/TaskPlanner/SetupWizard.test.tsx�[2m > �[22m�[2mSetupWizard�[2m > �[22m�[2mignores stale repo switches in edit mode when a newer selection finishes first
�[22m�[39mAn update to SetupWizard inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act
An update to SetupWizard inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act
An update to SetupWizard inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act
An update to SetupWizard inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act

 �[32m✓�[39m src/components/TaskPlanner/SetupWizard.test.tsx �[2m(�[22m�[2m13 tests�[22m�[2m)�[22m�[32m 197�[2mms�[22m�[39m
 �[32m✓�[39m src/hooks/useHeaderStats.recovery.test.tsx �[2m(�[22m�[2m11 tests�[22m�[2m)�[22m�[33m 1339�[2mms�[22m�[39m
   �[32m✓�[39m useHeaderStats live recovery �[2m(11)�[22m
     �[33m�[2m✓�[22m�[39m refreshes only queue activity and bounds identical periodic invalidations�[33m 306�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m revalidates missed same-count draft and health changes after a web reconnect�[33m 310�[2mms�[22m�[39m
�[90mstdout�[2m | src/hooks/useCurrentUserBootstrap.test.tsx�[2m > �[22m�[2mdesktop current-user bootstrap�[2m > �[22m�[2mmounts an active scope at generation one and enables exactly one stable Manager after current validation
�[22m�[39m[SocketContext] Cleaning up socket connection

�[90mstdout�[2m | src/hooks/useCurrentUserBootstrap.test.tsx�[2m > �[22m�[2mdesktop current-user bootstrap�[2m > �[22m�[2moverlaps validation with demo-mode loading but keeps Manager disabled until mode resolves in StrictMode
�[22m�[39m[SocketContext] Cleaning up socket connection

�[90mstdout�[2m | src/hooks/useCurrentUserBootstrap.test.tsx�[2m > �[22m�[2mdesktop current-user bootstrap�[2m > �[22m�[2mconstructs once after activated validation and removes that Manager when revalidation fails
�[22m�[39m[SocketContext] Cleaning up socket connection

 �[32m✓�[39m src/hooks/useCurrentUserBootstrap.test.tsx �[2m(�[22m�[2m8 tests�[22m�[2m)�[22m�[33m 335�[2mms�[22m�[39m
 �[32m✓�[39m src/pages/AiAgentsPage.test.tsx �[2m(�[22m�[2m11 tests�[22m�[2m)�[22m�[33m 1683�[2mms�[22m�[39m
   �[32m✓�[39m AiAgentsPage model selection �[2m(11)�[22m
     �[33m�[2m✓�[22m�[39m places each desktop header and content region in the same resizable pane�[33m 319�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m replaces Playground selections with the exact enabled agent/model pair and opens the mobile Playground�[33m 407�[2mms�[22m�[39m
�[90mstderr�[2m | src/components/MobileBottomNavigation.test.tsx�[2m > �[22m�[2mMobileBottomNavigation�[2m > �[22m�[2mkeeps all creation options inert in demo mode on /
�[22m�[39mAn update to AgentTankSidebar inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act

�[90mstderr�[2m | src/components/MobileBottomNavigation.test.tsx�[2m > �[22m�[2mMobileBottomNavigation�[2m > �[22m�[2mkeeps all creation options inert in demo mode on /
�[22m�[39mAn update to AgentTankSidebar inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act

�[90mstderr�[2m | src/components/MobileBottomNavigation.test.tsx�[2m > �[22m�[2mMobileBottomNavigation�[2m > �[22m�[2mkeeps all creation options inert in demo mode on /plans
�[22m�[39mAn update to AgentTankSidebar inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act

�[90mstderr�[2m | src/components/MobileBottomNavigation.test.tsx�[2m > �[22m�[2mMobileBottomNavigation�[2m > �[22m�[2mkeeps all creation options inert in demo mode on /plans
�[22m�[39mAn update to AgentTankSidebar inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act

�[90mstderr�[2m | src/components/MobileBottomNavigation.test.tsx�[2m > �[22m�[2mMobileBottomNavigation�[2m > �[22m�[2mkeeps all creation options inert in demo mode on /goals
�[22m�[39mAn update to AgentTankSidebar inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act

�[90mstderr�[2m | src/components/MobileBottomNavigation.test.tsx�[2m > �[22m�[2mMobileBottomNavigation�[2m > �[22m�[2mkeeps all creation options inert in demo mode on /goals
�[22m�[39mAn update to AgentTankSidebar inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act

�[90mstderr�[2m | src/components/MobileBottomNavigation.test.tsx�[2m > �[22m�[2mMobileBottomNavigation�[2m > �[22m�[2mhides the MCP log without the settings permission
�[22m�[39mAn update to AgentTankSidebar inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act

�[90mstderr�[2m | src/components/MobileBottomNavigation.test.tsx�[2m > �[22m�[2mMobileBottomNavigation�[2m > �[22m�[2mhides the MCP log without the settings permission
�[22m�[39mAn update to AgentTankSidebar inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act

�[90mstderr�[2m | src/components/MobileBottomNavigation.test.tsx�[2m > �[22m�[2mMobileBottomNavigation�[2m > �[22m�[2mhides Access without member management permission
�[22m�[39mAn update to AgentTankSidebar inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act

�[90mstderr�[2m | src/components/MobileBottomNavigation.test.tsx�[2m > �[22m�[2mMobileBottomNavigation�[2m > �[22m�[2mhides Access without member management permission
�[22m�[39mAn update to AgentTankSidebar inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act

�[90mstderr�[2m | src/components/MobileBottomNavigation.test.tsx�[2m > �[22m�[2mMobileBottomNavigation�[2m > �[22m�[2msigns out from the identity section
�[22m�[39mAn update to AgentTankSidebar inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act

�[90mstderr�[2m | src/components/MobileBottomNavigation.test.tsx�[2m > �[22m�[2mMobileBottomNavigation�[2m > �[22m�[2msigns out from the identity section
�[22m�[39mAn update to AgentTankSidebar inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act

 �[32m✓�[39m src/components/MobileBottomNavigation.test.tsx �[2m(�[22m�[2m26 tests�[22m�[2m)�[22m�[33m 1323�[2mms�[22m�[39m
 �[32m✓�[39m src/components/VoiceBriefingControl.test.tsx �[2m(�[22m�[2m14 tests�[22m�[2m)�[22m�[33m 522�[2mms�[22m�[39m
 �[32m✓�[39m src/pages/inboxUtils.test.ts �[2m(�[22m�[2m25 tests�[22m�[2m)�[22m�[32m 16�[2mms�[22m�[39m
�[90mstderr�[2m | src/components/TaskPlanner/useAutoDraftCreation.test.tsx�[2m > �[22m�[2museAutoDraftCreation�[2m > �[22m�[2mkeeps navigating when persisting the resolved baseBranch fails after auto-creating a draft
�[22m�[39mFailed to persist draft setup snapshot: Error: Transient update failure
    at �[90m/home/runner/work/propr/propr/propr-ui/�[39msrc/components/TaskPlanner/useAutoDraftCreation.test.tsx:103:39
    at file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:1628:35
    at file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:2783:26
    at file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:3319:20
    at new Promise (<anonymous>)
    at runWithCancel (file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:3314:10)
    at file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:3299:20
    at new Promise (<anonymous>)
    at runWithTimeout (file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:3257:10)
    at file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:3876:64

�[90mstderr�[2m | src/components/TaskPlanner/useAutoDraftCreation.test.tsx�[2m > �[22m�[2museAutoDraftCreation�[2m > �[22m�[2msurfaces the persistence warning only for in-place auto-created drafts
�[22m�[39mFailed to persist draft setup snapshot: Error: Transient update failure
    at �[90m/home/runner/work/propr/propr/propr-ui/�[39msrc/components/TaskPlanner/useAutoDraftCreation.test.tsx:268:39
    at file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:1628:35
    at file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:2783:26
    at file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:3319:20
    at new Promise (<anonymous>)
    at runWithCancel (file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:3314:10)
    at file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:3299:20
    at new Promise (<anonymous>)
    at runWithTimeout (file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:3257:10)
    at file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:3876:64

 �[32m✓�[39m src/components/TaskPlanner/useAutoDraftCreation.test.tsx �[2m(�[22m�[2m11 tests�[22m�[2m)�[22m�[32m 53�[2mms�[22m�[39m
 �[32m✓�[39m src/components/GlobalHeader.desktop.test.tsx �[2m(�[22m�[2m15 tests�[22m�[2m)�[22m�[33m 680�[2mms�[22m�[39m
 �[32m✓�[39m src/components/Layout.desktop.test.tsx �[2m(�[22m�[2m8 tests�[22m�[2m)�[22m�[33m 681�[2mms�[22m�[39m
 �[32m✓�[39m src/desktop/nativeCommands.integration.test.tsx �[2m(�[22m�[2m12 tests�[22m�[2m)�[22m�[32m 192�[2mms�[22m�[39m
�[90mstdout�[2m | src/hooks/useHeaderStats.push.test.tsx�[2m > �[22m�[2museHeaderStats pushed changes�[2m > �[22m�[2mdoes not re-read the queue for its subscription snapshot
�[22m�[39m[useHeaderStats] Received changed queue stats, scheduling stats refresh

�[90mstderr�[2m | src/pages/SettingsPage/AgentConfigModal.test.tsx�[2m > �[22m�[2mAgentConfigModal�[2m > �[22m�[2madds a loginable agent with an isolated managed credential path and requests login
�[22m�[39mAn update to AgentConfigModal inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act
An update to AgentConfigModal inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act
An update to AgentConfigModal inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act
An update to AgentConfigModal inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act
An update to AgentConfigModal inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act

�[90mstderr�[2m | src/pages/SettingsPage/AgentConfigModal.test.tsx�[2m > �[22m�[2mAgentConfigModal�[2m > �[22m�[2mallows a new agent to reuse an existing config directory instead of logging in
�[22m�[39mAn update to AgentConfigModal inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act
An update to AgentConfigModal inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act

�[90mstderr�[2m | src/pages/SettingsPage/AgentConfigModal.test.tsx�[2m > �[22m�[2mAgentConfigModal�[2m > �[22m�[2muses the agent alias in long model labels
�[22m�[39mAn update to AgentConfigModal inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act
An update to AgentConfigModal inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act
An update to AgentConfigModal inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act
An update to AgentConfigModal inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act

�[90mstderr�[2m | src/pages/SettingsPage/AgentConfigModal.test.tsx�[2m > �[22m�[2mAgentConfigModal�[2m > �[22m�[2msaves a model-specific reasoning level for Codex agents
�[22m�[39mAn update to AgentConfigModal inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act
An update to AgentConfigModal inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act
An update to AgentConfigModal inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act
An update to AgentConfigModal inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act

�[90mstderr�[2m | src/pages/SettingsPage/AgentConfigModal.test.tsx�[2m > �[22m�[2mAgentConfigModal�[2m > �[22m�[2mmarks a legacy cross-agent reasoning value as unsupported
�[22m�[39mAn update to AgentConfigModal inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act
An update to AgentConfigModal inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act
An update to AgentConfigModal inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act
An update to AgentConfigModal inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act

 �[32m✓�[39m src/pages/SettingsPage/AgentConfigModal.test.tsx �[2m(�[22m�[2m7 tests�[22m�[2m)�[22m�[33m 693�[2mms�[22m�[39m
 �[32m✓�[39m src/hooks/useHeaderStats.push.test.tsx �[2m(�[22m�[2m6 tests�[22m�[2m)�[22m�[33m 1260�[2mms�[22m�[39m
   �[32m✓�[39m useHeaderStats pushed changes �[2m(6)�[22m
     �[33m�[2m✓�[22m�[39m reads system status when indexing or capacity changes, and nothing else�[33m 358�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m ignores a repeated pushed state for a task it already reconciled�[33m 409�[2mms�[22m�[39m
 �[32m✓�[39m src/components/ApplicationShell.idle.test.tsx �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 79�[2mms�[22m�[39m
 �[32m✓�[39m src/desktop-deep-link.test.ts �[2m(�[22m�[2m9 tests�[22m�[2m)�[22m�[32m 17�[2mms�[22m�[39m
 �[32m✓�[39m src/components/QuickAddTodo.test.tsx �[2m(�[22m�[2m9 tests�[22m�[2m)�[22m�[33m 1461�[2mms�[22m�[39m
 �[32m✓�[39m src/pages/SettingsPage/NotificationSettingsSection.test.tsx �[2m(�[22m�[2m7 tests�[22m�[2m)�[22m�[33m 781�[2mms�[22m�[39m
 �[32m✓�[39m src/pages/SettingsPage/AgentRuntimePackagesSection.test.tsx �[2m(�[22m�[2m6 tests�[22m�[2m)�[22m�[33m 607�[2mms�[22m�[39m
   �[32m✓�[39m AgentRuntimePackagesSection �[2m(6)�[22m
     �[33m�[2m✓�[22m�[39m offers catalog suggestions and validates a selected package�[33m 341�[2mms�[22m�[39m
 �[32m✓�[39m src/api/currentUserResponse.test.ts �[2m(�[22m�[2m5 tests�[22m�[2m)�[22m�[32m 15�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskPlanner/PlanIssuesManager.notificationIntent.test.tsx �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[33m 524�[2mms�[22m�[39m
   �[32m✓�[39m PlanIssuesManager execution intent �[2m(3)�[22m
     �[33m�[2m✓�[22m�[39m cancel closes without starting implementation�[33m 330�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskPlanner/PlanEditor.notificationIntent.test.tsx �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[33m 507�[2mms�[22m�[39m
   �[32m✓�[39m PlanEditor notification intents �[2m(4)�[22m
     �[33m�[2m✓�[22m�[39m never approves from navigation and cancel leaves the plan unchanged�[33m 469�[2mms�[22m�[39m
 �[32m✓�[39m src/hooks/useCurrentUserBootstrap.mainProxy.test.tsx �[2m(�[22m�[2m1 test�[22m�[2m)�[22m�[32m 74�[2mms�[22m�[39m
 �[32m✓�[39m src/desktop/packagedAcceptanceRendererLifecycle.test.ts �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 6�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskPlanner/planIssueDefaultSelection.test.ts �[2m(�[22m�[2m5 tests�[22m�[2m)�[22m�[32m 4�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskDetails/TaskVisualPreviews.test.tsx �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[32m 239�[2mms�[22m�[39m
 �[32m✓�[39m src/api/dashboardApi.test.ts �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 10�[2mms�[22m�[39m
 �[32m✓�[39m src/pages/SettingsPage/VoiceSettingsSection.test.tsx �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[32m 215�[2mms�[22m�[39m
 �[32m✓�[39m src/desktop/DesktopExperience.accounts.test.tsx �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[33m 330�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskPlanner/planDisplayName.test.ts �[2m(�[22m�[2m12 tests�[22m�[2m)�[22m�[32m 7�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskList/displayTitles.test.ts �[2m(�[22m�[2m7 tests�[22m�[2m)�[22m�[32m 5�[2mms�[22m�[39m
 �[32m✓�[39m src/components/UserAvatar.test.tsx �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 176�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskList/durations.test.ts �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[32m 4�[2mms�[22m�[39m
 �[32m✓�[39m src/components/RouteChunkErrorBoundary.test.tsx �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 143�[2mms�[22m�[39m
 �[32m✓�[39m src/pages/PlanStudioPage.notificationIntent.test.tsx �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 66�[2mms�[22m�[39m
 �[32m✓�[39m src/desktop/scopedTaskEvents.test.ts �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 4�[2mms�[22m�[39m
�[90mstdout�[2m | src/components/TaskDetails/LiveFileChips.test.tsx�[2m > �[22m�[2mLiveFileChips live refreshes�[2m > �[22m�[2mturns the fixture baseline of three burst invalidations into one additional file-changes request
�[22m�[39m[LiveFileChips] Received task update, refreshing file changes: { taskId: �[32m'task-1'�[39m }
[LiveFileChips] Received task update, refreshing file changes: { taskId: �[32m'task-1'�[39m }
[LiveFileChips] Received task update, refreshing file changes: { taskId: �[32m'task-1'�[39m }

 �[32m✓�[39m src/components/TaskDetails/LiveFileChips.test.tsx �[2m(�[22m�[2m1 test�[22m�[2m)�[22m�[32m 23�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskPlanner/setupWizardDraftConfig.test.ts �[2m(�[22m�[2m9 tests�[22m�[2m)�[22m�[32m 6�[2mms�[22m�[39m
 �[32m✓�[39m src/hooks/repositoryNonBlockingChecks.test.ts �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 5�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskDetails/syntaxHighlighter.test.ts �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 9�[2mms�[22m�[39m
 �[32m✓�[39m src/api/proprApi.instanceCatalog.test.ts �[2m(�[22m�[2m1 test�[22m�[2m)�[22m�[32m 9�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskPlanner/RefinementChat.focus.test.tsx �[2m(�[22m�[2m1 test�[22m�[2m)�[22m�[32m 150�[2mms�[22m�[39m
 �[32m✓�[39m src/components/Repositories/RepoBrowsePanel.test.tsx �[2m(�[22m�[2m1 test�[22m�[2m)�[22m�[32m 24�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskDetails/ExecutionEventUtils.test.ts �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 4�[2mms�[22m�[39m

�[2m Test Files �[22m �[1m�[32m48 passed�[39m�[22m�[90m (48)�[39m
�[2m      Tests �[22m �[1m�[32m460 passed�[39m�[22m�[90m (460)�[39m
�[2m   Start at �[22m 09:57:37
�[2m   Duration �[22m 31.10s�[2m (environment 41%, tests 35%, import 9%, setup 8%, transform 6%)�[22m

�[2mEnvironment �[22m �[33mjsdom was created 48 times�[39m�[2m · 23.43s total, 41% of tracked time�[22m
�[2m            �[22m �[2mcreate it once per worker with �[22m�[33mpool: 'vmThreads'�[39m�[2m (keeps per-file isolation) or �[22m�[33misolate: false�[39m�[2m (shares it across files)�[22m
�[2m            �[22m �[2mlearn more: https://vitest.dev/guide/improving-performance#test-environments�[22m


> propr-ui@0.0.1 posttest
> npm run test:docker-context


> propr-ui@0.0.1 test:docker-context
> node --test scripts/docker-context-inputs.test.mjs

TAP version 13
# Subtest: focused UI selectors are forwarded only to Vitest
ok 1 - focused UI selectors are forwarded only to Vitest
  ---
  duration_ms: 0.579024
  type: 'test'
  ...
# Subtest: the UI Docker context contains its complete non-type external source import closure
ok 2 - the UI Docker context contains its complete non-type external source import closure
  ---
  duration_ms: 30.147474
  type: 'test'
  ...
1..2
# tests 2
# suites 0
# pass 2
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 72.122529
[150/150] passed propr-ui#1/4 (workspace test script) in 31.7s

### Shard 1/4: 149/150 passed in 206.5s

Assigned 150 of 600 discovered units.

| Duration | Status | Unit |
| ---: | --- | --- |
| 31.7s | passed | `propr-ui#1/4` (workspace) |
| 7.6s | passed | `packages/api/test/mcpWorkflows.test.ts` |
| 7.5s | passed | `apps/desktop/scripts/packaged-connect-journey.test.mjs` |
| 7.4s | passed | `packages/api/test/mcpRecentActivity.test.ts` |
| 7.3s | passed | `apps/desktop/scripts/published-preview-electron.test.mjs` |
| 6.9s | passed | `test/antigravityAgent.test.ts` |
| 6.6s | passed | `packages/api/test/webPushDispatcher.test.ts` |
| 4.6s | passed | `packages/api/test/connectAuthCallback.test.ts` |
| 4.1s | passed | `packages/core/test/pushSubscriptionExpiration.test.ts` |
| 3.8s | passed | `test/notificationPublicEntrypoint.test.ts` |
| 3.8s | passed | `test/generateContext.test.ts` |
| 3.8s | passed | `packages/core/test/notificationAnnouncementBounds.test.ts` |
| 3.2s | passed | `test/summaryMinerBatchFallback.test.ts` |
| 2.6s | passed | `packages/cli/src/desktopLocalSetup.test.ts` |
| 2.4s | passed | `apps/desktop/scripts/desktop-icon-assets.test.mjs` |


1/150 test runs failed in shard 1/4 after 206.5s:
- packages/api/test/activityDelivery.test.ts: exit 1

View shard 3/4 output
...(truncated)

-refinement produces plan workRef with planDraftId
      ---
      duration_ms: 0.120915
      type: 'test'
      ...
    # Subtest: plan without taskId still has workType plan and undefined planDraftId
    ok 3 - plan without taskId still has workType plan and undefined planDraftId
      ---
      duration_ms: 0.111077
      type: 'test'
      ...
    # Subtest: plan ignores taskNumber and prNumber
    ok 4 - plan ignores taskNumber and prNumber
      ---
      duration_ms: 0.18265
      type: 'test'
      ...
    1..4
ok 2 - buildAnalysisWorkRef — plan flow
  ---
  duration_ms: 1.141977
  type: 'suite'
  ...
# Subtest: buildAnalysisWorkRef — repository flow
    # Subtest: repo-chat without taskId produces repository workRef
    ok 1 - repo-chat without taskId produces repository workRef
      ---
      duration_ms: 0.188992
      type: 'test'
      ...
    # Subtest: repo-improvements without taskId produces repository workRef
    ok 2 - repo-improvements without taskId produces repository workRef
      ---
      duration_ms: 0.166059
      type: 'test'
      ...
    # Subtest: summarization without taskId produces repository workRef
    ok 3 - summarization without taskId produces repository workRef
      ---
      duration_ms: 0.263932
      type: 'test'
      ...
    # Subtest: undefined executionType with taskId produces task workRef
    ok 4 - undefined executionType with taskId produces task workRef
      ---
      duration_ms: 0.169506
      type: 'test'
      ...
    # Subtest: undefined executionType without taskId produces repository workRef
    ok 5 - undefined executionType without taskId produces repository workRef
      ---
      duration_ms: 0.132136
      type: 'test'
      ...
    1..5
ok 3 - buildAnalysisWorkRef — repository flow
  ---
  duration_ms: 1.193723
  type: 'suite'
  ...
# Subtest: buildAnalysisWorkRef — task with taskNumber/prNumber
    # Subtest: carries taskNumber when provided
    ok 1 - carries taskNumber when provided
      ---
      duration_ms: 0.197368
      type: 'test'
      ...
    # Subtest: carries both taskNumber and prNumber when provided
    ok 2 - carries both taskNumber and prNumber when provided
      ---
      duration_ms: 0.147064
      type: 'test'
      ...
    1..2
ok 4 - buildAnalysisWorkRef — task with taskNumber/prNumber
  ---
  duration_ms: 0.485595
  type: 'suite'
  ...
# Subtest: PR follow-up workRef — insert row mapping
    # Subtest: maps prNumber to pr_number DB column
    ok 1 - maps prNumber to pr_number DB column
      ---
      duration_ms: 0.200955
      type: 'test'
      ...
    # Subtest: pr_number is null for non-PR tasks
    ok 2 - pr_number is null for non-PR tasks
      ---
      duration_ms: 0.125043
      type: 'test'
      ...
    1..2
ok 5 - PR follow-up workRef — insert row mapping
  ---
  duration_ms: 0.545968
  type: 'suite'
  ...
# Subtest: buildAnalysisWorkRef — edge cases
    # Subtest: all undefined inputs produce repository workRef with undefined fields
    ok 1 - all undefined inputs produce repository workRef with undefined fields
      ---
      duration_ms: 0.135973
      type: 'test'
      ...
    # Subtest: non-plan executionType with taskId produces task workRef
    ok 2 - non-plan executionType with taskId produces task workRef
      ---
      duration_ms: 0.089908
      type: 'test'
      ...
    # Subtest: taskNumber without taskId produces task workRef (issue-scoped analysis)
    ok 3 - taskNumber without taskId produces task workRef (issue-scoped analysis)
      ---
      duration_ms: 0.127408
      type: 'test'
      ...
    # Subtest: taskNumber without taskId for PR follow-up carries prNumber
    ok 4 - taskNumber without taskId for PR follow-up carries prNumber
      ---
      duration_ms: 0.101489
      type: 'test'
      ...
    1..4
ok 6 - buildAnalysisWorkRef — edge cases
  ---
  duration_ms: 0.579931
  type: 'suite'
  ...
# [2026-09-29 09:58:37.191 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-y0EJRq/149-workRefHelpers.test.ts/propr.test.sqlite"
#     environment: "test"
# [2026-09-29 09:58:37.197 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
1..6
# tests 20
# suites 6
# pass 20
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 1406.08781
[149/150] passed test/workRefHelpers.test.ts in 1.5s

[150/150] propr-ui#3/4 (workspace test script)

> propr-ui@0.0.1 test
> vitest run --shard=3/4


�[1m�[30m�[46m RUN �[49m�[39m�[22m �[36mv5.0.0 �[39m�[90m/home/runner/work/propr/propr/propr-ui�[39m

 �[32m✓�[39m src/components/Dashboard.test.tsx �[2m(�[22m�[2m23 tests�[22m�[2m)�[22m�[33m 1976�[2mms�[22m�[39m
   �[32m✓�[39m Dashboard �[2m(23)�[22m
     �[33m�[2m✓�[22m�[39m shows running goals beside tasks and links to the scoped goal list and goal details�[33m 358�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m filters completed work by title through the heading search box�[33m 344�[2mms�[22m�[39m
 �[32m✓�[39m src/hooks/useRepositoryManagement.test.tsx �[2m(�[22m�[2m26 tests�[22m�[2m)�[22m�[33m 2606�[2mms�[22m�[39m
�[90mstderr�[2m | src/components/TaskPlanner/setupWizardHooks.test.tsx�[2m > �[22m�[2msetupWizardHooks branch resolution�[2m > �[22m�[2mclears the resolved base branch when branch lookup fails in new mode
�[22m�[39mFailed to load branches: Error: GitHub unavailable
    at �[90m/home/runner/work/propr/propr/propr-ui/�[39msrc/components/TaskPlanner/setupWizardHooks.test.tsx:246:43
    at file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:1628:35
    at file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:2783:26
    at file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:3319:20
    at new Promise (<anonymous>)
    at runWithCancel (file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:3314:10)
    at file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:3299:20
    at new Promise (<anonymous>)
    at runWithTimeout (file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:3257:10)
    at file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:3876:64

�[90mstderr�[2m | src/desktop/LocalSetupWizard.integration.test.tsx�[2m > �[22m�[2mproduction local setup journey�[2m > �[22m�[2mshows the authenticated identity, installation types, and explicit recovery controls
�[22m�[39mAn update to LocalSetupWizard inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act
An update to LocalSetupWizard inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act

�[90mstderr�[2m | src/components/TaskPlanner/setupWizardHooks.test.tsx�[2m > �[22m�[2msetupWizardHooks branch resolution�[2m > �[22m�[2mcontinues generation when persisting the resolved baseBranch fails after draft creation
�[22m�[39mFailed to persist draft setup snapshot: Error: Transient update failure
    at �[90m/home/runner/work/propr/propr/propr-ui/�[39msrc/components/TaskPlanner/setupWizardHooks.test.tsx:323:97
    at file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:2763:52
    at runWithSuite (file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:3248:8)
    at Object.collect (file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:2763:22)
    at Object.collect (file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:2768:51)
�[90m    at processTicksAndRejections (node:internal/process/task_queues:103:5)�[39m
    at file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:3449:26
    at collectTests (file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:3422:3)
    at startTests (file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:4156:17)
    at file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/base.BTS5G301.js:90:6

�[90mstderr�[2m | src/components/TaskPlanner/setupWizardHooks.test.tsx�[2m > �[22m�[2msetupWizardHooks branch resolution�[2m > �[22m�[2mpersists editable draft settings to context_config after debounce
�[22m�[39mFailed to persist draft settings: Error: Transient update failure
    at �[90m/home/runner/work/propr/propr/propr-ui/�[39msrc/components/TaskPlanner/setupWizardHooks.test.tsx:323:97
    at file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:2763:52
    at runWithSuite (file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:3248:8)
    at Object.collect (file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:2763:22)
    at Object.collect (file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:2768:51)
�[90m    at processTicksAndRejections (node:internal/process/task_queues:103:5)�[39m
    at file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:3449:26
    at collectTests (file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:3422:3)
    at startTests (file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/run.CQOUYP-x.js:4156:17)
    at file:///home/runner/work/propr/propr/node_modules/�[4mvitest�[24m/dist/chunks/base.BTS5G301.js:90:6

 �[32m✓�[39m src/components/TaskPlanner/setupWizardHooks.test.tsx �[2m(�[22m�[2m24 tests�[22m�[2m)�[22m�[33m 317�[2mms�[22m�[39m
�[90mstderr�[2m | src/desktop/LocalSetupWizard.integration.test.tsx�[2m > �[22m�[2mproduction local setup journey�[2m > �[22m�[2mhands completion into pairing, authenticated reprobe, and the current dashboard flow
�[22m�[39mAn update to LocalSetupWizard inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act
An update to LocalSetupWizard inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act

�[90mstderr�[2m | src/desktop/LocalSetupWizard.integration.test.tsx�[2m > �[22m�[2mproduction local setup journey�[2m > �[22m�[2mrequires a supported choice when a legacy saved setup used Demo mode
�[22m�[39mAn update to LocalSetupWizard inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act
An update to LocalSetupWizard inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act

�[90mstderr�[2m | src/desktop/LocalSetupWizard.integration.test.tsx�[2m > �[22m�[2mproduction local setup journey�[2m > �[22m�[2mshows and confirms corrected WebSocket intake for stale ProPR Connect recovery
�[22m�[39mAn update to LocalSetupWizard inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act
An update to LocalSetupWizard inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act

 �[32m✓�[39m src/desktop/LocalSetupWizard.integration.test.tsx �[2m(�[22m�[2m19 tests�[22m�[2m)�[22m�[33m 1695�[2mms�[22m�[39m
   �[32m✓�[39m production local setup journey �[2m(19)�[22m
     �[33m�[2m✓�[22m�[39m shows the authenticated identity, installation types, and explicit recovery controls�[33m 306�[2mms�[22m�[39m
�[90mstdout�[2m | src/components/TaskDetails/useTaskData.test.ts�[2m > �[22m�[2mtask detail history refreshes�[2m > �[22m�[2mturns the fixture baseline of three burst invalidations into one additional history request
�[22m�[39m[useTaskData] Received task update via WebSocket: { taskId: �[32m'task-1'�[39m, state: �[32m'processing'�[39m }
[useTaskData] Received task update via WebSocket: { taskId: �[32m'task-1'�[39m, state: �[32m'processing'�[39m }
[useTaskData] Received task update via WebSocket: { taskId: �[32m'task-1'�[39m, state: �[32m'processing'�[39m }

 �[32m✓�[39m src/components/TaskDetails/useTaskData.test.ts �[2m(�[22m�[2m23 tests�[22m�[2m)�[22m�[32m 79�[2mms�[22m�[39m
 �[32m✓�[39m src/desktop/electronAdapters.test.ts �[2m(�[22m�[2m18 tests�[22m�[2m)�[22m�[32m 246�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskPlanner/setupWizardHooks.persistence.test.tsx �[2m(�[22m�[2m10 tests�[22m�[2m)�[22m�[32m 56�[2mms�[22m�[39m
 �[32m✓�[39m src/components/ConnectPlusBanner.test.tsx �[2m(�[22m�[2m19 tests�[22m�[2m)�[22m�[33m 1741�[2mms�[22m�[39m
 �[32m✓�[39m src/pages/McpLogsPage.test.tsx �[2m(�[22m�[2m15 tests�[22m�[2m)�[22m�[33m 855�[2mms�[22m�[39m
 �[32m✓�[39m src/components/Dashboard/DashboardSummary.test.tsx �[2m(�[22m�[2m14 tests�[22m�[2m)�[22m�[33m 524�[2mms�[22m�[39m
�[90mstdout�[2m | src/hooks/useDraft.test.tsx�[2m > �[22m�[2museDraft�[2m > �[22m�[2mapplies generation socket snapshots without refetching the draft
�[22m�[39m[useDraft] Received draft update via WebSocket: {
  eventType: �[32m'draft:update'�[39m,
  draftId: �[32m'draft-1'�[39m,
  step: �[32m'context'�[39m,
  status: �[32m'in_progress'�[39m,
  timestamp: �[32m'2026-05-05T00:00:05Z'�[39m,
  draftStatus: �[32m'generating'�[39m,
  generationTrace: { steps: [ �[36m[Object]�[39m, �[36m[Object]�[39m ] }
}

�[90mstdout�[2m | src/hooks/useDraft.test.tsx�[2m > �[22m�[2museDraft�[2m > �[22m�[2mresyncs once when a socket event reports a terminal draft status
�[22m�[39m[useDraft] Received draft update via WebSocket: {
  eventType: �[32m'draft:update'�[39m,
  draftId: �[32m'draft-1'�[39m,
  step: �[32m'llm'�[39m,
  status: �[32m'completed'�[39m,
  timestamp: �[32m'2026-05-05T00:00:10Z'�[39m,
  draftStatus: �[32m'review'�[39m,
  generationTrace: { steps: [ �[36m[Object]�[39m, �[36m[Object]�[39m, �[36m[Object]�[39m ] }
}

�[90mstdout�[2m | src/hooks/useDraft.test.tsx�[2m > �[22m�[2museDraft�[2m > �[22m�[2minstalls the next run ID before accepting its first socket snapshot
�[22m�[39m[useDraft] Received draft update via WebSocket: {
  eventType: �[32m'draft:update'�[39m,
  draftId: �[32m'draft-1'�[39m,
  runId: �[32m'generation-run-2'�[39m,
  step: �[32m'context'�[39m,
  status: �[32m'in_progress'�[39m,
  timestamp: �[32m'2026-05-05T00:00:10Z'�[39m,
  draftStatus: �[32m'generating'�[39m,
  generationTrace: { runId: �[32m'generation-run-2'�[39m, steps: [ �[36m[Object]�[39m ] }
}

�[90mstdout�[2m | src/hooks/useDraft.test.tsx�[2m > �[22m�[2museDraft�[2m > �[22m�[2mapplies terminal draft status from the socket before the follow-up fetch resolves
�[22m�[39m[useDraft] Received draft update via WebSocket: {
  eventType: �[32m'draft:update'�[39m,
  draftId: �[32m'draft-1'�[39m,
  step: �[32m'complete'�[39m,
  status: �[32m'completed'�[39m,
  timestamp: �[32m'2026-05-05T00:00:10Z'�[39m,
  draftStatus: �[32m'review'�[39m,
  generationTrace: { steps: [ �[36m[Object]�[39m, �[36m[Object]�[39m, �[36m[Object]�[39m ] }
}

 �[32m✓�[39m src/hooks/useDraft.test.tsx �[2m(�[22m�[2m6 tests�[22m�[2m)�[22m�[33m 305�[2mms�[22m�[39m
 �[32m✓�[39m src/components/AgentTankSidebar.test.tsx �[2m(�[22m�[2m15 tests�[22m�[2m)�[22m�[33m 819�[2mms�[22m�[39m
 �[32m✓�[39m src/pages/SettingsPage/DesktopNotificationSettingsSection.test.tsx �[2m(�[22m�[2m8 tests�[22m�[2m)�[22m�[33m 668�[2mms�[22m�[39m
 �[32m✓�[39m src/components/Dashboard/HappeningNowSection.test.tsx �[2m(�[22m�[2m6 tests�[22m�[2m)�[22m�[33m 637�[2mms�[22m�[39m
�[90mstderr�[2m | src/components/Repositories/RepoMediaPanel.test.tsx�[2m > �[22m�[2mrepository Media tab�[2m > �[22m�[2momits tab and requests for disabled/legacy repositories (false)
�[22m�[39mAn update to RepoActionContainer inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act
An update to RepoActionContainer inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act
An update to RepoActionContainer inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act

�[90mstderr�[2m | src/components/Repositories/RepoMediaPanel.test.tsx�[2m > �[22m�[2mrepository Media tab�[2m > �[22m�[2momits tab and requests for disabled/legacy repositories (undefined)
�[22m�[39mAn update to RepoActionContainer inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act
An update to RepoActionContainer inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act
An update to RepoActionContainer inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act

�[90mstderr�[2m | src/components/Repositories/RepoMediaPanel.test.tsx�[2m > �[22m�[2mrepository Media tab�[2m > �[22m�[2mclears media on repository change and disabling, ignoring stale requests
�[22m�[39mAn update to RepoActionContainer inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act
An update to RepoActionContainer inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://react.dev/link/wrap-tests-with-act

 �[32m✓�[39m src/App.hostedFlow.test.tsx �[2m(�[22m�[2m5 tests�[22m�[2m)�[22m�[33m 1587�[2mms�[22m�[39m
   �[32m✓�[39m HostedFlowRouteSync �[2m(5)�[22m
     �[33m�[2m✓�[22m�[39m preserves the validated active flow across actual React Router navigation without duplicating query parameters�[33m 1257�[2mms�[22m�[39m
 �[32m✓�[39m src/components/Repositories/RepoMediaPanel.test.tsx �[2m(�[22m�[2m12 tests�[22m�[2m)�[22m�[33m 1269�[2mms�[22m�[39m
 �[32m✓�[39m src/pages/SettingsPage/ReviewContextSettings.test.tsx �[2m(�[22m�[2m8 tests�[22m�[2m)�[22m�[33m 416�[2mms�[22m�[39m
 �[32m✓�[39m src/hooks/useHeaderStats.status.test.tsx �[2m(�[22m�[2m5 tests�[22m�[2m)�[22m�[33m 301�[2mms�[22m�[39m
 �[32m✓�[39m src/hooks/useLiveRefreshScheduler.test.tsx �[2m(�[22m�[2m7 tests�[22m�[2m)�[22m�[32m 44�[2mms�[22m�[39m
�[90mstderr�[2m | src/components/AddRepositoryModal.test.tsx�[2m > �[22m�[2mAddRepositoryModal�[2m > �[22m�[2mkeeps branch selection in the scroll-owned form and submits through one form path
�[22m�[39mIn HTML, <button> cannot be a descendant of <button>.
This will cause a hydration error.

  <AddRepositoryModal isOpen={true} newRepo="integry/propr" newAlias="Production" newBaseBranch="release/20..." ...>
    <div className="fixed inse..." onClick={function handleBackdropClick}>
      <div ref={{...}} role="dialog" aria-modal="true" aria-labelledby="_r_9_" tabIndex={-1} className="bg-white r...">
        <div>
        <form onSubmit={function handleSubmit} className="flex min-h...">
          <div data-testid="add-reposi..." className="min-h-0 fl...">
            <div>
            <div>
            <div>
              <label>
              <BaseBranchSelector repoName="integry/propr" value="release/20..." onChange={function Mock} ...>
                <div className="relative" ref={{...}}>
>                 <button
>                   id="_r_d_"
>                   type="button"
>                   onClick={function handleOpen}
>                   disabled={false}
>                   aria-labelledby="_r_e_ _r_h_"
>                   aria-describedby="_r_f_"
>                   aria-haspopup="listbox"
>                   aria-expanded={false}
>                   className="w-full px-3 py-2 text-left bg-white border border-gray-300 rounded-md font-mono text-sm..."
>                 >
                    <span>
                    <div className="flex items...">
>                     <button
>                       type="button"
>                       onClick={function handleClear}
>                       className="p-0.5 hover:bg-gray-200 rounded text-gray-400 hover:text-gray-600"
>                       title="Clear selection"
>                     >
                      ...
              ...
            ...
          ...

<button> cannot contain a nested <button>.
See this log for the ancestor stack trace.

 �[32m✓�[39m src/components/AddRepositoryModal.test.tsx �[2m(�[22m�[2m5 tests�[22m�[2m)�[22m�[33m 583�[2mms�[22m�[39m
 �[32m✓�[39m src/pages/SettingsPage/VisualPreviewAuthSection.test.tsx �[2m(�[22m�[2m6 tests�[22m�[2m)�[22m�[33m 397�[2mms�[22m�[39m
 �[32m✓�[39m src/components/SystemHealthPush.test.tsx �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[33m 464�[2mms�[22m�[39m
 �[32m✓�[39m src/components/SummaryBrowser/index.test.tsx �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 191�[2mms�[22m�[39m
 �[32m✓�[39m src/components/Goals/goalTimeline.test.ts �[2m(�[22m�[2m9 tests�[22m�[2m)�[22m�[32m 12�[2mms�[22m�[39m
 �[32m✓�[39m src/pages/AccessManagementPage.test.tsx �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[33m 392�[2mms�[22m�[39m
   �[32m✓�[39m AccessManagementPage �[2m(3)�[22m
     �[33m�[2m✓�[22m�[39m loads assignments and adds a GitHub user�[33m 302�[2mms�[22m�[39m
 �[32m✓�[39m src/api/apiClient.accounts.test.ts �[2m(�[22m�[2m5 tests�[22m�[2m)�[22m�[32m 21�[2mms�[22m�[39m
 �[32m✓�[39m src/components/AgentChat/ModelSelector.test.tsx �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[33m 612�[2mms�[22m�[39m
   �[32m✓�[39m ModelSelector keyboard navigation �[2m(4)�[22m
     �[33m�[2m✓�[22m�[39m scrolls to and selects an active option beyond the initial viewport�[33m 421�[2mms�[22m�[39m
 �[32m✓�[39m src/desktop/DesktopExperience.shortcut-readiness.test.tsx �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 270�[2mms�[22m�[39m
 �[32m✓�[39m src/pages/SettingsPage/AgentsListSection.test.tsx �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[33m 672�[2mms�[22m�[39m
   �[32m✓�[39m AgentsListSection web login �[2m(4)�[22m
     �[33m�[2m✓�[22m�[39m folds legacy models within each provider until requested�[33m 413�[2mms�[22m�[39m
 �[32m✓�[39m src/api/compatibility.test.ts �[2m(�[22m�[2m6 tests�[22m�[2m)�[22m�[32m 137�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskDetails/ExecutionEventLog.test.tsx �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[33m 344�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskPlanner/PlanEditor.responsive.test.tsx �[2m(�[22m�[2m5 tests�[22m�[2m)�[22m�[32m 60�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskPlanner/SetupWizardHeaders.test.tsx �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[32m 253�[2mms�[22m�[39m
 �[32m✓�[39m src/App.hostedCompletion.test.tsx �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 188�[2mms�[22m�[39m
 �[32m✓�[39m src/desktop/browserAdapters.test.ts �[2m(�[22m�[2m6 tests�[22m�[2m)�[22m�[32m 30�[2mms�[22m�[39m
 �[32m✓�[39m src/pages/SummaryBrowserPage.test.tsx �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 70�[2mms�[22m�[39m
 �[32m✓�[39m src/desktop/DesktopPresentationBoundary.test.tsx �[2m(�[22m�[2m1 test�[22m�[2m)�[22m�[32m 261�[2mms�[22m�[39m
 �[32m✓�[39m src/components/Dashboard/workTitle.test.ts �[2m(�[22m�[2m5 tests�[22m�[2m)�[22m�[32m 11�[2mms�[22m�[39m
�[90mstderr�[2m | src/pages/DesktopPairingPage.test.tsx�[2m > �[22m�[2mDesktopPairingPage�[2m > �[22m�[2mshows the server-provided client name and requires an explicit approval click
�[22m�[39mIn HTML, <div> cannot be a descendant of <p>.
This will cause a hydration error.

  <MemoryRouter initialEntries={[...]}>
    <Router basename={undefined} location={{pathname:"...", ...}} navigationType="POP" navigator={{index:0, ...}} ...>
      <AuthProvider user={{id:"101", ...}}>
        <DesktopPairingPage>
          <main className="flex min-h...">
            <section className="w-full max...">
              <img>
              <h1>
              <p>
>             <p className="mt-3 text-sm">
                <GitHubAccountIdentity account={{id:"101", ...}}>
                  <span className="inline-fle...">
                    <UserAvatar user={{id:"101", ...}} className="flex h-6 w..." fallbackClassName="bg-slate-1..." ...>
                      <AvatarFallback accessibleName="alice avatar" className="flex h-6 w..." ...>
>                       <div
>                         className="flex h-6 w-6 flex-none items-center justify-center rounded-full border border-sla..."
>                         aria-hidden={true}
>                       >
                    ...
              ...

<p> cannot contain a nested <div>.
See this log for the ancestor stack trace.

 �[32m✓�[39m src/pages/DesktopPairingPage.test.tsx �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 263�[2mms�[22m�[39m
 �[32m✓�[39m src/desktop/PackagedAcceptanceLocalSetup.test.tsx �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[33m 340�[2mms�[22m�[39m
   �[32m✓�[39m packaged acceptance local-setup surfaces �[2m(3)�[22m
     �[33m�[2m✓�[22m�[39m drives the fixed prerequisites into the stable progress surface�[33m 313�[2mms�[22m�[39m
 �[32m✓�[39m src/contexts/SystemStatusContext.push.test.tsx �[2m(�[22m�[2m1 test�[22m�[2m)�[22m�[32m 28�[2mms�[22m�[39m
 �[32m✓�[39m src/api/usageTipsApi.test.ts �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 11�[2mms�[22m�[39m
 �[32m✓�[39m src/hooks/useSystemReadiness.test.tsx �[2m(�[22m�[2m1 test�[22m�[2m)�[22m�[32m 50�[2mms�[22m�[39m
 �[32m✓�[39m src/components/RepositoryListItem.test.tsx �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 200�[2mms�[22m�[39m
 �[32m✓�[39m src/hooks/useAgentTankSuggestion.test.tsx �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 79�[2mms�[22m�[39m
 �[32m✓�[39m src/pages/SettingsPage/UsageTipsSettingsSection.test.tsx �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 92�[2mms�[22m�[39m
 �[32m✓�[39m src/components/workflowSelectionInput.test.ts �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 7�[2mms�[22m�[39m
 �[32m✓�[39m src/utils/notificationIntents.test.ts �[2m(�[22m�[2m7 tests�[22m�[2m)�[22m�[32m 6�[2mms�[22m�[39m

�[2m Test Files �[22m �[1m�[32m48 passed�[39m�[22m�[90m (48)�[39m
�[2m      Tests �[22m �[1m�[32m368 passed�[39m�[22m�[90m (368)�[39m
�[2m   Start at �[22m 09:58:37
�[2m   Duration �[22m 44.49s�[2m (environment 48%, tests 27%, import 10%, setup 9%, transform 5%)�[22m

�[2mEnvironment �[22m �[33mjsdom was created 48 times�[39m�[2m · 39.65s total, 48% of tracked time�[22m
�[2m            �[22m �[2mcreate it once per worker with �[22m�[33mpool: 'vmThreads'�[39m�[2m (keeps per-file isolation) or �[22m�[33misolate: false�[39m�[2m (shares it across files)�[22m
�[2m            �[22m �[2mlearn more: https://vitest.dev/guide/improving-performance#test-environments�[22m


> propr-ui@0.0.1 posttest
> npm run test:docker-context


> propr-ui@0.0.1 test:docker-context
> node --test scripts/docker-context-inputs.test.mjs

TAP version 13
# Subtest: focused UI selectors are forwarded only to Vitest
ok 1 - focused UI selectors are forwarded only to Vitest
  ---
  duration_ms: 0.946243
  type: 'test'
  ...
# Subtest: the UI Docker context contains its complete non-type external source import closure
ok 2 - the UI Docker context contains its complete non-type external source import closure
  ---
  duration_ms: 51.052478
  type: 'test'
  ...
1..2
# tests 2
# suites 0
# pass 2
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 112.688526
[150/150] passed propr-ui#3/4 (workspace test script) in 45.3s

### Shard 3/4: 149/150 passed in 279.5s

Assigned 150 of 600 discovered units.

| Duration | Status | Unit |
| ---: | --- | --- |
| 45.3s | passed | `propr-ui#3/4` (workspace) |
| 8.8s | passed | `test/buildImages.test.mjs` |
| 8.2s | passed | `apps/desktop/src/authentication-handoff.test.ts` |
| 7.2s | passed | `packages/api/test/mcpAccessLogDispatch.test.ts` |
| 6.4s | passed | `test/notificationSchema.test.ts` |
| 6.3s | passed | `packages/api/test/mcpIntegration.test.ts` |
| 5.1s | passed | `packages/api/test/notificationProjectionPublish.test.ts` |
| 4.7s | passed | `packages/api/test/notificationBackgroundService.test.ts` |
| 4.6s | passed | `apps/desktop/scripts/test-native-artifact-lifecycle.test.mjs` |
| 4.3s | passed | `packages/api/test/liveOutputRetention.test.ts` |
| 4.2s | passed | `apps/desktop/scripts/run-bounded-darwin-command.test.mjs` |
| 3.8s | passed | `packages/core/test/managedPreviewStorage.test.ts` |
| 3.4s | passed | `packages/api/test/dashboardReadService.test.ts` |
| 3.4s | passed | `packages/api/test/configRoutesIndexingSummarization.test.ts` |
| 3.3s | failed | `packages/api/test/agentTankUsageObservation.test.ts` |


1/150 test runs failed in shard 3/4 after 279.5s:
- packages/api/test/agentTankUsageObservation.test.ts: exit 1

Details truncated; see the uploaded artifacts for complete failure rows and logs.

View Workflow
View uploaded artifacts

…very test and a related usage test. Added assertions for snapshot delivery, deduplication, and shutdown.

Fixed outdated Socket.IO mocks in the failing activity-delivery test and a related usage test. Added assertions for snapshot delivery, deduplication, and shutdown.

Validation: all 13 tests across three relevant files passed; targeted lint and diff checks passed.

The Node.js deprecation warning was unrelated. No visual changes or previews.

PR: #2611
Comment by: @propr-dev[bot] (ID: 5887919044)
Model: gpt-6-astra
@propr-dev

propr-dev Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

ℹ️ Analyzed the follow-up request by @propr-dev[bot], @github-actions[bot]

Analysis Summary

The reported failures are already fixed by branch commit 3a1717790.

CI logs confirm outdated Socket.IO mocks lacked io.sockets.sockets, causing snapshot delivery to throw. The existing fix updates both affected test files.

Verified locally: all 20 tests across four relevant files passed, plus targeted lint. No further changes or visual previews were needed.

No code changes were necessary based on the current state of the branch.


🤖 Analysis Details

  • Model: GPT-6 Astra (high)
  • Time: 2m 32s
  • Tokens: 440,773 (439,459 in / 1,314 out)
  • Cost: $1.04

View Task Execution


💡 ProPR Slash Commands
Command Description Example
/merge Merge target branch into this PR and resolve conflicts /merge
/review Request an AI code review /review or /review claude-sonnet
/fix Implement fixes for issues found by /review /fix or /fix address the null check issue
/switch Change the AI model for this PR /switch claude-opus
/use Override the model for a single follow-up run /use claude-sonnet
/ultrafix Loop review→fix cycles until score goal is met /ultrafix or /ultrafix goal=8 max=10

🤖 Generated by ProPR
Processing comment IDs: 5887936501✓, 5887955636✓, 5887956548✓

@integry
integry merged commit 81ae85b into main Sep 29, 2026
44 checks passed
@integry
integry deleted the perf/staging-read-latency branch September 29, 2026 10:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants