Skip to content

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

Open
Nicolas0315 wants to merge 2 commits into
openclaw:mainfrom
Nicolas0315:fix/wiretap-retry-unresolved-cache
Open

fix(wiretap): retry unresolved cache entries#190
Nicolas0315 wants to merge 2 commits into
openclaw:mainfrom
Nicolas0315:fix/wiretap-retry-unresolved-cache

Conversation

@Nicolas0315

@Nicolas0315 Nicolas0315 commented Aug 26, 2026

Copy link
Copy Markdown

Summary

  • keep unresolved cache fingerprints in the skipped state so later imports can retry them when channel metadata becomes available
  • bump the private wiretap file-index scope to v2 so entries checkpointed as imported by older builds receive one migration rescan
  • cover repeated retry, later resolution, and no duplicate message events

Why

checkpointScannedCandidates marked permanently unresolved candidates as imported. An unchanged cache file was therefore never reconsidered after bot sync or later cache artifacts supplied its channel metadata.

Verification

  • go test ./...
  • go vet ./...
  • go test -race ./internal/discorddesktop
  • targeted tests passed on Windows amd64 / Go 1.26.6 on two independent machines
  • production-copy canary: cache/DB gap 1,055 -> 870, 185 previously missing rows recovered, duplicate message IDs 0, pragma integrity_check = ok

The full Windows package run still hits the pre-existing TestDesktopPathAndImportHelpers APPDATA expectation failure on both machines; the three tests touched by this change pass on both.


Update 2026-09-03 — ClawSweeper review addressed

[P1] Preserve event history during the v2 migration — fixed

The blocking defect was real. Bumping wiretapFileIndexScope to v2 changes the key
loadScanState reads, so a database holding only the v1 index looks like it has no
checkpoints at all and rescans already-imported cache files. Parsed messages set
AppendEvent: true and the store inserts an event per mutation without dedup, so the
upgrade left message rows unchanged (they upsert) while duplicating message_events.

Rather than suppressing event appends, the index is now carried forward. When the v2
index is absent, migrateFileIndexFromV1 reads the v1 key and copies over only the
imported checkpoints
:

  • imported files keep their checkpoint, so they are not rescanned → no event replay
  • v1 could not distinguish "scanned but unresolved" from "imported", but this PR's
    checkpointScannedCandidates now records those as skipped, so unresolved entries
    are retried from here on — which is the whole point of the PR
  • files absent from v1 are scanned normally

internal/discorddesktop/import.gowiretapFileIndexScopeV1 added, loadScanState
falls back to the migration, migrateFileIndexFromV1 added.

Seeded upgrade regression test — added

TestImportMigratesV1FileIndexWithoutReplayingEvents in
internal/discorddesktop/import_pipeline_test.go:

  1. imports a routed cache entry (v1-equivalent state) and records the message_events count
  2. moves the resulting index to the v1 key and blanks the v2 key — the exact
    pre-upgrade shape
  3. re-runs Import and asserts FilesScanned == 0, FilesUnchanged == 1, and that the
    message_events count is unchanged
  4. asserts the message is still present exactly once

Real behavior proof

$ go build ./...
(no output)

$ gofmt -l internal/discorddesktop/
(no output)

$ go test ./internal/discorddesktop/ -run 'TestImportMigratesV1FileIndexWithoutReplayingEvents|TestImportCheckpointsUnresolvableRouteBearingCacheMisses' -v
=== RUN   TestImportCheckpointsUnresolvableRouteBearingCacheMisses
--- PASS: TestImportCheckpointsUnresolvableRouteBearingCacheMisses (0.01s)
=== RUN   TestImportMigratesV1FileIndexWithoutReplayingEvents
--- PASS: TestImportMigratesV1FileIndexWithoutReplayingEvents (0.01s)
PASS
ok  	github.com/openclaw/discrawl/internal/discorddesktop	0.247s

$ go test ./...
ok  (11 packages, 0 failures)

Mutation check — the new test does catch the defect. Reverting only the migration
(state.previous = map[string]fileFingerprint{} instead of the migrated index) makes it fail:

--- FAIL: TestImportMigratesV1FileIndexWithoutReplayingEvents (0.01s)
        Error Trace:  internal/discorddesktop/import_pipeline_test.go:465
        Error:        Not equal:
                      expected: 0
                      actual  : 1
        Messages:     v1 で取り込み済みのファイルは再走査しない
FAIL

So the assertion is bound to the migration, not to incidental state.

Not verified

This is a synthetic-fixture proof over a real SQLite archive created by store.Open, not a
capture from a Windows production copy. The upgrade path, the recovered-entry retry, and the
unchanged prior event count are all exercised against the real store and real import
pipeline; what is not covered is a live Discord Desktop cache directory.

🤖 Generated with Claude Code

@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: needs real behavior proof before merge. Reviewed September 3, 2026, 9:39 AM ET / 13:39 UTC.

ClawSweeper review

What this changes

This PR makes unresolved Discord Desktop cache entries retryable and adds a v1-to-v2 file-index migration intended to preserve archive event history during upgrades.

Merge readiness

Blocked before merge - 4 items remain

Keep open: the prior event-replay finding is addressed, but the v1 migration still treats legacy unresolved checkpoints as imported, so the central recovery behavior remains absent. Current main does not contain this PR’s changes.

Priority: P1
Reviewed head: 33f4f2da8acc5ffb601fda1ef68ed75e87e23947

Review scores

Measure Result What it means
Overall readiness 🦪 silver shellfish (2/6) The branch contains focused code and regression work, but a P1 legacy-migration defect remains and the supplied proof is synthetic test evidence only.
Proof confidence 🦪 silver shellfish (2/6) Needs real behavior proof before merge: The changed production owners are Wiretap’s persisted file index and SQLite archive writer; the supplied terminal output exercises synthetic cache fixtures through the real store and importer, but it is still test-only evidence and the PR explicitly lacks a live Discord Desktop cache result. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Patch quality 🦐 gold shrimp (3/6) 1 actionable review finding remain.

Verification

Check Result Evidence
Real behavior Needs proof Needs real behavior proof before merge: The changed production owners are Wiretap’s persisted file index and SQLite archive writer; the supplied terminal output exercises synthetic cache fixtures through the real store and importer, but it is still test-only evidence and the PR explicitly lacks a live Discord Desktop cache result. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed 7 items Current-main comparison: Current main still uses the v1 file-index scope and has no v1-to-v2 migration, so this PR’s behavior is not already implemented on the default branch.
Legacy unresolved checkpoints: Before this PR, the unresolved-candidate checkpoint path wrote every candidate as imported, including entries whose messages could not be classified.
Migration preserves the old unresolved state: The introduced migration copies every v1 fingerprint accepted as imported into the v2 index. Since legacy unresolved candidates were recorded as imported, they are skipped instead of being retried.
Findings 1 actionable finding [P1] Reprocess legacy unresolved cache entries
Security None None.

How this fits together

Wiretap reads Discord Desktop cache files into DisCrawl’s local SQLite archive. Its persisted file index determines which unchanged cache entries are skipped, retried, and allowed to append archive events.

flowchart LR
  A[Discord Desktop cache] --> B[Wiretap importer]
  B --> C[Persisted file index]
  C --> D{Checkpoint status}
  D -->|Imported| E[Skip unchanged file]
  D -->|Unresolved| F[Retry cache entry]
  B --> G[SQLite messages]
  B --> H[Event history]
Loading

Before merge

  • Add real behavior proof - Needs real behavior proof before merge: The changed production owners are Wiretap’s persisted file index and SQLite archive writer; the supplied terminal output exercises synthetic cache fixtures through the real store and importer, but it is still test-only evidence and the PR explicitly lacks a live Discord Desktop cache result. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
  • Reprocess legacy unresolved cache entries (P1) - The old checkpoint path stored unresolved candidates as imported, and this loop carries those same v1 records into v2 as imported. They therefore remain unchanged-file skips even after later cache data supplies channel metadata, leaving the PR’s central recovery case unfixed. Add a migration state/path that retries legacy entries without replaying existing events, with regression coverage for that exact upgrade shape.
  • Resolve merge risk (P1) - Upgrading an archive with v1 entries that were unresolved will retain those entries as imported, so later cache metadata cannot recover their messages; merging would leave the reported message-recovery failure in place.
  • Complete next step (P2) - Repair the v1 migration so legacy unresolved checkpoints retry without replaying events, add an upgrade regression for that state, and provide redacted real Discord Desktop cache proof before merge.

Findings

  • [P1] Reprocess legacy unresolved cache entries — internal/discorddesktop/import.go:268-272
Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Change size 3 files; production +46/-2, tests +77/-4 The migration adds persistent-index behavior alongside focused regression coverage, so upgrade semantics need to be correct before merge.

Merge-risk options

Maintainer options:

  1. Repair the legacy migration (recommended)
    Make v1 checkpointed entries eligible for one retry without appending duplicate prior events, and add a regression for an old unresolved entry that later gains channel metadata.
  2. Pause the PR
    Do not merge until the upgrade path can preserve existing event history while recovering the legacy unresolved entries this PR targets.

Technical review

Best possible solution:

Use an explicit legacy-migration state that retries v1 checkpointed entries without replaying existing events, then prove both legacy recovery and event preservation against a real Desktop cache import.

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

