Skip to content

Recoverable user-backup replacement commit strategy

Issue: #381
Environment for implementation and acceptance: myrecipes-test only
Decision date: 2026-08-21

This note is the required design checkpoint before implementing full replacement. It defines how a confirmed #379 archive replaces one authenticated user's in-scope recipe dataset without treating Firestore and Cloud Storage as one transaction. Production promotion is separate and remains gated by #391.

Chosen activation strategy

Version 1 uses a journaled multi-stage commit with a backend-enforced owner lease. It does not add a dataset-generation pointer to every application query.

The current global collections and numeric business IDs cannot be switched with one per-user pointer. A delete-then-recreate workflow would therefore be unrecoverable. Replacement instead uses these protections:

  1. lock the affected owner's recipe reads and writes;
  2. produce and independently validate a seven-day safety export;
  3. stage and verify the complete incoming graph and image bytes privately;
  4. journal the exact active documents and quarantine image content;
  5. activate in bounded idempotent batches;
  6. verify the active dataset before reporting success; and
  7. retain enough private material for a verified rollback for seven days.

Partial active data is never reported as successful. During active mutation, the affected owner's recipe dataset is temporarily unavailable to the owner and collaborators. Out-of-scope application features remain available.

Authoritative boundary

The included and excluded collections remain those in docs/release/recipe-backup-scope.md. The authenticated UID and trusted backend mappings establish ownership. Archive IDs establish relationships only; archive ownership fields, paths, URLs, bucket names, and membership claims never establish authority.

Phase Authoritative artifact Purpose
Before mutation Locked active dataset plus verified safety export Recovery point if staging or activation fails
Staging Validated archive generation/hash plus private staged graph Candidate dataset; never client-readable
Committing Exact rollback journal/quarantine plus operation batch ledger Resume or restore after interruption
Verifying Active graph plus immutable operation expectations Prove counts, ownership, relationships, and images
Completed Verified active graph Application source of truth
Rollback Exact journal/quarantine Restore the pre-import active state

The confirmed upload remains immutable by object generation and SHA-256. The safety export and rollback journal are independent artifacts; neither is discarded merely because the other exists.

Operation state machine

The existing #379 import operation transitions from confirmed into the #381 states below. Transitions require the expected prior state and record a trusted server timestamp. Publication and rollback publication use the specifically defined atomic multi-document batches rather than a separate state transition.

confirmed
   |
   v
safety_export --> failed                 (no active mutation)
   |
   v
staging ------> failed                   (no active mutation)
   |
   v
committing ---------> rollback_required
   |                        |
   v                        v
verifying ----------> rolling_back
   |
   v
publishing ------------> rollback_required
   |
   v
completed

rolling_back -> verifying_rollback -> publishing_rollback -> rolled_back
  • safety_export: lease acquired; internal complete export is being created and revalidated.
  • staging: trusted IDs and paths are assigned; documents and images are private and are verified against the validated archive.
  • committing: an active-mutation marker and exact journal exist; bounded batches are journaled and idempotently applied.
  • verifying: all expected active writes completed; final graph/image checks run while every replacement read block remains enabled. It never reports success or clears maintenance state.
  • publishing: verification succeeded; one atomic Firestore batch is making the complete verified dataset readable and marking the operation complete.
  • completed: publication succeeded and the verified dataset is readable. Only deferred cleanup may remain.
  • failed: terminal pre-mutation failure; active content was never changed.
  • rollback_required: mutation may have occurred and normal access stays locked until resume or rollback succeeds.
  • rolling_back: deterministic document/image restoration is running. Every original recipe-box document is restored with its original application fields, but its replacement projection is deliberately overridden to remain blocked for the current operation.
  • verifying_rollback: the complete restored logical state is checked while the deliberate replacement-projection overrides keep access blocked.
  • publishing_rollback: one atomic batch reopens all restored boxes and marks the operation rolled back.
  • rolled_back: exact pre-import active state was restored and verified.

An operation also stores a monotonic phase sequence, current batch number, completed batch keys, heartbeat, cleanup status, bounded error code, and counts. Retries compare these fields before doing work. Duplicate confirmation or task delivery returns the existing operation and never creates another dataset.

Lease ownership and access enforcement

