Refs #16549 — this blocks that ticket's discriminating experiment.
Context
The agreed reproduction for the in-place corpus loss was: restore a small slice by the same --mode merge --only-substrate=kb path into a throwaway collection name, verify retrieval, delete one document, re-count.
That invocation does not exist. restore.mjs's full argument surface is --mode merge|replace, --force, --force-topology-mismatch, --only-substrate=<csv>, --preserve-read-state. There is no --collection, no --target-name, no rename option — the collection is resolved from config, not from an argument.
So run as designed it would have restored 61,206 rows into the live canonical neo-knowledge-base: the exact collection under investigation, which we had explicitly agreed not to touch. Caught by reading the flags before firing rather than after.
The Problem
A restore tool that can only write to production cannot be used to diagnose itself.
Every property worth testing about a restore — does it land, is it durable across a restart, does it survive the first mutation, does a partial bundle behave — requires performing one. With no target override, each of those experiments is indistinguishable from a production restore, so the safe choice is to run none and the available choice is to risk the corpus.
That is the shape that makes an incident un-diagnosable: the tool's blast radius equals its only usable configuration.
The Fix
Not prescribed in detail; one deliberate open question below. The shape:
- A target override — a flag that redirects the restore's collection target to a caller-supplied name, so a slice can be restored somewhere disposable.
- It must be inexpressible to accidentally alias a canonical name. The override should refuse a canonical target rather than trusting the caller — the
DestructiveOperationGuard's uniform gate is the precedent, and it already fires for every destructive collection-delete regardless of name.
- A
--dry-run would cover a different and cheaper class: confirming what a restore would write without writing it. Worth considering alongside, not instead.
Open Question
OQ1 — is a target override the right shape, or should the diagnostic path avoid restore.mjs entirely? The alternative is a purpose-built probe that exercises collection.add() durability directly, with no bundle and no restore semantics. That is simpler and has a smaller blast radius, but it tests the client path rather than the restore, so it cannot catch a defect that lives in bundle parsing, id preflighting, or chunking. Whoever takes this should settle which property is actually under test before adding a flag — a flag that invites production-shaped experiments into a diagnostic role may be worse than no flag.
Acceptance Criteria
Out of Scope
- The durability defect itself — #16549 owns it. This ticket only makes it testable.
- Changing
--mode merge / --mode replace semantics. The two-mode contract and its guards are working as documented; the gap is target selection, not destructiveness.
- Backup-side tooling. Bundle production is unaffected.
Avoided Traps
- Adding the flag without a refusal. An override that can name a canonical collection turns a diagnostic convenience into a faster path to the incident it exists to study.
- Assuming the flag is obviously right. OQ1 is real: a purpose-built durability probe may be the better answer, and it tests less. Naming which property is under test has to precede the mechanism.
- Treating this as documentation-only. The missing JSDoc contributed, but a comment would not have made the experiment runnable — the capability is genuinely absent.
Related
- #16549 — the durability defect whose discriminating experiment this unblocks.
- #16521 · #16510 — the restorability-verdict lane; adjacent, and unaffected by target selection.
Live latest-open sweep: recent open issues plus targeted restore/durability searches run for #16549 in the same pass; nearest neighbours #15639 and #15693 are restore features and neither addresses target selection or diagnosability.
Origin Session ID: 11695cce-9854-4be2-80c3-8ea4322298bf
Retrieval Hint: query_raw_memories("restore.mjs no target collection flag cannot reproduce restore defect without writing to production canonical guard")
Refs #16549 — this blocks that ticket's discriminating experiment.
Context
The agreed reproduction for the in-place corpus loss was: restore a small slice by the same
--mode merge --only-substrate=kbpath into a throwaway collection name, verify retrieval, delete one document, re-count.That invocation does not exist.
restore.mjs's full argument surface is--mode merge|replace,--force,--force-topology-mismatch,--only-substrate=<csv>,--preserve-read-state. There is no--collection, no--target-name, no rename option — the collection is resolved from config, not from an argument.So run as designed it would have restored 61,206 rows into the live canonical
neo-knowledge-base: the exact collection under investigation, which we had explicitly agreed not to touch. Caught by reading the flags before firing rather than after.The Problem
A restore tool that can only write to production cannot be used to diagnose itself.
Every property worth testing about a restore — does it land, is it durable across a restart, does it survive the first mutation, does a partial bundle behave — requires performing one. With no target override, each of those experiments is indistinguishable from a production restore, so the safe choice is to run none and the available choice is to risk the corpus.
That is the shape that makes an incident un-diagnosable: the tool's blast radius equals its only usable configuration.
The Fix
Not prescribed in detail; one deliberate open question below. The shape:
DestructiveOperationGuard's uniform gate is the precedent, and it already fires for every destructive collection-delete regardless of name.--dry-runwould cover a different and cheaper class: confirming what a restore would write without writing it. Worth considering alongside, not instead.Open Question
OQ1 — is a target override the right shape, or should the diagnostic path avoid
restore.mjsentirely? The alternative is a purpose-built probe that exercisescollection.add()durability directly, with no bundle and no restore semantics. That is simpler and has a smaller blast radius, but it tests the client path rather than the restore, so it cannot catch a defect that lives in bundle parsing, id preflighting, or chunking. Whoever takes this should settle which property is actually under test before adding a flag — a flag that invites production-shaped experiments into a diagnostic role may be worse than no flag.Acceptance Criteria
restore.mjs's JSDoc states the target contract at the argument, so the next person designing an experiment does not assume a capability that is absent — that assumption is what produced this ticket.Out of Scope
--mode merge/--mode replacesemantics. The two-mode contract and its guards are working as documented; the gap is target selection, not destructiveness.Avoided Traps
Related
Live latest-open sweep: recent open issues plus targeted restore/durability searches run for #16549 in the same pass; nearest neighbours #15639 and #15693 are restore features and neither addresses target selection or diagnosability.
Origin Session ID: 11695cce-9854-4be2-80c3-8ea4322298bf
Retrieval Hint:
query_raw_memories("restore.mjs no target collection flag cannot reproduce restore defect without writing to production canonical guard")