Conversation
…load-Post Adds an optional social-publish stage to the stickman-video template so a finished delivery can be posted to TikTok, Instagram, YouTube, LinkedIn, Facebook, X, Threads and Bluesky after an explicit user confirmation. - Settings: new AI Services > Publishing category (Upload-Post API key + profile); the key is stored with the other service credentials. - Daemon: user-only confirm-social-publish action; the stage publishes one confirmation at most once (request id = Idempotency-Key + provider ledger), polls per-platform results, never re-sends the file after a dropped connection, and records a social_publish_result artifact. Preflight blocks it until publishing is configured. - Web: publish panel on the Video delivery step with platform choice, editable copy, YouTube private by default, AI-content label and a confirm dialog. - Docs: docs/social-publishing.md.
wulien
left a comment
There was a problem hiding this comment.
Thanks for the contribution and for disclosing the Upload-Post affiliation. I reviewed the user-confirmed publishing flow, credential storage/redaction, provider request recovery, and the actual integration against master bfbf59a8375ae8c11decb3ebd4ea5e5a7be741de.
The optional stage, shared Web/Daemon implementation, private YouTube default, and separate credential storage are good boundaries. Head 5d6b5a8e2abb0d8dc2865d3686caf9c751eae9c5 integrates without conflicts or unrelated commits. However, two reproduced behavior issues block approval.
Blocking issues
1. HTTP 5xx is treated as definitive rejection and permits resubmission without a new confirmation
In apps/daemon/src/creator/stickman/social-publish-executor.ts:119, every UploadPostApiError calls ledger.markFailed(...). The HTTPS client also wraps HTTP 500/502/503 responses in that class. A failed ledger entry is not in acceptedLedgerStatuses, so retrying the stage sends the video again with the same confirmation without querying remote acceptance first.
Evidence:
Using the actual repository, service, stage runner, and executor, I injected UploadPostApiError('upload_post_http_error', ..., 503) for the first submission and then retried the stage without another confirm-social-publish. The regression assertion expected one submission and received two. The existing definitive-401 rejection test passes, but does not cover this ambiguous outcome.
Impact:
A gateway/server failure does not establish that nothing was created remotely. This violates the PR's stated “file is never re-sent” recovery contract and bypasses the durable local ambiguity gate. The provider's idempotency header is useful but is not a substitute for that gate; its documentation describes a 24-hour duplicate-protection window.
Required change:
Only record a definitive pre-acceptance rejection as failed. Treat ambiguous 5xx/transport/invalid-response outcomes as potentially accepted: look up the request ID, continue tracking an existing request, or retain unknown_remote_acceptance and require the existing explicit resolution flow. Add tests proving that the same confirmation never submits a second file after an ambiguous HTTP failure, including after recovery/restart.
2. A technical draft with blocking checks can be published through the ordinary finished-delivery flow
apps/daemon/src/creator/stickman/action-handler.ts:96 checks the latest manifest's artifact status and ID, but not its delivery contents. social-publish-executor.ts:52 repeats only the artifact/ID checks. Existing delivery generation deliberately emits a completed manifest with packageStatus: 'technical-draft' when placeholder assets or blocking checks remain.
On the Web side, StickmanSocialPublishPanel.tsx:97 enables publishing based on a manifest ID and form validity. ResultStep renders the publishing panel whether or not its existing publishable check passes, and the confirmation dialog does not mention the unresolved checks/placeholders.
Evidence:
I constructed a manifest accepted by stickmanDeliveryManifestSchema, with packageStatus: 'technical-draft', a placeholder asset, and blockingChecks: ['visual_ocr_unverified']. The user confirmation was accepted and running the stage invoked the publish client once. The regression expected rejection and zero submissions.
Impact:
The new public side effect bypasses the pipeline's explicit distinction between a publishable package and a technical draft. A generic platform-selection confirmation does not acknowledge the actual unresolved publishing checks.
Required change:
Validate the confirmed delivery contents in the Daemon and block technical drafts/unresolved checks by default; mirror this state in the Web publishing entry. If publishing drafts is an intentional supported capability, define a separate explicit override that presents and records the specific outstanding risks instead of silently treating them as a normal finished delivery. Add backend and UI regression coverage.
Verification performed
- Reviewed the exact head and simulated its merge into current master; the resulting tree equals the PR head tree.
- In an isolated archive of that exact head, the supplied targeted Daemon tests pass: 37/37, covering publishing and service credential configuration.
- The targeted Web publishing panel, Stickman workspace, publishing settings, and route tests pass: 62/62. React
act(...)warnings were present; no unrelated test changes were made. - Two additional maintainer regression probes fail with the concrete outcomes above. These probes only use injected clients; no real social publishing occurred.
Remaining integration gate
GitHub currently reports no checks for this head. CI run 36631396111 has conclusion action_required, so it is awaiting maintainer action rather than providing passing CI evidence. After the behavior fixes, the applicable checks must run for the updated head before this P2 remote-side-effect change can be merged.
I acknowledge the private TikTok/YouTube production smoke evidence supplied in the PR description. I did not independently repeat that publish, run package typechecks, start/restart the Daemon, perform Web/Desktop parity or packaged App verification, or invoke the merge-commit verification handoff. No merge commit or master update was created.
…nical drafts - Only a definitive pre-acceptance rejection (HTTP 4xx other than 408) marks the Upload-Post request failed and allows retrying the same confirmation. 5xx, transport errors and unreadable responses look the request id up: a known request keeps being tracked, otherwise the ledger stays unknown_remote_acceptance for the existing resolution flow. - confirm-social-publish and the social-publish stage now read the confirmed delivery manifest and refuse technical drafts, placeholder assets and unresolved blocking checks, naming them. The Web panel mirrors the verdict and shows the outstanding checks instead of a publish button. - Regression tests for both, including a daemon restart after an ambiguous 503 and the technical-draft + placeholder + visual_ocr_unverified case.
|
Thanks for the thorough review and the reproductions. Both issues are fixed in f5e30fd. 1. Ambiguous HTTP failures no longer allow a resubmission
2. Technical drafts can no longer be published
I checked that the three new regressions fail against 5d6b5a8's executor and action handler, and pass with the fix. Verification on f5e30fd
|
Summary
The YouTube Shorts / stick figure pipeline (#327) ends with a publishable package (
short.mp4, thumbnail,publish-copy.md, delivery manifest), but it deliberately stops short of publishing it. This PR adds that last step as an optional, user-confirmed stage: a finished delivery can be posted to TikTok, Instagram, YouTube, LinkedIn, Facebook, X, Threads, and Bluesky through Upload-Post, with per-platform results shown on the Video delivery step.cc @wulien (code)
What's included
Settings. A new AI Services → Publishing category with the Upload-Post API key and profile name. The key goes through the existing credential split (
credentials.json), redaction and retain paths. Older saved configs get empty defaults.Daemon / protocol.
social-publishstage onstickman-video(executorupload-post-publish,optional,resultVersionPolicy: 'none',invalidateDependentArtifacts: false). The workflow never queues it.confirm-social-publishaction. It is user-only: the Agent getscreator_user_confirmation_required. It stores the platforms, copy, privacy, AI label and the exact delivery manifest instate.socialPublish.update-settings/undo-actionpatches can't writesocialPublish. The agent guidance says the Agent must not publish.request_idandIdempotency-Key, and the provider request ledger (registerBeforeSubmit/markWaitingRemote/ …) refuses a second submission for the same confirmation. Only a request that Upload-Post rejected (4xx, nothing created) can be retried with the same confirmation.unknown_remote_acceptanceand the existing resolution flow takes over.createUploadPostProviderCapabilitiesletsrecover()query an interrupted publish by request id./api/uploadposts/statusfor up to 10 min (the result becomessubmittedafter that) and tolerates transient poll errors. Results are normalized: private uploads with no public URL, skipped (not connected) platforms, TikTok inbox/drafts delivery, and per-platform errors.social_publish_resultartifact (social-publish.json) with the per-platform results in its metadata.#/settings?tab=ai-services§ion=publishing. A missing config maps toneeds_inputwith the same deep link.Web.
section=publishingroute, and a panel adapter label for the new stage.Docs.
docs/social-publishing.md(setup, behavior, how it works), linked from the README.Not in this PR (possible follow-ups)
submittedafter 10 minutes.skipped.Change risk level
CreatorServicesConfig.publishing), service configuration, Daemon stage/action, and a new remote side effect.Verification
pnpm typecheckfor affected packages (TypeScript changes)Actually ran:
pnpm --filter @opencreator/protocol build && pnpm --filter @opencreator/protocol test: 24 passed.pnpm --filter @opencreator/daemon typecheckandpnpm --filter @opencreator/web typecheck: clean.apps/daemon/test/unit/creator-stickman-social-publish.test.ts(11 tests):Apikeyauth andIdempotency-Key;socialPublishpatch forgery rejected;unknown_remote_acceptance;creator-services-config-store.test.ts: defaults for older configs, key kept incredentials.json, redaction and retention.StickmanSocialPublishPanel.test.tsx(config link, confirm dialog gating, results, running lock);StickmanVideoWorkspace.test.tsx(publishing dispatchesconfirm-social-publishthenrun-stage social-publishonly after confirming);CreatorServicesSettingsView.test.tsx(Publishing tab saves key and profile);routes.test.ts.apps/web: 1218 passed.apps/daemon: 1388 passed, 1 failed. The failure iscreator-download-executor.test.ts › preserves a non-ASCII output filename split across stdout chunks, which also fails on a cleanmasteron my macOS machine, so it is unrelated.createCreatorStageRunner+upload-post-publishexecutor + real HTTPS client against the production Upload-Post API, with a synthetic 1080×1920 H.264/AAC video on a test profile, TikTokSELF_ONLYand YouTubeprivate:Intentionally skipped, and why:
server.ts.Web / Desktop parity
Checklist
.runtime/, credentials, Codex sessions, build caches, or user data committed