You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
memesh import (CLI, the MCP import tool and POST /v1/import all call importMemories()) merges a bundle's metadata through an allow-list (#359). A key that is not on the list, and a value that fails one of the four validators (forgotten_observation_hashes, pin, signal_score, replaced_history), is dropped. Nothing says so: ImportResult has no field for it, nothing reaches errors, the CLI prints Imported: N, Skipped: 0, Appended: 0 and exits 0.
Dropping is the right decision. Dropping without saying so is the defect.
Reproduced with the built CLI against a throwaway HOME/MEMESH_DIR:
a bundle carrying guard, demo, task_state, consolidation_depth, compacted_into, proposal_id, session_id, evidence_for and one ordinary unknown key → Imported: 1, exit 0, all nine gone;
ten entities, nine with one invalid value each (signal_score: 5, pin: "true", one malformed hash, 1001 hashes, an extra key in a replaced_history entry, 51 entries, a 300 KiB entry) → Imported: 10, exit 0, each field dropped whole.
The sharp case is forgotten_observation_hashes: it is what lets a user's own backup carry its own exclusions to a new machine. One corrupted element drops the whole list, and every observation the user had removed with forget can come back from a later Stop snapshot — with a successful-looking import.
Before #359 the only dropped key was guard, equally silently; #359 widened the set from one name to "everything not on the list".
The source-scan test (tests/core/import-metadata-classification.test.ts) guards keys written by THIS source tree. It cannot see a key that arrives in a bundle from a newer or older build.
Suggested direction
Add dropped_metadata: { entity: string; key: string; reason: 'not_importable' | 'invalid' | 'existing_entity' }[] (or a per-key count) to ImportResult.
CLI: print it to stderr next to the existing skipped_relations line; MCP/HTTP: return it.
Document it in docs/api/API_REFERENCE.md; tests for each reason.
Related, same area
ExportResultSchema is the one deliberately non-strict object, so the MCP tool and the HTTP route strip an entity's metadata, created_at and status before importMemories() sees them. The API reference now says so for metadata; created_at and status are lost the same way and are not mentioned. A restore through MCP/HTTP silently loses creation time and archived state.
scripts/lib/npm-latest-guard.mjs decides confirmed = seen === pkgVersion. A failed read is null; if pkgVersion were ever null too, absence would equal absence and the release would be reported confirmed with exit 0. Carried over unchanged from the pre-4.10.1 review follow-ups: four fixes before promoting to latest #359 script and very likely unreachable (earlier preconditions would stop a null version), but it is one line to fail closed: require typeof pkgVersion === 'string' && pkgVersion before comparing.
tests/core/import-metadata-classification.test.ts scans src/, scripts/hooks/ and dashboard/src/ for metadata keys. scripts/audit/ is not a scan root, and scripts/audit/memory-invariants.mjs reads metadata.split_from — a key the allow-list admits on the grounds that its only reader is a one-shot, marker-guarded migration. The invariant only reports, so the impact is small, but the guard has a blind directory. Add scripts/audit (and check scripts/ more widely) to the scan roots and re-run the scrape.
pin: true is accepted on every entity an import creates, with no bound at bundle scale (20,000 of 20,000 measured), which exempts all of them from consolidation permanently. The other three validated exceptions are bounded. Decide whether a per-import cap, or a line in the import result, is wanted.
scripts/finish-release.mjs fails closed on a non-integer exit code only. A multiple of 256 would become 0 through process.exit() on POSIX. Not reachable today (runPostPublishFlow returns 0 or 1); restrict to 0 | 1 to keep it that way.
What happens
memesh import(CLI, the MCPimporttool andPOST /v1/importall callimportMemories()) merges a bundle'smetadatathrough an allow-list (#359). A key that is not on the list, and a value that fails one of the four validators (forgotten_observation_hashes,pin,signal_score,replaced_history), is dropped. Nothing says so:ImportResulthas no field for it, nothing reacheserrors, the CLI printsImported: N, Skipped: 0, Appended: 0and exits 0.Dropping is the right decision. Dropping without saying so is the defect.
Reproduced with the built CLI against a throwaway
HOME/MEMESH_DIR:guard,demo,task_state,consolidation_depth,compacted_into,proposal_id,session_id,evidence_forand one ordinary unknown key →Imported: 1, exit 0, all nine gone;signal_score: 5,pin: "true", one malformed hash, 1001 hashes, an extra key in areplaced_historyentry, 51 entries, a 300 KiB entry) →Imported: 10, exit 0, each field dropped whole.The sharp case is
forgotten_observation_hashes: it is what lets a user's own backup carry its own exclusions to a new machine. One corrupted element drops the whole list, and every observation the user had removed withforgetcan come back from a later Stop snapshot — with a successful-looking import.Before #359 the only dropped key was
guard, equally silently; #359 widened the set from one name to "everything not on the list".The source-scan test (
tests/core/import-metadata-classification.test.ts) guards keys written by THIS source tree. It cannot see a key that arrives in a bundle from a newer or older build.Suggested direction
dropped_metadata: { entity: string; key: string; reason: 'not_importable' | 'invalid' | 'existing_entity' }[](or a per-key count) toImportResult.skipped_relationsline; MCP/HTTP: return it.docs/api/API_REFERENCE.md; tests for each reason.Related, same area
ExportResultSchemais the one deliberately non-strict object, so the MCP tool and the HTTP route strip an entity'smetadata,created_atandstatusbeforeimportMemories()sees them. The API reference now says so formetadata;created_atandstatusare lost the same way and are not mentioned. A restore through MCP/HTTP silently loses creation time and archived state.scripts/lib/npm-latest-guard.mjsdecidesconfirmed = seen === pkgVersion. A failed read isnull; ifpkgVersionwere evernulltoo, absence would equal absence and the release would be reported confirmed with exit 0. Carried over unchanged from the pre-4.10.1 review follow-ups: four fixes before promoting to latest #359 script and very likely unreachable (earlier preconditions would stop anullversion), but it is one line to fail closed: requiretypeof pkgVersion === 'string' && pkgVersionbefore comparing.tests/core/import-metadata-classification.test.tsscanssrc/,scripts/hooks/anddashboard/src/for metadata keys.scripts/audit/is not a scan root, andscripts/audit/memory-invariants.mjsreadsmetadata.split_from— a key the allow-list admits on the grounds that its only reader is a one-shot, marker-guarded migration. The invariant only reports, so the impact is small, but the guard has a blind directory. Addscripts/audit(and checkscripts/more widely) to the scan roots and re-run the scrape.pin: trueis accepted on every entity an import creates, with no bound at bundle scale (20,000 of 20,000 measured), which exempts all of them from consolidation permanently. The other three validated exceptions are bounded. Decide whether a per-import cap, or a line in the import result, is wanted.scripts/finish-release.mjsfails closed on a non-integer exit code only. A multiple of 256 would become 0 throughprocess.exit()on POSIX. Not reachable today (runPostPublishFlowreturns 0 or 1); restrict to0 | 1to keep it that way.