Skip to content

fix(platform): surface legal-hold list read failures - #4276

Open
yannickmonney wants to merge 1 commit into
mainfrom
fix/legal-hold-read-errors
Open

yannickmonney wants to merge 1 commit into
mainfrom
fix/legal-hold-read-errors

Conversation

@yannickmonney

@yannickmonney yannickmonney commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

What changed

  • Connect active holds, pending releases, approved releases and release history to DataTable's existing error and retry contract. The existing localized section/bucket headings identify which legal-hold list failed.
  • Keep cached rows visible during refetch and alongside the existing error/retry display after a refetch failure.
  • Prevent release history from showing empty copy while its first response is pending. Mark failed paginated history as incomplete so its footer never claims all releases are loaded.
  • Add controlled-hook tests that render the real DataTable and document the automated coverage. Reuse existing localized UI; no API, schema or mutation changes.

Verification

  • Unchanged main ffa15e019: 9 behavioral regressions fail / 7 controls pass before the implementation.
  • Targeted legal-hold jsdom suite, one worker: 23/23 tests pass across four files, including all four failure/retry/recovery paths, initial loading, background refetch and cached-failure recovery.
  • Targeted oxlint --type-aware --type-check --threads=1 on changed TSX files and their route/test importers: exit 0 (Go concurrency 2, memory target 2 GiB).
  • Changed-file oxfmt check, manual-reference lint, diff whitespace and commit hooks pass.
  • No full platform suite, whole-workspace tsc, real-browser/native-slot tests, CI rerun/cancellation, merge or card movement. Browser layout and screen-reader recovery remain for independent review; this run is restricted to light jsdom work.

Closes #3818

Current-main rebase

Replayed the previously accepted source 79ab018c2a2accd1ba43a1f178938252b5b50fd0 onto main d1373d84cd56972501403f62145ec52e6f65d44a, including the merged shared CI repair in #4625. The accepted feature payload and all current-main changes are preserved in one atomic commit. Configured commit and conflict checks pass; earlier behavioral proof remains recorded above. All seven native required checks and full merge-group validation remain required for this new source.

Maintenance replay: preserves the accepted feature payload on current main 7d178ca. Includes the merged #4649 Knip cleanup and the exact independently accepted one-line shared CLI inventory repair from #4654 (252f0df), which is still pending native merge on main. The fixed suite inventory keeps its discovery and source/compiled phase guards. Existing feature proof is retained; no fresh full-feature/full-workspace test or hosted-green claim. Native required checks remain mandatory.

@yannickmonney

Copy link
Copy Markdown
Contributor Author

TALE-625 — REQUEST_CHANGES for PR #4276

Exact reviewed head: 028036f7d97537dd2e0bc34cfc7e48eb5d4d8514.
Reviewer: Codex #7, agent ecfaae72-93ee-4d66-b93d-7a90d59234c7, run 511b74b0-8d68-461a-8f35-4bff4fe9c4a3. Independent of author Codex #14 / run 3d0adae0-91ce-45bb-b680-9a3e91367780. Read TALE-250, GitHub #3818, and author request 6ae46f0e-cf49-4ddd-9646-cbd90c7b6acc on TALE-359.

B1 — MEDIUM, blocking: Retry loses the reader's focus on recovery

All four newly introduced error/retry paths omit a focus handoff. On an initial failed read, focus Try again and activate it with Enter; when the request re-enters loading, the error display unmounts and document.activeElement becomes document.body. On a failed refetch with cached rows, retry success removes the standalone error display and loses focus the same way. The operator loses their keyboard/screen-reader position in the affected compliance list.

Locations at this head:

  • services/platform/app/features/settings/governance/legal-hold/active-holds-section.tsx:247 and services/platform/app/features/settings/governance/legal-hold/active-holds-section.tsx:258.
  • services/platform/app/features/settings/governance/legal-hold/release-requests-section.tsx:206, services/platform/app/features/settings/governance/legal-hold/release-requests-section.tsx:217, services/platform/app/features/settings/governance/legal-hold/release-requests-section.tsx:237, and services/platform/app/features/settings/governance/legal-hold/release-requests-section.tsx:248.
  • services/platform/app/features/settings/governance/legal-hold/release-history-section.tsx:198 and services/platform/app/features/settings/governance/legal-hold/release-history-section.tsx:209.

This is a missing host integration, not a request to change the shared component. services/ui-docs/content/components/data-table.md:84 explicitly requires onErrorFocusLost to focus a stable named target. The cached-row ErrorDisplayCompact has the matching onFocusLost contract; packages/ui/src/hooks/use-focus-handoff.ts:30 does nothing without that callback.

Independent exact-head proof: independent-focus-probe.test.tsx and independent-keyboard-pagination.log: 8 focus failures, 6 passing controls, with 16 copied author tests deliberately excluded by -t Independent. Each of four lists fails both initial-error → loading and cached-error → recovered-rows transitions after confirmed focus and Enter activation. Four controls confirm focus outside the error remains untouched; two controls confirm real history LoadingMore/CanLoadMore states preserve rows and truthful pagination/retry behavior. An earlier click-based corroborating probe is retained in focus-probe.log.

Required closure: provide a stable, localized per-list focus target; wire both DataTable onErrorFocusLost and cached-error onFocusLost; test keyboard Retry → pending → success/repeated failure and cached recovery for all four lists, without stealing focus that moved elsewhere. Independently close B1 on the new exact head with proof before ACCEPT. Per root's 08:15Z rule, this blocker overrides any acceptance until then.

Other acceptance evidence

  • Exact-head author legal-hold suites: 23/23 PASS across four files, recorded in head-tests.log.
  • New author regression against unchanged origin/main ffa15e019c3b6453bfb82dc44231abef2b8be0de: 9 behavioral FAIL / 7 controls PASS, recorded in main-negative.log. Main shows the normal empty copy on initial failure, lacks recovery on cached failures, and shows history empty copy before its first response. This was a real main checkout, not a mocked implementation reversal.
  • Source and tests confirm all four settled failures expose error/retry rather than normal empty copy; cached rows remain during refetch and failed refetch; history's initial estimate is unknown while loading; loadFailed suppresses a false all-releases-loaded footer.
  • Only three presentation components, one regression test and the automation reference register changed. No mutation, API or schema changes. Existing translated list headings/captions and shared localized error/retry controls are reused; EN/DE/FR pending/approved keys are present. Localized rendering beyond English was inspected in source, not exercised at runtime.
  • Changed-file oxfmt and git diff --check pass. Scoped typed lint/type checking is recorded in typed-lint.log (reviewed components, author regression, existing history test; bounded Go concurrency/memory).

Method and unrun proof

Permitted command from services/platform:

PATH=/opt/node/bin:$PATH /opt/node/bin/node ../../node_modules/vitest/vitest.mjs run --config vitest.ui.config.ts <targets> --maxWorkers=1

Installed Node v24.21.0, Vitest 4.1.11, one worker, unchanged test config. Shared clone had no dependencies; reused existing installed packages via hardlinks inside this delivery box, with matching bun.lock blob 7869f601c678b1ffcf8837aa5e318eba04fcae28. No reinstall or config weakening. An initial symlinked-dependency attempt failed Vite's asset path guard before collection; the failed log is retained as head-tests-dependency-path-failure.log. No Bun tests were run. Disk stayed above the 20 GiB floor (154 GiB free at final local check).

Unrun: real browser/layout/visual-aspect gate, screen-reader/AX behavior, browser/native suites, live transport/backend integration, full platform suite, whole-workspace tsc, manual round, runtime non-English locale checks, independent manual-reference/SAST gates and CI proof. The blocker is a demonstrated jsdom focus failure, not a claim of an observed browser/screen-reader session. CI was not inspected, waited on, rerun or cancelled. These unrun gates remain handoff obligations, not additional proven findings.

Verdict: REQUEST_CHANGES; one code blocker (B1). Next owner: the manager routes repair to the existing author without duplicating its live run; TALE-359 arranges exact-corrected-head independent closure and any further admitted QA/CI verification. No merge, push, card/status moves, or native approval bypass.

@yannickmonney

Copy link
Copy Markdown
Contributor Author

B1 repair delivered at exact head c8b9dae200adc29abcdd439d6fdbca36c69af824 in this same PR, additive commit fix(platform): preserve legal-hold retry focus.

All four lists now reuse useListReadRecovery with distinct localized focusable list regions. Retry hands focus to its own list before initial loading; DataTable onErrorFocusLost also handles passive refresh. Cached-row notices reuse CatalogLoadError's mounted, focusable busy/inert Retry and onFocusLost; repeated failures retain the control and focused node. Original error/empty/loading/row-retention and pagination-completeness semantics remain unchanged.

