# Project Publishing

Publishing is the project-level distribution layer for Sleeper Hit Studio. It does not create or refine MP3/MP4 media. Source surfaces such as Table Read, Pitch Deck, Storyboarding, or later video tools create durable `StoryArtifact` assets; Publishing promotes those ready artifacts into releases, schedules them, publishes them to destinations, and tracks status.

Podcast/RSS is the first shipping medium because it can reuse the finalized Table Read MP3 already stored on `StoryArtifact.manifest.audio.recordingUrl`.

## Ownership Model

- `StoryProject` owns one or more `PublishingSeries` rows.
- `PublishingSeries` stores medium, format, prompt direction, podcast type/category metadata, feed metadata, owner metadata, its timezone, its standing approval, and provider destinations.
- **A `PublishingSeries` is also the platform's *show***. Its production is the
  `show` sub-resource (`/publishing-series/{seriesId}/show`: its setup, schedule,
  caps, on/off and episodes), which is how a series makes its own episodes on a
  schedule instead of only publishing ones made elsewhere. There is no separate
  show model: see `docs/shows.md`. A show's episodes are never in Ready to
  publish: the show publishes its own.
- `PublishingRelease` stores one episode/release under a series and keeps links back to `StoryProject`, Series Bible document, `StoryPlan`, `StoryJob`, and the promoted `StoryArtifact`.
- `PublishingReleaseAsset` stores the bound durable asset URL, media metadata, and source fingerprint.
- `PublishingReleaseTarget` stores destination-specific status for RSS and later provider integrations.
- `PublishingProviderConfig` stores user/provider configuration and encrypted credential references for future OAuth/API-key publishers.
- `PublishingAnalyticsSnapshot` stores normalized provider/feed metrics.

## Episode Promotion

Publishing v1 starts after Table Read refinement is complete:

1. The writer refines the episode in Table Read.
2. The existing artifact finalize path durably renders the MP3.
3. The writer promotes the finalized `StoryArtifact` into a `PublishingRelease`.
4. Publishing snapshots the MP3 URL, duration, source fingerprint, and metadata into `PublishingReleaseAsset`.
5. The writer schedules, cancels, or publishes the release.
6. RSS publication makes the published release visible in the first-party feed.

Promotion, scheduling, or publish means the user considers the source artifact ready. There is no separate publishing-side draft, generation, review, or approval loop in v1.

## RSS V1

The first-party RSS provider uses:

- series metadata from `PublishingSeries`
- episode metadata from `PublishingRelease`
- duration from the ready `PublishingReleaseAsset`; the enclosure URL is the episode download route below, never the stored file
- public feed route `GET /api/v1/publishing-feeds/{seriesSlug}.xml`
- public episode download `GET /api/v1/publishing-feeds/{seriesSlug}/episodes/{releaseId}.mp3` (BL-3196): counts the download and 302s to the ready asset's file; in the OpenAPI spec and llms.txt like the feed

RSS does not require OAuth. The feed includes Apple-compatible podcast type and category metadata from the series. Apple Podcasts and Spotify can be tracked as destinations around that feed first; direct provider submission should be added only after current provider requirements and review constraints are confirmed.

## Four Surfaces

Publishing operations are exposed through:

- Writers Lab Publishing tab
- Story API `/api/v1` routes and OpenAPI
- `sleeperhit publishing ...` CLI commands
- MCP publishing tools

The operation registry, capabilities manifest, generated surface contract, CLI metadata, and MCP schemas must stay aligned. Mutating API operations require idempotency where retrying could duplicate rows or jobs.

## In Slug

Slug reaches the same publishing cores as the other surfaces. In a conversation bound to
a project, on a paid workspace, Slug carries 10 tools — the same names MCP serves:

- `list_publishing_series`, `create_publishing_series`, `update_publishing_series`
- `list_publishing_releases`, `get_publishing_release`, `create_publishing_release`, `update_publishing_release`
- `schedule_publishing_release`, `publish_publishing_release`, `cancel_publishing_release`

The series field allowlist is deliberate: create and update take only `title`, `description`, `medium`
(audio / video / short_form), `format`, `language` and `coverArtUrl`, plus `status` on
update only. The slug, synthetic-disclosure line, production mode, show config and cadence, the
standing-approval key, the credit caps, the publish policy and visibility/metadata are never chat
inputs — recurring shows stay with the show tools, and standing approval and caps stay with the UI,
Story API, MCP and CLI.

Schedule, publish and cancel are outward-facing and take TWO CALLS. Called without `confirmed`, each
answers a quote — the release, the series' destinations and feed URL, and a note ending "Nothing has
been published." / "Nothing has been scheduled." / "Nothing has been canceled." — and changes nothing.
Called again with `confirmed: true` after the writer has said so in their own words, it goes through
the same core the schedule/publish/cancel routes call. A published release records
`publishedVia: user_confirmed` with `publishedByKeyId: null` — the writer, in the app. None of these
tools spends credits.

Slug can also list a project's connected apps (`list_app_connections`) on those same paid,
project-bound conversations: connection metadata only —
id, origins, sign-in page and method, which secrets are SAVED (kinds and dates) and what is still
missing. A login is typed only on the project page's Connect an app form; the chat never asks for one
and never repeats one, and no secret value ever leaves the core.

## Workers

Publishing workers are queue-backed and idempotent. V1 ships the release publish worker for scheduled/immediate publish and RSS target completion. Future workers can add cadence suggestions, provider status refresh, analytics fetches, and provider-specific publishers without changing the core release/target model.

Workers must refuse stale or live-preview-only assets. If the source artifact fingerprint changes after promotion, the release asset and targets are marked stale until the current durable artifact is promoted again.
