Skip to content

Media Workflow And Validation

Use this page when deciding how much media proof a change needs. The architecture page explains the model; this page chooses the day-to-day loop: mocked storage, local emulators, real deployed dev, media smoke, browser media smoke, and discrepancy reporting.

The default posture is simple: use the cheapest lane that proves the risk, and keep real cloud media validation explicit.

Artist, Event, Venue, Event Series, and Event Series Edition have implemented entity-owned image-media authoring workflows. Ordinary behavior is proven through entity-focused contract, handler, persistence, workflow, and presentation tests while provider behavior remains owned by the shared storage adapter. A new media-bearing entity does not require duplicating S3Mock or deployed-cloud proof when no new provider path, IAM boundary, private-bucket behavior, or CloudFront delivery risk was introduced.

Change ShapeRecommended LaneWhy
UI layout, form mapping, media draft state, validation messages, or non-media feature workMocked storageProves app behavior without object-store startup or cloud state.
Upload/delete path construction, S3 adapter behavior, public URL resolution, or local cleanup behaviorS3 emulator through S3MockExercises AWS-shaped storage without touching deployed dev.
Azure-specific continuity checksAzure emulator through AzuriteKeeps the older Azure path smokeable without making Azure the roadmap driver.
IAM, bucket policy, S3/CloudFront delivery, same-origin /media/*, or deployed runtime configDeployed dev media validationOnly real AWS dev proves cloud permissions and edge delivery.
Browser rendering of deployed mediaBrowser media smokeProves the actual user-visible media path after seeded media is available.
DB/S3 drift after reset, media smoke, manual media testing, or bucket investigationMedia discrepancy reportRead-only comparison of deployed DB rows and S3 objects.

Do not use real AWS dev storage for ordinary local iteration. Real-cloud checks should be intentional because local database resets, upload experiments, and cleanup scripts can otherwise leave durable objects without matching rows and introduce persistent hidden costs as stored objects accumulate.

Local media behavior is selected by the following backend env values:

  • MEDIA_STORAGE_PROVIDER
  • MEDIA_STORAGE_EXECUTION_TARGET
  • MEDIA_STORAGE_APPLICATION_ENVIRONMENT

Mocked storage is the default app-logic lane. It is the right place for most frontend and backend feature work where the storage provider itself is not under investigation.

Use S3 emulator mode when the provider behavior matters:

  • Uploading, deleting, and resolving stored objects.
  • Verifying object key construction.
  • Exercising provider-specific adapter code.
  • Checking local cleanup and reset behavior.

pnpm dev:docker:up reads the selected local media mode and adds the S3 or Azure emulator compose layer only when that mode requires it. Both emulator paths retain state through named volumes and expose the same exact-confirmed, emulator-only reset boundary.

Keep these distinctions intact:

  • S3Mock and Azurite are execution targets, not persisted media providers.
  • Mocked storage is a deliberate provider double for development and tests.
  • AWS_PROFILE is a cloud credential selector, not a media-mode selector.
  • Generic local reset commands must not target real cloud storage.

Use deployed dev when the change needs cloud proof:

  • Runtime media config from SSM and host-side env rendering.
  • Runtime host IAM access to the private media bucket.
  • S3 object writes and reads through the deployed backend.
  • Browser-facing delivery through same-origin CloudFront /media/*.
  • Media behavior after runtime deploy, reset, or rollback.

Choose the narrowest proof:

ProofUse WhenNotes
Endpoint smokeMedia code changed but the risk is ordinary app readiness.No media-specific proof by itself.
Seeded smokeA reset or seeded route is needed before media/browser checks.Destructive database reset must be deliberate.
Media smokeYou need a temporary DB/S3 media write/read proof in deployed dev.Mutates app media state and should stay profile-scoped.
Browser media smokeYou need user-visible proof that seeded media renders through the deployed frontend and edge path.Read-only once seeded prerequisites exist; uses browser evidence on failure.
Media discrepancy reportYou need to inspect drift between deployed DB rows and S3 objects.Read-only telemetry; cleanup remains a separate approval-gated mutation.
Media bucket replacement pathA Pulumi preview changes or destroys deployed-dev media storage.Use the media bucket replacement runbook before applying infrastructure changes.

Keep deploy-media and deploy-lifecycle separate unless the combined signal is deliberate. Select the explicit deploy-full-validation recipe for that combined proof; it communicates that media mutation and a shared-host stop are both intended.

Media proof should leave enough evidence for the next operator or developer to understand what happened:

  • Selected target, profile, commit, and app URL.
  • Whether the proof was mocked, emulator-backed, or deployed dev.
  • For deployed media smoke, the SSM command ID or workflow job summary when live SSM execution ran.
  • For browser media smoke failures, the browser artifact bundle and the rendered media URL or currentSrc when available.
  • For discrepancy reports, inspected row/object counts and discrepancy-kind summary.
  • Any explicit cleanup decision or follow-up command that remains outside the proof.

Do not publish raw logs, live identifiers, signed URLs, provider object keys, or workflow artifacts into the public docs site. Reduce live proof to the reusable lesson before promoting it.

Database reset does not delete deployed-dev S3 media. That can leave orphaned objects. Media bucket changes can also leave database rows pointing at missing objects.

Current rule:

  • Use the media discrepancy report for read-only inspection.
  • Treat discrepancy counts as telemetry, not automatic failure or cleanup.
  • Require a separate approval-gated cleanup command before deleting DB rows or provider objects.
  • Print the exact rows or object keys before any future cleanup mutation.

For local emulator work, reset local emulator media when the goal is a clean app/media state. Preserve emulator media only when creating intentional orphan or reconciliation fixtures.

Choose proof around the workflow that actually owns the change:

  • Artist, Event, Venue, Series, and Edition Add/Edit flows sequence parent persistence separately from media upload and complete-collection sync through their own routes and tables.
  • Event Add creates the Event once, uploads new images sequentially with progress, then synchronizes final Poster/Gallery purpose, order, and alt text. Retry resumes unfinished uploads or repeats only final sync; it never recreates the Event or reuploads successful assets.
  • Venue Add/Edit follows the same recovery invariant without borrowing Event ownership: save the Venue once, preserve uploaded public identities across failures, resume at the first unfinished image, and retry only final sync when every upload already succeeded. Venue media authorization remains independent from scalar Venue update permission.
  • Series and Edition authoring keeps TanStack Form as the JSON draft owner while selected File values remain a separate in-memory side-effect draft. Parent-first creation, sequential upload, complete-set synchronization, and partial- success retry follow the same recovery invariant without moving media into sessionStorage.
  • Event Edit media hydration, no-op detection, persisted deletion/restore, and revision-aware recovery are proven separately from Add. Proof for one lifecycle must not be cited as proof for the other.
  • Public presentation tests should choose the relevant density: thumbnail for compact cards, rows, typeahead, and related previews; full poster where the feed or future Event Details composition warrants it.
  • The manual backend discrepancy command can inspect Artist, Event, Venue, Series, and Edition locators through explicit entity adapters, but it remains evidence-only and cannot authorize cleanup. Deployed-dev orchestration has its own narrower command surface; see Event Series Integrity for the Series/Edition invocation and boundary.

Keep active media implementation sequencing in roadmap notes while it is moving quickly. Promote material into curated docs when it becomes a repeated decision. Example scenarios may include:

  • A future media-bearing entity or repeated cross-entity authoring pressure provides enough evidence to reconsider the current entity-owned column pattern. Five implemented columns still have not justified a universal table or route family because their product language, authorization, ownership, and recovery remain entity-specific.
  • Direct-to-storage upload sessions become real.
  • Scheduled discrepancy reporting or cleanup becomes justified.
  • Cross-entity media management leaves the roadmap.
  • Browser media smoke becomes routine enough to change deploy profiles.
  • Staging or production adds different media durability, privacy, or CDN requirements.