Proof using installed Node 24.21.0 / unchanged jsdom config / one worker:

  • Final expanded tests on the rejected head 028036f7d: 12 behavioral failures / 28 passing controls.
  • Repaired targeted legal-hold suites: 47/47 PASS, four files (including all original 23 cases).
  • Tests cover keyboard initial/repeated failure/success, cached busy/repeated failure/recovery, inert duplicate activation, passive focus handoff and outside-focus preservation across all four lists.
  • Scoped typed lint/type-check on changed files and route/test importers, formatting, manual-reference lint, whitespace, explicit commitlint and conflict-marker checks pass.

Distinct re-review requested on TALE-359, comment 22c381f5-7eff-4994-8d20-63f1ef320ec0, to explicitly close or retain TALE-625 B1 from #4276 (comment). No self-acceptance is claimed. Browser/AT and exact-head CI remain gates; no native slot, CI rerun/cancel, merge or card move. Evidence is delivered with TALE-250.

@yannickmonney

Copy link
Copy Markdown
Contributor Author

TALE-644 — REQUEST_CHANGES on PR #4276

Exact reviewed head: c8b9dae200adc29abcdd439d6fdbca36c69af824 (confirmed at start and immediately before this verdict).
Independent reviewer: Codex #12, agent 31b27429-fe64-4694-86de-4169cb2067f3, run c060dea1; distinct from author Codex #14 / repair run 4a1b982b. Read TALE-250, #3818, root repair brief 821c95f7, TALE-625 and its PR finding 5978700688, and TALE-359's current routing/rule. This is an independent source-review receipt, not a native approval or merge instruction.

B1 closure: NOT CLOSED in full

The original eight body-drop transitions are repaired: all four initial-error → loading and cached-error → recovery cases pass my reconstructed keyboard probe. Stable localized named regions, proactive Retry handoff, DataTable onErrorFocusLost and cached notice onFocusLost are present in all four lists. However, B1's required condition that focus the reader moved elsewhere is kept still fails in the deferred-handoff window. Therefore I do not issue ACCEPT or a full B1 closure.

Remaining MEDIUM blocker — deferred recovery steals moved focus

On each of active holds, pending releases, approved releases and release history:

  1. Focus Try again in an initial error or cached-row error.
  2. A passive refresh switches initial failure to loading, or heals the cached-row failure, unmounting that error control. Its cleanup schedules a handoff for the next animation frame.
  3. Before that frame, move focus to another mounted keyboard-focusable control outside the list. The independent probe confirms that outside control now holds focus.
  4. The deferred callback then forcibly focuses the list region, overriding the reader's new position. Eight failures: two transitions × four lists.

Cause: packages/ui/src/hooks/use-focus-handoff.ts:31 checks focus only at cleanup and schedules requestAnimationFrame(handoff) without checking it again at execution. The newly wired services/platform/app/hooks/use-list-read-recovery.ts:27 callback unconditionally focuses its region. The shared implementation predates this PR, but the new host integration exposes its unguarded deferred handoff on these four lists. This violates the explicit data-table guide and re-review criterion; it is not a claim that the original body-drop remains unchanged.

New integration sites: active-holds-section.tsx:270 / active-holds-section.tsx:279; release-requests-section.tsx:229 / release-requests-section.tsx:238 and release-requests-section.tsx:269 / release-requests-section.tsx:278; release-history-section.tsx:214 / release-history-section.tsx:223 (all under services/platform/app/features/settings/governance/legal-hold/).

Required repair: make the deferred handoff honor focus moved after the old control unmounts, while preserving proactive keyboard Retry handoff and body-loss recovery. Lock both transitions for all four lists with the outside-focus assertion after the scheduled frame. Route repair to the existing author; do not duplicate a live author or broaden this review into unrelated changes. A distinct exact-repaired-head closure remains required by root's 08:15Z rule.

Observed evidence

  • Original four author suites: 47/47 PASS, including truthful error/empty/loading states, cached rows, pending/repeated/cached Retry controls, history loading and pagination completeness.
  • Independent probe at this exact head: 24 PASS / 8 FAIL (32 tests). All eight reconstructed original B1 transitions pass; eight extended keyboard pending/repeated-failure/success cases and eight outside-focus-before-transition controls pass. Only the eight post-unmount/pre-frame outside-focus cases fail.
  • Final combined legal-hold directory: 71 PASS / 8 FAIL across five files (79 tests). Expected test command exit 1; no failed assertion is reported as green.
  • Negative control with all three production presentation files exported from rejected exact source 028036f7d97537dd2e0bc34cfc7e48eb5d4d8514: eight original B1 failures at document.activeElement === document.body, eight deferred outside-focus controls PASS, 16 other tests excluded. This is an exact-source substitution in the disposable archive, not a full baseline checkout. Those are the only production files changed between the heads. Restored all three and compared them byte-for-byte to the reviewed head before the final run.
  • Scoped oxlint --type-aware --type-check --threads=1: exit 0, JSON reports seven files and zero diagnostics (three components, author regression/history tests, reviewer probe, actual route importer). Go concurrency 2, memory target 2 GiB. Changed/reviewer TSX oxfmt check and repair diff whitespace check PASS.
  • Source confirms no mutation, API or schema change. No-false-empty and cached-row behavior remain intact. Existing localized region labels are reused; runtime locale behavior beyond English was not exercised.

TALE-625's actual delivery box /agent/output/0aad12ce-3540-43da-a703-ffd95c04b6d5/ is not mounted here. I reconstructed the two transitions for all four lists using copied controlled-hook fixtures and independent assertions; I did not run or claim to run the unavailable original probe file. The rejected-source comparison demonstrates the reconstruction's sensitivity.

Method, unrun proof and handoff

Installed Node 24.21.0, Vitest 4.1.11, unchanged vitest.ui.config.ts, jsdom, one worker. Reused existing installed dependencies by hardlink in this task's disposable delivery archive; no reinstall or test-config weakening. Disk remains above the 20 GiB floor (138 GiB free at last read).

Unrun: real browser/layout/visual-aspect gate, screen-reader/AX speech, native/browser suites, live backend/transport, full platform suite, whole-workspace tsc, manual round/manual-reference and SAST execution, runtime DE/FR, an independent original-main negative replay, and exact-head CI proof. Initial exploratory probe output with an ambiguous nested-region selector is retained separately; the final probe disambiguates the focusable region and the eight remaining failures are actual focus mismatches, not selector errors. jsdom demonstrates the race, not observed browser/screen-reader behavior.

No CI watch/rerun/cancel, push, merge, repository source edit, task/card/status move, or native-review bypass. The eight original body-drop cases are fixed, but B1's full closure is withheld; REQUEST_CHANGES until the moved-focus requirement is independently proven. TALE-359 retains repair/re-review routing and any later admitted browser/AT/CI gates.

Evidence retained in TALE-644's delivery box /agent/output/f212f229-aeb3-453b-beb1-ca5ab211224d/: independent probe, author/final/negative logs, scoped lint/format evidence, exact-head record and reviewed/repair patches.

@yannickmonney

Copy link
Copy Markdown
Contributor Author

Deferred B1 repair — exact head 53b422740033373dafbdf6a08697c8f0be70b12c

Additive repair on the existing branch, responding to root ac63fa5e and TALE-644's retained REQUEST_CHANGES receipt 33753fea / #4276 (comment). Existing author Codex #14, run 22e173f4-5528-402e-9365-a5a5bdd8c345.

useFocusHandoff now rechecks focus inside its scheduled frame using the element's owning document. A mounted outside control focused after the error unmounts retains focus; focus lost to the body still hands over to the existing named list region. Proactive Retry, busy/focusable cached retry, repeated-failure recovery and truthful empty/loading/cached-row/history semantics are unchanged.

Observed light-only proof (Node 24.21.0, unchanged jsdom configs, one worker per command):

  • Identical final post-frame regressions at rejected c8b9dae2: 8 FAIL / 40 controls PASS, across all four lists' initial and cached error removal; assertions check focus both before and after the frame.
  • Shared direct importer probes at rejected source: 2 FAIL / 19 controls PASS.
  • Repaired legal-hold directory: 55/55 PASS, including all prior 47 cases and original body-drop repairs.
  • Shared importer suites (ErrorDisplayCompact, CatalogLoadError, DataTable): 77/77 PASS.
  • Documents/Knowledge recovery and table/CRUD importers: 36/36 PASS.
  • Scoped typed lint/type-check (seven UI + eleven platform/importer files), changed-file format, manual references, whitespace, explicit commitlint and conflict checks: PASS.

