feat: registered-blob family contract — declared index blobs are undeletable (ADR-004 Pass 2)
The class-killer for the lost-main.dkann incident (ADR-004 §7). A provider can declare a derived-index blob FAMILY (a set of members that are load-bearing together, e.g. vector-base = main.dkann + main.slotmap + main.slotrev). Once declared: - deleteBinaryBlob / removeRawPrefix REFUSE to remove a declared member, throwing the new ProtectedArtifactError — an in-process GC / sweeper is now INCAPABLE of deleting a load-bearing index file (COLD != DEAD). Intentional retirement is an explicit unregisterDerivedFamily(name) first. - The declaration persists to _system/derived-artifacts.json, so protection survives a reopen; clear() resets it with the rest of the derived footprint. - checkDerivedFamiliesPresent() names any member missing on open (the catch for an EXTERNAL deleter that bypasses the in-process refusal) → rebuild from canonical. - Transients (*.tmp.*, *.rebuild-tmp, *.rotate-tmp) are never protected; a namespace family protects a growing prefix (seg-*). New StorageAdapter surface (optional): registerDerivedFamily / unregisterDerivedFamily / listDerivedFamilies + DerivedFamilyDeclaration; new exported errors ProtectedArtifactError / DerivedArtifactMissingError. Enforcement is inert until a provider declares a family (no regression). Cor declares its 6 families + does the atomic set-swap in 3.0.15 (M2); brainy builds the contract now. 8 tests.
This commit is contained in:
parent
6bcb54f0d9
commit
bfa1762107
7 changed files with 429 additions and 5 deletions
|
|
@ -630,6 +630,9 @@ export class FileSystemStorage extends BaseStorage {
|
|||
*/
|
||||
public override async removeRawPrefix(prefix: string): Promise<void> {
|
||||
await this.ensureInitialized()
|
||||
// Registered-blob contract: a prefix-nuke must not take out a protected
|
||||
// family member (ADR-004 §7). Throws if the prefix intersects one.
|
||||
await this.assertPrefixNotProtected(prefix)
|
||||
await fs.promises.rm(path.join(this.rootDir, prefix), { recursive: true, force: true })
|
||||
}
|
||||
|
||||
|
|
@ -1250,6 +1253,11 @@ export class FileSystemStorage extends BaseStorage {
|
|||
*/
|
||||
public async deleteBinaryBlob(key: string): Promise<void> {
|
||||
await this.ensureInitialized()
|
||||
// Registered-blob contract (ADR-004 §7): refuse to delete a declared
|
||||
// derived-index family member — an in-process GC/sweeper cannot remove a
|
||||
// load-bearing index file. Throws ProtectedArtifactError; no-op when no
|
||||
// families are registered.
|
||||
await this.assertBlobKeyDeletable(key)
|
||||
try {
|
||||
await fs.promises.unlink(this.blobPath(key))
|
||||
} catch {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue