Purpose
Choose the shortest-lived storage boundary that satisfies the next real consumer, and make ownership, expiry, replacement, and deletion explicit. This skill is the cross-repo routing contract; implementation details remain in each owning repository's docs.
Required Workflow
- Read the active repo's
AGENTS.mdand the relevant storage references listed in references/decision-guide.md. - Name the next consumer before choosing storage:
- same process/container;
- another Modal container;
- WIN or another background job;
- browser or HTTP-only provider;
- user-facing deliverable;
- durable product inventory; or
- external provider that becomes authoritative.
- Choose the first target in the decision table that satisfies that consumer.
- Define an owner and stable key family for any public object. A lifecycle prefix alone is not ownership.
- Define what happens when the object expires, is replaced, or its owner is deleted.
- Add focused proof for the selected boundary and update the owning repo's durable docs when behavior changes.
Storage Decision Table
| Boundary | Use when | Required behavior |
|---|---|---|
| Container-local file | Producer and consumer run in the same container invocation | Delete in finally; never serialize or return the path remotely |
| Modal internal artifact | Another Modal container or bounded retry needs the bytes | Use typed MediaArtifactRef; validate capability/manifest; let the 72-hour artifact lifecycle or exact run cleanup remove it |
R2 cache/ |
A browser, independent caller, background job, or HTTP-only consumer needs a URL | Treat as temporary transport; persist provenance; validate before reuse and reacquire or regenerate when missing |
R2 share/ |
A deliberately time-bounded, user-facing or cross-machine deliverable must outlive ordinary cache | Require a named product owner and documented expiry; never use as an unowned processing default |
R2 permanent/ |
The object is canonical durable inventory or a retained product asset | Use an owner-stable key, authoritative reference, replacement cleanup, and owner-deletion cleanup |
| Provider-owned storage | Ghost, YouTube, Transistor, Frame.io, or another destination accepts and owns the final bytes | Upload directly where possible; persist the provider identity needed by the product; do not retain a duplicate R2 final without a separate consumer |
Use local or Modal-internal storage for every intermediate in an all-Modal subgraph. Materialize publicly only at the first consumer that genuinely needs an HTTP URL.
Ownership And Cleanup Rules
- Generic utilities default to
cache/;share/andpermanent/require an explicit owning workflow. - Browser APIs expose purpose/upload intents, not raw lifecycle-folder choice.
- Every durable key should identify its owner, for example
permanent/ads/<source_id>/...orpermanent/channel-assets/<channel>/.... - Replace safely: commit the new authoritative reference first, then delete the previous exact managed object only when no other record shares it.
- Delete completely: remove dependent records/configuration and the exact owned object; missing media is an idempotent no-op.
- If post-commit cleanup fails, keep the new reference valid, log the orphan, and let inventory reporting detect it. Do not roll back to a dead URL.
- A
cache/orshare/URL stored in Mongo is never durable merely because the string remains. Active work must validate it and reacquire, regenerate, or fail with a clear re-upload requirement. - Preserve original provider/source provenance whenever temporary storage may expire.
- Never bulk-delete from a prefix without resolving its exact candidate set and protected references first. Record before/after counts and bytes for approved destructive cleanup.
Repo Routing
- For Modal entrypoints or artifact contracts, also use
$modal-function-sync. - For WIN/AIP public job or generated frontend contracts, also use
$win-aip-contract-sync. - Keep cross-repo decisions here; keep exact functions, schemas, paths, retention configuration, and commands in the owning repo docs linked from the decision guide.
- Put deterministic inventory, orphan detection, and lifecycle validation in the owning repository's scripts/checks. A skill teaches the workflow; it is not the enforcement mechanism.
Validation Checklist
- The selected target matches the next consumer and no earlier public boundary remains.
- No new function or browser upload defaults to
share/orpermanent/. - Expiring references have validation and recovery behavior.
- Replacement and deletion tests prove ordering and shared-reference safety.
- Provider uploads prove that no unnecessary R2 final copy is created.
- Scheduled cleanup covers new Modal cache namespaces and abandoned multipart uploads when applicable.
- Storage behavior is recorded in durable architecture/reference docs, not only an active project tracker.