Yes, from source: seed a v1 index entry produced by the old unresolved checkpoint path, add later channel metadata, then import on the PR head; the migration marks that entry imported and the unchanged cache file is skipped.

Is this the best way to solve the issue?

No. The patch correctly avoids replaying events for known imported entries, but it cannot distinguish those from the unresolved entries that v1 also wrote as imported, so it does not deliver the requested legacy recovery.

Full review comments:

  • [P1] Reprocess legacy unresolved cache entries — internal/discorddesktop/import.go:268-272
    The old checkpoint path stored unresolved candidates as imported, and this loop carries those same v1 records into v2 as imported. They therefore remain unchanged-file skips even after later cache data supplies channel metadata, leaving the PR’s central recovery case unfixed. Add a migration state/path that retries legacy entries without replaying existing events, with regression coverage for that exact upgrade shape.
    Confidence: 0.99

Overall correctness: patch is incorrect
Overall confidence: 0.98

AGENTS.md: not found in the target repository.

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

Labels

Label changes:

  • add rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🦐 gold shrimp.
  • remove rating: 🧂 unranked krab: Current PR rating is rating: 🦪 silver shellfish, so this older rating label is no longer current.

Label justifications:

  • P1: The patch affects recovery of locally cached Discord messages and currently leaves legacy unresolved entries permanently skipped.
  • merge-risk: 🚨 compatibility: The persisted file-index upgrade path changes how existing archives classify prior checkpoints.
  • merge-risk: 🚨 message-delivery: Legacy unresolved cache entries can remain unscanned after later channel metadata makes their messages importable.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🦐 gold shrimp.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The changed production owners are Wiretap’s persisted file index and SQLite archive writer; the supplied terminal output exercises synthetic cache fixtures through the real store and importer, but it is still test-only evidence and the PR explicitly lacks a live Discord Desktop cache result. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Evidence

Acceptance criteria:

  • [P1] go test ./internal/discorddesktop -run 'TestImportCheckpointsUnresolvableRouteBearingCacheMisses|TestImportMigratesV1FileIndexWithoutReplayingEvents' -v.
  • [P1] go test ./internal/discorddesktop.
  • [P1] Provide a redacted real Discord Desktop cache import trace demonstrating recovery and unchanged prior event history.

What I checked:

  • Current-main comparison: Current main still uses the v1 file-index scope and has no v1-to-v2 migration, so this PR’s behavior is not already implemented on the default branch. (internal/discorddesktop/import.go:121, 0853d40e0525)
  • Legacy unresolved checkpoints: Before this PR, the unresolved-candidate checkpoint path wrote every candidate as imported, including entries whose messages could not be classified. (internal/discorddesktop/import.go:689, 5ab5d11351b9)
  • Migration preserves the old unresolved state: The introduced migration copies every v1 fingerprint accepted as imported into the v2 index. Since legacy unresolved candidates were recorded as imported, they are skipped instead of being retried. (internal/discorddesktop/import.go:269, 33f4f2da8acc)
  • Existing regression coverage misses the legacy-unresolved case: The added migration test seeds a successfully imported cache entry, then asserts it is not replayed; it does not seed a v1 entry that was unresolved and later becomes classifiable. (internal/discorddesktop/import_pipeline_test.go:424, 33f4f2da8acc)
  • Product boundary: Repository documentation defines Wiretap as the local SQLite import path for classifiable Discord Desktop cache messages and says unclassifiable payloads are counted as skipped. (docs/guides/wiretap.md:3, 33f4f2da8acc)
  • Feature history: The Wiretap import path has substantial prior work by Peter Steinberger, including the earlier skipped-fingerprint recheck change. (internal/discorddesktop/import.go, 055095aadfc2)

Likely related people:

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

Rank-up moves

Optional improvements that raise the rating; they are not merge blockers.

  • Repair the v1 migration so legacy unresolved checkpoints can be retried without replaying events, and add the corresponding upgrade regression.
  • Post a redacted real Discord Desktop cache import trace showing recovery after later metadata and preserved prior event history; update the PR body for re-review.

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 (11 earlier review cycles; latest 8 shown)
  • reviewed 2026-08-28T23:02:16.238Z sha 73e18c0 :: needs real behavior proof before merge. :: [P1] Prevent event replay during the v2 rescan
  • reviewed 2026-08-29T07:02:34.528Z sha 73e18c0 :: needs real behavior proof before merge. :: [P1] Prevent event replay during the v2 rescan
  • reviewed 2026-08-29T09:01:28.956Z sha 73e18c0 :: needs real behavior proof before merge. :: [P1] Prevent event replay during the v2 rescan
  • reviewed 2026-08-29T14:59:23.767Z sha 73e18c0 :: needs real behavior proof before merge. :: [P1] Prevent event replay during the v2 rescan
  • 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

@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
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. labels Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. 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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant