Repository navigation
fix(sqlite): bound the online backup so backup create cannot hang forever - #137543
shojikumaru wants to merge 5 commits into
Conversation
|
🦞👀 Pull request received. I will update this pull request when review starts. ClawSweeper review completeClawSweeper finished reviewing this revision. The review result is being finalized. |
|
Codex review: blocked before merge. Reviewed September 5, 2026, 1:45 AM ET / 05:45 UTC. ClawSweeper reviewWhat this changesCentralizes SQLite copying behind progress limits and preserves contention errors across worker processes so backup commands can explain recovery options. Warning SQLite table change This PR modifies persisted SQLite tables in Merge readiness⛔ Blocked before merge - 6 items remain The previous diagnostic-assertion defect is resolved, and current main still lacks the backup bound. No remaining discrete patch defect was established; the explicitly deferred stall-policy decision remains. Priority: P1 Review scores
Verification
How this fits togetherOpenClaw snapshots live SQLite databases before creating archive, SQLite, and Git backups. The shared copy operation also supports read-only state inspection, producing a private database copy or an actionable failure. flowchart TD
A[Backup commands] --> C[Snapshot preparation]
B[Read-only state inspection] --> C
C --> D[Shared online copy]
D --> E{Progress within limits?}
E -->|Yes| F[Verified private snapshot]
E -->|No| G[Typed failure and recovery guidance]
Decision needed
Why: The contributor explicitly defers this operational tradeoff; tests demonstrate the chosen behavior but cannot establish the owner's tolerance for rejecting large converging copies. Before merge
Agent review detailsSecurityNone. PR surfaceSource +110, Tests +504. Total +614 across 9 files. View PR surface stats
Review metrics
Stored data modelPersistent data-model change detected: Merge-risk optionsMaintainer options:
Technical reviewBest possible solution: Keep one shared copy owner with an accepted stall policy, pinned validation, and typed recovery guidance; the new monitor and worker handoff justify growth beyond the removed duplicate lifecycle code. Do we have a high-confidence way to reproduce the issue? Unclear for a deterministic current-main reproduction: the contributor records an intermittent WAL commit-storm hang, while this read-only review confirms unbounded calls without rerunning that scenario. Is this the best way to solve the issue? Yes as a bounded mitigation at the shared owner: it consolidates lifecycle handling and preserves existing safeguards. A whole-worker deadline or restart-count limit would also penalize progressing copies; the elapsed cutoff still needs explicit acceptance. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 336b75a11d13. LabelsLabel changes:
Label justifications:
EvidenceWhat I checked:
Likely related people:
Rank-up movesOptional improvements that raise the rating; they are not merge blockers.
Rating scale
Overall follows the weaker of proof and patch quality. Workflow
HistoryReview history (7 earlier review cycles)
|
|
Thanks — the "real behavior proof" gap was the right call, and it changed the PR. The body is updated Post-fix proof through the actual CLI, built from this head ( On the reported version this hung with zero output past 180 s and left an orphan Being straight about what that run does and does not show: case 1 completed rather than tripping On the data-model item: there is no schema change, no schema-version bump, no new table, column, On the main-history item: that one was a DNS failure on the review side, so I cannot close it from Rank-up move applied: redacted terminal output from this head for both the contended and quiesced Two things I have deliberately left open for maintainer judgement rather than deciding myself, both in Human + AI disclosure: reproduction, implementation and review were produced by me working with AI |
1f0f073 to
ac1e9a5
Compare
|
Follow-up on the remaining items. Head is now Monotonic clock (P2) — done. The no-progress deadline is armed, compared and re-armed from
Data-model compatibility — proof added. No DDL, no schema-version bump, no new table/column/index; 30-minute policy (P1) — yours to set, not mine. Everything measured is in the body: elapsed time Human + AI disclosure: this delta was produced by me working with AI coding agents and passed three |
|
Head is now Preserve contention classification across the snapshot worker (P2) — done. The isolated read-only Regression One accepted tradeoff, named so it is a decision rather than an oversight: the parent validates the Production delta for this commit is +11 net (the worker lost its private Still open for you: the 30-minute no-net-progress ceiling. Both options from my previous comment Human + AI disclosure: this delta was produced by me working with AI coding agents and passed three |
Node restarts a stepped SQLite backup whenever another connection commits, so a source under continuous write never converges: measured 17406 backup steps with 17404 restarts and remainingPages never falling below 1558759 of 4467250. Nothing bounded that livelock, and nothing above it was bounded either, so `openclaw backup create` waited forever and printed nothing while leaving an orphan read-only worker process behind. The duplicated "consistent online copy of a live source" policy in sqlite-snapshot.ts and sqlite-readonly-location.ts now lives in one owner that pins the read transaction, runs the caller's validation inside it, and fails the backup once it has taken many consecutive steps without reaching a new minimum remaining-page count. Progress, not wall clock, decides, so a slow backup that is still copying is never interrupted and the false-timeout regressions fixed in openclaw#122213 and openclaw#122235 do not return. The failure names the concurrent writer as the cause and points at the paths that still work. The worker launches also gain a generous timeout and kill signal so a wedged child can no longer strand the CLI with no output. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Independent review of the first commit found the bound itself could abort a backup that was still copying. Its counter only reset when a step reached a new all-time minimum, but a restart makes Node replay remainingPages from the top, so every step of the recovering pass looked like non-progress until it beat the old minimum. At the default hundred pages per step that recovery costs pagesAlreadyCopied/100 steps, measured linear at 43, 169 and 507 steps for 5279, 21108 and 63319 pages: past roughly four gigabytes a single well-timed commit killed the whole snapshot, and blamed a writer that may have stopped. The bound now watches two things that are both independent of database size. Restarts since the copy last reached a new all-time minimum count wasted passes, and steps since remainingPages last fell catch a copy that stopped moving. A backup that is advancing lowers remainingPages every step, so it pins both counters at zero however large it is and however late a restart lands, while every non-converging shape still fails quickly. The worker launches lose the wall-clock timeout added earlier. It was a fixed deadline over work of unbounded size, the defect openclaw#122213 and openclaw#122235 removed, and it asserted contention as the cause without establishing it. With the backup itself bounded the child is already guaranteed to exit. The failure text no longer points at `openclaw backup sqlite create`, which requires a repository argument and reaches this same owner through createOpenClawSnapshotCopy, so it would fail identically. It now names quiescing writes, and `openclaw backup create --only-config` for configuration. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
node:sqlite restarts its stepped backup whenever another connection commits, so a continuously written source never converges and `openclaw backup create` hung forever. The previous commit bounded that with a restart count and a step count. Measuring the counters against real WAL sources under a concurrent writer shows a restart count cannot separate the two populations: backups that completed peaked at 2 to 38 restarts without a new all-time low here and at 207 to 257 on a slower machine, while stalled runs reached 803 to 2,001, and the requirement grows with database size, so 1,000 says nothing at the reported 4,467,250 pages. Discarded work behaves the same way - completing runs threw away 9.7 to 107 full copies, stalled ones 1,148 - so bounding wasted pages instead would false-abort the same runs. Elapsed time since the copy last reached a new all-time low does separate them, and it is the cost the operator actually pays: completing runs never went more than 1.1s without a new low at 21k, 63k or 211k pages, while stalled ones sat 190s and counting, so unlike the counters this quantity barely moves with size. Thirty minutes is a product decision on top of that: it is the smallest value that cannot abort a healthy copy of the reported database part-way through a pass, since one uncontended full copy of it measured 733.8s. A smaller number fails faster on the reported livelock at the risk of aborting a slow but converging copy of a very large database. The plateau guard for a stream that copies nothing at all stays as it was. Measurements: .artifacts/delta5-measurements.md. The failure text also stops naming a backup command. This owner is reached from read-only state-database opens and ownership checks, so it now reports cause plus a generic remedy and exports a predicate; the backup snapshot boundary adds the `--only-config` suggestion when it recognises that typed failure, and a sibling test asserts an unrelated failure does not get it. Production LOC vs d2a616b: +140/-52 (net +88); tests +406/-0. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Date.now() can step backward under an NTP or operator correction, and the online backup deadline read from it would wait the whole correction on top of its budget. Read performance.now() instead, as the restart health wait already does, and cover a ten-minute backward step.
afac782 to
ebd2fa8
Compare
The read-only snapshot worker serialized only an error message, so a contended online backup that ran in the child came back to the parent as a plain error and the backup command lost its --only-config remedy. The worker now reports a closed failure code, and the parent rebuilds the typed cause.
ebd2fa8 to
d82ca04
Compare
|
Independent follow-up on head I found two gaps that need resolving before preparing this PR.
I ran the existing current-main read-only location, cancellation and worker suites: 87 passed, 1 Linux-only test skipped on macOS. I also stopped actual snapshot child processes with SIGSTOP, then exercised cancellation and the existing timeout boundary. Both settled only after SIGKILL/close, removed the owned staging root, preserved source bytes and released the writer lock. The timeout probe shortened only the child-process timeout at the test boundary, from the recorded 31,000 ms production value to 250 ms. The nonduplicative continuation is on this same PR: first retain the read pin in a current-main failing reproduction, then carry only the necessary repair into the existing lifecycle owner. Preserve its cancellation/cleanup guarantees and prove healthy slow progress and a genuinely non-progressing child separately. The 30-minute callback constant alone cannot establish those guarantees. These are current-main diagnostic receipts, not passing validation of the contributor head. No contributor code was executed on the host, and no contributor-branch changes, replacement PR or merge were made in this review. |
|
Thank you for the follow-up, and for running the pinned path yourself — you were right, and I want to set the record straight before closing. What I re-measured (current
So: I cannot produce a failing case through the pinned production path, and the lifecycle owner you pointed at already carries the guarantees this PR was reaching for. The right outcome is to close this PR rather than carry a repair whose premise does not hold. If a reproduction of the 8.1 orphan ever turns up that survives the 30 s bound, I would be glad to take it, but it should start from that evidence and land in Probe scripts and raw logs (pinned/unpinned contrast, the real-function matrix) are kept and I can attach them if useful for #134215. Investigation by Sho (human) + Alpha (AI agent). |
Closes #134215
What Problem This Solves
openclaw backup createhangs forever with zero output while the Gateway is running, produces noarchive, and leaves an orphan
sqlite-readonly-location.workerprocess behind.openclaw backup git create --allfails the same way. The documented first-class recovery command is unusable on a liveinstall — a silent, indefinite non-outcome on a default path.
The cause is not a lock the snapshot waits on. It is a livelock in the stepped online backup.
node:sqliterestarts a stepped backup whenever another connection commits, so a source undercontinuous write never converges. Measured on current
main, against a live database with oneconcurrent writer process:
17 406 steps, 17 404 of them restarts, and
remainingPagesnever falling below its starting value.(This probe passed
rate: 64for finer-grained samples; production uses Node's default of 100 pagesper step, so step counts scale accordingly — the page counts and the frozen minimum do not.)
sqlite-snapshot.ts:657already named the hazard — "Node restarts stepped backups on concurrentwrites" — but nothing bounded it, and nothing above it was bounded either:
sqlite-snapshot.ts:662andsqlite-readonly-location.ts:445—sqlite.backupwith no bound.sqlite-readonly-location.ts:661—execFilelaunching the snapshot worker with only{ encoding: "utf8" }: notimeout, nokillSignal. Its callback fires on child exit, so achild stuck inside its own backup strands the parent forever. That is the orphan process in the report.
sqlite-readonly-location.ts:686—spawnSync, same omission.MAX_SNAPSHOT_ATTEMPTS(:19, loops at:483/:563) bounds the retry count only, so oneunbounded attempt makes the whole loop unbounded.
Why This Change Was Made
The obvious repair — a timeout on the backup — is the wrong one here, and this repo has already
learned that: #122213 and #122235 replaced timeouts with no-progress detection for the archive
stream after legitimately slow work was being false-timeouted, and that watchdog still lives at
backup-create-stream.ts:92-121. A 13.7 MB snapshot is legitimately slow; a livelocked one is not.node:sqlite'sprogresscallback distinguishes them exactly: a step that copies pages lowersremainingPages, and a restart does not.So the bound is on net page progress, not wall clock:
The duplicated "consistent online copy of a live source" policy — open read-only,
busy_timeoutand
trusted_schemapragmas,BEGIN, validate, back up,ROLLBACK, close — was copied betweensqlite-snapshot.ts:648-668andsqlite-readonly-location.ts:441-451. It now lives in one owner,src/infra/sqlite-online-backup.ts, which takes the caller's in-transaction validation as a hook sothe snapshot path keeps running
schema_version, the vec extension load, the integrity check, andoptions.validateinside the same pinned read transaction, in the same order. The consolidationalso fixes a latent bug:
createOnlineReadOnlyBackuppreviously ranROLLBACKonly on the successpath, so a mid-backup throw left the read transaction open until close.
That owner bounds the copy by elapsed time since it last reached a new all-time-low remaining
page count, read from the monotonic clock (
performance.now(), the repo's own precedent insrc/cli/daemon-cli/restart-health.ts:385) at the top of the progress callback, plus an unchangedplateau guard for a stream that copies nothing at all. Deliberately not a
setTimeout: theprogress callback is the only place production regains control of an in-flight
node:sqlitebackup, so a timer could at best set a flag the next callback reads, and it would make the bound
untestable without real elapsed time.
Getting here took three attempts, and the discarded ones are worth stating because they are the
obvious designs. Counting steps against an all-time minimum is wrong: after a restart Node
replays
remainingPagesfrom the top, so recovery costspagesCopied / ratesteps that look likenon-progress while the copy runs at full speed — measured at exactly
pagesCopied ÷ 100. Countingrestarts, or wasted work, is also wrong, and that one is only visible with instrumentation:
both quantities overlap between backups that finish and backups that never do, and both grow with
database size, so neither can be calibrated. See the table under Evidence.
The failure names the cause and a way forward instead of dead-ending: the source is being written
concurrently, typically by a running Gateway, and quiescing those writes lets the same snapshot
complete. The remediation text deliberately does not point at
openclaw backup sqlite create,because that command reaches the same owner (
commands/backup-sqlite.ts:85->snapshot/local-repository.ts:287->createOpenClawSnapshotCopy->createVerifiedSqliteSnapshot) and would hit the same bound.The worker launches gain no deadline of their own. An earlier revision added a 30-minute
timeout+killSignalthere; review was right that a fixed cap over the whole workerregardless of progress is the defect fix(backup): prevent false archive stalls during active traversal #122213 and fix(backup): keep large file reads from false-timeout #122235 removed, and that mapping its expiry to
"concurrent writes" asserts a cause it never established. With the backup inside the child bounded
and rearmed by observed progress, the child is already guaranteed to exit.
No new configuration surface, no schema change, no change to
MAX_SNAPSHOT_ATTEMPTSsemantics, the#130962 audit-lease flow, restore/archive formats, or the #127855 child-process lock isolation.
User Impact
openclaw backup createandopenclaw backup git createnow terminate instead of hanging forever.Under continuous writes they either complete — measured above, 66 s with a live writer — or fail with
an actionable message naming the cause, rather than producing nothing and stranding an orphan worker.
When they can copy the database they behave exactly as before, including on large, slow snapshots.
See the open question below for the one case where the current threshold could still cut a converging
copy short.
Evidence
All measurements are real runs, not estimates.
Reproduction of the defect on current
main(d2a616bdf373), Linux 6.8.0-138 x86_64, Nodev24.20.0, SQLite 3.53.4, inside a
node:24Docker container. The exact production chain(
createVerifiedSqliteSnapshot->withSqliteSnapshotSource->prepareSqliteReadOnlyLocation) wasdriven against a live database that a separate writer process kept committing to:
prepareSqliteReadOnlyLocationcreateVerifiedSqliteSnapshotReported honestly as a race: the same scenario hung once and completed on a rerun. That is why the
defect is intermittent for operators and hard to reproduce, and it is why the fix is a bound rather
than a change to the copy strategy. The instrumented run quoted above pins the mechanism, and the
control — the identical snapshot with the writer stopped — completes in 733.8 s on the grown database.
Real
openclaw backup createon this PR head, built from the branch (pnpm build) and run in anode:24container against a 145 MB WAL state database created byopenclaw doctor, with a separateprocess committing continuously — the reported scenario:
On the reported version this command hung with zero output past 180 s and left an orphan
sqlite-readonly-location.worker. On this head it finishes while the writer is still committing,produces a real archive, and leaves no orphan process.
Two honest notes. First, case 1 completed rather than tripping the new bound, so this run proves the
hang is gone and the healthy path works under contention — it does not exercise the bounded-failure
message end-to-end through the CLI; that path is covered by the unit and owner-boundary tests below,
because forcing it through the CLI means driving the 30-minute deadline. Second, case 1 printed
[sqlite/transaction] SQLite transaction lock wait failedandWarning: the backup outcome could not be recorded: database is locked— the archive was still produced; that is a separate contended-writepath recording the outcome, not the snapshot, and this PR does not change it.
Calibration. Twelve instrumented runs against real WAL sources with a real
node:sqlitebackupand an in-process writer at a fixed cadence:
Read the last three columns across the two groups. Restart counts overlap (completing runs needed
up to 38; a stalled one sat at 406). Discarded work overlaps far worse — a run that finished in
18.7 s had thrown away 107 full copies, more than two of the stalled runs. Both quantities also grow
with database size, so a constant calibrated at 63 K pages means nothing at 4,467,250. The last column
does not overlap at all: every completing run reached a new low within 38–1,076 ms, every stalled
run sat 120–320 seconds without one. That is the axis the bound uses.
Two-sided proof, Linux Docker. With both bounds set to
POSITIVE_INFINITY— the pre-fixunbounded behaviour — exactly the four non-convergence cases fail and every anti-false-abort case
stays green:
The suite covers both directions: shapes that must abort (restart on nearly every step, restart every
few steps without beating the best, a stream that never moves, and headway so slow it costs a whole
pass per page) and shapes that must not (a long strictly-decreasing copy, a single restart followed by
recovery far longer than the bounds, and many restarts each reaching a new all-time low). One case
drives a real, unstubbed
node:sqlitebackup to confirm that throwing fromprogressrejects it.Backward clock correction (added after review). The deadline is read from
performance.now(),so a wall-clock step cannot postpone it. The added test arms the deadline, sets the system clock back
ten minutes, then feeds one-minute livelock cycles, and pins the abort at cycle 31 (thirty one-minute
cycles exhaust the budget). On the previous head, which read
Date.now(), the same test fails with thedeadline postponed by exactly the rollback:
node:sqliteprogress contract, rechecked upstream. Node v26.x reference forsqlite.backup(sourceDb, path[, options])(added in v23.8.0 / v22.16.0), quoted:
And the same contract exercised against the real API, no OpenClaw code involved (Node v26.5.0,
SQLite 3.53.3, 16-page WAL source):
The last line is the restart the docs describe: a commit from another connection sends
remainingPagesback up (12 → 15), which is exactly the signal the bound keys on. The docs do not promise that a throw
from
progressrejects the backup; that is observed behaviour, and the unit testrejects when a real node:sqlite progress callback throwspins it on every CI run (Node 24) so a change in Node would showup there rather than in production.
Data-model compatibility. There is no DDL in this diff, no schema-version bump, no new table, column,
or index; the review tool's "SQLite table change" flag matched the
PRAGMA schema_versionandPRAGMA trusted_schemareads moving fromsqlite-snapshot.ts/sqlite-readonly-location.tsintothe owner, unchanged in meaning. Copy fidelity, measured through
backupSqliteOnlineon a WAL sourcewith two
STRICTtables, an index, a view, a trigger,user_version = 41and 500 rows:The copy's
schema_versioncookie is written by SQLite's own backup API (sqlite3_backup_finishsetsthe destination's previous cookie + 1) and the same
sqlite.backupcall did that onmain, so nothingabout how a copy is read back or upgraded has changed.
Contention classification across the snapshot worker (added after review). A rollback-journal
source is snapshotted inside the isolated read-only worker, which used to serialize only an error
message; the parent rebuilt a plain
Error, so a contended copy on that path reached the backupcommand without its typed cause and lost the
--only-configremedy. The worker now reports a closedfailure code (
failure: "contention" | "error") next to the message, the parent validates the closedshape and rebuilds the typed cause through one small owner factory (the error class stays private),
on both the
execFileandspawnSyncroutes since they share the same adopter. Regressionpreserves online-backup contention across the isolated worker boundarydrives the publicprepareSqliteReadOnlyLocationin a child process on a rollback-journal source, with aNODE_OPTIONS --importpreload that replacesnode:sqlite'sbackupwith a wedged progress stream sothe real owner's plateau guard throws a real contention error inside the real grandchild worker; the
child reports whether
isSqliteBackupContentionErrorholds in the parent:Suites. On Linux (Docker
node:24),sqlite-online-backup,sqlite-readonly-location,sqlite-snapshot,backup-create,backup-create.legacy-audit-lease,backup-archive-publication,and
state-migrations.source-snapshotall pass: 218 passed, 2 skipped, 7 files, including theexisting concurrent-writer coverage at
sqlite-readonly-location.test.ts:652,:725, and:768,unchanged.
node scripts/check-changed.mjspasses for every changed file.One test,
backup-create.test.ts > sanitizes every in-state symlink and hardlink alias of a canonical agent SQLite DB, times out at 120 s on macOS. It fails identically on the unmodified base commit onthe same machine, and passes on Linux, so it is pre-existing local slowness rather than a regression
from this change.
Production LOC delta.
git diff --numstatagainst the base:Production net +101 (new owner +118, backup boundary +7,
sqlite-readonly-location.ts−7,worker −3,
sqlite-snapshot.ts−14); tests +502. Roughly half the owner's lines are the comment recordingwhat the constant was calibrated against; the rest is the bound, the typed error and its predicate.
Absorbing the duplicated backup policy into the owner is the net-negative half of the diff and is what
keeps the production delta this small.
Open design question for maintainers
The 30 minutes is a product decision, not a measurement, and it is yours to set.
An online backup of a continuously written database has no guaranteed finish time —
node:sqlitediscards the whole pass every time another connection commits — so the only real question is how long
an operator should wait before being told to quiesce writes. The measurements above rule out every
alternative axis: restart counts and wasted work both overlap between the populations and grow with
size. Elapsed time since the copy last beat its own best is the one quantity that separates them.
A caveat I want to put in front of you rather than bury. Independent review ran the experiment my
own table lacks — holding contention constant relative to pass duration instead of at a fixed writer
interval — and found the gap without a new low is flat at 0.20–0.24 pass-durations across
20 K/60 K/200 K pages while the absolute figure grows 65 → 138 → 526 ms. Uncontended pass duration is
linear in size. So the size-invariant quantity is the gap measured in pass-durations, and an
absolute millisecond threshold silently re-introduces a size dependence. Real completing backups in
that experiment needed up to 11.6 pass-durations; against the 733.8 s control for the reported
4,467,250-page source, 30 minutes buys only 2.45. My table cannot refute this — its cadence was
raised with size (55 ms at 21 K, 160 ms at 63 K, 2 s at 211 K), so it contains no high-contention
large-database point that completed.
Concretely: at the reported database size this constant could abort a copy that would have
finished. The suggested fix is to derive the budget from observed pass cost inside the callback
(
totalPagesis already in the payload) and keep 30 minutes as a ceiling rather than the whole rule.I have not done that here because it changes the policy surface and I would rather you chose it.
The other disclosed cost: the reported livelock now fails after 30 minutes rather than in ~1.2 s.
A smaller number buys a faster failure there and increases the false-abort risk above. If you want the
fast abort back, the honest options are a shorter deadline or a second, explicitly-owned cap — not a
return to counting restarts, which the data refutes.
Known gaps I would fix on request, none of which affect the hang itself: the copy is still silent
while it runs, so the reported "zero output" becomes "silent for up to the deadline, then a good
error" — the repo's own
backup-create-stream.ts:92-121progress reporting is the precedent; theretry paths (
MAX_SNAPSHOT_ATTEMPTS,withSqliteSnapshotSource) each restart the window rather thansharing one deadline; and on the prepared-copy branch the message names the
staging path rather than the operator's database.
One more result worth your attention on its own: at 211 K pages under a 160 ms writer, nothing
completed at all within 400 s, and at 400 ms it was still at 77 % after 401 s. That suggests a large,
continuously written database may simply have no finishing time for an online backup, and that saying
so promptly is the useful behaviour rather than a failure of this bound.
CI status
Head
d82ca04eb9is the branch rebased ontod5992278b3(main as of 2026-09-05) so that #137999'serrcode suffix in the snapshot worker message and this PR's failure tag coexist; the five commits are
unchanged in content, and the expectation in the upstream worker diagnostics test (added by #137999) now
includes the failure tag. Earlier checks below were taken on the pre-rebase heads and re-run on this one.
Rebased onto
8c6653a78dto pick up thecreateBackupResourceInventorysignature change(
configPath/oauthDir→configPaths/oauthDirs), which my new test had to follow;pnpm check:test-typesis green.The remaining red checks —
checks-node-compact-small-49,-small-55,-large-2and theopenclaw/ci-gatethat aggregates them — are already red onmainand unrelated to this change.Scoped proof, same machine, same command:
Both failures are
full release candidate discovery CLI > marks bounded candidate evaluation unavailable ...and... marks a selected candidate with a missing constituent unavailable, inrelease-candidate tooling that this PR does not touch. Per
AGENTS.mdI have not tried to fix themhere.
security-fastalso failed on an earlier push and is likewise red onmain(run 33813389736); it is green on this head.
Notes
clawsweeper:no-new-fix-pron the issue reads as "does not recommend queueing a new automatedfix PR", and its review asked for a live reproduction before an automated patch. That reproduction
is above and was posted to the issue before this PR was opened; this is a human-authored PR, not a
queued automated one. Happy to stand down if that reading is wrong.
createVerifiedSqliteSnapshotboundary is open on the issue; this PR implements the extracted-ownerversion and can be reshaped.
Human + AI disclosure: the reproduction, analysis, implementation, and review were produced by me
working with AI coding agents, across four independent read-only review passes plus a second round
against the settled commit. Two of those passes returned NO-GO on the first implementation and are
the reason the bound was rebuilt and the wall-clock timeout dropped; their measurements are quoted
above. Every number in this PR comes from the runs shown.