The authoritative lease document is user_recipe_replacement_locks/{ownerUid}. Lease ownership is the tuple ownerUid + operationId + approved replacement service identity; it never belongs to one worker invocation. It contains the operation ID, phase sequence, acquired/renewed/expiry timestamps, active-mutation marker, latest heartbeat, service identity, and schema version.

  • Lease duration: 30 minutes.
  • Worker heartbeat: at least every two minutes.
  • Any worker using the approved service identity may renew it after validating owner UID, operation ID, current state, expected phase sequence, active-mutation status, and latest lease metadata.
  • Another import cannot take over an expired lease.
  • A pre-mutation stale lease may be released only after proving no active mutation occurred, no active-mutation marker exists, rollback is unnecessary, and staging cleanup is safe.
  • A post-mutation stale lease transitions to rollback_required; access does not reopen automatically.

Expected operational phase ceilings are 15 minutes for safety export, 30 minutes for staging/journaling, 30 minutes for committing, 15 minutes for verification, five minutes for publication, 30 minutes for rollback, and five minutes for rollback publication. Heartbeat absence for five minutes alerts the operator. Any post-mutation lock older than 45 minutes creates a high-priority recovery incident. The escalation path is: inspect operation and batch ledgers, stop duplicate dispatch, invoke deterministic resume when its preconditions hold, otherwise invoke the restricted rollback function, and record the reason/result in the administrative audit. Expiry never reopens post-mutation access.

Emulator-backed read-lock feasibility result

The design does not add another lease-document lookup to every rule. Cloud Storage recipe-image authorization already reads the recipe and box documents; adding a third Firestore access would exceed the safe established shape. The authoritative lease is instead projected by trusted backend code to:

  • users/{ownerUid}.recipe_replacement_read_blocked; and
  • every affected recipeboxes/{boxId}.replacement_read_blocked.

Both projections also carry the operation ID. They remain true through committing, verifying, and rollback_required, even if the lease timestamp expires. New active boxes are created blocked and are unblocked only after final verification. Firestore recipe/child rules use the already-read box record; Storage uses its existing recipe-plus-box reads. Direct staging/journal paths remain default-deny.

The proof is implemented by functions/src/recipe_replacement_lock_poc.test.ts with service-only rules in functions/test-fixtures/. npm run test:replacement-lock-poc passes nine emulator tests and proves:

  • current owned/shared box and recipe query shapes succeed while unlocked;
  • owner, collaborator, child, and image reads fail while the owner is blocked;
  • the owner can read the intentional maintenance marker;
  • a shared-box query constrained by replacement_read_blocked == false excludes a locked owner's boxes while returning unrelated unlocked owners;
  • the subsequent realistic recipe query succeeds with ten distinct unlocked boxes, the existing Firestore rule-access ceiling;
  • an outdated mixed recipe query that includes a locked box fails closed;
  • stale post-mutation projection remains blocking; and
  • all staging/journal Storage and Firestore namespaces deny client access.

The application must add the unlocked constraint to owned/shared box queries, hydrate recipes only from the returned unlocked box IDs, and render the owner's maintenance marker intentionally. Before rollout, existing box records require replacement_read_blocked: false plus the corresponding composite indexes. Modified or outdated clients cannot read partial data: a direct locked document read fails, and a mixed query containing a locked result is rejected wholesale.

Projection occurs before activeMutationStartedAt. It may use multiple bounded batches because active content has not changed. Before mutation, a fresh check must prove the user marker and every affected current box carry true and the current operation ID; every staged future box is configured to be created blocked; direct affected document/image reads fail; recipe writes and membership changes fail; and the journal, quarantine, and staged candidate are complete and verified. Interrupted projection either finishes idempotently or removes only the current operation's projections and leaves the original dataset unchanged.

Both the incoming normalized archive and the user's current owned dataset must contain no more than 25 recipe boxes before mutation begins. An account exceeding that limit must be rejected before block projection or handled by a separately approved migration.

During committing, verifying, rollback_required, rolling_back, and verifying_rollback, reads of every in-scope collection that may be partially replaced are denied by Firestore rules. Recipe boxes, recipes, recipe_ingredients, recipe_directions, recipe_notes, private_recipe_notes, and recipe_categories follow the projected box relationship. recipe_user_metadata, recipe_user_measures, and recipe_user_ingredients also consult the existing owner user-document maintenance projection. No in-scope collection in recipe-backup-scope.md is excluded from this guarantee.

A modified or outdated client therefore cannot use a direct auxiliary-document read to observe partial replacement data. Normal reads resume only after atomic publication or rollback publication clears the projections. Direct user measure/ingredient reads add one user-document rules access. User metadata requires at most the user, recipe, and box documents (three accesses for a direct read); private notes use the recipe and box relationship (two). Emulator tests cover every in-scope auxiliary collection while unlocked, blocked, and republished and must remain within Firestore's access-call limits.

Cost/performance impact is bounded: box queries use the resource field and add no document access; recipe/child queries retain their existing box lookup; Storage retains its existing two Firestore accesses; the owner maintenance preflight adds one user-document read per hydration cycle. Query chunks remain at no more than ten distinct box IDs.

The following paths must check the owner lease:

Path Enforcement
Recipe-box create/update/delete Callable transaction and Firestore rules
Recipe and child save/delete Callable transaction and Firestore rules
User recipe measures/ingredients/metadata reads and writes Owner maintenance projection, relationship-aware Firestore rules, and callable transaction
Image reserve/upload/finalize/delete Callable transaction and Storage rules
Recipe-box membership mutation Callable transaction and Firestore rules
Administrative recipe migrations Explicit backend lease preflight
Owner/collaborator in-scope reads during mutation and rollback verification Trusted projected block, Firestore/Storage rules, filtered queries, and maintenance UI

Shopping lists, profiles/Auth, entitlements, purchases, usage/rate-limit data, and administrative audit data are outside the lock and replacement boundary.

Projection fields are server-owned. Firestore rules allow clients to read the owner maintenance status but require ordinary user updates to preserve these fields exactly:

users/{ownerUid}.recipe_replacement_read_blocked
users/{ownerUid}.recipe_replacement_operation_id
recipeboxes/{boxId}.replacement_read_blocked
recipeboxes/{boxId}.replacement_operation_id

Only myrecipes-user-replacement@${projectId}.iam.gserviceaccount.com may change them. Ordinary box creation runs through trusted code and writes the normal false value with no operation ID. Owners, collaborators, anonymous/unrelated users, old clients, migrations, and administrative helpers cannot clear or replace projections; non-replacement trusted tools must explicitly preserve them. Rules tests exercise all client roles. Backend integration tests exercise administrative preservation and the replacement identity allowlist.

Firestore IAM is not field-granular: any project principal with broad Datastore write authority is privileged at the platform layer. Deployment therefore limits deliberate projection mutation to replacement functions running as the approved identity, removes that identity from unrelated functions, inventories every other privileged runtime/tool, and requires their code and tests to preserve projections. Client rules provide field-level enforcement for SDK clients; IAM, function deployment review, and audit alerts provide the trusted backend boundary.

Safety-export gate

The replacement worker creates an internal #378-format export after acquiring the lease. This export does not consume the user's ordinary export/recovery allowance. It must be:

  • complete, never incomplete;
  • bound to the locked owner;
  • checksum-valid and revalidated with the released import validator;
  • immutable by recorded object generation and SHA-256;
  • count-matched to the locked active inventory; and
  • retained privately for seven days.

The operation records the safety-export operation ID, generation, hash, counts, creation time, and expiry. Staging may begin after this gate, but active mutation may not begin until both the safety export and staged candidate are verified.

Physically isolated staging and trusted identity mapping

Firestore staging uses exactly:

user_backup_replacement_staging/{operationId}
user_backup_replacement_staging/{operationId}/entries/{sequenceId}
user_backup_replacement_staging/{operationId}/image_entries/{imageId}

The parent is a manifest; each entry is one bounded normalized future active document with collection, trusted document/business IDs, canonical payload checksum, ordering key, and owner/operation identity. It is never stored in an active global collection.

The dedicated private bucket ${projectId}-user-replacement uses:

staging/{ownerUid}/{operationId}/images/{archiveImageId}.{jpg|png}
safety-exports/{ownerUid}/{operationId}/archive.zip
journals/{ownerUid}/{operationId}/firestore/chunk-{sequence}.ndjson
quarantine/{ownerUid}/{operationId}/images/{encodedLogicalPath}

Firebase Storage rules deny all client access to the bucket/prefixes. Only myrecipes-user-replacement@${projectId}.iam.gserviceaccount.com and the separately documented cleanup identity receive least-privilege object access. Firestore rules deny all client access to staging, journal manifests, locks, batch ledgers, and replacement audit records.

On activation, trusted workers read staged entries and write newly mapped active documents in bounded batches; images are copied to controlled canonical active paths with generation preconditions. Pre-mutation failure deletes staging after verification. Completion/rollback marks it cleanup-eligible; a scheduled sweep finds expired manifests by expiresAt, verifies no rollback dependency, deletes bounded pages/prefixes, and records completion.

Trusted code allocates new Firestore document IDs, numeric business IDs, canonical image paths, and object metadata. It creates explicit maps for boxes, recipes, children, ingredient parents, catalog rows, and images. It preserves valid portable content timestamps and records restoredAt separately.

Before activation, verification proves:

  • expected document and image counts;
  • authenticated ownership on every staged root;
  • every recipe-to-box and child-to-recipe relationship;
  • every ingredient parent relationship within the same recipe;
  • unique controlled IDs and paths;
  • image length, SHA-256, MIME type, extension, and byte signature; and
  • absence of out-of-scope fields and archive authority.

Version 1 sharing policy

Every imported recipe box is private. Names and archive-local IDs never establish box identity, so v1 performs no name matching and transfers no membership. The archive cannot create collaborators. Current owned boxes absent from the archive are removed, and their existing collaborator access ends. Users may share imported boxes again after completion.

Before confirmation, preview and confirmation both display:

Imported recipe boxes will be private. Existing sharing and collaborator access for recipe boxes being replaced will be removed. You can share the imported boxes again after the import finishes.

Memberships on boxes owned by other users remain unchanged. Those boxes, recipes, images, and personal metadata are outside staging/journaling. Tests assert no write targets them; semantic snapshots, document/business IDs, membership/access relationships, and out-of-scope sentinels remain equal. Their Storage paths, generations, hashes, metadata, and bytes remain unchanged.

Scalable journal and journaled activation

Before the first active mutation, trusted code creates and verifies:

  • a complete checksummed journal in bounded Cloud Storage chunks;
  • a private quarantine copy of each current owned image with source generation, path, metadata, size, and SHA-256; and
  • a deterministic activation plan divided into bounded batches.

The Firestore manifest is user_backup_replacement_journals/{operationId}. Journal objects are immutable NDJSON chunks containing at most 500 entries and at most 4 MiB uncompressed. Each entry contains schema/sequence, original collection/document identity, original business identifiers, complete original field/timestamp values, membership state, owner/operation identity, and a canonical SHA-256. The manifest records schema version, expected entry/chunk counts, ordered chunk paths/generations/lengths/checksums, creation status, and trusted completion timestamp.

Chunk creation is resumable by deterministic sequence. Verification rereads every immutable chunk by generation, validates entry/chunk checksums and order, and compares expected counts with a fresh locked inventory. A manifest written before a missing chunk remains incomplete. activeMutationStartedAt cannot be set until independent verification marks the manifest complete and all image quarantine copies are verified.

The operation then records activeMutationStartedAt before applying batch one. Firestore activation and rollback batches use at most 450 deterministic content mutations. The corresponding batch-ledger completed write and verification metadata are included in the same atomic Firestore WriteBatch, remaining below the 500-write limit. There is no separate durable claim whose existence implies success. If a worker dies before commit, neither mutations nor completion exist; if commit returns or is uncertain, the next worker reads the ledger and verifies all deterministic postconditions before proceeding. Deletes cover only the authoritative #374 selection. Writes use only staged trusted data.

Storage cannot join that batch. Each deterministic image action has explicit pending, applying, and completed ledger state. A resumed applying action checks source/destination generations, path, hash, length, MIME, metadata, and signature to decide whether to complete, retry with preconditions, or require rollback. A claim alone never proves an object mutation completed.

Firestore and Storage steps are deliberately not called atomic. The lease, journal, batch ledger, immutable object generations, and verification make interruption recoverable.

Verification and success

Success is reported only when a fresh active inventory proves:

  • document counts equal the normalized staged expectations;
  • ownership and all relationships are valid;
  • no replaced old document remains active;
  • every expected image exists at its controlled path and matches its recorded new generation, size, hash, MIME type, metadata, and signature;
  • every imported box is private with an empty collaborator list;
  • all excluded-data sentinels are unchanged; and
  • two-user isolation sentinels are unchanged.

A complete empty archive follows the same process. It removes all current in-scope content. The application requires a default recipe box, so an archive with zero boxes is normalized to one empty private default box. Preview and confirmation disclose that normalization; expected counts include it; and the completion report identifies it. Verification states: the active dataset equals the normalized interpretation of the archive, not its literal box count.

