Spaces:
Sleeping
Sleeping
| # Unified Publishing Center | |
| ## Architecture | |
| The Publishing Center is a composition layer over `app/social`. It does not | |
| own provider connections, tokens, media files, publishing jobs, retries, or | |
| provider payload translation. | |
| The authoritative flow is: | |
| 1. A canonical `SocialPost` references a workspace project and Social media | |
| asset. | |
| 2. Each exact connected account creates one `SocialPostTarget`. | |
| 3. `PublishingService` validates account state, authorization scopes, provider | |
| capabilities, canonical asset ownership, project provenance, and adapter | |
| media constraints. | |
| 4. Publish-now creates idempotent `SocialJob` records. Scheduling creates one | |
| durable `SocialSchedule` stored in UTC with its IANA display timezone. | |
| 5. Existing Social workers claim jobs atomically and call the registered | |
| provider adapter. | |
| 6. Provider results and reconciliation update each target independently. | |
| Post reconciliation reports `published`, `failed`, or `partial_success`. | |
| Provider adapters remain the only components that translate canonical post | |
| data into provider API payloads. An adapter that is registered but does not | |
| advertise publishing remains unavailable and must raise | |
| `SOCIAL_PROVIDER_NOT_IMPLEMENTED` or `SOCIAL_CAPABILITY_UNSUPPORTED`; neither | |
| condition is converted to success. | |
| ## Canonical post and targets | |
| `SocialPost` now records optional immutable project provenance, a bounded | |
| canonical caption, and bounded canonical hashtags. Platform overrides remain | |
| on targets and never mutate the canonical copy. | |
| Target input is strict. YouTube, TikTok, X, and LinkedIn use their existing | |
| typed provider metadata models. Providers without implemented publishing | |
| contracts are capability-gated instead of accepting arbitrary payloads. | |
| No binary media or credentials are stored in Social records. A Social media | |
| asset binds an existing canonical MediaRouter asset to a validated immutable | |
| output locator. | |
| ## Validation | |
| `POST /v1/social/posts/{post_id}/validate` returns a structured result for | |
| every target. Validation reuses: | |
| - connected-account and OAuth scope checks; | |
| - provider capability metadata; | |
| - canonical Social media resolution; | |
| - existing FFprobe/media validation; | |
| - each provider adapter's `validate_media` implementation. | |
| The publish and schedule paths revalidate server-side. Frontend validation is | |
| advisory and cannot authorize publishing. | |
| ## Scheduling and jobs | |
| Scheduling uses `SocialSchedule`; timestamps must be offset-aware and are | |
| stored as UTC. The supplied IANA timezone is retained for display. The | |
| existing scheduler claims due records using row locks and `SKIP LOCKED`. | |
| Publishing jobs use deterministic per-target idempotency keys and bounded | |
| worker concurrency. | |
| Provider-native scheduling is used only by an adapter when explicitly | |
| supported. The Publishing Center otherwise relies on MediaRouter's durable | |
| scheduler. The separate Calendar interface is outside this phase. | |
| ## Retries, reconciliation, and partial success | |
| The existing worker retry classifier applies exponential backoff and jitter | |
| to transient errors. Invalid credentials, invalid media, permission denial, | |
| unsupported operations, cancellation, and non-retryable provider responses | |
| are not retried indefinitely. OAuth 401 handling refreshes once through | |
| `OAuthService`. | |
| The Publishing Center also exposes a target-scoped retry endpoint. It only | |
| accepts a failed target with no external provider ID, rejects permanent | |
| configuration/auth/media failures, and derives a deterministic retry job key. | |
| It never requeues successful or uncertain provider operations. | |
| Async provider acceptance is reconciled through the adapter's status or | |
| recovery contract. MediaRouter does not claim exactly-once external delivery | |
| where the provider cannot support lookup or idempotency; local idempotency | |
| prevents duplicate internal operations. | |
| Targets reconcile independently. A mixture of published and failed targets | |
| produces `partial_success`, never `published`. | |
| ## Cancellation | |
| Draft, scheduled, queued, and retrying jobs can transition to `cancelled`. | |
| Preparing, processing, uploading, and publishing jobs instead record | |
| `cancellation_requested_at`. Their target remains in its actual state until | |
| the worker/provider result is reconciled. This prevents MediaRouter from | |
| claiming cancellation after a provider may have accepted a post. | |
| ## Security, permissions, and RLS | |
| The API uses existing Social scopes: | |
| - `social:accounts:read` / `social:accounts:write` | |
| - `social:posts:read` / `social:posts:write` | |
| - `social:posts:publish` | |
| - `social:schedules:write` | |
| Project and asset ownership is checked server-side. The additive migration | |
| adds a project foreign key, a same-workspace integrity trigger, indexes, and | |
| retains forced RLS on posts, targets, and jobs. Social accounts, media | |
| references, targets, schedules, and jobs continue using the existing | |
| workspace policies. Runtime PostgreSQL RLS certification remains deferred. | |
| Tokens are available only through `TokenService`. REST, SDK, MCP, n8n, logs, | |
| audits, and job payloads never expose access tokens, refresh tokens, provider | |
| secrets, authorization headers, or signed upload/session URLs. | |
| ## Audit and retention | |
| The Publishing Center uses existing bounded Social audit records, including | |
| post creation, validation, scheduling, publish start, completion/failure, and | |
| cancellation. Audit and application logs contain opaque resource identifiers, | |
| provider, status, duration, and error categories only. | |
| Raw provider payloads are not retained by the composer. Provider recovery | |
| state that must survive restarts remains encrypted in the existing dedicated | |
| job field. Public result metadata is filtered before transport. | |
| ## Frontend | |
| Routes: | |
| - `/publish` | |
| - `/projects/[projectId]/publish` | |
| TanStack Query owns providers, accounts, assets, posts, jobs, and validation | |
| responses. The composer keeps only draft selections in local UI state. It | |
| requires exact account selection, capability-gates unavailable adapters, | |
| registers an existing project output as a Social media reference when needed, | |
| and requires an accessible confirmation dialog before external publishing. | |
| Status uses the existing Social job polling rather than a second realtime | |
| system. | |
| Project actions link to the project-aware route. Content Studio, AI Studio, | |
| and template-created projects use the same project/asset provenance and | |
| publishing APIs; there is no specialized publishing path. | |
| ## Copilot | |
| The shared Copilot action registry contains: | |
| - `publishing.validate` | |
| - `publishing.create_post` | |
| - `publishing.schedule` | |
| - `publishing.publish` | |
| - `publishing.cancel` | |
| Publish, schedule, and cancellation are registered as external side effects | |
| and always require confirmation. Copilot uses exact post/account identifiers | |
| and the canonical Social services; it cannot invoke an adapter or arbitrary | |
| function directly. | |
| ## SDK, MCP, and n8n | |
| TypeScript and Python SDKs extend the existing Social resource with structured | |
| validation and the new canonical fields. Existing methods remain compatible. | |
| MCP exposes typed validation and job listing alongside existing create, | |
| publish, schedule, cancel, and status tools. Authorization remains enforced by | |
| the MCP registry. | |
| The existing n8n Social node adds List Posts, Validate Post, and List | |
| Publishing Jobs. Operations use the official SDK, preserve idempotency and | |
| Continue On Fail, and return real per-target backend state. | |
| ## Provider certification boundary | |
| Unit tests may mock provider boundaries. Live provider certification, live | |
| OAuth credentials, Docker, PostgreSQL runtime/RLS verification, FFmpeg runtime | |
| verification, and final production hardening remain deferred. An | |
| `implementation_status` of `registered` or a false publishing capability is | |
| shown as unavailable and is never simulated. | |