Skip to content

fix(memory-wiki): preserve human notes block on source re-ingest#95614

Merged
openclaw-clownfish[bot] merged 2 commits into
openclaw:mainfrom
yetval:fix/memory-wiki-ingest-preserve-notes
Jun 23, 2026
Merged

fix(memory-wiki): preserve human notes block on source re-ingest#95614
openclaw-clownfish[bot] merged 2 commits into
openclaw:mainfrom
yetval:fix/memory-wiki-ingest-preserve-notes

Conversation

@yetval

@yetval yetval commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Re-ingesting an already-ingested memory-wiki source destroys the user's notes. memory-wiki source pages carry a ## Notes block delimited by <!-- openclaw:human:start --> / <!-- openclaw:human:end --> markers that the user is meant to edit. When the page is regenerated (same slug, same sources/<slug>.md path), it is rewritten with an empty Notes block, silently dropping anything the user wrote inside the human-managed block. This affects every source-page writer: openclaw wiki ingest <path> / the gateway ingest tool, and the bridge and unsafe-local imported-source sync paths.

This contradicts the documented contract in docs/plugins/memory-wiki.md:129: "Managed content stays inside generated blocks. Human note blocks are preserved."

Root cause

extensions/memory-wiki/src/ingest.ts rebuilds the full page body with a hardcoded empty human block and writes it unconditionally, never reading the existing page:

// before
      "## Notes",
      "<!-- openclaw:human:start -->",
      "<!-- openclaw:human:end -->",
      "",
    ].join("\n"),
  });

  await fs.writeFile(pagePath, markdown, "utf8");

The shared imported-source writer source-page-shared.ts writeImportedSourcePage (used by bridge.ts and unsafe-local.ts) has the same defect: it reads the existing page only to compare, then writes the freshly rendered page over it. The synthesis (apply.ts) and ChatGPT-import (chatgpt-import.ts) writers already preserve the human block; ingest and the imported-source writer were the ones that skipped it.

Fix

Add one helper next to the existing human-block markers in markdown.ts, and call it from both writers before writing.

// after - markdown.ts
const SOURCE_CONTENT_HEADING = "\n## Content\n";

function afterSourceContentFence(page: string): number {
  const headingIndex = page.indexOf(SOURCE_CONTENT_HEADING);
  if (headingIndex === -1) {
    return 0;
  }
  const fenceLineStart = headingIndex + SOURCE_CONTENT_HEADING.length;
  const fence = /^`+/.exec(page.slice(fenceLineStart))?.[0];
  if (!fence) {
    return fenceLineStart;
  }
  const closeIndex = page.indexOf(`\n${fence}`, fenceLineStart + fence.length);
  if (closeIndex === -1) {
    return fenceLineStart;
  }
  return closeIndex + fence.length + 1;
}

function findNotesHumanBlock(page: string): { start: number; end: number } | null {
  const searchFrom = afterSourceContentFence(page);
  const start = page.indexOf(HUMAN_START_MARKER, searchFrom);
  if (start === -1) {
    return null;
  }
  const endMarker = page.lastIndexOf(HUMAN_END_MARKER);
  if (endMarker < start) {
    return null;
  }
  return { start, end: endMarker + HUMAN_END_MARKER.length };
}

export function preserveHumanNotesBlock(rendered: string, existing: string): string {
  const existingBlock = findNotesHumanBlock(existing);
  const renderedBlock = findNotesHumanBlock(rendered);
  if (!existingBlock || !renderedBlock) {
    return rendered;
  }
  return (
    rendered.slice(0, renderedBlock.start) +
    existing.slice(existingBlock.start, existingBlock.end) +
    rendered.slice(renderedBlock.end)
  );
}
// after - ingest.ts
  const existing = created ? "" : await fs.readFile(pagePath, "utf8").catch(() => "");
  await fs.writeFile(
    pagePath,
    existing ? preserveHumanNotesBlock(markdown, existing) : markdown,
    "utf8",
  );
// after - source-page-shared.ts (writeImportedSourcePage)
  const existing = pageStat ? await vault.readText(params.pagePath).catch(() => "") : "";
  const nextRendered = existing ? preserveHumanNotesBlock(rendered, existing) : rendered;
  if (existing !== nextRendered) {
    ...
    await vault.write(params.pagePath, nextRendered);

The only untrusted region on the page is the source text, which is fenced inside ## Content. renderMarkdownFence chooses a fence delimiter strictly longer than any backtick run in the content, so the source can never close its own fence early; the rest of the page (the ## Notes block, then the generated ## Related block, which carries only openclaw:wiki:related markers) is structurally trusted. The helper scans for the human block only after that fence closes, taking the first start marker and the last end marker, so the whole Notes block is preserved verbatim even when the source content or the user's own note text contains the human marker comments or Markdown headings. New page creation is unchanged.

Why this is the right boundary

There are four memory-wiki source-page writers. Two already preserved the human block (synthesis, ChatGPT import); this change fixes the other two by routing both through one shared preserveHumanNotesBlock helper colocated with the markers it protects: ingestMemoryWikiSource (the wiki ingest CLI command at cli.ts:588 and the gateway ingest tool) and writeImportedSourcePage (the bridge and unsafe-local sync paths). No writer is left one-sided.

Verification

  • node scripts/run-vitest.mjs extensions/memory-wiki/src/ingest-human-notes.test.ts (four ingest cases) and extensions/memory-wiki/src/source-page-shared.test.ts (imported-source update case) pass with the patch; each new case fails on the unpatched writer.
  • node scripts/run-vitest.mjs extensions/memory-wiki/src/ingest.test.ts (existing create-path assertion) passes unchanged.
  • node scripts/run-oxlint.mjs and oxfmt --check clean on the five changed files.
  • tsgo core typecheck clean.

Real behavior proof

Behavior addressed: regenerating a memory-wiki source page (via wiki ingest or via the bridge/unsafe-local imported-source writer) wiped user notes in the human-managed ## Notes block; with the patch the notes survive while the regenerated source content is still updated, including when the source content or the note text contains the human markers or a Markdown heading.
Real environment tested: drove the real ingestMemoryWikiSource and the real writeImportedSourcePage runtime functions against a real on-disk vault on pristine base 4cb94cc2cf and on the patched head 1095fe887c, with identical inputs across five scenarios. Everything stayed real (real vault on disk, real render/write paths); nothing was stubbed.
Exact steps or command run after this patch: for each scenario, write the source page, edit its Notes block to add a note, change the source, write the page again through the real writer, then read the page back from disk.
Evidence after fix:

# BEFORE (pristine base 4cb94cc2cf)
[scenario 1: wiki ingest plain re-ingest]
updated source content present: true
user note present after re-ingest: false
[scenario 2: wiki ingest, source content itself contains the human markers]
source content INJECTED FROM SOURCE present: true
Notes block keeps user note: false
Notes block free of source content: true
[scenario 3: wiki ingest, note text itself contains a marker comment]
note kept before pasted marker (EARLY NOTE): false
note kept after pasted marker (LATE NOTE): false
[scenario 4: wiki ingest, note text contains a Markdown heading]
note kept above pasted heading (NOTE TOP): false
note kept below pasted heading (NOTE BOTTOM): false
[scenario 5: bridge/unsafe-local imported source page update via writeImportedSourcePage]
updated source content present: true
user note present after update: false

# AFTER (this patch 1095fe887c, identical inputs)
[scenario 1: wiki ingest plain re-ingest]
updated source content present: true
user note present after re-ingest: true
[scenario 2: wiki ingest, source content itself contains the human markers]
source content INJECTED FROM SOURCE present: true
Notes block keeps user note: true
Notes block free of source content: true
[scenario 3: wiki ingest, note text itself contains a marker comment]
note kept before pasted marker (EARLY NOTE): true
note kept after pasted marker (LATE NOTE): true
[scenario 4: wiki ingest, note text contains a Markdown heading]
note kept above pasted heading (NOTE TOP): true
note kept below pasted heading (NOTE BOTTOM): true
[scenario 5: bridge/unsafe-local imported source page update via writeImportedSourcePage]
updated source content present: true
user note present after update: true

Observed result after fix: across all five scenarios the regenerated page keeps the user's note while still showing the updated source content; on pristine base every note is gone. This holds for both the wiki ingest writer and the bridge/unsafe-local imported-source writer, and even when the source content or note text contains the markers or a ## Notes heading.
What was not tested: no live network or provider request is involved; full repo build not run.

@clawsweeper

clawsweeper Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 21, 2026, 9:24 PM ET / 01:24 UTC.

Summary
The PR adds a shared memory-wiki helper that preserves existing human Notes blocks when regenerating local-ingest and imported-source pages, plus regressions for marker, heading, CRLF, and imported-source cases.

PR surface: Source +58, Tests +329. Total +387 across 5 files.

Reproducibility: yes. Source inspection on current main shows both affected writers overwrite existing source pages, and the PR body includes before/after real on-disk output showing notes lost on base and preserved after the patch.

Review metrics: 1 noteworthy metric.

  • Human-note writer coverage: 2 updated, 2 already preserved. The patch covers the two source-page writers that dropped notes and matches the two sibling writers that already preserved human blocks.

Stored data model
Persistent data-model change detected: serialized state: extensions/memory-wiki/src/ingest-human-notes.test.ts, serialized state: extensions/memory-wiki/src/ingest.ts, serialized state: extensions/memory-wiki/src/source-page-shared.test.ts, unknown-data-model-change: extensions/memory-wiki/src/ingest-human-notes.test.ts, unknown-data-model-change: extensions/memory-wiki/src/source-page-shared.test.ts, vector/embedding metadata: extensions/memory-wiki/src/ingest-human-notes.test.ts, and 3 more. Confirm migration or upgrade compatibility proof before merge.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • none.

Next step before merge

  • No ClawSweeper repair lane is needed; this PR is ready for ordinary maintainer review and normal merge gates.

Security
Cleared: The diff changes memory-wiki TypeScript and tests only; no dependency, workflow, lockfile, secret-handling, permission, or external code-execution surface was added.

Review details

Best possible solution:

Land the shared preservation fix after normal CI and maintainer merge gates, keeping the helper as the canonical path for generated source-page rewrites.

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

Yes. Source inspection on current main shows both affected writers overwrite existing source pages, and the PR body includes before/after real on-disk output showing notes lost on base and preserved after the patch.

Is this the best way to solve the issue?

Yes. A shared helper colocated with the marker/fence logic and used by both missing writer paths is narrower and less drift-prone than duplicating preservation logic in each writer.

AGENTS.md: found and applied where relevant.

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

Label changes

Label justifications:

  • P2: This is a bounded memory-wiki data-preservation bugfix with limited plugin scope and focused regression coverage.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body includes before/after live output from real on-disk vault writes through the ingest and imported-source runtime functions, showing notes lost on base and preserved after the patch.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes before/after live output from real on-disk vault writes through the ingest and imported-source runtime functions, showing notes lost on base and preserved after the patch.
Evidence reviewed

PR surface:

Source +58, Tests +329. Total +387 across 5 files.

View PR surface stats
Area Files Added Removed Net
Source 3 63 5 +58
Tests 2 329 0 +329
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 5 392 5 +387

What I checked:

  • Current main local ingest still overwrites notes: On current main, ingestMemoryWikiSource renders an empty Notes block and writes the regenerated source page directly to pagePath, so an existing human-managed block is not preserved. (extensions/memory-wiki/src/ingest.ts:85, a2675756b875)
  • Current main imported-source writer still overwrites notes: writeImportedSourcePage reads an existing page only for comparison and then writes the freshly rendered page, which drops user edits inside the human Notes block for bridge and unsafe-local source pages. (extensions/memory-wiki/src/source-page-shared.ts:70, a2675756b875)
  • Documented memory-wiki contract requires preservation: The memory-wiki docs say managed content stays inside generated blocks and human note blocks are preserved, making the reported behavior a real contract bug rather than a new feature request. Public docs: docs/plugins/memory-wiki.md. (docs/plugins/memory-wiki.md:129, a2675756b875)
  • PR head centralizes block preservation after generated content: The PR head locates the generated Content fence with LF or CRLF line endings, finds the Notes human block after that trusted generated source section, and splices the existing block into the new render. (extensions/memory-wiki/src/markdown.ts:449, bab77ff9257a)
  • PR head updates both missing writer paths: The local ingest writer and shared imported-source writer both call preserveHumanNotesBlock before writing, while sibling synthesis and ChatGPT-import paths already preserve human blocks. (extensions/memory-wiki/src/source-page-shared.ts:72, bab77ff9257a)
  • Regression coverage includes normal, marker, heading, and CRLF cases: The added ingest tests cover re-ingest, marker text in source content, marker text in notes, a Notes heading inside notes, and CRLF source pages with marker comments. (extensions/memory-wiki/src/ingest-human-notes.test.ts:10, bab77ff9257a)

Likely related people:

  • vincentkoc: Current-line blame for ingest, marker constants, docs contract lines, and the source-page write bodies points into the memory-wiki feature stack, with earlier history showing memory-wiki ingest and markdown implementation commits under this contributor. (role: feature introducer and recent area contributor; confidence: high; commits: f6d432e545e2, 516a43f9f28c, 5716d83336fd; files: extensions/memory-wiki/src/ingest.ts, extensions/memory-wiki/src/markdown.ts, extensions/memory-wiki/src/source-page-shared.ts)
  • steipete: Git history shows the shared memory-wiki source-page writer dedupe and the memory-wiki documentation commit under this contributor, making them a likely reviewer for the shared writer boundary and documented contract. (role: shared writer and docs contributor; confidence: medium; commits: efd9aaea3f3e, 9eacd29138a7; files: extensions/memory-wiki/src/source-page-shared.ts, extensions/memory-wiki/src/bridge.ts, extensions/memory-wiki/src/unsafe-local.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

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
  • 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.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. 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. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. labels Jun 21, 2026
@yetval
yetval force-pushed the fix/memory-wiki-ingest-preserve-notes branch from e751b5f to baa95a4 Compare June 21, 2026 23:20
@yetval

yetval commented Jun 21, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Addressed the P1 blocker: preserveHumanNotesBlock now anchors to the trailing human-marker pair instead of the first pair in the page. Since source text is fenced in ## Content before ## Notes and the generated ## Related block carries only openclaw:wiki:related markers, the last openclaw:human pair is always the real Notes block, so source content that itself contains the marker comments is no longer mistaken for the Notes block. Added a regression covering that case (it fails on the prior first-match helper and passes now). Refreshed before/after live proof for both scenarios at head baa95a4.

@clawsweeper

clawsweeper Bot commented Jun 21, 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 repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@yetval
yetval force-pushed the fix/memory-wiki-ingest-preserve-notes branch from baa95a4 to b36ef75 Compare June 21, 2026 23:42
@yetval

yetval commented Jun 21, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Addressed the remaining P2: findNotesHumanBlock no longer uses the last marker pair in the page. It now anchors to the ## Notes heading (first start marker after the heading, last end marker), so the whole Notes block is preserved verbatim even when the user's note text itself contains a pasted human start marker. Added a regression for that case (it fails on the prior last-marker helper and passes now). Refreshed before/after live proof for all three scenarios (plain re-ingest, marker text in source content, marker text inside the note) at head b36ef75.

@clawsweeper

clawsweeper Bot commented Jun 21, 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 repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@yetval
yetval force-pushed the fix/memory-wiki-ingest-preserve-notes branch from b36ef75 to e6c048a Compare June 21, 2026 23:57
@yetval

yetval commented Jun 21, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Addressed the remaining edge: the matcher no longer anchors on a ## Notes heading (which the note text could itself contain). The only untrusted region is the ## Content source, which is fenced with a delimiter renderMarkdownFence guarantees is longer than any backtick run in the content, so the source cannot close its fence early. findNotesHumanBlock now scans for the human block only after that ## Content fence closes, then takes the first start marker and the last end marker. The whole Notes block is preserved verbatim regardless of what markers or headings the source content or the note text contains. Added a regression for a ## Notes heading inside the note. Refreshed before/after live proof for all four scenarios at head e6c048a.

@clawsweeper

clawsweeper Bot commented Jun 21, 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 repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@yetval
yetval force-pushed the fix/memory-wiki-ingest-preserve-notes branch from e6c048a to 1095fe8 Compare June 22, 2026 00:18
@yetval

yetval commented Jun 22, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Addressed the scope finding: extended the same preservation to the shared imported-source writer writeImportedSourcePage (used by the bridge and unsafe-local sync paths), so all four memory-wiki source-page writers now preserve the human Notes block (synthesis and chatgpt-import already did). Both ingest and the imported-source writer route through one shared preserveHumanNotesBlock helper. Added a focused regression for an imported source page whose content changes after the user edits the Notes block (it fails on the unpatched writer, passes now). Refreshed before/after live proof to five scenarios, including driving the real writeImportedSourcePage, at head 1095fe8.

@clawsweeper

clawsweeper Bot commented Jun 22, 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 repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@yetval

yetval commented Jun 22, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Addressed the CRLF rank-up finding at head bab77ff: afterSourceContentFence now locates the generated ## Content heading and closing fence with LF or CRLF line endings while preserving original slice offsets. Added regressions for existing CRLF source pages whose fenced source content contains openclaw:human markers for both ingestMemoryWikiSource and writeImportedSourcePage.

Local verification:

  • node scripts/run-vitest.mjs extensions/memory-wiki/src/ingest-human-notes.test.ts extensions/memory-wiki/src/source-page-shared.test.ts extensions/memory-wiki/src/ingest.test.ts
  • ./node_modules/.bin/oxlint extensions/memory-wiki/src/markdown.ts extensions/memory-wiki/src/ingest.ts extensions/memory-wiki/src/source-page-shared.ts extensions/memory-wiki/src/ingest-human-notes.test.ts extensions/memory-wiki/src/source-page-shared.test.ts
  • ./node_modules/.bin/oxfmt --check extensions/memory-wiki/src/markdown.ts extensions/memory-wiki/src/ingest.ts extensions/memory-wiki/src/source-page-shared.ts extensions/memory-wiki/src/ingest-human-notes.test.ts extensions/memory-wiki/src/source-page-shared.test.ts

@clawsweeper

clawsweeper Bot commented Jun 22, 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 repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed 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. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. labels Jun 22, 2026
Re-ingesting an existing source regenerated the page with an empty
wrote inside the human-managed markers. This broke the documented
contract that human note blocks are preserved, and diverged from the
synthesis and chatgpt-import writers that already preserve the block.

When a source page already exists, read it and re-inject its human Notes
block before writing. The block is located by scanning past the fenced
the content, then taking the first human start marker and the last end
marker, so the whole Notes block is preserved verbatim even when the
source content or the note text contains the markers or Markdown
headings. The same preservation is applied to writeImportedSourcePage so
the bridge and unsafe-local source-update writers keep notes too. New
page creation is unchanged.

Adds regressions for plain re-ingest, marker text in source content,
marker text inside the note, a heading inside the note, and an imported
source page update.
@yetval
yetval force-pushed the fix/memory-wiki-ingest-preserve-notes branch from bab77ff to 66a8e3c Compare June 22, 2026 22:18
@openclaw-clownfish
openclaw-clownfish Bot merged commit 0ec12df into openclaw:main Jun 23, 2026
84 checks passed
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jun 23, 2026
…nclaw#95614)

* fix(memory-wiki): preserve human notes block on source re-ingest

Re-ingesting an existing source regenerated the page with an empty
wrote inside the human-managed markers. This broke the documented
contract that human note blocks are preserved, and diverged from the
synthesis and chatgpt-import writers that already preserve the block.

When a source page already exists, read it and re-inject its human Notes
block before writing. The block is located by scanning past the fenced
the content, then taking the first human start marker and the last end
marker, so the whole Notes block is preserved verbatim even when the
source content or the note text contains the markers or Markdown
headings. The same preservation is applied to writeImportedSourcePage so
the bridge and unsafe-local source-update writers keep notes too. New
page creation is unchanged.

Adds regressions for plain re-ingest, marker text in source content,
marker text inside the note, a heading inside the note, and an imported
source page update.

* fix(memory-wiki): preserve notes on CRLF source pages
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

extensions: memory-wiki P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: M status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant