Fixes map-memories validation rejecting valid model output when assistantText returns whitespace-only. Full root-cause analysis and evidence are in the linked issue.
Change
Tighten the module-private assistantText in packages/plugin/src/features/magic-context/dreamer/map-memories.ts to return null for whitespace-only output, so the || text fallback (hidden path) and the || ... fallback fire instead of validating whitespace. Make the client path (validateOutput) null-safe. Extract the two validation dispatch callbacks into named functions for testability.
assistantText: return joined.trim() ? joined : null (whitespace-only → null; un-trimmed content preserved so currentManifest slicing is unaffected).
Client path: raw == null guard before currentManifest (→ "map-memories returned no output").
Hidden path (:492) and the failure-recovery site (:621) are unchanged and benefit automatically via ||.
Behavior note
On the client path, whitespace-only output now throws map-memories returned no output instead of missing complete root element — a more accurate message; intentional. The hasLengthCappedOutput guard is preserved from the original :576 and is covered by a test.
Harness coverage (required)
OpenCode 1: covered — validation lives in the shared TS core (packages/plugin/src/features/magic-context/dreamer/).
OpenCode 2: covered — same shared core.
Pi: covered — reuses the shared core via @magic-context/core (task-executor imports mapMemories).
OMP: covered — same shared core path as Pi.
Rust module: not applicable — crates/ contains no manifest validation (grep-confirmed: no validateMapMemoriesManifest / extractCompleteManifestBody / mappingsBody).
Tests
bun test map-memories.test.ts: 41 pass / 0 fail (12 new regression tests). Both dispatch paths covered: whitespace / empty / tool-only → fall back to completion.text (hidden) or "no output" (client); a real manifest carried by messages keeps priority over completion.text; length-capped guard preserved. Replay reproduced fail-before / pass-after for both paths.
Summary by cubic
Fixes map-memories validation rejecting valid model output when assistantText returns whitespace-only content. assistantText now returns null for whitespace-only output, so the hidden path falls back to completion.text (the real manifest) and the client path throws map-memories returned no output instead of validating whitespace as a manifest.
Extracted the two validation dispatch callbacks into the exported parseMapMemoriesCompletion and validateClientMapMemories helpers for direct testing.
Added regression tests covering whitespace, empty, and tool-only messages on both paths, plus the preserved length-capped guard.
Confidence Score: 4/5
The PR should not merge until the previously reported ID type mismatch is fixed.