Skip to content

[3320 by Claude Opus 5.5] Desktop logger output failure can block the app with an uncaught main-process error - #3321

Merged
integry merged 3 commits into
mainfrom
3320/claude-opus-5-5-desktop-logger-output-fai-20261022-2201-u8g
Oct 22, 2026
Merged

integry merged 3 commits into
mainfrom
3320/claude-opus-5-5-desktop-logger-output-fai-20261022-2201-u8g

Conversation

@propr-dev

@propr-dev propr-dev Bot commented Oct 22, 2026

Copy link
Copy Markdown
Contributor

AI Implementation Summary

Closes #3320

Branch: 3320/claude-opus-5-5-desktop-logger-output-fai-20261022-2201-u8g
Commits: ✅ Changes committed (b30b8e2)


AI Processing Completed

Execution Details:

Summary:
Logging from the desktop app is now best-effort. If stdout/stderr or the log file can't be written, log() no longer throws back into its caller, so DesktopCredentialService.reportCredentialDecision can no longer trigger the "JavaScript error occurred in the main process" dialog. The new regression tests pass, and the full desktop suite passes (npm test: 1408 tests, 1352 passed, 0 failed, the rest skipped). Typecheck is clean. The repo's ESLint config doesn't cover apps/desktop, so no lint gate applies to these files.

Fix, in apps/desktop/src/logger.ts:

  • I added a small helper that calls the console method inside a try/catch and discards any error the console write throws. Both places that write to the console now go through it: the main per-record output, and the desktop.log.write_failed message printed when appending to the file fails.
  • Because the console write can no longer throw, the file append is always queued, even when the console fails.
  • The append's .catch can no longer reject, so there's no unhandled rejection even when the append, onWriteFailure and the fallback console all fail.
  • Nothing else changed: redaction, field bounds, formatting, 0o700/0o600 permissions and the onWriteFailure containment are as before. Errors from outside the console calls still propagate, and I added no global exception handler.

New tests, in apps/desktop/src/logger-sinks.test.ts. The existing formatting/redaction suite is untouched. The tests swap in fake console methods that throw ENOSPC or EPIPE, and they never fill a real disk:

  • Normal behaviour: each level goes to its expected console method, the console and file lines match, and the file and directory get 0600/0700 permissions.
  • Console fails (ENOSPC and EPIPE): log() doesn't throw for any level, every record still reaches the log file with secrets and paths redacted, and there are no unhandled rejections.
  • Append fails, then the failure hook and fallback console also throw: the append is made to fail by putting the log file under a path that is actually a regular file. onWriteFailure runs once per failure, the fixed LOG_WRITE_FAILED message is attempted, and there are no unhandled rejections.
  • Recovery: logging keeps working after the console recovers from a failure.

I also ran the new tests against the old logger.ts: 4 of the 5 fail. The one that still passes is the normal-behaviour test, as it should.

No preview was created because nothing in the change is visible. Nothing has been merged or published; the Astra review and root validation at the final head still need to happen.

Detailed Logs:

  • Conversation: 24 messages
  • Session: 95f3e41a-0d1d-4e0b-a95b-afdfcd43812c

Log files stored at:

  • conversation: /usr/src/app/logs/executions/issue-3320-2026-10-22T22-07-13-183Z-conversation.json
  • output: /usr/src/app/logs/executions/issue-3320-2026-10-22T22-07-13-183Z-output.txt
Latest Conversation Messages
ASSISTANT: [content unavailable]

ASSISTANT: Logging from the desktop app is now best-effort. If stdout/stderr or the log file can't be written, `log()` no longer throws back into its caller, so `DesktopCredentialService.reportCredentialDecision...


This PR was created automatically by ProPR after processing issue #3320.


💡 Need changes?

Comment on this PR to request refinements — the AI agent monitors comments and will update the implementation based on your feedback. Keep iterating until you're satisfied!

…the log file can't be written, `log()` no longer throws back into its caller, so `DesktopCredentialService.reportCredentialDecision` can no longer trigger the "JavaScript error occurred in the main process" dialog. The new regression tests pass, and the full desktop suite passes (`npm test`: 1408 tests, 1352 passed, 0 failed, the rest skipped). Typecheck is clean. The repo's ESLint config doesn't cover `apps/desktop`, so no lint gate applies to these files.

Fix, in `apps/desktop/src/logger.ts`:
- I added a small helper that calls the console method inside a `try`/`catch` and discards any error the console write throws. Both places that write to the console now go through it: the main per-record output, and the `desktop.log.write_failed` message printed when appending to the file fails.
- Because the console write can no longer throw, the file append is always queued, even when the console fails.
- The append's `.catch` can no longer reject, so there's no unhandled rejection even when the append, `onWriteFailure` and the fallback console all fail.
- Nothing else changed: redaction, field bounds, formatting, `0o700`/`0o600` permissions and the `onWriteFailure` containment are as before. Errors from outside the console calls still propagate, and I added no global exception handler.

New tests, in `apps/desktop/src/logger-sinks.test.ts`. The existing formatting/redaction suite is untouched. The tests swap in fake console methods that throw ENOSPC or EPIPE, and they never fill a real disk:
- Normal behaviour: each level goes to its expected console method, the console and file lines match, and the file and directory get `0600`/`0700` permissions.
- Console fails (ENOSPC and EPIPE): `log()` doesn't throw for any level, every record still reaches the log file with secrets and paths redacted, and there are no unhandled rejections.
- Append fails, then the failure hook and fallback console also throw: the append is made to fail by putting the log file under a path that is actually a regular file. `onWriteFailure` runs once per failure, the fixed `LOG_WRITE_FAILED` message is attempted, and there are no unhandled rejections.
- Recovery: logging keeps working after the console recovers from a failure.

I also ran the new tests against the old `logger.ts`: 4 of the 5 fail. The one that still passes is the normal-behaviour test, as it should.

No preview was created because nothing in the change is visible. Nothing has been merged or published; the Astra review and root validation at the final head still need to happen.
@integry

integry commented Oct 22, 2026

Copy link
Copy Markdown
Owner

/review gpt-6-astra

Review exact head b30b8e2 for issue #3320. Independently assess console ENOSPC/EPIPE containment, file-sink independence, rejection containment when both append and fallback diagnostics fail, preserved redaction/permissions, and meaningful failure-path tests. The operator observed a real Linux native main-process error dialog, and independently reproduced synchronous failures on all three levels in the previous logger. Do not merge or publish. Root independent validation is separate from this review.

@propr-dev

propr-dev Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor Author

✅ AI Code Review Complete requested by @integry

Posted 1 review:

View Task Details

@propr-dev

propr-dev Bot commented Oct 22, 2026

Copy link
Copy Markdown
Contributor Author

CI failed: Validate unsigned linux-x64 package

Please investigate and fix this CI failure.

  • Check: Validate unsigned linux-x64 package
  • Result: failure
  • Commit: b30b8e2c83c6 (b30b8e2c83c64b248c79a658129510d256f2b195)
  • Details: View CI failure

Failure evidence

.github:66
Process completed with exit code 1.

@propr-dev

propr-dev Bot commented Oct 22, 2026

Copy link
Copy Markdown
Contributor Author

CI failed: Validate unsigned linux-arm64 package

Please investigate and fix this CI failure.

  • Check: Validate unsigned linux-arm64 package
  • Result: failure
  • Commit: b30b8e2c83c6 (b30b8e2c83c64b248c79a658129510d256f2b195)
  • Details: View CI failure

Failure evidence

.github:66
Process completed with exit code 1.

@propr-dev

propr-dev Bot commented Oct 22, 2026

Copy link
Copy Markdown
Contributor Author

CI failed: Validate unsigned darwin-arm64 package

Please investigate and fix this CI failure.

  • Check: Validate unsigned darwin-arm64 package
  • Result: failure
  • Commit: b30b8e2c83c6 (b30b8e2c83c64b248c79a658129510d256f2b195)
  • Details: View CI failure

Failure evidence

.github:66
Process completed with exit code 1.

.github:1
Due to capacity constraints, jobs targeting macOS arm64 runners may experience longer queue times.

@propr-dev

propr-dev Bot commented Oct 22, 2026

Copy link
Copy Markdown
Contributor Author

🔍 AI Code Review — gpt-6-astra

Overall Evaluation

Ready to merge within the reviewed scope, conditional on pending checks completing successfully. The change directly contains the reported synchronous console failures without disrupting file logging.

✅ Independent sinks — Both normal console writes and fallback diagnostics use the guarded helper. A synchronous ENOSPC/EPIPE exception no longer escapes or prevents scheduling the file append.

✅ Rejection containment — The file-write rejection handler contains exceptions from both the failure hook and fallback console output, allowing subsequent queued records to proceed.

✅ Preserved protections — Formatting, redaction, and directory/file creation permissions remain unchanged. New tests cover all logging levels, both console failure codes, simultaneous diagnostic failures, and console recovery.

This assessment is a static review of the supplied code; no commands or tests were run. The supplied issue body is truncated, so unseen requirements could not be assessed.

Merge blockers

No merge blockers.

Suggestions

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

S1: 🟢 Exercise append failure directly

In apps/desktop/src/logger-sinks.test.ts:108, the “failed append” test actually fails at mkdir because a parent component is a regular file; appendFile is never reached. Optionally add a case where directory creation succeeds but appending fails, then restore the destination and verify a later record persists. This would strengthen coverage of file-sink recovery. It is non-blocking because both operations already share the same rejection handler.

Score

The implementation is localized and correctly addresses the demonstrated failure paths, with meaningful regression coverage and no verified blockers. The supplied current-head status has no failed checks, though 13 checks remain pending.

Score: 9/10


🤖 Review Details

  • Model: GPT-6 Astra
  • Time: 18s
  • Tokens: 29,015 (28,565 in / 450 out)
  • Cost: $0.20

View Task


💡 Next step: Comment /fix to address every F# merge blocker, /fix all for every pending blocker and suggestion, 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 or request /fix all, and they never relax a merge blocker.

Copy and edit to choose records from this review:

/fix S1

🤖 Review by ProPR

@propr-dev

propr-dev Bot commented Oct 22, 2026

Copy link
Copy Markdown
Contributor Author

CI failed: Validate unsigned darwin-x64 package

Please investigate and fix this CI failure.

  • Check: Validate unsigned darwin-x64 package
  • Result: failure
  • Commit: b30b8e2c83c6 (b30b8e2c83c64b248c79a658129510d256f2b195)
  • Details: View CI failure

Failure evidence

.github:66
Process completed with exit code 1.

@propr-dev

propr-dev Bot commented Oct 22, 2026

Copy link
Copy Markdown
Contributor Author

CI failed: Finalize unsigned validation checksums

Please investigate and fix this CI failure.

  • Check: Finalize unsigned validation checksums
  • Result: failure
  • Commit: b30b8e2c83c6 (b30b8e2c83c64b248c79a658129510d256f2b195)
  • Details: View CI failure

Failure evidence

.github:42
Process completed with exit code 1.

.github:41
Unsigned packaging matrix finished with result 'failure'.

.github:1
"The ubuntu-latest label will migrate to Ubuntu 26 beginning October 19, 2026. For more information, see actions/runner-images#14748"

@propr-dev

propr-dev Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor Author

✅ Applied the requested follow-up changes in commit 8c5007c

Processed 3 comments:

Summary of Changes

I fixed the three failing checks by moving ip-address from 10.7.2 to 10.7.4 in the root package-lock.json. That's the only file changed, and it's a three-line edit to the lock entry.

Cause: none of the three package jobs got to packaging. They all stopped at the "Audit committed dependency resolution" step, where npm run audit:runtime reported Blocking vulnerabilities: ip-address. A new moderate advisory was published against ip-address versions up to 10.7.3 (GHSA-rxp7-6hxx-x8q5, a long string can stall the process). It's pulled in indirectly through express-rate-limit, which asks for ^10.2.0. This PR never touched the lockfile, so the gate failed because the advisory data changed, not because of the logger change. The darwin-arm64 queue-time line in the log is just GitHub's notice about macOS runner capacity.

Fix: 10.7.4 is already inside express-rate-limit's ^10.2.0 range, so I didn't need to touch package.json. I also didn't add any exception to scripts/audit-dependencies.mjs. docs/package-lock.json doesn't include ip-address.

Checks run:

  • npm run desktop:audit, which is the runtime and packaging audits together: both pass.
  • I made the edit with npm 11, then ran npm 10 (npm install --package-lock-only), which is what CI's npm ci uses on Node 22. It left the lockfile byte-for-byte the same, so CI won't rewrite it.

The logger change and its tests are unchanged. Nothing visible changed, so there's no preview.


🤖 Implementation Details

  • Model: Claude Opus 5.5 (medium)
  • Requested By: @propr-dev[bot]
  • Time: 2m 34s
  • Tokens: 282,756 (280,854 in / 1,902 out)
  • Cost: $0.24

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 Address review blockers, or blockers and suggestions with /fix all /fix, /fix all, or /fix F3 S5
/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: 6352389766✓, 6352390484✓, 6352392472✓

…m 10.7.2 to 10.7.4 in the root `package-lock.json`. That's the only file changed, and it's a three-line edit to the lock entry.

I fixed the three failing checks by moving `ip-address` from 10.7.2 to 10.7.4 in the root `package-lock.json`. That's the only file changed, and it's a three-line edit to the lock entry.

**Cause:** none of the three package jobs got to packaging. They all stopped at the "Audit committed dependency resolution" step, where `npm run audit:runtime` reported `Blocking vulnerabilities: ip-address`. A new moderate advisory was published against `ip-address` versions up to 10.7.3 (GHSA-rxp7-6hxx-x8q5, a long string can stall the process). It's pulled in indirectly through `express-rate-limit`, which asks for `^10.2.0`. This PR never touched the lockfile, so the gate failed because the advisory data changed, not because of the logger change. The darwin-arm64 queue-time line in the log is just GitHub's notice about macOS runner capacity.

**Fix:** 10.7.4 is already inside `express-rate-limit`'s `^10.2.0` range, so I didn't need to touch `package.json`. I also didn't add any exception to `scripts/audit-dependencies.mjs`. `docs/package-lock.json` doesn't include `ip-address`.

**Checks run:**
- `npm run desktop:audit`, which is the runtime and packaging audits together: both pass.
- I made the edit with npm 11, then ran npm 10 (`npm install --package-lock-only`), which is what CI's `npm ci` uses on Node 22. It left the lockfile byte-for-byte the same, so CI won't rewrite it.

The logger change and its tests are unchanged. Nothing visible changed, so there's no preview.

PR: #3321
Comment by: @propr-dev[bot] (ID: 6352389766)
Comment by: @propr-dev[bot] (ID: 6352390484)
Comment by: @propr-dev[bot] (ID: 6352392472)
Model: Claude Opus 5.5
@propr-dev

propr-dev Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor Author

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

Analysis Summary

No code change was needed for these two failures. The previous follow-up commit on this branch (8c5007c4a) already fixes them.

  • Validate unsigned darwin-x64 package: at b30b8e2c8, the job stopped at the "Audit committed dependency resolution" step with Blocking vulnerabilities: ip-address. The darwin-arm64, linux-x64 and linux-arm64 jobs in that run failed at the same step. The cause is a new advisory against ip-address up to 10.7.3, not the logger change.
  • Finalize unsigned validation checksums: this job failed only because the packaging jobs before it failed ("Unsigned packaging matrix finished with result 'failure'").

The current head already pins ip-address to 10.7.4 in package-lock.json. I re-ran npm run audit:runtime here and it passes ("runtime dependency audit passed").

The workflow runs on 8c5007c4a (Desktop Package and Release, Build & Lint, Full Test Suite and others) show as cancelled, not failed, so the packaging jobs haven't finished on the fixed commit yet. They need to be re-run to confirm the fix in CI. Nothing visible changed, so there's no preview.

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


🤖 Analysis Details

  • Model: Claude Opus 5.5 (medium)
  • Time: 2m 57s
  • Tokens: 188,405 (187,081 in / 1,324 out)
  • Cost: $0.20

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 Address review blockers, or blockers and suggestions with /fix all /fix, /fix all, or /fix F3 S5
/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: 6352398900✓, 6352399990✓

propr-dev Bot pushed a commit that referenced this pull request Oct 22, 2026
… in the root `package-lock.json`. That is the only change: three lines in the `node_modules/ip-address` entry (version, resolved URL, integrity). I didn't touch any `package.json`, audit script or exception list.

What I found on current main
- Before the change, `npm run audit:runtime` failed with "Blocking vulnerabilities: ip-address", matching the CI failure. `npm run desktop:audit:packaging` passed on its own.
- The only copy of ip-address is through `express-rate-limit@8.6.2`, whose `^10.2.0` range already allows 10.7.4. The full chain from `npm explain` is: root project, `@propr/api`, `@modelcontextprotocol/sdk` and `repomix` (via `@propr/core`) → `express-rate-limit@8.6.2` → `ip-address@10.7.4`.
- The only other committed lockfile, `docs/package-lock.json`, doesn't contain ip-address, so I left it alone.

How I updated it
- I ran `npm update ip-address --package-lock-only` with npm 10, the version CI's Node 22 uses. npm 11 also rewrote about 30 unrelated `peer` flags, so I threw that result away.
- Running `npm install --package-lock-only` with npm 10 afterwards leaves the lock byte-for-byte unchanged.

Checks run
- A fresh `npm ci` succeeded, and the installed copy reports version 10.7.4.
- `npm ls ip-address --all` shows only `express-rate-limit@8.6.2 → ip-address@10.7.4`, and `npm audit --omit=dev` reports nothing for ip-address.
- `npm run audit:runtime` and `npm run desktop:audit:packaging` both pass with no exceptions.
- The rate-limit and MCP tests all pass, 8 of 8 files: `requestRateLimits`, `extensionCsrfScope`, `oauthLoopbackOrigin`, `hostedConnectStorage`, `mcpBrowser`, `routeAuthorization`, `demoMode` and `githubAuthCallback`.

Left as is
- `THIRD_PARTY_LICENSES.md` still lists `ip-address@10.4.0`. It was already out of date before this change (main had 10.7.2), and `scripts/build-images.sh` rebuilds it when images are built, so I didn't edit it.
- `scripts/validate-third-party-notices.mjs file THIRD_PARTY_LICENSES.md` fails because entries for other packages (better-sqlite3, braces, undici) are missing. It says nothing about ip-address or express-rate-limit.

This is a lockfile-only change with nothing visible, so I made no previews. The logger PR #3321 is untouched, and merging is left to you after review and exact-head validation.
@integry

integry commented Oct 22, 2026

Copy link
Copy Markdown
Owner

/review gpt-6-astra

Review exact current head8c5007c4a1feda9ec9772c79a4db8910dc0f3f87. Compared with your previous reviewed b30b8e2, the automatic CI followup updates only the root ip-address lock entry10.7.2 ->10.7.4 to address GHSA-rxp7-6hxx-x8q5. Independently inspect the resulting complete PR. Root independently verified13 original/new logger cases at b30 and a separate actual append failure plus recovery probe; current-head source/lock continuity and audits are being validated separately. Do not merge or publish. CI is currently running for8c5007c; no rerun needed while those handles remain active.

@propr-dev

propr-dev Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor Author

✅ AI Code Review Complete requested by @integry

Posted 1 review:

View Task Details

@propr-dev

propr-dev Bot commented Oct 22, 2026

Copy link
Copy Markdown
Contributor Author

🔍 AI Code Review — gpt-6-astra

Overall Evaluation

The supplied diff for head 8c5007c4a1feda9ec9772c79a4db8910dc0f3f87 correctly contains synchronous console failures while preserving file logging. No merge-blocking defects found; merge-ready within the reviewed scope, conditional on remaining checks passing.

✅ Independent sink attempts — Console exceptions are caught before file writes are queued, so synchronous ENOSPC/EPIPE failures no longer escape or suppress the file sink.

✅ Contained fallback failures — Both the failure callback and fallback console diagnostic are guarded. The promise chain therefore recovers after a file-write failure and can process subsequent records.

✅ Preserved logging protections — Formatting, redaction, and private creation modes remain unchanged. New tests exercise all log levels, console failures, combined sink failures, and console recovery.

This was a static review of the supplied complete diff and context; no commands or tests were run. The original issue excerpt is truncated, so assessment covers its visible objective and the supplied review requirements. Authoritative checks show no failures and 11 pending checks.

Merge blockers

No merge blockers.

Suggestions

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

S2: 🟢 Exercise actual append failure recovery

In apps/desktop/src/logger-sinks.test.ts, the test named “contains a failed append” creates a regular file where the parent directory should be, causing mkdir to fail before appendFile executes. Optionally add a case with an existing parent directory and a directory occupying the log-file path, then remove that obstruction and verify a later record succeeds. This would preserve automated coverage of actual append failure and file-sink recovery. It is non-blocking because both operations share the same guarded rejection path, whose recovery is sound by static inspection.

Score

The focused implementation addresses the demonstrated failure without changing redaction or permissions, and its regression tests cover the principal failure combinations. No code blockers or current check failures are established; remaining CI results are pending.

Score: 9/10


🤖 Review Details

  • Model: GPT-6 Astra
  • Time: 23s
  • Tokens: 29,528 (29,024 in / 504 out)
  • Cost: $0.21

View Task


💡 Next step: Comment /fix to address every F# merge blocker, /fix all for every pending blocker and suggestion, 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 or request /fix all, and they never relax a merge blocker.

Copy and edit to choose records from this review:

/fix S2

🤖 Review by ProPR

@propr-dev

propr-dev Bot commented Oct 22, 2026

Copy link
Copy Markdown
Contributor Author

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: 8c5007c4a1fe (8c5007c4a1feda9ec9772c79a4db8910dc0f3f87)
  • Details: View CI failure

Failure evidence

.github:11
Process completed with exit code 1.

.github:35796
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 Oct 22, 2026

Copy link
Copy Markdown
Contributor Author

CI failed: Run Full Test Suite

Please investigate and fix this CI failure.

Failure evidence

.github:67
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 3).

  • Dependency install: success
  • Workspace build: success
  • Docs validation: success
  • Shard coverage verification: success
  • Hosted native Electron units: success
  • Shard 1/4: passed in 479.1s on GitHub Actions 1000102391
  • Shard 2/4: passed in 587.5s on GitHub Actions 1000102390
  • Shard 3/4: failed during Test shard in 1044.3s on GitHub Actions 1000102392
    • test/prPublicationRecoveryJob.test.ts: exit 1
  • Shard 4/4: passed in 810.3s on GitHub Actions 1000102389
View shard 3/4 output
...(truncated)

t

�[90mstderr�[2m | src/components/MobileBottomNavigation.test.tsx�[2m > �[22m�[2mMobileBottomNavigation�[2m > �[22m�[2mkeeps creation out of the More sheet
�[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/RepositorySettingsBar.test.tsx�[2m > �[22m�[2mRepositorySettingsBar follow-up CI cancellation�[2m > �[22m�[2mrenders the option off by default with helper text about restarting checks
�[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.

�[90mstderr�[2m | src/components/RepositorySettingsBar.test.tsx�[2m > �[22m�[2mRepositorySettingsBar follow-up CI cancellation�[2m > �[22m�[2masks for a selection and discloses the instance fallback while the enabled option selects nothing
�[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.

�[90mstderr�[2m | src/components/RepositorySettingsBar.test.tsx�[2m > �[22m�[2mRepositorySettingsBar follow-up CI cancellation�[2m > �[22m�[2masks for a selection and discloses the instance fallback while the enabled option selects nothing
�[22m�[39mAn update to CancelCiDuringFollowupControl 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/RepositorySettingsBar.test.tsx�[2m > �[22m�[2mRepositorySettingsBar follow-up CI cancellation�[2m > �[22m�[2mshows the selected workflows and reports an edited selection as exact identities
�[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.

�[90mstderr�[2m | src/components/RepositorySettingsBar.test.tsx�[2m > �[22m�[2mRepositorySettingsBar follow-up CI cancellation�[2m > �[22m�[2mdoes not report a selection that did not change
�[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.

�[90mstderr�[2m | src/components/RepositorySettingsBar.test.tsx�[2m > �[22m�[2mRepositorySettingsBar follow-up CI cancellation�[2m > �[22m�[2mpreserves comma-containing names on unchanged blur and when editing another selection
�[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.

�[90mstderr�[2m | src/components/RepositorySettingsBar.test.tsx�[2m > �[22m�[2mRepositorySettingsBar follow-up CI cancellation�[2m > �[22m�[2mdoes not save incomplete quoted 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.

�[90mstderr�[2m | src/components/RepositorySettingsBar.test.tsx�[2m > �[22m�[2mRepositorySettingsBar follow-up CI cancellation�[2m > �[22m�[2mreflects the stored value and reports a toggle
�[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.

�[90mstderr�[2m | src/components/RepositorySettingsBar.test.tsx�[2m > �[22m�[2mRepositorySettingsBar follow-up CI cancellation�[2m > �[22m�[2mreflects the stored value and reports a toggle
�[22m�[39mAn update to CancelCiDuringFollowupControl 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/RepositorySettingsBar.test.tsx�[2m > �[22m�[2mRepositorySettingsBar detected workflows�[2m > �[22m�[2mrecognizes and deselects a padded display name with triggers [ 'pull_request' ]
�[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.

�[90mstderr�[2m | src/components/RepositorySettingsBar.test.tsx�[2m > �[22m�[2mRepositorySettingsBar detected workflows�[2m > �[22m�[2mrecognizes and deselects a padded display name with triggers [ 'push' ]
�[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.

�[90mstderr�[2m | src/components/RepositorySettingsBar.test.tsx�[2m > �[22m�[2mRepositorySettingsBar detected workflows�[2m > �[22m�[2mrecognizes and deselects a padded display name with triggers null
�[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.

�[90mstderr�[2m | src/components/RepositorySettingsBar.test.tsx�[2m > �[22m�[2mRepositorySettingsBar detected workflows�[2m > �[22m�[2mpreserves a normalized display name when removing unavailable selections after discovery
�[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.

�[90mstderr�[2m | src/components/RepositorySettingsBar.test.tsx�[2m > �[22m�[2mRepositorySettingsBar detected workflows�[2m > �[22m�[2moffers the repository workflows and matches stored entries by any identity
�[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.

�[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/RepositorySettingsBar.test.tsx�[2m > �[22m�[2mRepositorySettingsBar detected workflows�[2m > �[22m�[2mallows manual identities outside a cached listing and keeps checkbox edits in sync
�[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.

�[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

�[90mstderr�[2m | src/components/RepositorySettingsBar.test.tsx�[2m > �[22m�[2mRepositorySettingsBar detected workflows�[2m > �[22m�[2mallows manual identities outside a cached listing and keeps checkbox edits in sync
�[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.

�[90mstderr�[2m | src/components/RepositorySettingsBar.test.tsx�[2m > �[22m�[2mRepositorySettingsBar detected workflows�[2m > �[22m�[2mpreserves manual input across a successful populated discovery without saving it
�[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.

�[90mstderr�[2m | src/components/RepositorySettingsBar.test.tsx�[2m > �[22m�[2mRepositorySettingsBar detected workflows�[2m > �[22m�[2mpreserves manual input across a successful empty discovery without saving it
�[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.

�[90mstderr�[2m | src/components/RepositorySettingsBar.test.tsx�[2m > �[22m�[2mRepositorySettingsBar detected workflows�[2m > �[22m�[2mstores a picked workflow by file name and removes every spelling of an unpicked one
�[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.

�[90mstderr�[2m | src/components/RepositorySettingsBar.test.tsx�[2m > �[22m�[2mRepositorySettingsBar detected workflows�[2m > �[22m�[2mflags stored entries that match no workflow in the repository
�[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.

�[90mstderr�[2m | src/components/RepositorySettingsBar.test.tsx�[2m > �[22m�[2mRepositorySettingsBar detected workflows�[2m > �[22m�[2mflags a stored selection only after an empty listing successfully loads
�[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.

�[90mstderr�[2m | src/components/RepositorySettingsBar.test.tsx�[2m > �[22m�[2mRepositorySettingsBar detected workflows�[2m > �[22m�[2mdoes not flag an empty selection after an empty listing successfully loads
�[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.

�[90mstderr�[2m | src/components/RepositorySettingsBar.test.tsx�[2m > �[22m�[2mRepositorySettingsBar detected workflows�[2m > �[22m�[2mfalls back to typing workflows when GitHub cannot list them
�[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/MobileBottomNavigation.test.tsx �[2m(�[22m�[2m34 tests�[22m�[2m)�[22m�[33m 2577�[2mms�[22m�[39m
�[90mstderr�[2m | src/components/RepositorySettingsBar.test.tsx�[2m > �[22m�[2mRepositorySettingsBar detected workflows�[2m > �[22m�[2mdoes not ask GitHub for workflows while the option is off
�[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.

�[90mstderr�[2m | src/components/RepositorySettingsBar.test.tsx�[2m > �[22m�[2mRepositorySettingsBar non-blocking checks�[2m > �[22m�[2mshows the stored checks and saves an edited list on blur
�[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.

�[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 1810�[2mms�[22m�[39m
�[90mstdout�[2m | src/hooks/useHeaderStats.recovery.test.tsx�[2m > �[22m�[2museHeaderStats live recovery�[2m > �[22m�[2mcommits untouched initial resources when a partial live refresh supersedes queue
�[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�[2mreconciles a successful completion to zero from the queue subscription
�[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�[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

 �[32m✓�[39m src/hooks/useHeaderStats.recovery.test.tsx �[2m(�[22m�[2m11 tests�[22m�[2m)�[22m�[33m 1307�[2mms�[22m�[39m
   �[32m✓�[39m useHeaderStats live recovery �[2m(11)�[22m
     �[33m�[2m✓�[22m�[39m revalidates missed same-count draft and health changes after a web reconnect�[33m 307�[2mms�[22m�[39m
 �[32m✓�[39m src/pages/TasksPage.split.test.tsx �[2m(�[22m�[2m14 tests�[22m�[2m)�[22m�[33m 2236�[2mms�[22m�[39m
   �[32m✓�[39m TasksPage split workspace �[2m(14)�[22m
     �[33m�[2m✓�[22m�[39m keeps the open task's runs and current state when a filter takes it off the list�[33m 530�[2mms�[22m�[39m
�[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�[33m 362�[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 357�[2mms�[22m�[39m
 �[32m✓�[39m src/pages/AiAgentsPage.test.tsx �[2m(�[22m�[2m11 tests�[22m�[2m)�[22m�[33m 2577�[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 439�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m replaces Playground selections with the exact enabled agent/model pair and opens the mobile Playground�[33m 606�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m blocks synthetic mutations when committed state cannot be reloaded�[33m 323�[2mms�[22m�[39m
 �[32m✓�[39m src/components/Agents/AgentRunDetail.test.tsx �[2m(�[22m�[2m12 tests�[22m�[2m)�[22m�[33m 820�[2mms�[22m�[39m
   �[32m✓�[39m AgentRunDetail �[2m(12)�[22m
     �[33m�[2m✓�[22m�[39m renders a completed run report as Markdown with copy, the truncation note and its task link�[33m 326�[2mms�[22m�[39m
�[90mstderr�[2m | src/components/usageHistory/UsageMetricHistoryOverlay.test.tsx�[2m > �[22m�[2msidebar usage history overlay�[2m > �[22m�[2mopens on hover after the intent delay and reads the last two sessions
�[22m�[39mThe width(-1) and height(-1) of chart should be greater than 0,
       please check the style of container, or the props width(100%) and height(100%),
       or add a minWidth(0) or minHeight(undefined) or use aspect(undefined) to control the
       height and width.
The width(0) and height(0) of chart should be greater than 0,
       please check the style of container, or the props width(100%) and height(100%),
       or add a minWidth(0) or minHeight(undefined) or use aspect(undefined) to control the
       height and width.

�[90mstderr�[2m | src/components/usageHistory/UsageMetricHistoryOverlay.test.tsx�[2m > �[22m�[2msidebar usage history overlay�[2m > �[22m�[2mreads two weeks for a weekly limit, toggled by tap
�[22m�[39mThe width(-1) and height(-1) of chart should be greater than 0,
       please check the style of container, or the props width(100%) and height(100%),
       or add a minWidth(0) or minHeight(undefined) or use aspect(undefined) to control the
       height and width.
The width(0) and height(0) of chart should be greater than 0,
       please check the style of container, or the props width(100%) and height(100%),
       or add a minWidth(0) or minHeight(undefined) or use aspect(undefined) to control the
       height and width.

�[90mstderr�[2m | src/components/usageHistory/UsageMetricHistoryOverlay.test.tsx�[2m > �[22m�[2msidebar usage history overlay�[2m > �[22m�[2mcloses on Escape and with the close button
�[22m�[39mThe width(-1) and height(-1) of chart should be greater than 0,
       please check the style of container, or the props width(100%) and height(100%),
       or add a minWidth(0) or minHeight(undefined) or use aspect(undefined) to control the
       height and width.
The width(0) and height(0) of chart should be greater than 0,
       please check the style of container, or the props width(100%) and height(100%),
       or add a minWidth(0) or minHeight(undefined) or use aspect(undefined) to control the
       height and width.

�[90mstderr�[2m | src/components/usageHistory/UsageMetricHistoryOverlay.test.tsx�[2m > �[22m�[2msidebar usage history overlay�[2m > �[22m�[2mcloses on Escape and with the close button
�[22m�[39mThe width(-1) and height(-1) of chart should be greater than 0,
       please check the style of container, or the props width(100%) and height(100%),
       or add a minWidth(0) or minHeight(undefined) or use aspect(undefined) to control the
       height and width.
The width(0) and height(0) of chart should be greater than 0,
       please check the style of container, or the props width(100%) and height(100%),
       or add a minWidth(0) or minHeight(undefined) or use aspect(undefined) to control the
       height and width.

�[90mstderr�[2m | src/components/usageHistory/UsageMetricHistoryOverlay.test.tsx�[2m > �[22m�[2msidebar usage history overlay�[2m > �[22m�[2mgroups Gemini 4 Argon under its own pool and charts its weekly quota
�[22m�[39mThe width(-1) and height(-1) of chart should be greater than 0,
       please check the style of container, or the props width(100%) and height(100%),
       or add a minWidth(0) or minHeight(undefined) or use aspect(undefined) to control the
       height and width.
The width(0) and height(0) of chart should be greater than 0,
       please check the style of container, or the props width(100%) and height(100%),
       or add a minWidth(0) or minHeight(undefined) or use aspect(undefined) to control the
       height and width.

�[90mstderr�[2m | src/components/usageHistory/UsageMetricHistoryOverlay.test.tsx�[2m > �[22m�[2msidebar usage history overlay�[2m > �[22m�[2mreads again when new usage is recorded while the overlay stays open
�[22m�[39mThe width(-1) and height(-1) of chart should be greater than 0,
       please check the style of container, or the props width(100%) and height(100%),
       or add a minWidth(0) or minHeight(undefined) or use aspect(undefined) to control the
       height and width.

�[90mstderr�[2m | src/components/usageHistory/UsageMetricHistoryOverlay.test.tsx�[2m > �[22m�[2msidebar usage history overlay�[2m > �[22m�[2mreads again when new usage is recorded while the overlay stays open
�[22m�[39mThe width(0) and height(0) of chart should be greater than 0,
       please check the style of container, or the props width(100%) and height(100%),
       or add a minWidth(0) or minHeight(undefined) or use aspect(undefined) to control the
       height and width.

 �[32m✓�[39m src/components/TaskDetails/RunTimeline.test.tsx �[2m(�[22m�[2m13 tests�[22m�[2m)�[22m�[33m 1074�[2mms�[22m�[39m
   �[32m✓�[39m RunTimeline �[2m(12)�[22m
     �[33m�[2m✓�[22m�[39m keeps all 50 runs in the shared task scroll flow�[33m 448�[2mms�[22m�[39m
�[90mstderr�[2m | src/components/usageHistory/UsageMetricHistoryOverlay.test.tsx�[2m > �[22m�[2msidebar usage history overlay�[2m > �[22m�[2mreads again when new usage is recorded while the overlay stays open
�[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/usageHistory/UsageMetricHistoryOverlay.test.tsx�[2m > �[22m�[2msidebar usage history overlay�[2m > �[22m�[2mreads again when new usage is recorded while the overlay stays open
�[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/usageHistory/UsageMetricHistoryOverlay.test.tsx�[2m > �[22m�[2msidebar usage history overlay�[2m > �[22m�[2moffers history again once Agent Tank is re-enabled
�[22m�[39mThe width(-1) and height(-1) of chart should be greater than 0,
       please check the style of container, or the props width(100%) and height(100%),
       or add a minWidth(0) or minHeight(undefined) or use aspect(undefined) to control the
       height and width.
The width(0) and height(0) of chart should be greater than 0,
       please check the style of container, or the props width(100%) and height(100%),
       or add a minWidth(0) or minHeight(undefined) or use aspect(undefined) to control the
       height and width.

�[90mstderr�[2m | src/components/usageHistory/UsageMetricHistoryOverlay.test.tsx�[2m > �[22m�[2msidebar usage history overlay�[2m > �[22m�[2mlets a disabled answer lapse, so rows outside the sidebar can retry
�[22m�[39mThe width(-1) and height(-1) of chart should be greater than 0,
       please check the style of container, or the props width(100%) and height(100%),
       or add a minWidth(0) or minHeight(undefined) or use aspect(undefined) to control the
       height and width.
The width(0) and height(0) of chart should be greater than 0,
       please check the style of container, or the props width(100%) and height(100%),
       or add a minWidth(0) or minHeight(undefined) or use aspect(undefined) to control the
       height and width.

 �[32m✓�[39m src/components/usageHistory/UsageMetricHistoryOverlay.test.tsx �[2m(�[22m�[2m13 tests�[22m�[2m)�[22m�[33m 1716�[2mms�[22m�[39m
   �[32m✓�[39m sidebar usage history overlay �[2m(11)�[22m
     �[33m�[2m✓�[22m�[39m opens on hover after the intent delay and reads the last two sessions�[33m 556�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m does not open when the pointer passes over the row�[33m 364�[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�[2m12 tests�[22m�[2m)�[22m�[32m 74�[2mms�[22m�[39m
 �[32m✓�[39m src/components/VoiceBriefingControl.test.tsx �[2m(�[22m�[2m14 tests�[22m�[2m)�[22m�[33m 827�[2mms�[22m�[39m
 �[32m✓�[39m src/desktop/nativeCommands.integration.test.tsx �[2m(�[22m�[2m14 tests�[22m�[2m)�[22m�[32m 298�[2mms�[22m�[39m
 �[32m✓�[39m src/components/AssigneeList.test.tsx �[2m(�[22m�[2m25 tests�[22m�[2m)�[22m�[33m 508�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskDetails/useTaskAssignment.test.tsx �[2m(�[22m�[2m11 tests�[22m�[2m)�[22m�[33m 908�[2mms�[22m�[39m
   �[32m✓�[39m AssignmentControl while saving �[2m(4)�[22m
     �[33m�[2m✓�[22m�[39m does not open the editor until the pending save settles�[33m 337�[2mms�[22m�[39m
�[90mstderr�[2m | src/components/QuickAddTodo.test.tsx�[2m > �[22m�[2mEscape in the toolbar popover�[2m > �[22m�[2mdismisses the popover opened by the native New To-do shortcut
�[22m�[39mAn update to QuickAddTodo 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 QuickAddTodo 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/QuickAddTodo.test.tsx�[2m > �[22m�[2mEscape in the toolbar popover�[2m > �[22m�[2mdismisses with nothing focused, but ignores Escape from controls outside it
�[22m�[39mAn update to QuickAddTodo 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 QuickAddTodo 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�[2mpreserves distinct migrated effort labels when editing and saving an Antigravity agent
�[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�[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

 �[32m✓�[39m src/components/QuickAddTodo.test.tsx �[2m(�[22m�[2m17 tests�[22m�[2m)�[22m�[33m 4323�[2mms�[22m�[39m
   �[32m✓�[39m QuickAddTodo popover �[2m(2)�[22m
     �[33m�[2m✓�[22m�[39m keeps the success confirmation and Add another available until dismissed�[33m 546�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m resets content and category, retains the submitted repository, focuses and submits again�[33m 347�[2mms�[22m�[39m
   �[33m�[2m✓�[22m�[39m uses the normal repository fallback: acme/gamma�[33m 365�[2mms�[22m�[39m
   �[33m�[2m✓�[22m�[39m uses the normal repository fallback: acme/alpha�[33m 307�[2mms�[22m�[39m
   �[33m�[2m✓�[22m�[39m uses the normal repository fallback: acme/gamma�[33m 324�[2mms�[22m�[39m
   �[33m�[2m✓�[22m�[39m ignores a category response from the previous to-do after Add another�[33m 325�[2mms�[22m�[39m
   �[32m✓�[39m Escape in the toolbar popover �[2m(7)�[22m
     �[33m�[2m✓�[22m�[39m closes an open category list before the popover�[33m 302�[2mms�[22m�[39m
�[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

�[90mstderr�[2m | src/pages/SettingsPage/AgentConfigModal.test.tsx�[2m > �[22m�[2moffers one Antigravity entry per model with model-specific reasoning choices
�[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�[2m9 tests�[22m�[2m)�[22m�[33m 1459�[2mms�[22m�[39m
   �[32m✓�[39m AgentConfigModal �[2m(8)�[22m
     �[33m�[2m✓�[22m�[39m preserves distinct migrated effort labels when editing and saving an Antigravity agent�[33m 315�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskDetails/ContextStrip.test.tsx �[2m(�[22m�[2m17 tests�[22m�[2m)�[22m�[33m 609�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskDetails/TaskVisualPreviews.test.tsx �[2m(�[22m�[2m8 tests�[22m�[2m)�[22m�[33m 680�[2mms�[22m�[39m
�[90mstderr�[2m | src/components/TaskDetails/ReviewScoreHistory.test.tsx�[2m > �[22m�[2musePullRequestScores�[2m > �[22m�[2mnever returns a previous PR's history for a new PR, while loading or after a failed read
�[22m�[39mAn update to TestComponent 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/TaskDetails/ReviewScoreHistory.test.tsx �[2m(�[22m�[2m10 tests�[22m�[2m)�[22m�[33m 392�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskDetails/LogFilesModal.test.tsx �[2m(�[22m�[2m5 tests�[22m�[2m)�[22m�[33m 686�[2mms�[22m�[39m
   �[32m✓�[39m LogFilesModal �[2m(3)�[22m
     �[33m�[2m✓�[22m�[39m searches literal metacharacters, counts what it highlights, and steps through matches�[33m 513�[2mms�[22m�[39m
�[90mstderr�[2m | src/components/TaskDetails/LatestRunCommands.projection.test.tsx�[2m > �[22m�[2mfollow-up commands read from the API task projection�[2m > �[22m�[2mthe task header keeps /fix for findings and sends it only after the projection is read again
�[22m�[39mAn update to FollowupCommands 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/TaskDetails/LatestRunCommands.projection.test.tsx �[2m(�[22m�[2m13 tests�[22m�[2m)�[22m�[32m 269�[2mms�[22m�[39m
 �[32m✓�[39m src/pages/PlanStudioPage.inPlaceDraft.test.tsx �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[33m 310�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskDetails/index.followupTarget.test.tsx �[2m(�[22m�[2m7 tests�[22m�[2m)�[22m�[33m 722�[2mms�[22m�[39m
   �[32m✓�[39m the follow-up dialog names the thread its submission posts to �[2m(7)�[22m
     �[33m�[2m✓�[22m�[39m an issue implementation that created a PR names that PR and submits that task ID�[33m 318�[2mms�[22m�[39m
 �[32m✓�[39m src/components/ApplicationShell.idle.test.tsx �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 80�[2mms�[22m�[39m
 �[32m✓�[39m src/components/ui/Skeleton.test.tsx �[2m(�[22m�[2m12 tests�[22m�[2m)�[22m�[33m 330�[2mms�[22m�[39m
 �[32m✓�[39m src/pages/SettingsPage/AgentRuntimePackagesSection.test.tsx �[2m(�[22m�[2m7 tests�[22m�[2m)�[22m�[33m 774�[2mms�[22m�[39m
   �[32m✓�[39m AgentRuntimePackagesSection �[2m(7)�[22m
     �[33m�[2m✓�[22m�[39m adds a package and submits the complete desired profile�[33m 308�[2mms�[22m�[39m
     �[33m�[2m✓�[22m�[39m offers catalog suggestions and validates a selected package�[33m 345�[2mms�[22m�[39m
 �[32m✓�[39m src/desktop-deep-link.test.ts �[2m(�[22m�[2m9 tests�[22m�[2m)�[22m�[32m 15�[2mms�[22m�[39m
 �[32m✓�[39m src/components/Goals/GoalAttentionPanel.test.tsx �[2m(�[22m�[2m5 tests�[22m�[2m)�[22m�[33m 416�[2mms�[22m�[39m
 �[32m✓�[39m src/pages/SettingsPage/NotificationSettingsSection.test.tsx �[2m(�[22m�[2m7 tests�[22m�[2m)�[22m�[33m 1233�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskDetails/FollowupModal.test.tsx �[2m(�[22m�[2m5 tests�[22m�[2m)�[22m�[33m 522�[2mms�[22m�[39m
 �[32m✓�[39m src/pages/SettingsPage/AgentNetworkSettingsSection.test.tsx �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[33m 404�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskPlanner/planDisplayName.test.ts �[2m(�[22m�[2m17 tests�[22m�[2m)�[22m�[32m 11�[2mms�[22m�[39m
 �[32m✓�[39m src/api/currentUserResponse.test.ts �[2m(�[22m�[2m5 tests�[22m�[2m)�[22m�[32m 15�[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�[2m3 tests�[22m�[2m)�[22m�[33m 480�[2mms�[22m�[39m
   �[32m✓�[39m LiveFileChips live refreshes �[2m(3)�[22m
     �[33m�[2m✓�[22m�[39m shows the shared directory once with 32 filenames ordered by changed lines and keeps every diff accessible�[33m 400�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskPlanner/PlanIssuesManager.notificationIntent.test.tsx �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[33m 610�[2mms�[22m�[39m
   �[32m✓�[39m PlanIssuesManager execution intent �[2m(3)�[22m
     �[33m�[2m✓�[22m�[39m cancel closes without starting implementation�[33m 349�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskList/hooks.lazyRoute.test.tsx �[2m(�[22m�[2m8 tests�[22m�[2m)�[22m�[33m 433�[2mms�[22m�[39m
 �[32m✓�[39m src/components/globalSearchModel.test.ts �[2m(�[22m�[2m7 tests�[22m�[2m)�[22m�[32m 14�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskPlanner/PlanEditor.notificationIntent.test.tsx �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[33m 674�[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 606�[2mms�[22m�[39m
 �[32m✓�[39m src/hooks/useCurrentUserBootstrap.mainProxy.test.tsx �[2m(�[22m�[2m1 test�[22m�[2m)�[22m�[32m 80�[2mms�[22m�[39m
 �[32m✓�[39m src/components/RepositoryAutoAssignControl.test.tsx �[2m(�[22m�[2m6 tests�[22m�[2m)�[22m�[33m 326�[2mms�[22m�[39m
 �[32m✓�[39m src/desktop/packagedAcceptanceRendererLifecycle.test.ts �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 11�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskPlanner/TaskCard.test.tsx �[2m(�[22m�[2m6 tests�[22m�[2m)�[22m�[33m 663�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskPlanner/planIssueDefaultSelection.test.ts �[2m(�[22m�[2m5 tests�[22m�[2m)�[22m�[32m 7�[2mms�[22m�[39m
 �[32m✓�[39m src/pages/SettingsPage/AgentWatchdogSettingsSection.test.tsx �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[32m 111�[2mms�[22m�[39m
�[90mstdout�[2m | src/components/TaskDetails/useTaskData.completion.test.ts�[2m > �[22m�[2mreplaces the live view of a subscribed client with persisted details when the task completes
�[22m�[39m[useTaskData] Received task update via WebSocket: { taskId: �[32m'task'�[39m, state: �[32m'COMPLETED'�[39m }

 �[32m✓�[39m src/components/TaskDetails/useTaskData.completion.test.ts �[2m(�[22m�[2m1 test�[22m�[2m)�[22m�[32m 29�[2mms�[22m�[39m
 �[32m✓�[39m src/api/dashboardApi.test.ts �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 19�[2mms�[22m�[39m
 �[32m✓�[39m src/pages/SettingsPage/VoiceSettingsSection.test.tsx �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[33m 358�[2mms�[22m�[39m
 �[32m✓�[39m src/desktop/DesktopExperience.accounts.test.tsx �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[33m 489�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskPlanner/generationTelemetryUtils.test.ts �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[32m 7�[2mms�[22m�[39m
 �[32m✓�[39m src/components/UserAvatar.test.tsx �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 247�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskDetails/PromptModal.test.tsx �[2m(�[22m�[2m1 test�[22m�[2m)�[22m�[33m 306�[2mms�[22m�[39m
   �[32m✓�[39m PromptModal �[2m(1)�[22m
     �[33m�[2m✓�[22m�[39m is a named modal dialog with a labelled close, Tab containment and focus return�[33m 302�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskPlanner/setupWizardHooks.editMode.test.tsx �[2m(�[22m�[2m1 test�[22m�[2m)�[22m�[32m 24�[2mms�[22m�[39m
�[90mstderr�[2m | src/components/RepositoryAutoResolveConflictsControl.test.tsx�[2m > �[22m�[2mRepositoryAutoResolveConflictsControl�[2m > �[22m�[2mreflects a stored override
�[22m�[39mAn update to RepositoryAutoResolveConflictsControl 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/RepositoryAutoResolveConflictsControl.test.tsx�[2m > �[22m�[2mRepositoryAutoResolveConflictsControl�[2m > �[22m�[2msets always, never, and inherit (null)
�[22m�[39mAn update to RepositoryAutoResolveConflictsControl 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/RepositoryAutoResolveConflictsControl.test.tsx �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 262�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskPlanner/ContextLevelSlider.test.tsx �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[32m 64�[2mms�[22m�[39m
 �[32m✓�[39m src/components/modalShells.test.tsx �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 249�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskList/durations.test.ts �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[32m 6�[2mms�[22m�[39m
 �[32m✓�[39m src/components/RouteChunkErrorBoundary.test.tsx �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 239�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskPlanner/PlanEditorDesktopLayout.assistant.test.tsx �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 293�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskPlanner/useSelectedModelMaxTokens.test.ts �[2m(�[22m�[2m5 tests�[22m�[2m)�[22m�[32m 75�[2mms�[22m�[39m
 �[32m✓�[39m src/desktop/scopedTaskEvents.test.ts �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 9�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskPlanner/setupWizardDraftConfig.test.ts �[2m(�[22m�[2m9 tests�[22m�[2m)�[22m�[32m 11�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskList/Pagination.test.tsx �[2m(�[22m�[2m6 tests�[22m�[2m)�[22m�[32m 260�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskPlanner/TaskCardList.breakpoint.test.tsx �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 248�[2mms�[22m�[39m
 �[32m✓�[39m src/hooks/repositoryNonBlockingChecks.test.ts �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 9�[2mms�[22m�[39m
 �[32m✓�[39m src/api/automationRunTrigger.test.ts �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 11�[2mms�[22m�[39m
 �[32m✓�[39m src/components/Agents/AgentAutonomySection.test.tsx �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 257�[2mms�[22m�[39m
 �[32m✓�[39m src/pages/InboxPageComponents.test.tsx �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[32m 203�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskDetails/syntaxHighlighter.test.ts �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 13�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskDetails/taskKind.test.ts �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 9�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskDetails/AssignmentControl.test.tsx �[2m(�[22m�[2m1 test�[22m�[2m)�[22m�[32m 252�[2mms�[22m�[39m
 �[32m✓�[39m src/api/proprApi.instanceCatalog.test.ts �[2m(�[22m�[2m1 test�[22m�[2m)�[22m�[32m 10�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskPlanner/executionConfigSummary.test.ts �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 6�[2mms�[22m�[39m
 �[32m✓�[39m src/components/Dashboard/KPICard.test.tsx �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 58�[2mms�[22m�[39m
 �[32m✓�[39m src/components/Repositories/RepoBrowsePanel.test.tsx �[2m(�[22m�[2m1 test�[22m�[2m)�[22m�[32m 38�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskPlanner/RefinementChat.focus.test.tsx �[2m(�[22m�[2m1 test�[22m�[2m)�[22m�[32m 209�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskPlanner/tokenFormat.test.ts �[2m(�[22m�[2m1 test�[22m�[2m)�[22m�[32m 5�[2mms�[22m�[39m
 �[32m✓�[39m src/components/TaskDetails/ExecutionEventUtils.test.ts �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 5�[2mms�[22m�[39m

�[2m Test Files �[22m �[1m�[32m83 passed�[39m�[22m�[90m (83)�[39m
�[2m      Tests �[22m �[1m�[32m662 passed�[39m�[22m�[90m (662)�[39m
�[2m   Start at �[22m 22:33:17
�[2m   Duration �[22m 83.99s�[2m (environment 43%, tests 33%, import 11%, setup 9%, transform 5%)�[22m

�[2mEnvironment �[22m �[33mjsdom was created 83 times�[39m�[2m · 67.23s total, 43% 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.941348
  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: 69.931499
  type: 'test'
  ...
1..2
# tests 2
# suites 0
# pass 2
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 128.071459
[232/232] passed propr-ui#1/4 (workspace test script) in 84.8s

### Shard 3/4: 231/232 passed in 1044.3s

Assigned 232 of 930 discovered units.

| Duration | Status | Unit |
| ---: | --- | --- |
| 156.9s | passed | `test/taskIntentWithdrawal.test.ts` |
| 84.8s | passed | `propr-ui#1/4` (workspace) |
| 54.2s | passed | `test/agentRunScheduler.test.ts` |
| 33.7s | passed | `packages/api/test/agentDefinitionRoutes.test.ts` |
| 32.6s | passed | `apps/desktop/scripts/rpm-signing.test.mjs` |
| 32.1s | passed | `packages/core/test/managedDiagnostics.test.ts` |
| 31.9s | passed | `packages/api/test/mcpPublishPlan.test.ts` |
| 29.1s | passed | `apps/desktop/scripts/signing-key-verification.test.mjs` |
| 27.8s | passed | `packages/api/test/planRevisions.test.ts` |
| 24.0s | passed | `packages/api/test/taskFollowupIssueQueueRecoveryHistory.test.ts` |
| 23.6s | passed | `test/agentAutonomy.test.ts` |
| 22.8s | passed | `packages/core/test/instanceStorage.test.ts` |
| 17.3s | passed | `packages/core/test/notificationService.test.ts` |
| 15.9s | passed | `test/agentRunTrigger.test.ts` |
| 14.7s | passed | `test/connectCliIntegration.test.ts` |

Passing units at or above 60% of the 180.0s per-unit timeout. Split or speed these up before they fail:

- `test/taskIntentWithdrawal.test.ts` used 87% (156.9s)

::warning title=Test unit near the per-unit timeout::test/taskIntentWithdrawal.test.ts used 87% (156.9s) of the 180.0s per-unit timeout

1/232 test runs failed in shard 3/4 after 1044.3s:
- test/prPublicationRecoveryJob.test.ts: exit 1

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

View Workflow
View uploaded artifacts

@propr-dev

propr-dev Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor Author

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

Summary of Changes

I fixed the Shard 3/4 failure. It wasn't caused by this PR's logger or lockfile changes; it was a test mock that fell behind a commit already on main.

Cause: test/prPublicationRecoveryJob.test.ts crashed while loading, so none of its tests ran:

SyntaxError: The requested module './prCommentCollisionRecovery.js' does not provide an export named 'delayPRCommentJobInPlace'

Commit f5e35100f ("wait in place behind a blocked predecessor follow-up handoff") is in this branch's base. It made src/jobs/prCommentHandoffIntegration.ts import two new functions from prCommentCollisionRecovery.js: delayPRCommentJobInPlace and predecessorWaitDelay. This suite replaces that module with a mock that lists each export by name, and the list didn't include the two new ones. The suite loads the real handoff module, so the import failed.

Fix: In test/prPublicationRecoveryJob.test.ts I added the two missing exports to the mock:

  • delayPRCommentJobInPlace: async () => false
  • predecessorWaitDelay: () => 60_000

Returning false means "can't wait in place". The code then falls back to scheduling a replacement job, which this suite already mocks, so the suite tests the same behaviour as before. That one test file is the only change.

Checks:

  • test/prPublicationRecoveryJob.test.ts: 39 passed, 0 failed.
  • The unchanged copy from HEAD still fails with the same SyntaxError, so the fix is what makes the difference.
  • The two other suites that mock this module still pass: prPublicationJob 29/29 and prCommentCollisionRecovery 12/12.

This is a test-only change with nothing visual, so there are no previews.


🤖 Implementation Details

  • Model: Claude Opus 5.5 (medium)
  • Requested By: @propr-dev[bot]
  • Time: 3m 11s
  • Tokens: 520,637 (517,298 in / 3,339 out)
  • Cost: $0.37

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 Address review blockers, or blockers and suggestions with /fix all /fix, /fix all, or /fix F3 S5
/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: 6352800921✓

…s logger or lockfile changes; it was a test mock that fell behind a commit already on main.

I fixed the Shard 3/4 failure. It wasn't caused by this PR's logger or lockfile changes; it was a test mock that fell behind a commit already on main.

**Cause:** `test/prPublicationRecoveryJob.test.ts` crashed while loading, so none of its tests ran:
```
SyntaxError: The requested module './prCommentCollisionRecovery.js' does not provide an export named 'delayPRCommentJobInPlace'
```
Commit `f5e35100f` ("wait in place behind a blocked predecessor follow-up handoff") is in this branch's base. It made `src/jobs/prCommentHandoffIntegration.ts` import two new functions from `prCommentCollisionRecovery.js`: `delayPRCommentJobInPlace` and `predecessorWaitDelay`. This suite replaces that module with a mock that lists each export by name, and the list didn't include the two new ones. The suite loads the real handoff module, so the import failed.

**Fix:** In `test/prPublicationRecoveryJob.test.ts` I added the two missing exports to the mock:
- `delayPRCommentJobInPlace: async () => false`
- `predecessorWaitDelay: () => 60_000`

Returning `false` means "can't wait in place". The code then falls back to scheduling a replacement job, which this suite already mocks, so the suite tests the same behaviour as before. That one test file is the only change.

**Checks:**
- `test/prPublicationRecoveryJob.test.ts`: 39 passed, 0 failed.
- The unchanged copy from HEAD still fails with the same SyntaxError, so the fix is what makes the difference.
- The two other suites that mock this module still pass: `prPublicationJob` 29/29 and `prCommentCollisionRecovery` 12/12.

This is a test-only change with nothing visual, so there are no previews.

PR: #3321
Comment by: @propr-dev[bot] (ID: 6352800921)
Model: Claude Opus 5.5
@propr-dev

propr-dev Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor Author

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

Analysis Summary

No new change was needed: the current branch head 3d3b36b already fixes this failure. CI failed on 8c5007c, the commit before it.

Cause: test/prPublicationRecoveryJob.test.ts crashed while loading, so none of its tests ran. Commit f5e35100f on main, which this branch is based on, made src/jobs/prCommentHandoffIntegration.ts import two new functions from prCommentCollisionRecovery.js: delayPRCommentJobInPlace and predecessorWaitDelay. This suite replaces that module with a mock that lists each export by name, and the list didn't include the two new ones, so the import failed. The PR's logger and lockfile changes had nothing to do with it.

Fix (already on the branch): 3d3b36b adds the two missing exports to the mock at test/prPublicationRecoveryJob.test.ts:309. delayPRCommentJobInPlace returns false, so the code falls back to scheduling a replacement job, which the suite already mocks. That one test file is the only change.

Checked at the current head:

  • Run through the CI test runner (scripts/run-test-suite.mjs), test/prPublicationRecoveryJob.test.ts passes: 39 passed, 0 failed.
  • The other two suites that mock this module also pass: prPublicationJob (29/29) and prCommentCollisionRecovery (12/12). No other test suite mocks it.
  • Running the recovery suite directly with node --test, outside the CI runner, it reports 1 failed test. I didn't look into why. That isn't how CI runs it, and the CI-runner run of the same file passed.

A new CI run on 3d3b36b should clear the Shard 3/4 failure. Nothing here changes what users see, so there are no previews.

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


🤖 Analysis Details

  • Model: Claude Opus 5.5 (medium)
  • Time: 2m 56s
  • Tokens: 470,518 (468,529 in / 1,989 out)
  • Cost: $0.42

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 Address review blockers, or blockers and suggestions with /fix all /fix, /fix all, or /fix F3 S5
/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: 6352803060✓, 6352803459✓

@integry

integry commented Oct 22, 2026

Copy link
Copy Markdown
Owner

/review gpt-6-astra

Review exact head3d3b36b5abc1e630d17cf505cfda4244585e3171. The only delta from your reviewed8c5007c is in test/prPublicationRecoveryJob.test.ts: its mock now exports delayPRCommentJobInPlace and predecessorWaitDelay, matching the real import graph that failed in full-suite shard3. Production logger and dependency fix are unchanged. Independently verify the whole final PR remains scoped and the test change does not weaken recovery assertions or hide real failures. Root logger13cases and runtime/packaging audits passed at identical product sources; exact-head CI and root fixture validation remain required. Do not merge or publish.

@propr-dev

propr-dev Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor Author

✅ AI Code Review Complete requested by @integry

Posted 1 review:

View Task Details

@propr-dev

propr-dev Bot commented Oct 22, 2026

Copy link
Copy Markdown
Contributor Author

🔍 AI Code Review — gpt-6-astra

Overall Evaluation

The PR is scoped and appears merge-ready within the supplied code, conditional on exact-head CI and root fixture validation completing successfully. No verified code blocker was found.

✅ Independent output sinks — Console exceptions are contained before scheduling file writes. The fallback diagnostic is also guarded, allowing the promise chain to recover when both sinks fail.

✅ Preserved logging protections — Record sanitization, serialized file writes, private creation permissions, and fixed failure diagnostics remain intact. New tests cover synchronous ENOSPC/EPIPE failures across all levels.

✅ Narrow fixture repair — The recovery mock adds two missing exports without changing assertions or production recovery logic. Returning false matches the real helper’s fallback contract for jobs that cannot delay in place; it does not simulate successful recovery.

This was a static review of the supplied diff and context; no commands or tests were run. The complete recovery fixture was not supplied, so its runtime behavior remains unverified here. The issue specification is explicitly truncated; conclusions cover the visible requirements. Current checks show no failures, with 13 pending.

Merge blockers

No merge blockers.

Suggestions

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

S3: 🟢 Exercise actual append recovery

In apps/desktop/src/logger-sinks.test.ts, the “failed append” test actually fails at mkdir because the parent path is a regular file. It meaningfully exercises the shared rejection handler. An optional additional test could let directory creation succeed, force appendFile to fail, then restore the target and verify a later record persists. This would retain automated coverage of the separately reported append-recovery probe; the current shared catch path provides no demonstrated blocker.

Score

The implementation is localized, preserves existing security behavior, and adds meaningful failure-path coverage. Merge readiness remains conditional on the pending exact-head checks and fixture validation.

Score: 8/10


🤖 Review Details

  • Model: GPT-6 Astra
  • Time: 28s
  • Tokens: 52,398 (51,864 in / 534 out)
  • Cost: $0.44

View Task


💡 Next step: Comment /fix to address every F# merge blocker, /fix all for every pending blocker and suggestion, 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 or request /fix all, and they never relax a merge blocker.

Copy and edit to choose records from this review:

/fix S3

🤖 Review by ProPR

@integry
integry merged commit 960a437 into main Oct 22, 2026
64 of 96 checks passed
propr-dev Bot pushed a commit that referenced this pull request Oct 22, 2026
…ally (39/39). Two neighbouring test files that use the same module also pass. I didn't re-run the rest of the shard.

I fixed the shard 2/4 failure: the test file now passes locally (39/39). Two neighbouring test files that use the same module also pass. I didn't re-run the rest of the shard.

**Cause.** Only `test/prPublicationRecoveryJob.test.ts` failed in shard 2. It didn't fail on an assertion; the test crashed on load because of a missing import:

```
SyntaxError: The requested module './prCommentCollisionRecovery.js' does not provide an export named 'delayPRCommentJobInPlace'
```

The jobs fix f5e3510, which this branch includes, made `src/jobs/prCommentHandoffIntegration.ts` import two new functions, `delayPRCommentJobInPlace` and `predecessorWaitDelay`. This test replaces that module with a stand-in that lists every function by name, and those two weren't on the list. That commit isn't part of the planner fix, so this failure comes from the base the branch was cut from, not from the F1/S1 changes.

**Fix.** I added the two missing functions to the stand-in at `test/prPublicationRecoveryJob.test.ts:309`, copying the change main already made in #3321 (960a437). The branch was cut before that commit, so it didn't have it.

No product code changed and nothing visible changed, so there are no previews.

PR: #3325
Comment by: @propr-dev[bot] (ID: 6353859686)
Model: Claude Opus 5.5
integry pushed a commit that referenced this pull request Oct 23, 2026
…erver but studio remains on Define (#3325)

* I reproduced the bug in tests and fixed it. A fresh plan now goes Define → Generating → Review without a reload or a second generation. I haven't tried the fix in the installed native app, and the independent Astra review and real native/mobile check you asked for still need to happen before merge.

Cause. The bug shows up when Generate is clicked before the autosave delay finishes:
1. Clicking Generate creates the draft in place, so the page starts its own `GET` of that draft.
2. The server takes seconds to reply to `POST /generate`, because it prepares the repository before marking the draft `generating`. The page's `GET` comes back first, with `status: draft`.
3. When `/generate` is acknowledged, `useDraftCreation` navigates to `/studio/:id` and puts the generating draft in the router state. But in `App.tsx` the two studio routes render the same component tree, so `PlanStudioPage` is not remounted. Its `useDraft` still holds the `draft` copy and ignores the router state.
4. Nothing ever tells the page about the new run, so it doesn't subscribe for progress and stays on Define with the prompt.

My first Playwright mock answered `/generate` instantly, and the test passed on the broken code. It only failed once the mock held the reply until after the draft read, which is how the real server behaves.

Fix:
- `useDraftCreation.ts`: after `/generate` is acknowledged and before navigating, it hands the run ID and the generating draft to the page, the same way the existing-draft Generate path already did. Navigation still replaces `/studio/new`, so back/forward is unchanged.
- `useDraft.ts`:
  - It accepts that generating draft even if its first fetch hasn't returned yet.
  - A fetch started before the run was acknowledged can no longer overwrite it; fetches started afterwards still win.
  - Reopening a page from history or a reload with a generating snapshot triggers one background refresh, so a finished plan isn't shown as still generating. It's a single fetch, not polling.
- `SetupWizard.tsx`, `setupWizardHooks.ts`, `PlanStudioPage.tsx`: pass the draft along with the run ID.
- Run-ID checks on live updates are unchanged: an update from a different run still can't complete this one.

Tests:
- `PlanStudioPage.freshGenerate.test.tsx` (new): uses the real page, routing, `useDraft` and `SetupWizard`, with create, generate and the draft fetch each held and released by the test. It covers web and desktop/hash routing; the stale read arriving before and after the acknowledgement; ignoring an update from another run; then Review. It also checks one draft and one generation, the URL, back/forward, prompt and focus after autosave, and the prompt kept for retry when Generate fails. All four race cases fail on the original code and pass now.
- `useDraft.test.tsx`: three new unit tests for the behaviour above.
- `e2e/planner-fresh-generate.pw.ts` (new): Playwright at 320px, 390px, 1440px, and the native minimum window (880×620 with the Linux titlebar). It fails on the original code in all four layouts and passes now. These Playwright runs use the web router; the desktop hash routing is only covered by the vitest test.
- One existing assertion in `setupWizardHooks.test.tsx` was updated for the extra argument.
- Results:
  - Full UI unit suite: 3053 of 3054 passed. The one failure was that outdated assertion; after updating it, those files and the planner/page suites pass (927 tests).
  - 58 related planner Playwright tests pass.
  - `tsc` and `eslint --max-warnings 0` are clean.

Preview screenshots of the Generating and Review states at 390px and in the native minimum window are attached.

* feat(ai): I fixed F1 (a late acknowledgement from one draft could take over another draft) and made the browser test's ordering explicit (S1). The full UI unit suite passes (3058/3058), `tsc` and `eslint --max-warnings 0` are clean, and the fresh-generate Playwright test passes in all 4 layouts. I haven't tried it in the installed native app.

I fixed F1 (a late acknowledgement from one draft could take over another draft) and made the browser test's ordering explicit (S1). The full UI unit suite passes (3058/3058), `tsc` and `eslint --max-warnings 0` are clean, and the fresh-generate Playwright test passes in all 4 layouts. I haven't tried it in the installed native app.

**F1 fix**
- **`useDraft.ts`:** `activateGenerationRun` now requires the ID of the draft the run was started for: `(runId, draftId, snapshot?)`. If that ID isn't the draft currently shown, or a snapshot belongs to another draft, it returns before touching any run ID, draft, fetch counter, error or loading state. This applies whether or not a snapshot is passed.
- **`setupWizardHooks.ts`:** the same mistaken assumption was in the existing-draft Generate handler. The wizard stays mounted when you go from `/studio/A` to `/studio/B`, so A's late acknowledgement used to call `startPolling(runA)` while B was on screen. B's own updates would then be rejected for having the wrong run ID. The handler now checks which draft is shown before handing off the run or polling.
- **Callers:** `useDraftCreation.ts`, `SetupWizard.tsx` and `PlanStudioPage.tsx` pass the new draft-ID argument.

**Tests**
- The old mismatched-snapshot test, which expected A's run ID to be applied to the other draft, is replaced. The new one requires status and run ID to stay unchanged, both with a foreign snapshot and with no snapshot.
- New hook test for A → B while A's acknowledgement is pending. A's late acknowledgement arrives twice, once while B's fetch is still running and once after it finishes. B keeps its own status and run ID both times, and still accepts its own live updates.
- New wizard-handler test: switching to B before A's acknowledgement means neither the activation callback nor polling runs.
- With the fixes temporarily removed, all three new tests fail.

**S1, `e2e/planner-fresh-generate.pw.ts`**
- The five-second fallback timer is gone. The page's reads now return a name the create response doesn't have, and the generate route holds the acknowledgement until the page's title shows that name. That proves React has rendered the pre-generation read first.
- The test asserts that this happened, so a missing read now fails with a clear message instead of passing by timing.
- Against the pre-PR source, the test fails at "Generating visible" in all layouts.

No preview images: nothing visible changed.

**Possible separate issue (not changed, not tested):** in `useDraftCreation.ts`, the fresh-plan flow still does `navigate('/studio/<id>', { replace: true })` after the acknowledgement arrives. If the user leaves `/studio/new` while Generate is pending, it may send them back to the new draft. That code came before this PR and is outside F1.

PR: #3325
Comment by: @integry (ID: 6353387716)
Model: Claude Opus 5.5

* feat(ai): I fixed the shard 2/4 failure: the test file now passes locally (39/39). Two neighbouring test files that use the same module also pass. I didn't re-run the rest of the shard.

I fixed the shard 2/4 failure: the test file now passes locally (39/39). Two neighbouring test files that use the same module also pass. I didn't re-run the rest of the shard.

**Cause.** Only `test/prPublicationRecoveryJob.test.ts` failed in shard 2. It didn't fail on an assertion; the test crashed on load because of a missing import:

```
SyntaxError: The requested module './prCommentCollisionRecovery.js' does not provide an export named 'delayPRCommentJobInPlace'
```

The jobs fix f5e3510, which this branch includes, made `src/jobs/prCommentHandoffIntegration.ts` import two new functions, `delayPRCommentJobInPlace` and `predecessorWaitDelay`. This test replaces that module with a stand-in that lists every function by name, and those two weren't on the list. That commit isn't part of the planner fix, so this failure comes from the base the branch was cut from, not from the F1/S1 changes.

**Fix.** I added the two missing functions to the stand-in at `test/prPublicationRecoveryJob.test.ts:309`, copying the change main already made in #3321 (960a437). The branch was cut before that commit, so it didn't have it.

No product code changed and nothing visible changed, so there are no previews.

PR: #3325
Comment by: @propr-dev[bot] (ID: 6353859686)
Model: Claude Opus 5.5

* feat(ai): I fixed S3. If the user leaves a fresh plan while its generation acknowledgement is still pending, the late acknowledgement no longer navigates, replaces their history entry or touches another draft. Generation isn't cancelled: the run stays on the server, and reopening the draft shows Generating and then Review, with one create and one generation. The new tests pass in real Chromium for all four layouts. I haven't tried it in the installed native app.

I fixed S3. If the user leaves a fresh plan while its generation acknowledgement is still pending, the late acknowledgement no longer navigates, replaces their history entry or touches another draft. Generation isn't cancelled: the run stays on the server, and reopening the draft shows Generating and then Review, with one create and one generation. The new tests pass in real Chromium for all four layouts. I haven't tried it in the installed native app.

**What was wrong.** After the generate response arrived, the fresh-plan flow always called `navigate('/studio/<id>', { replace: true })`, even if the user had left. My first fix checked the router's location and whether the page was still mounted. It passed the unit tests but failed in the real browser. In the production build, Back loads the `/plans` page in the background before switching, so for a moment the studio is still mounted and the router still reports the old location. The browser's history has already moved by then.

**The fix.**
- **New `useHistoryEntryOwnership` hook** (`propr-ui/src/hooks/useHistoryEntryOwnership.ts`): it records which browser history entry the user is on when work starts. Later, the work only counts as "still here" if the page is mounted and the browser is on that same entry. It reads the browser's history, not React state.
- **Autosave vs. leaving:** the autosave URL change from `/studio/new` to `/studio/<id>` keeps the same history entry, so it still counts as "still here". Back, Forward, opening another draft or opening another page all fail the check.
- **`useDraftCreation.ts`:** the check runs right after `generatePlan` returns. If the user has left, it skips the run hand-off and the navigation. The request is never cancelled or repeated.
- **`PlanStudioPage.tsx` (same defect, one step earlier):** if the user left while the draft was still being created (`POST /drafts` pending), the in-place URL update overwrote whatever entry they were on, e.g. `/plans` became `/studio/<id>`. It now uses the same check. This path wasn't in your reproduction; I fixed it because it's the same flow and the same defect.

**Tests**
- **Hook tests (`useDraftCreation.test.tsx`):** the acknowledgement is dropped both when the wizard unmounts and when it stays mounted but the browser has moved to another entry. The test harness now performs the real in-place URL update, so the existing tests prove the autosave path still navigates.
- **Page tests (`PlanStudioPage.freshGenerate.test.tsx`)**, run under both BrowserRouter and native HashRouter:
  - Back, then the acknowledgement arrives: `/plans` stays, with no new history entry.
  - Opening another existing draft while the acknowledgement is pending: that draft is untouched and isn't shown as Generating.
  - Back while the draft is still being created: the `/plans` entry isn't rewritten.

  Each case then reopens the generated draft and checks Generating → Review with one create and one generation.
- **Playwright (`planner-fresh-generate.pw.ts`):** your reproduction in all four layouts, then Forward to reopen and complete the run. These four failed against my first fix and pass now. The original four fresh-generate cases still pass.

**Results**
- Full UI unit suite: 3066/3066.
- `tsc --noEmit` and `eslint --max-warnings 0` on `propr-ui`: clean.
- Playwright fresh-generate file: 8/8.
- With either check disabled, the new tests fail.

HashRouter is covered by the jsdom page tests only, because the Playwright web build uses BrowserRouter. The `native-minimum` layout is still a BrowserRouter page with the native window frame added.

I didn't create previews, because the result is the user staying on the page they chose, which a still image can't show. Permission, credential, provider and repository settings are unchanged.

PR: #3325
Comment by: @integry (ID: 6354022697)
Model: Claude Opus 5.5

* feat(ai): I fixed F2. A late read for draft A — whether it succeeds, fails, or finishes loading — can no longer change draft B, its active run, or a newer read of A after you come back to it.

I fixed F2. A late read for draft A — whether it succeeds, fails, or finishes loading — can no longer change draft B, its active run, or a newer read of A after you come back to it.

**Fix** (`propr-ui/src/hooks/useDraft.ts`):
- Each read of a draft now gets a sequence number. Changing `draftId` or unmounting invalidates every read still in flight.
- A read only updates the draft, the active run ID or the error if it is still the newest read and its draft is still selected. That second check is what covers A → B → A: the ID matches again, but the old read is no longer the newest.
- Loading is cleared by the newest read for the current draft, so a late A read can't end B's loading state. If you go to `/studio/new` (nothing selected), an abandoned read still clears loading, as it did before.
- `fetchDraft` won't start for a draft that is no longer selected. Otherwise a `refetch` callback held from A's render could start an A read after switching to B, and that read would outrank B's.
- The existing guard that stops a pre-acknowledgement read from replacing a just-started run is unchanged. The draft/run checks on the acknowledgement path (F1) and the history-entry navigation checks (S3) aren't touched. Nothing cancels or repeats a generation request.

One behaviour change for the same draft: if two reads overlap, only the newest result is used. Before, whichever finished last won.

**New tests** (`propr-ui/src/hooks/useDraft.selection.test.tsx`, 7 cases). They live in their own file because adding them to `useDraft.test.tsx` broke the 400-line lint limit. Each starts by reopening A from a history entry whose saved state says "generating", with A's background read held open:
- B loads, then A's read resolves: B and its run stay selected, B's live updates are still accepted and updates carrying A's run ID are ignored.
- A's read resolves while B is still loading: B stays loading, then B is shown.
- A's read fails while B is loading: no error appears on B and B stays loading.
- A → B → A, then the obsolete A read resolves or fails (two cases): A's newer result stays.
- A → B → A, where the obsolete read finishes before the new A read: loading stays on until the new read lands.
- A `refetch` held from A's render is called after switching to B: no read starts and B stays.

**Verification:**
- All 7 new tests fail against the previous `useDraft.ts` and pass with the fix.
- `useDraft.test.tsx` is byte-identical to HEAD.
- Full UI unit suite passes (3073/3073). I ran it before moving the tests into their own file; after the move, `useDraft*`, `PlanStudioPage*` and `TaskPlanner` pass (17 and 299 tests). `tsc --noEmit` and `eslint --max-warnings 0` on `src/hooks/` are clean.
- `planner-fresh-generate.pw.ts` passes in real Chromium in all 4 layouts (8/8), covering the original fresh-generation flow and leaving before the acknowledgement arrives.

I haven't tried it in the installed native app; your independent diagnostic and signed-native validation are still separate. I made no preview images, since nothing visible changes beyond the wrong-draft state being gone.

PR: #3325
Comment by: @integry (ID: 6354464426)
Model: Claude Opus 5.5

---------

Co-authored-by: propr-dev[bot] <1316198+propr-dev[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Desktop logger output failure can block the app with an uncaught main-process error

1 participant