Timestamp restoration rules

  • createdAt and updatedAt preserve portable content history when they are RFC 3339 UTC values from 2000-01-01T00:00:00Z through the manifest exportedAt, inclusive.
  • updatedAt is preserved rather than replaced by the import time; when both exist it may not precede createdAt.
  • Missing portable timestamps remain null.
  • Malformed, pre-boundary, future-after-export, and inverted timestamps are fatal validation errors; they are not silently normalized.
  • restoredAt, operation transitions, leases, audits, journal completion, and cleanup timestamps always use trusted server timestamps.
  • Ownership, entitlement, membership, purchase, usage, and administrative timestamps are outside the archive contract and are never restored.

Tests cover valid historical values, both inclusive boundaries, null/missing values, malformed text, pre-boundary values, future-after-export values, and updatedAt < createdAt.

Atomic publication

No imported box or image is readable before the normalized active dataset and all canonical image relationships pass verification. With the enforced ACCOUNT_LIMITS.recipeBoxes == 25, replacement publication uses one atomic Firestore WriteBatch containing:

  • at most 25 imported box updates clearing the read block and operation ID;
  • one owner user-document update clearing its maintenance projection;
  • one replacement-operation update to completed with trusted publishedAt; and
  • one deterministic publication-ledger write containing completion metadata.

The maximum is therefore 28 writes, leaving 472 writes of headroom below Firestore's 500-write limit. The implementation uses a stricter 450-write internal ceiling. Archive validation already rejects 26 boxes; confirmation and the pre-mutation inventory gate recheck the same enforced bound. Visibility flags are never cleared in multiple publication batches.

Publication preconditions are: state verifying, current immutable verification evidence, matching owner/operation projections on all expected boxes and user, complete image verification, and absent/conflicting publication ledger. The atomic batch clears every projection, records the publication ledger/timestamp, and changes the state to completed together. Every update uses the exact document update-time observed during the precondition read so a concurrent change fails the entire batch.

On retry or an uncertain commit response, the worker rereads the operation, user marker, publication ledger, and every expected box marker:

  • all postconditions true means publication completed;
  • all preconditions still true and no publication mutation means retry the same deterministic batch;
  • any impossible mixed state remains blocked and becomes rollback_required or restricted administrative recovery.

An attempted batch never implies success. Duplicate delivery of a completed operation performs no write. Publication failure is not deferred cleanup: it cannot set completed, clear maintenance, or report success.

Atomic rollback reopening

During rolling_back, every original recipe-box document is restored with its original logical application fields except the two projected replacement fields, which are intentionally forced to:

replacement_read_blocked: true
replacement_operation_id: currentOperationId

Those overrides remain unchanged throughout rolling_back and verifying_rollback. Only after complete rollback verification does publishing_rollback use one atomic batch of the same maximum 28 writes to clear every restored original box projection, clear the user marker, set rolled_back, record trusted rollbackPublishedAt, and write the deterministic rollback-publication ledger.

Uncertain rollback publication uses the same complete pre/postcondition reconciliation. Restored boxes never reopen incrementally. Any mixed or failed result remains locked for deterministic retry or restricted recovery.

Retry and rollback

Pre-mutation failures become failed, release the lease after cleanup is verified, and leave active content unchanged. Post-mutation failures remain locked and become rollback_required unless the same operation can safely resume its deterministic plan.

Rollback reverses the activation plan using the journal and quarantined image content. “Exact Firestore restoration” means exact original logical application fields, document IDs, business IDs, memberships, and timestamps. The temporary replacement projections are deliberately not restored from the journal during rolling_back or verifying_rollback; they remain blocked for the current operation until atomic rollback publication.

Rollback also restores the same logical image paths, bytes, SHA-256, required custom metadata, MIME types, and relationships. Cloud Storage normally assigns a new generation; rollback records and verifies that restored generation. Original source generations are recorded and generation-match preconditions protect the copy into quarantine. “Exact rollback” means exact logical application state, subject to those temporary projection overrides and never reuse of a provider-assigned generation number.

Automatic workers may resume the same operation or execute its predetermined rollback. Manual resolution requires an administrative recovery function, authorized operator, reason, and audit record. It cannot accept replacement content or arbitrary paths.

Failure-injection matrix

Integration tests must stop the worker immediately before and after every boundary, redeliver the task, and prove the expected result.

Boundary Expected result
Lease acquisition One owner/operation wins; duplicate delivery reuses it
Before initial block projection Active dataset unchanged; safe retry
During multi-batch pre-mutation projection Active dataset unchanged; finish projection or safely remove it
After block projection verification Mutation begins only when user and every affected box are blocked
Safety export creation No mutation; retry or terminal failed
Safety export revalidation No mutation; invalid export blocks activation
Document staging No mutation; deterministic resume or cleanup
Image staging No mutation; generation/hash mismatch blocks activation
Staged verification No mutation; bounded fatal result
Journal creation No mutation until complete journal is verified
Journal chunk interrupted Deterministic resume; incomplete manifest cannot pass
Manifest before missing chunk Verification rejects it before mutation
Active-mutation marker Resume batch one or rollback; never release lock
Worker death after nominal batch claim No claim implies completion; inspect atomic ledger/postconditions
Worker death after document mutations Atomic content-plus-ledger batch is wholly applied or unapplied
Each delete/write batch Idempotent resume or exact rollback
Quarantine source-generation mismatch Copy fails before active mutation
Each image activation step Generation-aware resume or exact rollback
Active verification No success; repair/resume or rollback_required
Before replacement publication Verified dataset remains fully blocked
Publication batch commit All boxes, user marker, ledger, and operation state change atomically
Uncertain publication response Verify every deterministic publication postcondition
Duplicate publication delivery Return completed without another write
Rollback document batch Idempotent resume until pre-state matches
Rollback image step Restore logical path/content/metadata; record and verify new generation
Before rollback publication Restored dataset remains fully blocked
Rollback publication batch Restored boxes, user marker, ledger, and operation change atomically
Uncertain rollback publication Verify every rollback-publication postcondition
Duplicate rollback publication Return rolled_back without another write
Lease release Only after verified completion or rollback
Deferred cleanup Dataset remains completed; retry and alert cleanup

Tests also cover double submission, repeated Cloud Tasks delivery, different workers renewing the same operation, another operation failing to renew/take over, expired pre/post-mutation leases, post-mutation escalation threshold, staging queried by a normal client, an empty archive and disclosed default-box normalization, 1,000 recipes, Unicode, maximum children/images, a same-name shared current box becoming private, realistic shared-query rule limits, and another user with reciprocal sharing.

Automated publication coverage additionally proves no imported box/image is readable before publication, all boxes reopen together, the user marker remains set until success, collaborator isolation, failure immediately before publication, uncertain-result reconciliation, duplicate idempotency, the 25-box/28-write bound with 472-write headroom, rejection at 26 boxes, atomic rollback reopening, projection-field client immutability, normal post-publication queries, and the planned composite-index contract. Administrative projection preservation and deployed migration/index verification remain mandatory implementation gates in myrecipes-test.

Retention and cleanup

Artifact Retention
Unfinished upload/pre-commit staging At most 24 hours
Verified safety export Seven days after completion/rollback
Exact document rollback journal Seven days after completion/rollback
Superseded image quarantine Seven days after completion/rollback
Failed post-mutation material Until verified rollback, then seven days
Bounded operation/audit metadata Existing administrative retention policy

After atomic publication, cleanup failure does not misreport the dataset as failed. The operation is completed with cleanupPending: true; scheduled idempotent cleanup retries and emits a content-free alert. Cleanup never removes the last recoverable artifact early.

Entitlement and audit

A write-enabled entitlement is checked at confirmation and again before lease acquisition. After active mutation begins, entitlement expiry cannot strand a partial commit; the service identity must complete verification or rollback.

Audit fields are limited to actor UID, operation IDs, state transitions, timestamps, validator/export versions, archive/safety hashes and generations, counts, lease events, bounded error codes, cleanup status, and administrative resolution reason. Recipe content, filenames, collaborator addresses, URLs, archive bytes, tokens, and signed credentials are prohibited from logs/audit.

Development acceptance exercise

Implementation is accepted only after automated tests and an end-to-end myrecipes-test exercise prove:

  1. export and record the target user's current counts and two-user sentinels;
  2. confirm a validated archive and observe the owner maintenance lock;
  3. verify the complete seven-day safety export before mutation;
  4. inject and recover from every state boundary in the matrix;
  5. complete replacement and verify normalized archive counts/relationships/images;
  6. prove absent old content is no longer active;
  7. prove no write targets the other user and semantic Firestore/Storage/access snapshots and exclusions are unchanged;
  8. exercise duplicate confirmation/task delivery and concurrent write denial;
  9. force a post-mutation failure and verify exact rollback;
  10. exercise a valid empty archive and default-box normalization;
  11. record duration, document/object operations, temporary storage, and cost;
  12. verify seven-day retention and cleanup/alert behavior.

No production replacement deployment or customer-data exercise is authorized by this note. The exact runtime identities, buckets, functions, rules, indexes, alerts, commands, and rollback evidence must be added to the development runbook and #391 production migration guide during implementation.