fix(capture): audit remediation for redaction, capture lifecycle, contract validation and snapshot performance

Merged
TH
4h ago
+1448-315

Summary

Fixes 13 correctness findings and 4 performance findings from the 2026-11-10 audit, in self-contained commits. Review fixes followed, and origin/main is merged in with #4357's app-log rotation kept.

  • Redaction: shared references are no longer marked [Circular]. A shared subtree is redacted once, so a diamond-shaped input is no longer exponential.
  • App-log redact patterns:
    • Commas inside {m,n} quantifiers, [...] and (...) stay in their pattern.
    • A pattern that does not compile fails logs start with INVALID_ARGS (app_log_redact_pattern_invalid, patternIndex). The pattern text is not echoed.
  • Typed errors:
    • alert wait timeouts carry details.reason: alert-wait-timeout.
    • An admitted operation whose interactor method is missing reports runtime-contract-invalid. This covers hover, which used to report UNSUPPORTED_OPERATION. An operation the runtime does not advertise is still refused with UNSUPPORTED_OPERATION.
    • Scroll-edge capture keeps typed and cancel errors.
  • Contracts: the summary and activation readers reject values their types forbid. The merge uses at most three weighted points per run, which give the same values the old per-sample rebuild did. One BackendSnapshotResult remains.
  • Events: every page returns an absolute endCursor, so a reader following the log can resume without replaying events.
  • Capture lifecycle:
    • App-log capture survives a failed PID poll or a slow provider read.
    • No read is started while an abandoned one is still in flight, and finish keeps the reader until that read settles.
    • The recovery deadline is renamed to say it bounds only acquisition.
  • React Native overlay hint: passive app text such as "Error: …" no longer triggers the hint. A bare "Error:"/"Warning:" prefix counts only with React Native context on screen; the !, LogBox banner label is still detected on its own.
  • Performance:
    • The Android helper presentation and overlay labels were O(n²) or worse. They now run in linear passes, and tests count parent-link reads to pin that.
    • Runner presentation validates an unchanged payload once instead of twice.
    • The snapshot diff change is a constant-factor reduction: typed trace slices instead of Map copies, plus a prefix skip. The trace is still O(D²) memory.
    • The Android, overlay, diff and runner outputs are identical to the merge-base on fixtures and random trees.

Deferred: CMD-2 (needs its own PR), CAP-5, CAP-6, CAP2-5, CAP-9. Follow-ups: one shared children-by-parent builder, and the quadratic memory of the diff trace.

Validation

  • check:affected --run at 7d70a9165, exactCommit=true: format, lint, typecheck, layering, fallow and build pass.
  • vitest-related failed 3 tests:
    • Two are the root-chmod baseline (BASE-1).
    • runner-lease-accessibility timed out at 5 s with host load average around 11. It passed 3 of 3 alone, which does not prove contention. This PR does not touch apple-runner.
  • The eager-closure budget test passes.
  • Live device runs are still required and are not attached. See the review thread.

Generated by Claude Code