T-HUB/packages/publishing-execution
glass beadian a6843e904b feat: rich-media publishing chain (repository gate lift + assembly builder + produce store)
publishing-execution: approved posts may carry mediaAssetIds + altTexts.
MAX_MEDIA_ATTACHMENTS = 4 (Mastodon's cap). The old text-only gate is lifted.
assertApprovedMediaShape validates the shapes; the provider-side
mediaIds stay reserved.

media-worker: producer module — pg-store (claim via produce_job_claim
SECURITY DEFINER fn), runner (getObject → filter_complex → putContent →
derived asset), command-builder (normalize, transpose, drawtext, ducking),
s3-storage (SPENC1 decrypt + putContent), env (lane, font). Migration 040:
produce_job_claim + produce_job_workspace definer functions.

produce.ts: scratch dir bootstrap at startup.
2026-10-15 10:07:09 +00:00
..
src feat: rich-media publishing chain (repository gate lift + assembly builder + produce store) 2026-10-15 10:07:09 +00:00
package.json Tactical Media Broadcast Hub — initial release 2026-10-10 11:56:42 +00:00
README.md Tactical Media Broadcast Hub — initial release 2026-10-10 11:56:42 +00:00
tsconfig.json Tactical Media Broadcast Hub — initial release 2026-10-10 11:56:42 +00:00

Publishing execution

The API and scheduler share this built PostgreSQL repository. Callers own the pg.Pool and its lifecycle. scheduleAttempt and cancelAttempt run inside the API's existing tenant transaction; other methods establish their own RLS-bound transaction. Mutation order is workspace, draft, schedule, attempt, audit.

A business attempt gets a positive decimal attempt number and one provider key. Transport retries, Temporal replay and lease recovery retain that key. retryLater requires proof that the attempt has no unresolved delivery: 429/refusal of the current request cannot disprove an earlier ambiguous send. Use retryDeduplicated for that case; it retains dispatch evidence and the original dedupe deadline. Both settlements are owner/epoch/expiry fenced. Unknown work cannot get a new key or send automatically.

The SDK around-send hook holds withDispatchGate across the bounded request, after rate-limiter waits. Provider evidence is appended separately before finalization, including evidence from stale owners. A current owner can finalize a receipt after authorization changes; the audit records those changes. Receipt recovery uses a separate workflow ID and never needs another provider send.

Runtime boot never executes owner DDL. Apply migrations through the API's offline migrate CLI with an explicit SUPER_DATABASE_URL; API writes and scheduler startup require completed 019, FORCE RLS, valid indexes and the sp_app role. 018 is retained unchanged because local evidence records its application.