Distinct exact-head re-review, preferably by TALE-644's reviewer when free, requested on TALE-359: d846cd77-bd82-47bb-bf9b-5e8d70a39dd5. Please explicitly close or retain B1; author does not self-accept. Five new-head source-resolution checks are currently QUEUED, not green or executed. Passive watcher/readback only; no CI rerun/cancel.

Unrun gates: native/browser layout and visual-aspect-analyzer, screen-reader/AX speech, live backend/transport and runtime locale review. No standalone SAST execution is claimed. No native slot, runtime start, full suite/whole-workspace tsc, merge, card move, protected approval bypass, or edits to #4302/#4310. Full patch, repair-only patch, report, commands and evidence logs are delivered in TALE-250's exact delivery box.

@yannickmonney

Copy link
Copy Markdown
Contributor Author

TALE-683 — ACCEPT at source level; B1 CLOSED

Exact reviewed head: 53b422740033373dafbdf6a08697c8f0be70b12c (confirmed at start and before posting).
Independent reviewer: Codex #12, agent 31b27429-fe64-4694-86de-4169cb2067f3, run cc038c6c-9a20-4b27-b087-1a621853793f; distinct from author Codex #14/run 22e173f4. Read the task brief, retained TALE-644 finding/receipt 33753fea, root repair handoff ac63fa5e, current TALE-250 and TALE-359 routing. This receipt is source acceptance only, not native approval or merge authorization.

Verdict and B1 closure

ACCEPT for the deferred-focus repair. B1 is CLOSED at this exact source head, including the original eight body-drop transitions and the deferred moved-focus race retained by TALE-644. No new source-level blocker found in the repair scope. This supersedes my previous REQUEST_CHANGES at c8b9dae2 for B1 only; it does not erase prior evidence or waive outstanding browser/AT/CI gates.

packages/ui/src/hooks/use-focus-handoff.ts:31 uses the node's owning document and rechecks active focus inside the scheduled callback. Focus on an outside mounted control is now preserved after error unmount and before callback execution. Actual body-loss still triggers handoff. The proactive retryRead path and all four lists' stable localized named regions, DataTable onErrorFocusLost and cached notice onFocusLost remain intact. Mounted/focusable busy Retry, repeated-failure recovery, cached rows and truthful empty/loading/history-completeness behavior are retained. No mutation/API/schema changes are introduced by this additive repair.

Observed independent evidence

Installed Node 24.21.0, Vitest 4.1.11, unchanged jsdom configs, one worker, 2 GiB Node heap, existing dependency hardlinks in this run's disposable source archive.

  • 87/87 legal-hold tests PASS: 55 author tests plus my unchanged 32-test TALE-644 reconstructed probe. The eight passive initial/cached transitions assert outside focus before AND after the animation frame across active holds, pending releases, approved releases and history. The prior eight reconstructed original body-loss cases and proactive keyboard Retry/pending/repeated-failure/success controls pass.
  • Rejected c8b9dae2 negative replay: 8 FAIL / 8 controls PASS, with 16 non-selected tests. All failures are the deferred outside-focus post-frame assertions, two per list; all eight original body-drop controls pass. Substituted the exact rejected hook, the only production delta between heads, while retaining the unchanged reviewer probe/config. This is a production-source reconstruction, not a full rejected checkout. Restored the exact reviewed hook byte-for-byte afterwards.
  • 77/77 shared importer tests PASS: ErrorDisplayCompact, CatalogLoadError/CatalogView and DataTable, including the direct deferred-focus regressions.
  • 68/68 restored-head platform/probe tests PASS: Documents/Knowledge read-recovery, Documents table and Knowledge CRUD (36 tests), plus a second unchanged 32-test reviewer probe run after restoring the hook. Total: 200 distinct tests, 232 positive test executions.
  • Initial full negative replay also hit one 5s timeout and a subsequent fixture-cascade failure. Its log is retained, not counted as two additional source defects. Final focused replay changes no timeout/assertion/config and yields exactly the eight intended focus mismatches.

Remaining proof and handoff

Unrun: real browser/layout/native component suites, visual-aspect score-100 gate, screen-reader/AX speech, live backend/transport, runtime DE/FR, full platform/workspace suites, whole-workspace tsc, standalone SAST, manual round/manual-reference lint, original-main negative replay, and executed exact-head CI/cache provenance. jsdom establishes focus logic, not real-browser or assistive-technology behavior.

Passive final GitHub readback shows the same exact head with five Candidate source / Resolve source checks QUEUED, without conclusions. Not green and not merge-ready. No CI watch/rerun/cancel was performed. TALE-359 and existing authorized QA/merge owners retain the outstanding proof/admission and protected native review gates. Root's 08:15Z rule is honored by this explicit, independently observed exact-source B1 closure; a later blocking finding still overrides acceptance.

No push, merge, card/status move, native approval bypass, runtime start, infrastructure change, other-repository access or repository source edit. Evidence (unchanged probe, positive/negative logs, scoped checks, patches, hashes and head/CI readbacks) is retained in /agent/output/73dfb3ae-e21d-452a-b1d6-c918fa3886f4/. The source archive/dependency hardlinks are disposable and removed before delivery; shared clones and prior-task artifacts are preserved.

Scoped local gates

  • Typed lint/type-check PASS: oxlint --type-aware --type-check --threads=1, explicit 15-file scope spanning repaired hook, direct shared importers, legal-hold hosts/probe, Documents/Knowledge hosts and actual route importer. Exit 0, zero diagnostics; Go concurrency 2 and memory target 2 GiB. This is not a whole-workspace tsc claim.
  • Formatting PASS: oxfmt checks five changed/reviewer TS/TSX files; no source fix needed. Repair git diff --check PASS.

@yannickmonney

Copy link
Copy Markdown
Contributor Author

Merge state: this PR now conflicts with main in the manual register only. From agent #2, TALE-359 run d6483ad7.

#4294 merged as 69413520a. It inserted its coverage paragraph directly under ## Coverage map in services/platform/tests/manual/reference/automation.md, at the line where this PR inserts its own paragraph. At this head (53b422740033373dafbdf6a08697c8f0be70b12c), git merge-tree against main conflicts only in that file. No source file conflicts.

Resolution: keep main's paragraph and this PR's, as separate paragraphs. Five open PRs insert at that same line: #4276, #4319, #4310, #4309 and #4266. Placing this paragraph somewhere else would stop it colliding with them, for example after a related existing paragraph or as a table row beside its suite. Run bun run lint:manual before pushing.

The ACCEPT at this head covers source that a register-only merge leaves unchanged. The new head needs green CI and an interdiff confirmation that only the register changed. I pushed nothing.

@yannickmonney

Copy link
Copy Markdown
Contributor Author

CI is red at this exact head (53b422740033373dafbdf6a08697c8f0be70b12c), so it can't merge yet. TALE-683's source ACCEPT stands for the legal-hold lists. From agent #2, TALE-359 run b4da8395.

  • UI (job 111471836130) fails 1 of 5,430 tests, outside the legal-hold files: knowledge-entry-view-dialog.read-failure.test.tsx > KnowledgeEntryViewDialog when the version history read fails > moves the focus to the history when a background refresh heals it … (expect(element).toHaveFocus() on the history <section tabindex="-1">).
  • Likely cause, from reading the source (not run locally): this PR changes the shared packages/ui/src/hooks/use-focus-handoff.ts.
    • Before, the handoff ran whenever the node held focus as it unmounted.
    • Now it runs only if, one animation frame later, focus is null, body or still inside the node.
    • KnowledgeEntryViewDialog renders CatalogLoadError, which uses this hook. So a dialog whose focus lands elsewhere (for example, on the dialog itself) no longer hands it to the history section.
  • Also: since fix(platform): recover task agent Details read failures #4294 merged, this head conflicts with main in tests/manual/reference/automation.md, at the paragraph under ## Coverage map.

Needed: keep the knowledge-entry handoff working, either by narrowing the new guard or by adjusting that consumer with its test. Then merge main and confirm the full UI suite; CI has to be green at the new head before a merge. I pushed nothing.

@yannickmonney
yannickmonney force-pushed the fix/legal-hold-read-errors branch from 2b90617 to 07e5535 Compare October 9, 2026 15:01
@yannickmonney
yannickmonney added this pull request to the merge queue Oct 9, 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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Legal Hold tables turn settled read failures into empty queues with no retry

1 participant