fix(memory-wiki): retry transient existing-page reads in wiki_apply and chatgpt import#98787
Conversation
|
Codex review: needs maintainer review before merge. Reviewed July 1, 2026, 9:18 PM ET / 01:18 UTC. Summary PR surface: Source +40, Tests +206. Total +246 across 3 files. Reproducibility: yes. Current main still has catch-to-empty reads at the synthesis and ChatGPT import sites, and a one-shot read fault has a direct source-level path to losing Notes/frontmatter; I did not execute the fault injection because this review is read-only. Review metrics: 1 noteworthy metric.
Stored data model Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the focused retry/fail-closed fix once maintainers explicitly accept the persistent-read-error behavior for memory-wiki workflows. Do we have a high-confidence way to reproduce the issue? Yes. Current main still has catch-to-empty reads at the synthesis and ChatGPT import sites, and a one-shot read fault has a direct source-level path to losing Notes/frontmatter; I did not execute the fault injection because this review is read-only. Is this the best way to solve the issue? Yes, with maintainer acceptance. The patch fixes the owner-local read sites directly, follows the merged sibling retry/fail-closed pattern, and avoids a shared abstraction across different I/O error shapes. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against a7a444e7ef1e. Label changesLabel justifications:
Evidence reviewedPR surface: Source +40, Tests +206. Total +246 across 3 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
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. How this review workflow works
|
448269a to
8db6560
Compare
…nd chatgpt import A create_synthesis re-run and a ChatGPT conversations re-import swallowed every existing-page read error and treated the page as brand-new, so one transient read failure silently emptied the user's ## Notes block and dropped hand-added frontmatter. Route both reads through a retry-once helper that treats only a missing page as new and propagates persistent failures, matching the ingest and imported-source fix from openclaw#98360.
8db6560 to
ea1263a
Compare
|
Maintainer repair and final verification are complete on
No remaining proof gaps. |
|
Merged via squash.
|
…nd chatgpt import (openclaw#98787) * fix(memory-wiki): retry transient existing-page reads in wiki_apply and chatgpt import A create_synthesis re-run and a ChatGPT conversations re-import swallowed every existing-page read error and treated the page as brand-new, so one transient read failure silently emptied the user's ## Notes block and dropped hand-added frontmatter. Route both reads through a retry-once helper that treats only a missing page as new and propagates persistent failures, matching the ingest and imported-source fix from openclaw#98360. * fix(memory-wiki): preserve fs-safe policy failures --------- Co-authored-by: Vincent Koc <[email protected]>
…nd chatgpt import (openclaw#98787) * fix(memory-wiki): retry transient existing-page reads in wiki_apply and chatgpt import A create_synthesis re-run and a ChatGPT conversations re-import swallowed every existing-page read error and treated the page as brand-new, so one transient read failure silently emptied the user's ## Notes block and dropped hand-added frontmatter. Route both reads through a retry-once helper that treats only a missing page as new and propagates persistent failures, matching the ingest and imported-source fix from openclaw#98360. * fix(memory-wiki): preserve fs-safe policy failures --------- Co-authored-by: Vincent Koc <[email protected]>
Summary
Re-running
wiki_applycreate_synthesisfor an existing synthesis page, or re-running the ChatGPT conversations import over already-imported pages, silently wipes the user's hand-written## Notesblock and drops hand-added frontmatter when the existing-page read fails transiently (EBUSY/EMFILE/ENFILE style faults). The page is treated as brand-new and the regenerated content replaces the user's edits without any error surfacing.PR #98360 fixed this defect class for
ingest.tsandsource-page-shared.ts(issue #98345) but did not cover these two surfaces.Root cause
Both surfaces swallow every existing-page read error and continue with an empty page.
Before,
extensions/memory-wiki/src/apply.ts:231inapplyCreateSynthesisMutation:Before,
extensions/memory-wiki/src/chatgpt-import.ts:762inimportChatGptConversations:With an empty
existing,applyCreateSynthesisMutationkeeps none of the frontmatter that only lived on disk (for example a hand-addedprivacyTier) andbuildSynthesisBodyregenerates an empty## Notesscaffold, whichwriteWikiPagethen persists. In the importer,preserveExistingPageBlocksreceives an emptyexisting, so the human notes and## Relatedblocks are not carried over, the write is misclassified ascreateinstead ofupdateorskip, and the rollback record snapshots an emptyexistingfor that page.Fix
Route both reads through a local retry-once helper that treats only a genuinely missing page as new and propagates a failure that persists across the retry, the same shape #98360 shipped for ingest and imported-source pages. Unlike those two call sites, these reads have no existence gate (a missing page is the normal create path), so the helpers translate missing-page errors to an empty string instead of rethrowing them.
After,
extensions/memory-wiki/src/apply.ts:isMissingWikiPageErrormatchesFsSafeErrorcodesnot-foundandpath-alias, the same missing-page classificationokf.tsalready uses for vault stat calls.chatgpt-import.tsgets the equivalentreadExistingConversationPagekeyed to plainfsENOENT.writeWikiPage's change-detection read atapply.ts:207is routed through the same helper so the file has one canonical existing-page read; a transient failure there previously caused only a spurious identical rewrite, never data loss.Why this is the right boundary
apply.tsowns the synthesis page lifecycle andchatgpt-import.tsowns imported conversation pages, so each gets the same local helper pattern fix(memory-wiki): preserve notes after transient page reads #98360 established iningest.tsandsource-page-shared.ts, keyed to its own missing-page error shape (FsSafeErrorfor the vault root,ENOENTfor plainfs)..catch(() => "")reads in the plugin are not user-content-lossy:okf.ts:461andcompile.ts:1377use the read only for change detection before writing fully caller-computed content, andokf.ts:496fails safe by skipping removal.compile.tswriteManagedMarkdownFileandwriteDashboardPagefall back to a fresh scaffold for plugin-generated index/dashboard/report pages; a transient read failure there can reset plugin-generated scaffolding but does not touch user-authored notes pages. Flagging as a candidate follow-up if the same retry treatment is wanted there.Verification
node scripts/run-vitest.mjs extensions/memory-wiki/src/wiki-notes-read-retry.test.ts extensions/memory-wiki/src/apply.test.ts extensions/memory-wiki/src/import-runs.test.ts: 3 files, 13 tests passed with the patch.node scripts/run-oxlint.mjson the three changed files: clean.oxfmt --checkon the three changed files: clean.node scripts/run-tsgo.mjs -p tsconfig.extensions.jsonand-p test/tsconfig/tsconfig.extensions.test.json: clean.Real behavior proof
Behavior addressed: a single transient read failure of the existing page during a create_synthesis re-run or a ChatGPT re-import silently empties the user's ## Notes block, drops hand-added frontmatter, and misclassifies the import operation.
Real environment tested: drove applyMemoryWikiMutation (the production wiki_apply mutation entry point) and importChatGptConversations (the production ChatGPT import entry point) against a real on-disk vault, on pristine main 17482a4 and on the patched tree with identical inputs; vault initialization, markdown render/parse, compile, and all page writes were the real runtime on disk; the one injected fault was a single EBUSY error on the existing-page read.
Exact steps or command run after this patch: create the synthesis page via applyMemoryWikiMutation create_synthesis "Release Plan", hand-edit its notes block and add privacyTier frontmatter on disk, re-run the same mutation with one EBUSY injected on the syntheses/release-plan.md read; separately import a ChatGPT export via importChatGptConversations, hand-edit the conversation page notes, re-run the identical import with one EBUSY injected on that page read; then print the resulting page files.
Evidence after fix:
Observed result after fix: after one transient EBUSY on the existing-page read, the re-synthesized page keeps the user's notes line and hand-added privacyTier frontmatter, and the re-imported conversation page keeps its notes and is classified skipped instead of being recreated with an empty notes block.
What was not tested: no live gateway session drove wiki_apply over RPC; the EBUSY fault was injected at the read seam rather than produced by a real file lock; full build not run on this machine.
Related
ingest.tsandsource-page-shared.tsonly.apply.tsorchatgpt-import.ts.