Skip to content

fix(wiretap): safely retry unresolved cache entries - #190

Merged
steipete merged 5 commits into
openclaw:mainfrom
Nicolas0315:fix/wiretap-retry-unresolved-cache
Sep 4, 2026
Merged

fix(wiretap): safely retry unresolved cache entries#190
steipete merged 5 commits into
openclaw:mainfrom
Nicolas0315:fix/wiretap-retry-unresolved-cache

Conversation

@Nicolas0315

@Nicolas0315 Nicolas0315 commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Unresolved Desktop cache files were recorded as imported, so unchanged files stayed skipped after channel metadata arrived. The original v2 migration copied those ambiguous checkpoints. Retrying a mixed batch also rewrote its already-resolved messages, allowing old cache content to replace a newer archived edit and its search, attachment, and mention data.

This completes the existing retry fix with one recheck of v1/v2 checkpoints, while retaining the legacy index so an upgrade does not repeat first-import pruning. Unresolved payloads remain retryable in default cache, JSON/context, and full-cache modes. Message writes preserve newer edited revisions and tombstones inside the same transaction as related rows; identical Wiretap events are not appended twice. Equal-revision metadata refreshes and genuinely newer cache edits remain supported.

Validation includes the complete importer/store suites, their race run, SQLC v1.31.1 regeneration, docs build, and a 30-command proof using stock built CLIs, synthetic Desktop cache files, and real SQLite. A small fixture helper uses the production Store API to seed a newer archived message and its related rows.

Behavior Before After
v1 or v2 archive after missing channel metadata arrives 1 message, 1 event; second message remains missing 2 messages, 2 events; original event preserved
Unchanged mixed batch after a newer archive edit Old content replaces the newer message and removes its related rows Newer content, FTS, attachment, and mention remain intact
Genuinely newer cached edit Supported Still supported
Replay after deletion Could restore stale searchable data Tombstone and FTS state preserved
JSON/context or full-cache payload after later metadata 0 messages 1 message and 1 event
Repeat completed import Rows and events unchanged; SQLite integrity ok

The regression tests first reproduced missing legacy rows, stale-message replacement, and the context/full-cache retry gaps. They also cover retaining legacy archive rows during migration and preserving the first events when a previously partial batch finally resolves.

Current main is integrated without rewriting either contributor commit. The first CI run caught two test-fixture lint issues (unchecked JSON encoding and constant formatting). Both are fixed; full local lint now passes. All exact-head checks passed for the final full-cache correction at c84c341dbe97a05e897f5bc717265237465ac7cb: CI, Docker, CodeQL, and secret scanning. A subsequent review found that one unresolved full-cache payload invalidated every scanned-file checkpoint. The correction tracks each message’s source files, so only unresolved sources remain retryable and completed files keep their checkpoints.

Thanks @Nicolas0315 (Nicolas Hidemaru Ogoshi / 大越ニコラス秀丸) for the original retry work and investigation.

Curated CLI and archive evidence

Rebuilt and ran the stock CLI at c84c341dbe97a05e897f5bc717265237465ac7cb. Commands used an isolated synthetic config and cache directory. This excerpt selects relevant JSON fields from actual command output; fixture paths and timestamps are omitted. All 30 CLI invocations exited 0.

Command in the legacy-v1 sequence: discrawl --config fixture.toml --json wiretap --path fixture-cache. The first main import archives the known message; the second encounters an unresolved message; metadata is added before the third. Candidate then upgrades that same archive and repeats:

[
  {
    "binary": "main",
    "exit": 0,
    "files_scanned": 1,
    "files_unchanged": 0,
    "messages": 1,
    "skipped_messages": 0
  },
  {
    "binary": "main",
    "exit": 0,
    "files_scanned": 1,
    "files_unchanged": 1,
    "messages": 0,
    "skipped_messages": 1
  },
  {
    "binary": "main",
    "exit": 0,
    "files_scanned": 1,
    "files_unchanged": 2,
    "messages": 0,
    "skipped_messages": 0
  },
  {
    "binary": "candidate",
    "exit": 0,
    "files_scanned": 3,
    "files_unchanged": 0,
    "messages": 2,
    "skipped_messages": 0
  },
  {
    "binary": "candidate",
    "exit": 0,
    "files_scanned": 0,
    "files_unchanged": 3,
    "messages": 0,
    "skipped_messages": 0
  }
]

Actual SQLite snapshots (select id,content,edited_at,deleted_at from messages order by id) after metadata arrives:

{
  "legacy_v1_before": [
    [
      "333333333333333346",
      "Sapphire already archived",
      null,
      null
    ]
  ],
  "legacy_v1_after": [
    [
      "333333333333333346",
      "Sapphire already archived",
      null,
      null
    ],
    [
      "333333333333333347",
      "Sapphire newly recovered",
      null,
      null
    ]
  ]
}

The original event remains byte-for-byte equal; event count changes 1 → 2 and stays 2 on repeat. discrawl --config fixture.toml --json search '"newly recovered"' returns one row with content Sapphire newly recovered. The v2 upgrade produces the same result.

For the mixed-batch case, the production Store helper seeds a newer edited message plus one attachment and one mention. The old PR CLI replaces it with old cached message and removes both related rows. Candidate retains Sphinx authoritative newer message, new.txt, and the mention; actual search Sphinx returns that retained message. A later cache edit with a newer timestamp is accepted. After production Store deletion and another import, the archived deletion timestamp and FTS state remain unchanged. All final SQLite integrity checks return ok.

Full-cache checkpoint correction

A final discussion review identified over-broad full-cache retry: with one unresolved payload, completed files were rescanned and their messages rewritten on each invocation. The correction preserves source-file provenance while still reconciling channel metadata globally. Every file containing an unresolved message stays retryable, including duplicate copies; other completed fingerprints remain imported.

An additional 16 actual stock-CLI invocations used discrawl --config fixture.toml --json wiretap --full-cache --path fixture-cache with synthetic files and SQLite. Selected observed output on the second unchanged import:

Fixture Before files_scanned / messages Corrected files_scanned / messages After late metadata Final repeat
One completed file plus one unresolved file 2 / 1 1 / 0 2 archive rows, 2 events 0 files scanned
One completed file plus two copies of the unresolved payload 3 / 1 2 / 0 2 archive rows, 2 events 0 files scanned

All SQLite integrity checks returned ok. The new mixed-file regression failed before the correction and passes afterward. The original 30-command upgrade/replay proof also passes again, as do go test ./..., focused race coverage, full static analysis and the docs build.

@clawsweeper

clawsweeper Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

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

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@clawsweeper clawsweeper Bot added merge-risk: 🚨 message-delivery 🚨 Merging this PR could drop, duplicate, misroute, suppress, or wrongly target messages. P1 Urgent regression or broken agent/channel workflow affecting real users now. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Aug 26, 2026
@clawsweeper

clawsweeper Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Codex review: blocked before merge. Reviewed September 4, 2026, 7:57 AM ET / 11:57 UTC.

ClawSweeper review

What this changes

This PR makes Discord Desktop Wiretap retry cache messages whose channel metadata was initially unavailable while preserving newer or deleted archived messages, deduplicating events, and checkpointing completed full-cache files.

Regression provenance

Possible regression — probable (reviewed change; failure trace). No predecessor PR is attributed.

Merge readiness

Blocked before merge - 1 item remains

The prior full-cache checkpoint finding is resolved on the current head, with focused regression coverage and credible real CLI/SQLite proof. The PR remains necessary and is ready for normal merge gating.

Priority: P1
Reviewed head: c84c341dbe97a05e897f5bc717265237465ac7cb

Review scores

Measure Result What it means
Overall readiness 🦞 diamond lobster (5/6) The previous concrete checkpoint blocker is resolved with targeted coverage and strong production-boundary CLI/SQLite evidence.
Proof confidence 🦞 diamond lobster (5/6) Sufficient (terminal): The changed production owners are the Wiretap importer and transactional archive writes; the supplied after-fix stock-CLI runs exercise them with Desktop cache inputs and real SQLite, showing retry recovery, stable repeat scans, and preserved data.
Patch quality 🦞 diamond lobster (5/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Verified Sufficient (terminal): The changed production owners are the Wiretap importer and transactional archive writes; the supplied after-fix stock-CLI runs exercise them with Desktop cache inputs and real SQLite, showing retry recovery, stable repeat scans, and preserved data.
Evidence reviewed 5 items Prior finding fixed: The final commit tracks each parsed message’s source files and marks only files contributing unresolved messages retryable, fixing the prior over-broad full-cache checkpoint behavior.
Checkpoint decision: Completed files retain imported fingerprints, while source files for unresolved messages are retained as skipped and retried on the next scan.
Regression coverage: The added replay tests cover legacy indexes, retries across input modes, preservation of newer/deleted messages, event stability, and mixed completed/unresolved full-cache files.
Findings None None.
Security None None.

How this fits together

Wiretap imports Discord Desktop cache artifacts into Discrawl’s local SQLite archive. It turns cache payloads into message records and events, while per-file checkpoints determine which artifacts are retried later.

flowchart LR
  A[Desktop cache files] --> B[Wiretap importer]
  B --> C[Channel metadata lookup]
  C --> D[Archive write decisions]
  D --> E[SQLite messages and events]
  C --> F[Per-file checkpoints]
  F --> G[Later cache scans]
Loading

Before merge

  • Resolve merge risk (P1) - The final-head test workflow is still in progress, so the exact reviewed commit should pass it before merge.
Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Production and test scope production +136/-6, tests +256/-4, docs +4 Most of the change is targeted regression coverage for a bounded importer and archive-write reliability fix.

Merge-risk options

Maintainer options:

  1. Decide the mitigation before merge
    Merge the focused importer and store safeguards after the exact-head test workflow completes successfully, retaining the source-specific full-cache checkpoint behavior and regression coverage.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Technical review

Best possible solution:

Merge the focused importer and store safeguards after the exact-head test workflow completes successfully, retaining the source-specific full-cache checkpoint behavior and regression coverage.

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

Yes. The PR provides a clear source-backed path and recorded current-main versus candidate CLI/SQLite traces for the unresolved-metadata and repeat-scan cases.

Is this the best way to solve the issue?

Yes. Per-source retry checkpoints plus transactional preservation of newer revisions and tombstones is a narrow solution to the reported importer behavior.

AGENTS.md: found, but no applicable review policy affected this item.

Codex review notes: model internal, reasoning high; reviewed against b262f754180d.

Labels

Label changes:

  • add rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster. Replaced prior rating: 🦐 gold shrimp.
  • remove rating: 🦐 gold shrimp: Current PR rating is rating: 🦞 diamond lobster, so this older rating label is no longer current.
  • remove merge-risk: 🚨 availability: Current PR review selected no merge-risk labels.

Label justifications:

  • P1: The PR repairs a Wiretap archive-import failure that could leave recoverable Discord Desktop messages absent or repeatedly rescan cache files.
  • rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster. Replaced prior rating: 🦐 gold shrimp.
  • status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action. Sufficient (terminal): The changed production owners are the Wiretap importer and transactional archive writes; the supplied after-fix stock-CLI runs exercise them with Desktop cache inputs and real SQLite, showing retry recovery, stable repeat scans, and preserved data.
  • proof: sufficient: Contributor real behavior proof is sufficient. The changed production owners are the Wiretap importer and transactional archive writes; the supplied after-fix stock-CLI runs exercise them with Desktop cache inputs and real SQLite, showing retry recovery, stable repeat scans, and preserved data.

Evidence

What I checked:

  • Prior finding fixed: The final commit tracks each parsed message’s source files and marks only files contributing unresolved messages retryable, fixing the prior over-broad full-cache checkpoint behavior. (internal/discorddesktop/import.go:378, c84c341dbe97)
  • Checkpoint decision: Completed files retain imported fingerprints, while source files for unresolved messages are retained as skipped and retried on the next scan. (internal/discorddesktop/import.go:418, c84c341dbe97)
  • Regression coverage: The added replay tests cover legacy indexes, retries across input modes, preservation of newer/deleted messages, event stability, and mixed completed/unresolved full-cache files. (internal/discorddesktop/import_replay_test.go:164, c84c341dbe97)
  • Real behavior proof: The PR body records after-fix stock-CLI Wiretap runs against synthetic Desktop cache inputs and real SQLite, including repeat full-cache scans, preserved archive rows/events, and SQLite integrity checks. (internal/discorddesktop/import.go:363, c84c341dbe97)
  • CI status: Completed checks shown in the supplied context passed; the test workflow remains in progress on the exact reviewed head. (c84c341dbe97)

Likely related people:

  • steipete: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

History

Review history (15 earlier review cycles; latest 8 shown)
  • reviewed 2026-08-29T22:04:25.920Z sha 73e18c0 :: needs real behavior proof before merge. :: [P1] Avoid appending duplicate events during the v2 rescan
  • reviewed 2026-08-30T04:52:08.308Z sha 73e18c0 :: needs real behavior proof before merge. :: [P1] Preserve event history during the v2 migration
  • reviewed 2026-08-30T13:11:07.200Z sha 73e18c0 :: needs real behavior proof before merge. :: [P1] Preserve event history during the v2 migration
  • reviewed 2026-08-30T22:06:22.902Z sha 73e18c0 :: needs real behavior proof before merge. :: [P1] Preserve event history during the v2 migration
  • reviewed 2026-09-03T13:39:15.442Z sha 33f4f2d :: needs real behavior proof before merge. :: [P1] Reprocess legacy unresolved cache entries
  • reviewed 2026-09-04T08:59:55.322Z sha 33f4f2d :: needs real behavior proof before merge. :: [P1] [P1] Reprocess legacy unresolved cache entries | [P1] [P1] Preserve newer messages when retrying mixed cache batches
  • reviewed 2026-09-04T11:27:29.105Z sha c0703c3 :: needs real behavior proof before merge. :: none
  • reviewed 2026-09-04T11:43:28.490Z sha d7c1722 :: blocked before merge. :: [P2] Keep completed full-cache files checkpointed

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. status: needs maintainer proof decision A ClawSweeper-authored PR needs a maintainer proof capture or override decision. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. merge-risk: 🚨 message-delivery 🚨 Merging this PR could drop, duplicate, misroute, suppress, or wrongly target messages. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. status: needs maintainer proof decision A ClawSweeper-authored PR needs a maintainer proof capture or override decision. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. merge-risk: 🚨 message-delivery 🚨 Merging this PR could drop, duplicate, misroute, suppress, or wrongly target messages. labels Aug 26, 2026
レビュー指摘 [P1] Preserve event history during the v2 migration への対応。

## 問題

`wiretapFileIndexScope` を v1 から v2 へ変えると、`loadScanState` が読むキーが
変わるため v1 の checkpoint が全て消えたように見え、取り込み済みキャッシュ
ファイルまで再走査される。解析済みメッセージは `AppendEvent: true` を立て、
store はそのミューテーションごとに無条件でイベントを挿入するため、message は
upsert されて件数が変わらない一方 `message_events` だけが重複追記される。
既存アーカイブのイベント履歴がアップグレードで複製される。

## 対応

イベント追記を抑止するのではなく、索引を引き継ぐ形にした。v2 の索引が未作成の
ときだけ v1 を読み、**imported の checkpoint だけを** v2 へ移す
(`migrateFileIndexFromV1`)。

- 取り込み済みファイルは checkpoint が残るので再走査されない → イベント複製なし
- v1 は解決できなかった項目も imported として記録していたため区別できないが、
  本PRの `checkpointScannedCandidates` が今後は skipped で記録するので、
  以降の未解決項目は正しく再試行される
- v1 に無いファイルは通常どおり走査される

## 回帰テスト

`TestImportMigratesV1FileIndexWithoutReplayingEvents` を追加。v1 相当の取り込みを
行い、索引を v1 キーへ移して v2 を空にした「アップグレード直前」を作り、再走査後に
`FilesScanned == 0` / `message_events` の件数が不変であることを固定する。

ミューテーション検証: 移行を無効化して `state.previous` を空マップへ戻すと
`FilesScanned` が 0 ではなく 1 になり本テストが落ちることを確認済み。

検証: go build ./... 成功、gofmt 差分なし、go test ./... = 11 パッケージ全 ok。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Nicolas0315

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

[P1] Preserve event history during the v2 migration を修正し、シード付きアップグレード回帰テストと実挙動トレースを追加しました(PR本文の Update 2026-09-03 節)。

  • 修正: migrateFileIndexFromV1 を追加。v2 索引が未作成のとき v1 を読み、imported の checkpoint だけを引き継ぐ。取り込み済みファイルは再走査されないためイベント複製が起きません。イベント追記の抑止ではなく索引の引き継ぎで解決しています。
  • 回帰テスト: TestImportMigratesV1FileIndexWithoutReplayingEvents — v1 索引のみの状態を作り、再走査後に FilesScanned == 0message_events 件数不変を固定。
  • ミューテーション検証: 移行を外すと FilesScanned が 1 になり本テストが落ちることを確認済み(失敗出力を本文に添付)。
  • go test ./... = 11 パッケージ全 ok、gofmt 差分なし。

head: 33f4f2d

@clawsweeper

clawsweeper Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event exact_review_queue).
Result: when the review finishes, ClawSweeper will create the durable review comment if needed or update the existing comment in place.

Re-review progress:

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. merge-risk: 🚨 message-delivery 🚨 Merging this PR could drop, duplicate, misroute, suppress, or wrongly target messages. labels Sep 3, 2026
Recheck ambiguous legacy checkpoints once and keep unresolved payloads
retryable across default, context, and full-cache input paths. Preserve the
existing first-import pruning boundary during upgrades.

Protect newer edited messages and tombstones inside the message transaction,
including FTS, attachments and mentions, and deduplicate identical cache
events. Keep newer edits and equal-revision metadata updates supported.

Co-authored-by: Nicolas Hidemaru Ogoshi / 大越ニコラス秀丸 <40846197+Nicolas0315@users.noreply.github.com>
@steipete steipete changed the title fix(wiretap): retry unresolved cache entries fix(wiretap): safely retry unresolved cache entries Sep 4, 2026
@clawsweeper clawsweeper Bot added the merge-risk: 🚨 session-state 🚨 Merging this PR could lose, corrupt, stale, or mis-associate session or agent state. label Sep 4, 2026
@clawsweeper clawsweeper Bot added merge-risk: 🚨 availability 🚨 Merging this PR could cause crashes, hangs, restart loops, stalls, or process outages. proof: sufficient Contributor real behavior proof is sufficient. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. merge-risk: 🚨 session-state 🚨 Merging this PR could lose, corrupt, stale, or mis-associate session or agent state. labels Sep 4, 2026
@steipete
steipete merged commit e1cdb57 into openclaw:main Sep 4, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 availability 🚨 Merging this PR could cause crashes, hangs, restart loops, stalls, or process outages. P1 Urgent regression or broken agent/channel workflow affecting real users now. proof: sufficient Contributor real behavior proof is sufficient. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants