Skip to content

fix: CI fails intermittently on loaded runners, and oversized A2UI SVGs freeze the UI - #2019

Open
larroy wants to merge 6 commits into
openclaw:mainfrom
larroy:fix/ci-flaky-tests-2026-11-04
Open

larroy wants to merge 6 commits into
openclaw:mainfrom
larroy:fix/ci-flaky-tests-2026-11-04

Conversation

@larroy

@larroy larroy commented Nov 5, 2026

Copy link
Copy Markdown
Collaborator

Related: #2000, #1981

What Problem This Solves

Fixes: Build and Test fails on unrelated PRs and main pushes when hosted runners are loaded. Separately, a pathological A2UI SVG can freeze the chat UI thread for longer than the 8 s render cap.

User Impact

User impact: CI stops failing intermittently on four tests that were timing-sensitive. An A2UI SVG with more than 64 KiB of path data now shows the broken-image fallback immediately instead of freezing the UI. Ordinary SVGs are unaffected: the largest SVG still allowed decodes in about 0.2–0.3 s locally.

Why This Change Was Made

Each failure was traced to a cause before any test was changed, and none of the assertions were loosened:

  • ChatComposerControllerTests.Dispose_VoiceCancellationCallbackCanWaitForReentrantControllerWork: the test blocked shared thread-pool threads that its own cleanup work needed. It now uses dedicated threads, the same fix the sibling lifetime test already had.
  • ActivationRouterTests.StopAsync_CancelsAndDrainsInFlightDirectConfirmation: hard-coded 2 s waits now use a named constant (default 10 s, OPENCLAW_TRAY_TEST_ACTIVATION_WAIT_SECONDS).
  • GatewayProtocolLiveRoundTripTests.HandshakeGate_Reconnect_SuppressesEarlyMutationThenSendsAfterHelloOk: polling could miss the roughly 1 s window before the client reconnects. The test now waits for the Disconnected event, and the mock server holds the reconnect by not sending the challenge on the second socket until the test sends it.
  • GatewayFixtureUiTests.HomeSidebar_CustomizeDoesNotNavigate_AndHomeRestoresCanonicalConversation: the sidebar geometry was read before layout settled after unpinning. The test now waits for the settled layout and keeps the same pixel ranges.
  • SVG (product): SvgImageSource.SetSourceAsync can block the UI thread past SvgRenderTimeout, because the timeout stops only the wait, not the parse. The new SvgGeometryBudget rejects oversized d/points path data before decode.
  • Tray UI testhost crash (ReactorChatLayoutProofTests flyout tests): diagnostics only. When Tray UI tests fail, a new CI step saves the crash entries from the Windows event log (crashing module, exception code, offset; no memory dumps) to TrayUI/wer-testhost-events.txt in the test-results-ui artifact. The test was not reordered: closing the flyout first would remove the live-update behavior that SandboxFlyout_* tests.
  • HistoricalFractionalVoiceConversationRendersWithoutLoadingPlaceholder: not changed here; improve(workspace): share one footer with Dashboard and notifications across Chat and Settings #1996 fixed it on main after these failing runs.

docs/CI_FAILURES.md now covers the crash investigation (adds SandboxFlyout_*, the run links, and the new crash-records file), notes the SVG fix, and adds an entry for the cluster of UI Automation "Operation timed out" errors in run 41786895448.

Evidence

  • Composer dispose test. Throwaway repro with the thread pool saturated: the old pattern still hadn't finished after 10 s; the dedicated-thread pattern finished at once. After the fix, 30/30 passes (together with the ActivationRouter tests).
  • Handshake reconnect test. 30/30 passes of the GatewayProtocolLiveRoundTripTests class with all 32 CPUs busy (24 tests per run). The trace shows socket #2 accepted; challenge delivered.
  • SVG. PathologicalGeometry_RejectedBeforeDecode finishes in 30–42 ms (5/5 runs of A2UISvgTests). The largest SVG still allowed (3,560 path segments, 65,608 bytes) decodes in 204–319 ms.
  • Sidebar test. The first 10 local runs went 6/10. The four failures were at other assertions (a "customize flyout" timeout and an Assert.Equal), not the new geometry wait. The next 16 runs all passed.
  • Fractional voice test. 10/10 locally on current main (unchanged by this PR).

Change Type

  • Bug fix
  • Feature
  • Refactor
  • Docs or instructions
  • Tests or validation
  • Security hardening
  • Chore or infrastructure

Scope

  • Tray or WinUI UX
  • Windows node capability
  • Local MCP or winnode
  • Gateway, connection, or pairing
  • Setup or onboarding
  • Permissions, privacy, or security
  • Tests, CI, or docs

Required proof pools

  • none: the changes are test stabilization, a pure SVG input guard covered by UI tests and unit tests, and a CI diagnostic step. No custom Windows proof pool is needed.

Validation

  • ./build.ps1: all builds succeeded.
  • dotnet test ./tests/OpenClaw.Tray.Tests/OpenClaw.Tray.Tests.csproj: 5070/5070 passed.
  • dotnet test ./tests/OpenClaw.Shared.Tests/OpenClaw.Shared.Tests.csproj (with OPENCLAW_REPO_ROOT set): 4927 passed, 1 failed, 24 skipped.
    • The failure is MarkdownParserFuzzTests.Build_ManyListItems_ScalingCurve, a timing limit (642 ms against 500 ms) that this PR does not touch; it is already in docs/CI_FAILURES.md. It passes when run alone (3/3).
    • An earlier full run also failed GatewayHandshakeObservationTests.RefusedTransport_ReportsNetworkWithoutClassifyingStackFrames (got Server instead of Network). It also passes alone (3/3), and this PR doesn't touch it.
    • I did not run a baseline without this PR's changes, so whether these two fail on main is not confirmed locally.
  • Focused runs:
    • ChatComposerControllerTests.Dispose_* with ActivationRouterTests: 30/30.
    • GatewayProtocolLiveRoundTripTests under full CPU load: 30/30.
    • A2UISvgGeometryBudgetTests: 8/8.
    • A2UISvgTests: 5 runs, all passed.
    • Gateway fixture tests: sidebar 22/26 (see Evidence), fractional voice 10/10.
  • pwsh ./scripts/test-ci-workflow-contract.ps1: passed. The new crash-capture step's script also ran locally and wrote its output file.

Real Behavior Proof

  • Environment tested: local Windows 11 x64 workstation (interactive desktop), Debug build.
  • PR head or commit tested: b880a642.
  • Exact steps or command run: the fixture test filters with OPENCLAW_RUN_GATEWAY_FIXTURE_UI=1 and isolated tray data directories; A2UISvgTests with isolated UI-test directories.
  • Evidence after fix: the pathological SVG load returns null in about 35 ms with the warning SVG rejected before decode. The sidebar and fractional voice fixture tests pass against the real app.
  • Observed result: as in Evidence above.
  • Screenshot or artifact links verified? N/A
  • Not verified or blocked:
    • Hosted CI on this head (pending).
    • The crash-capture step can only be seen working on CI when a testhost actually crashes.
    • No independent rubber-duck review was performed.

Security Impact

  • New permissions or capabilities? No
  • Secrets or tokens handling changed? No
  • New or changed network calls? No
  • Command or tool execution surface changed? No
  • Data access scope changed? No
  • If any answer is Yes, explain the risk and mitigation: N/A. The CI step reads only testhost crash entries from the Application event log (Application Error, WER and .NET Runtime). It writes no memory dumps, so the existing no-dumps rule for secret safety still holds.

Compatibility and Migration

  • Backward compatible? Yes. SVGs with more than 64 KiB of d/points path data now show the broken-image fallback.
  • Config or environment changes? No. The new test-only timeout overrides are OPENCLAW_TRAY_TEST_REENTRANCY_TIMEOUT_SECONDS, OPENCLAW_TRAY_TEST_ACTIVATION_WAIT_SECONDS and OPENCLAW_TEST_GATEWAY_TRANSITION_TIMEOUT_SECONDS.
  • Migration needed? No
  • If yes, list the exact upgrade steps: N/A

Review Conversations

  • I replied to or resolved every bot review conversation addressed by this PR.
  • I left unresolved only conversations that still need maintainer judgment.

@clawsweeper

clawsweeper Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@clawsweeper clawsweeper Bot added clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-environment Preliminary submission classification; full source review pending. clawsweeper:intake:needs-repro Preliminary submission classification; full source review pending. P2 Normal priority bug or improvement with limited blast radius. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-environment Preliminary submission classification; full source review pending. clawsweeper:intake:needs-repro Preliminary submission classification; full source review pending. labels Nov 5, 2026
@clawsweeper

clawsweeper Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

Codex review: needs maintainer review before merge.

What this changes

This PR stabilizes timing-sensitive tests and rejects oversized SVG geometry before native decoding.

Example: An A2UI image contains 65,537 bytes of path attribute data.

  • Before: Native SVG parsing can block the UI thread beyond the eight-second timeout.
  • After: The resolver rejects the image before native decoding.

Review scores

Measure Result What it means
Overall readiness 🧂 unranked krab (1/6) The source changes are coherent and prior findings are resolved, but the required app-level rendering evidence is not established by the supplied tests or adjacent screenshots.
Proof confidence 🧂 unranked krab (1/6) Real behavior proof is not required for maintainer- or bot-authored pull requests.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Product

Kind: Bug fix · Worth it: Yes · Fix scope: Complete
User problem: Oversized A2UI SVGs can freeze the UI, and scheduling-sensitive tests intermittently fail on loaded runners.
Reason: The collaborator-authored direction accepts bounded image rejection, and the patch preserves existing assertions while addressing concrete recorded failures.

Merge readiness

✅ Ready for maintainer review

Keep open: this collaborator-authored PR contains useful work absent from main, and both previous code findings are fixed; no new actionable code defect was found.

Priority: P2
Reviewed head: ec5b9ed0bbae803484cf735a0bd7153d176db013

Before merge

None.

Findings

None.

Tests

  • Missing end-to-end proof: Current-head shipped-app evidence of oversized SVG rejection with a responsive UI and an ordinary SVG still rendering is absent; a complete base-fail/head-pass capture is also not supplied.
Agent review details

How this fits together

A2UI images and agent avatars enter MediaResolver as image URLs; permitted SVG bytes pass through preprocessing before becoming native image sources or returning the existing failure result.

flowchart LR
  A[Image URL] --> B[MediaResolver]
  B --> C[Encoding and DOCTYPE policy]
  C --> D[Geometry and element budgets]
  D --> E[Native SVG decoder]
  D --> F[Image failure result]
  E --> G[Native image source]
Loading

Technical review

Best possible solution:

Land the focused preprocessing mitigation and deterministic test repairs after scoped runtime proof, while retaining the broader native-decoder investigation.

Do we have a high-confidence way to reproduce the issue?

Main invokes native SVG parsing before its asynchronous timeout can bound synchronous work, matching the recorded UI-thread heartbeat and failure trace; this review did not execute a reproduction.

Is this the best way to solve the issue?

The smallest scoped repair keeps one preprocessing policy before the existing decoder and preserves behavioral assertions while repairing test scheduling; it is a mitigation rather than a complete bound on native rendering cost.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning medium; reviewed against b6f158ab257a.

Provenance checked

  • Native SVG preprocessing and decoding changes intended behavior with a stated reason (9fa43f3: feat: native WinUI A2UI renderer + MCP/security hardening #239 introduced native A2UI rendering with DOCTYPE stripping and timeout-based defense in depth.)
  • SVG timeout regression changes intended behavior with a stated reason (b6c849e: test: stabilize flaky UI-job and SetupEngine CI failures #1981 intentionally retained the strict single-decode bound because the native parser stall was a product defect; this PR changes the oversized-input case to pre-decode rejection.)
  • Composer cancellation reentrancy tests keeps the original intent (686afdc: refactor(chat): extract composer ownership #1098 requires cancellation callbacks to run outside the operation gate so they can wait for reentrant cleanup; dedicated workers retain that assertion.)
  • Activation shutdown tests keeps the original intent (17a8b03: refactor(tray): extract final App orchestration owners #1088 established activation confirmation and lifecycle quiescence; the changed waits preserve cancellation, drain, and no-late-dispatch expectations.)
  • Handshake reconnect regression keeps the original intent (e0a9ec3: fix(connection): require hello-ok before operator RPCs #1425 protects suppression of mutations until a fresh hello-ok; withholding the reconnect challenge makes that state deterministic without removing wire assertions.)
  • Pinned sidebar layout regression keeps the original intent (7967d50: feat: pin Gateway pages beside chat #1940 introduced pinned Gateway pages beside Home; the new layout wait retains the same spacing ranges and navigation assertions.)
  • Gateway client loopback wait ceilings keeps the original intent (unknown: Path history was searched across OpenClawGatewayClientTests and its Moltbot predecessor; no single rationale was established for the scattered two-second ceilings, while the diff preserves response and timeout assertions.)

Testing

Proof path: in-process harness.

Security

None.

Evidence

What I checked:

Likely related people:

  • Chris Anderson: Raw commit 9fa43f3 adds src/OpenClaw.Tray.WinUI/A2UI/Rendering/MediaResolver.cs:329 relative to its recorded parents. This identifies author metadata, not feature responsibility or a PR merger. (role: source-line author; confidence: high; commits: 9fa43f347703; files: src/OpenClaw.Tray.WinUI/A2UI/Rendering/MediaResolver.cs)
  • Pedro Larroy: Raw commit b6c849e adds tests/OpenClaw.Tray.UITests/A2UISvgTests.cs:31 relative to its recorded parents. This identifies author metadata, not feature responsibility or a PR merger. (role: source-line author; confidence: high; commits: b6c849e8ed44; files: tests/OpenClaw.Tray.UITests/A2UISvgTests.cs)
  • Barbara Kudiess: Raw commit 686afdc adds tests/OpenClaw.Tray.Tests/ChatComposerControllerTests.cs:878 relative to its recorded parents. This identifies author metadata, not feature responsibility or a PR merger. (role: source-line author; confidence: high; commits: 686afdc8d4a2; files: tests/OpenClaw.Tray.Tests/ChatComposerControllerTests.cs)
  • Agustin Rivera: Raw commit e0a9ec3 adds tests/OpenClaw.Shared.Tests/GatewayProtocolLiveRoundTripTests.cs:394 relative to its recorded parents. This identifies author metadata, not feature responsibility or a PR merger. (role: source-line author; confidence: high; commits: e0a9ec3513d4; files: tests/OpenClaw.Shared.Tests/GatewayProtocolLiveRoundTripTests.cs)

Preflight source

Source unknown. Source comparison at the observed PR snapshot only. Completion is self-reported; body, policy, tests, execution, and report bytes are not verified.

Review metrics

Metric Value Why it matters
Production and test changes Production +273/-70; tests and test project +461/-88 Production growth contains the byte-scanning policy and relocated DOCTYPE preprocessing; tests protect distinct parsing, native-decoder, reconnect, and shutdown boundaries.

Labels

Label changes:

  • add P2: The patch addresses specific pathological-image stalls and CI timing failures without establishing an urgent widespread release regression.
  • remove status: ⏳ waiting on author: Current PR status label is status: 👀 ready for maintainer look.
  • remove P1: Current review triage priority is P2, so this older priority label is no longer current.

Label justifications:

  • P2: The patch addresses specific pathological-image stalls and CI timing failures without establishing an urgent widespread release regression.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR.

Rating scale

6/6 🦀 challenger crab · 5/6 🦞 diamond lobster · 4/6 🐚 platinum hermit · 3/6 🦐 gold shrimp · 2/6 🦪 silver shellfish · 1/6 🧂 unranked krab. Overall follows the weaker of proof and patch quality; ✨ marks media proof (a screenshot, video, or linked artifact) that directly shows the changed behavior.

Workflow

ClawSweeper edits this one comment on every review. Comment @clawsweeper re-review for a fresh review only. Repair and merge execution lanes are retired.

History

Review history (3 earlier review cycles)
  • reviewed 2026-11-05T19:42:33.015Z sha b880a64 :: needs changes before merge. :: [P2] [P2] Count geometry attributes within XML structure
  • reviewed 2026-11-05T20:31:39.553Z sha dbb5a18 :: needs changes before merge. :: [P2] Strip the complete DOCTYPE before applying the geometry budget
  • reviewed 2026-11-06T05:00:29.031Z sha e4685fc :: needs changes before merge. :: [P2] Strip the complete DOCTYPE before applying the geometry budget | [P3] Dispose the timer-free listener when the test fails

Reviewed November 7, 2026, 1:24 AM ET / 06:24 UTC (Revision 4).

@larroy

larroy commented Nov 5, 2026

Copy link
Copy Markdown
Collaborator Author

@clawsweeper re-review

Pushed dbb5a18: SvgGeometryBudget now walks XML structure (skips comments, CDATA, PIs, DOCTYPE; reads d/points only in start tags) and UTF-16/UTF-32 SVGs are rejected before decode. Regression tests cover both comment cases from the P2 finding.

@clawsweeper

clawsweeper Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

🦞👀
Exact review queued.

Re-review progress:

@clawsweeper clawsweeper Bot added clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-environment Preliminary submission classification; full source review pending. clawsweeper:intake:needs-repro Preliminary submission classification; full source review pending. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-environment Preliminary submission classification; full source review pending. clawsweeper:intake:needs-repro Preliminary submission classification; full source review pending. labels Nov 5, 2026
@larroy

larroy commented Nov 5, 2026

Copy link
Copy Markdown
Collaborator Author

Hanselman dual-model review (initial, pre-fix state)

Scope

  • Head: 2566c5793a9c6d2ffd2c6dd19490f8c600cee0b8 on fix/ci-flaky-tests-2026-11-04.
  • Base: merge-base with origin/main, the same commit, so the reviewed changes are the uncommitted working tree.
  • Files: 10 modified files plus 2 new untracked files (src/OpenClaw.Tray.WinUI/A2UI/Rendering/SvgGeometryBudget.cs, tests/OpenClaw.Tray.Tests/A2UISvgGeometryBudgetTests.cs).
  • Excluded: untracked *.md plan files and .vscode/.
  • Head unchanged while the reviews ran.

Reviewers

Two separate omp -p --no-session --thinking high sessions got the same prompt and the same patch. The task tool cannot select a model, so the CLI was used. The CLI has no long_context flag, so each model ran with its default window.

Reviewer Model ID Effort Context
Opus inference-nvidia/aws/anthropic/bedrock-claude-opus-5-5 high 1M
Astra inference-nvidia/azure/openai/gpt-6-astra high 1.1M

Reviewers could read repo files. They did not edit files, build, or run tests.

Both models agree (HIGH consensus)

None. The two reviews found different issues. Each was checked against the code by the coordinator.

Only one model flagged (LOW consensus)

Issue Opus 5.5 GPT-6 Astra Verified Fix confidence
XML comment bypasses the guard. <!-- d=' --> before a real <path d='...'> makes Exceeds pair its quote with the real path's opening quote and skip the real attribute. The oversized path is not counted. not flagged HIGH Yes, by tracing Exceeds. Same UI freeze the change tries to prevent. 90% (needs XML-aware scan or reader)
UTF-16 SVG bypasses the guard. The byte before d is 0x00, so IsXmlSpace never matches and Exceeds returns false. MEDIUM not flagged Logic confirmed. D2D accepting UTF-16 is untested [INFERENCE]. StripDoctype has the same gap. 90% (reject non-UTF-8 input)
64 KiB limit is unmeasured and may reject valid SVGs (charts, maps, logos). MEDIUM not flagged Plausible. No timing data behind the limit; failing input was about 750 KB. 75% (needs measurement)
CI_FAILURES.md calls the change a "Fix". The renamed test no longer reaches the "SVG decode timed out" branch. Under-budget SVGs (many <use> copies) have no coverage. MEDIUM not flagged Yes for docs wording and missing test. Residual risk is [INFERENCE]. 95% (reword to "mitigated", keep entry open)
StopAsync_CancelsPendingConfirmationAndPreventsLateDispatch: the listener token has a 10 s timer equal to the new 10 s ActivationWaitTimeout. The timer can cancel the confirmation before the stop wait expires, so a broken StopAsync could pass. Old wait was 2 s. not flagged MEDIUM Structurally true (ActivationRouterTests.cs lines 625, 634). Whether the token cancels the confirmation was not traced. 90% (timer-free listener token)
Reconnect test: one 15 s WaitForNextSocketAsync timer starts before the Close frame and covers both the disconnect wait and the reconnect. It can fault unobserved if an earlier assert fails. LOW not flagged Yes. 95%
GatewayFixtureUiTests: a layout timeout reports only a fixed description; measured gaps are lost from the failure message. LOW not flagged Yes. 95%
CI crash-capture step: -ErrorAction SilentlyContinue can write "No events" when Get-WinEvent failed. Events with null Message are dropped by -match 'testhost'. LOW not flagged Yes. 90%

Notes

  • Opus found the ChatComposerControllerTests and GatewayProtocolLiveRoundTripTests changes sound. The withheld challenge makes the disconnected window deterministic.
  • No finding was disputed by the other reviewer.
  • A clean source review does not replace required proof: ./build.ps1, Shared and Tray tests, and the UI test lane (A2UISvgTests) on this head. None were run.

Suggested next step

Fix the two SVG bypasses (stricter Exceeds scan with tests) and reword the CI_FAILURES.md entry. Single-model findings need user authorization before fixing.

@larroy

larroy commented Nov 5, 2026

Copy link
Copy Markdown
Collaborator Author

Update: status after commit dbb5a184

The PR was updated. Head is now dbb5a18425179dba348c394355a7b7002b7ce409 (on top of b880a642, base 2566c57). The first review's working-tree changes are now committed, and dbb5a184 addresses part of the findings.

No second model run was done for this update, by user decision. The statuses below come from the coordinator reading the dbb5a184 diff only. They are not a dual-model verdict. Nothing was built or run, so none of the new unit tests are proven to pass.

# Finding Status Evidence
1 XML comment bypasses the SVG guard (Astra, HIGH) Fixed (by reading) Exceeds now walks XML structure. It skips comments, CDATA, processing instructions and DOCTYPE, and reads d/points only inside start tags with quoted values skipped. Tests added: Exceeds_CommentContainingAttributeLikeText_DoesNotHideRealAttribute, ..._OversizedAttributeInsideComment_IsIgnored, ..._CdataAndProcessingInstruction_AreSkipped, ..._QuotedValueContainingAttributeText_IsNotRescanned.
2 UTF-16 bypasses the guard (Opus, MEDIUM) Fixed for UTF-16/UTF-32 New IsUnsupportedEncoding rejects UTF-16/32 BOMs and any NUL in the first 8 bytes. MediaResolver.SvgFromBytesAsync calls it before the budget check. Test added. Other non-ASCII-compatible encodings (for example EBCDIC) are still not rejected. This is [INFERENCE] and unlikely to matter.
3 64 KiB limit is unmeasured (Opus, MEDIUM) Open MaxAttributeChars is unchanged. No timing data, no env override.
4 CI_FAILURES.md overclaims "Fix" (Opus, MEDIUM) Mostly fixed The bullet now reads "Mitigated for oversized path data" and says the entry stays open. Two gaps remain: the Next: line still says "check this entry off once the fix lands", and no test covers SVGs under the budget.
5 StopAsync test 10 s timer can mask a bug (Astra, MEDIUM) Fixed The listener is started with CancellationToken.None. Only StopAsync can cancel the pending confirmation.
6 Reconnect test shares one 15 s timer (Opus, LOW) Fixed WaitForNextSocketAsync() has no timeout. WaitAsync(TransitionTimeout) is applied where it is awaited.
7 GatewayFixtureUiTests loses measured gap values on timeout (Opus, LOW) Open Not touched in dbb5a184.
8 CI crash-capture step uses SilentlyContinue (Opus, LOW) Open Not touched in dbb5a184.

New observations (coordinator only, unverified by a second model)

  • The new scanner looks linear. Every branch advances i, and SkipPast returns the end of input when a terminator is missing.
  • Prefixed attribute names such as svg:d are not counted. This is a bypass only if D2D treats a prefixed attribute as the plain d. Untested.
  • A DOCTYPE internal subset that contains > inside a quoted entity value ends the DOCTYPE skip early. The DOCTYPE is stripped before decode, so this looks harmless. Untested.
  • A UI-level test for the new "non-UTF-8 rejected" path in MediaResolver was not added. Only the pure helper is tested.

Next

Remaining before treating the SVG change as done: run ./build.ps1, the Shared and Tray test projects, and the A2UISvgTests UI lane on dbb5a184. Decide on findings 3, 7 and 8, and remove the stale Next: line in the CI_FAILURES.md SVG entry.

Models used for the review

Reviewer Model ID Effort Context
Opus inference-nvidia/aws/anthropic/bedrock-claude-opus-5-5 high 1M
Astra inference-nvidia/azure/openai/gpt-6-astra high 1.1M

Both ran on the first review (head 2566c57 plus working tree). This update was assessed by the coordinator only (inference-nvidia/aws/anthropic/bedrock-claude-sonnet-5-5); no reviewer model was run on dbb5a184.

@larroy
larroy requested review from bkudiess and karkarl and removed request for karkarl November 5, 2026 22:45
@larroy

larroy commented Nov 5, 2026

Copy link
Copy Markdown
Collaborator Author

Hanselman delta review of dbb5a184 (both models)

The delta reviewers finished after the earlier "no second model run" note, so this section supersedes it. Both ran on head dbb5a18425179dba348c394355a7b7002b7ce409. Models: Opus inference-nvidia/aws/anthropic/bedrock-claude-opus-5-5 and Astra inference-nvidia/azure/openai/gpt-6-astra, both high effort. Astra noted that one of its own sub-reviewers returned no assessment, so its SVG analysis is a single-pass trace. Neither reviewer built or ran tests. Raw outputs are in %TEMP%\hr2.

Status of the 8 earlier findings (both models agree)

# Status
1, 2, 5, 6 Fixed
3, 7, 8 Open
4 Partial. The Next: check this entry off once the fix lands line still contradicts "Mitigated", and no test reaches the SVG decode timed out branch.

New findings

Issue Opus Astra Notes
DOCTYPE internal subset bypasses the guard (HIGH consensus). Exceeds runs on the raw bytes and skips a <!...> declaration to its first >. MediaResolver then calls StripDoctype on the bytes D2D receives, so the scan and the strip disagree. Opus payload: <!DOCTYPE x [ > <a b=" ]><svg ...><path d="...huge..."/></svg>. Astra payload: <!DOCTYPE svg [<!-- > <![CDATA[ -->]> before an oversized path. Either way the real path is never counted and D2D receives a clean oversized SVG. HIGH HIGH Verified by the coordinator: MediaResolver.cs runs the budget check before StripDoctype (line 345), and Exceeds ends a <! declaration at the first > (SvgGeometryBudget.cs:41). Neither payload was executed in C#; Opus ran a line-for-line Python copy. Fix confidence 90%: run the scan on the stripped bytes, reject any remaining <!DOCTYPE, and add the payloads as regression tests.
Activation test leaks the listener and pipe when setup fails (no try/finally after the timer was removed). LOW LOW Both flagged it. Fix confidence 95%.
encoding= in the XML declaration (for example UTF-7, ISO-2022-JP) passes IsUnsupportedEncoding, so the helper does not enforce UTF-8 as the commit title says. MEDIUM LOW (stated as a limit) Native acceptance is [INFERENCE]. Fix: reject any encoding= other than UTF-8 or US-ASCII.
StripDoctype removes a decoy <!DOCTYPE inside a comment and leaves the real DTD, so entity expansion could defeat the budget. Existing bug, now relevant. MEDIUM not flagged Covered by the same fix as the DOCTYPE bypass. Needs D2D entity behavior to matter ([INFERENCE]).
Prefixed attribute names (s:d) are not counted. LOW LOW (stated as a limit) Matching on the local name only adds false positives.
UTF-32 BE BOM branch is dead code (the NUL check already catches it). LOW not flagged Trivial.
Unit tests call Exceeds on raw bytes and never go through StripDoctype, which is why the DOCTYPE bypass was missed. LOW LOW Move the strip into the pure file and test the combined path.
Crash-capture log window (30 min) can include the Functional UI Tests testhost. LOW not flagged Record a start time before the Tray UI step.
No limit on element count (about 70,000 <rect/> fit in 2 MiB), so the mitigation is not a render-time bound. note note Already documented. Do not call the entry fixed.

Performance: both models judge the new scanner linear time with no non-advancing loop.

Verdict (both models): not ready. Fix the DOCTYPE bypass first. A stronger option from Opus is to use XmlReader with DTD processing prohibited on the stripped bytes.

@larroy

larroy commented Nov 6, 2026

Copy link
Copy Markdown
Collaborator Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

🦞👀
Exact review queued.

Re-review progress:

@clawsweeper clawsweeper Bot added clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-environment Preliminary submission classification; full source review pending. clawsweeper:intake:needs-repro Preliminary submission classification; full source review pending. and removed clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-environment Preliminary submission classification; full source review pending. clawsweeper:intake:needs-repro Preliminary submission classification; full source review pending. labels Nov 6, 2026
@larroy

larroy commented Nov 6, 2026

Copy link
Copy Markdown
Collaborator Author

Hanselman dual-model review (adversarial)

Review of the current branch state, post-rebase. Both requested models completed on the same revision.

Reviewed head 596b9469 (local fix/ci-flaky-tests-2026-11-04, rebased onto origin/main)
Base 04f413d2 (origin/main)
Opus anthropic/bedrock-claude-opus-5-5, thinking high, 1M context
GPT openai/gpt-6.1-sol, thinking high, 1.1M context
Method independent source-only reviews of origin/main..HEAD (13 files, +682/-168); no builds or tests by either reviewer

Note: the reviewed head is 2 commits ahead of this PR's current head (e4685fc9) and is not pushed yet, so this review covers more than the checks below ran on. The first GPT run returned an incomplete answer and was retried once on the same revision.

Both models agree: HIGH consensus

Issue Opus 5.5 GPT-6.1 Sol Fix confidence
The pre-decode budget counts only d/points text. Element count is unbounded, so many small shapes (<rect>, <circle>, <use>) reach SetSourceAsync with a count of zero and can still stall the UI thread MEDIUM LOW (known risk, no code change requested) 80%

No CRITICAL or HIGH findings from either model. GPT verdict: correct for its stated scope. Opus verdict: strip-then-scan design sound; the remaining exposure is what the budget does not measure.

Only one model flagged: LOW consensus (all Opus)

Issue Opus 5.5 GPT-6.1 Sol Fix confidence
MaxAttributeChars = 64 KiB has no measurement behind it, and no test shows a realistic large SVG still renders MEDIUM not flagged 75% (needs measurement; separate change)
The encoding check reads only BOM/NUL. An XML declaration naming UTF-7 or EBCDIC passes, so the strip and scan see no tags while the decoder may decode them MEDIUM not flagged 80% (D2D honoring the declaration is unverified)
ChatComposerControllerTests.Dispose_VoiceCancellationCallbackCanWaitForReentrantControllerWork starts an outer callbackWork.Wait(ReentrancyTimeout) and an inner cleanupReached.Task.Wait(ReentrancyTimeout) with the same budget, so the outer wait can time out first near the limit LOW not flagged 99%
.github/workflows/ci.yml crash capture uses a fixed -30 minute window (can include other lanes' testhost crashes) and -ErrorAction SilentlyContinue (turns a log read error into "No ... events") LOW not flagged 92%
docs/CI_FAILURES.md: "The entry stays open" contradicts "Next: check this entry off once the fix lands"; no test reaches the SVG decode timed out branch now LOW not flagged 95%
The new A2UISvgTests cases distinguish rejection reasons by log text instead of a typed reason LOW not flagged 88%

Taken from the HIGH item

The element-count part is being addressed: an element start-tag budget (MaxElements = 8,192) is added to the same scan, with a typed SvgBudgetLimit result so attribute and element rejections are distinguishable, unit tests for the boundary and for comments/CDATA/end tags, and a UI test that a document of many small shapes is rejected before D2D. Documentation now scopes the mitigation to path data and element counts, and keeps the entry open for input under both limits.

Measured locally on the pre-decode scan: an icon-sized SVG (20 shapes + 1 path) and a 500-shape chart are accepted; 8,192 start tags are accepted and 8,193 rejected in about 5 ms.

Validation on the reviewed head

  • ./build.ps1: all builds succeeded.
  • OpenClaw.Tray.Tests: 5251/5251 passed.
  • OpenClaw.Tray.UITests --filter FullyQualifiedName~A2UISvgTests: 18/18 passed.
  • OpenClaw.Shared.Tests: 4861 passed, 24 skipped, 6 failed, all Mxc* (WSL prerequisites and a missing generated installer.iss on this host), unrelated to the diff.

A clean source review does not replace CI on the pushed head; the checks on this PR ran on e4685fc9, not on the reviewed revision.

@larroy
larroy force-pushed the fix/ci-flaky-tests-2026-11-04 branch from e4685fc to d9293b5 Compare November 7, 2026 05:31
- ChatComposerControllerTests: run voice-dispose reentrancy work on dedicated
  threads (thread-pool starvation) with an env-overridable bound.
- ActivationRouterTests: replace hard-coded 2 s waits with a named constant.
- GatewayProtocolLiveRoundTripTests: observe the drop via StatusChanged and
  withhold the reconnect challenge instead of polling a ~1 s window.
- GatewayFixtureUiTests: wait for settled sidebar geometry after unpinning.
- MediaResolver: reject SVGs over a 64 KiB path-data budget before D2D decode
  (SvgGeometryBudget), so pathological geometry cannot block the UI thread.
- CI: capture testhost crash event-log records when Tray UI tests fail.
- docs/CI_FAILURES.md: update crash and SVG entries; record UIA timeout cluster.
Skip comments/CDATA/PIs/DOCTYPE and read d/points only in start tags; reject
UTF-16/UTF-32 SVGs before decode. Tighten StopAsync and reconnect test timers;
reword CI_FAILURES SVG entry as mitigated.
…StepTimeout

Fixes flaky PatchConfigDetailedAsync_RejectedSave_AfterTimeoutDoesNotLogRemoteText
on loaded CI runners. Override: OPENCLAW_TEST_GATEWAY_STEP_TIMEOUT_SECONDS.
The budget scan ends a <!...> declaration at its first '>', while
StripDoctype honors the DTD internal subset, and the scan ran on the
unstripped bytes. A DOCTYPE comment containing '>' therefore made the
scan disagree with what D2D parses: it could reject a small SVG (counting
a path inside the DTD comment) or miss real oversized geometry.

StripDoctype and its ASCII helpers now live in SvgGeometryBudget, and
TryPrepareSvgForDecode is the single pre-decode policy (encoding check,
strip, budget) so MediaResolver decodes exactly the bytes that were
scanned. The duplicate late strip is gone.

Both halves are covered through the real entry point: unit tests on the
combined policy and UI tests against D2D for the false-rejection payload
and for oversized geometry that follows a DOCTYPE.

Also dispose the timer-free activation listener on every exit path
(await using), so a failing assertion can no longer leave the listener
task and named pipe alive; the timer-free token needed by the shutdown
assertion is unchanged.
StripDoctype removes the first textual <!DOCTYPE, so a decoy inside a
comment or processing instruction before the real declaration leaves the
DTD in the bytes handed to D2D. The geometry scan then ends that DTD at
its first '>', which can hide real oversized geometry.

TryPrepareSvgForDecode now fails closed when a declaration survives the
strip, so Exceeds only ever sees bytes without one. The tradeoff is
documented: an SVG keeping a literal <!DOCTYPE in its own comment is
rejected instead of decoded.

Test repairs from review:
- The oversized-after-DOCTYPE unit and UI tests used a payload the raw
  scan already rejected, so they proved nothing about the ordering fix.
  They now use a '<?' DTD tail that makes the unprefixed scan skip to end
  of input, and pin that miss before asserting rejection.
- New coverage for a decoy DOCTYPE that survives the strip, at the policy
  level and through MediaResolver/D2D.
- Drop the Exceeds assertion that locked in the raw scan's DTD flaw.
- ActivationRouter shutdown test: replace await using with try/finally so
  the failure path this test guards returns a bounded TimeoutException
  instead of hanging on the same unbounded stop task.
The pre-decode guard summed only d/points attribute text, so a document of
many cheap shapes reached SetSourceAsync with a measured size of zero while
D2D still had to parse every node. A 2 MiB data URL holds about 65,000
<rect/> elements, and the 8 MiB remote cap about 200,000.

Count element start tags in the same single-pass scan and reject above
MaxElements (8,192). End tags, comments, CDATA and processing instructions
do not count, matching how the attribute budget already treats them. The
limits are now reported as a typed SvgBudgetLimit so the caller can log
which budget tripped and tests can assert it directly.

Measured on the scan: an icon (20 shapes, 1 path) and a 500-shape chart are
accepted; 8,192 start tags are accepted and 8,193 rejected in about 5 ms;
65,536 rects, or the same text inside a comment, behave accordingly.

Coverage: unit boundary and structure tests, a policy-level rejection, and a
UI test that a many-shape SVG is rejected before D2D. CI_FAILURES scope now
names path data and element counts and keeps the entry open for input under
both limits.
@larroy
larroy force-pushed the fix/ci-flaky-tests-2026-11-04 branch from d9293b5 to ec5b9ed Compare November 7, 2026 05:47
@larroy larroy added status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. P1 Urgent regression or broken agent/channel workflow affecting real users now. and removed P2 Normal priority bug or improvement with limited blast radius. labels Nov 7, 2026
@larroy

larroy commented Nov 7, 2026

Copy link
Copy Markdown
Collaborator Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

🦞👀
Exact review queued.

Re-review progress:

@clawsweeper clawsweeper Bot added clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-environment Preliminary submission classification; full source review pending. clawsweeper:intake:needs-repro Preliminary submission classification; full source review pending. P2 Normal priority bug or improvement with limited blast radius. and removed status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. P1 Urgent regression or broken agent/channel workflow affecting real users now. clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-environment Preliminary submission classification; full source review pending. clawsweeper:intake:needs-repro Preliminary submission classification; full source review pending. labels Nov 7, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Normal priority bug or improvement with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant