Sources: what was proven, and what not to copy
The publish research draws on three earlier projects. This page says what each one actually proved live against facebook.com, and lists the bugs found in each so they aren't carried into log. Read it before reusing code or ideas from any of them.
It was read from local checkouts on 2026-10-18; nothing was re-run for this page. "Live" means a doc or commit in that project says it worked against facebook.com.
The Camoufox client
A Python browser driver built on Camoufox.
Proven live:
- One real publish through the spike script (
docs/okf/marketplace_create.md:47-49). It went to duplicate hold because the photo and title were reused (:50-52). - A dry run through
MarketplaceCreateAction(:53-54). - A photo added to a live listing (
docs/okf/listing_photo_update.md:17-21). - A relist check that read ACTIVE.
Not proven live: a real publish through MarketplaceCreateAction, a relist repost (RELIST_LIVE=1), and the group post selectors (domain_groups/post_driver.py:3-8).
Tests cover only a dry run, auth, a category miss and an unknown category (tests/domain_marketplace/test_create.py:94-158).
Bugs:
_capture_dashboard_idreturns the first/marketplace/item/link on the page, which may belong to another listing (create.py:203-219).- It returns
published=Trueeven when no id was found (:152-164). - When relisting hits a fatal state, it
breaks and then rewrites the state file without the rest of the watch list (relist_watcher.py:105-106,:162). - A new relist generation doesn't copy
title, so the next health check can see a "missing card" and repost again (:141-156,health.py:93-96). - The first repost has no cooldown (
is_dueis true whennext_eligible_atis None). - Pillow and httpx aren't in
pyproject.toml. Without them, the photo change and the webhooks silently do nothing. - The API create path skips the
SingleFlightlock the watcher uses (api/app.py:71-83).
The production poster
A TypeScript poster using Playwright over AdsPower, used in production.
The live path is the server route /api/process-posts → postToFacebookMarketplace, on an AdsPower profile that's already signed in. Its desktop agent is switched off (posting-session-manager.ts:699-700, useAgent = false). A Python posting engine isn't in the repo, but its scheduled job is still registered (functions.ts:704-710).
Bugs:
- It returns success with no listing URL or id (
facebook-poster.ts:2432-2443); the listing id is never saved. - Nothing checks the upload finished; a fixed wait stands in (
:1266). - The AI selector fallback never runs (a Promise tested with
||,:1345). - Condition values never match Facebook's labels (
process-posts/route.ts:707). - Two workers can claim the same post:
pending → postinghas no status guard (:360-363), and the scheduled job picks up the same rows (functions.ts:201-203). - The browser lock force-releases after 3 minutes, but a post can take up to 5 (
browser-lock.ts:26). - The desktop agent re-runs a failed post once, which duplicates it if Publish had gone through (
agent/src/poll-loop.ts:79-83). - The agent's result route calls
increment_account_posts({account_id}), but the parameter isp_account_id(agent/result/route.ts:122-124). - Its posting routes have no auth.
- A captcha-service API key is committed in its source.
The dashboard
A listings dashboard with no browser.
apps/web/src/lib/listings/server.tsis an in-memory store marked "Mock-backed for now" (:82-87).POST /listingsmakes up a 17-digit id and setsunderReviewwithout posting anything (:91-137).- Its photos are data URLs, at most 4. The comment saying the live client "will upload the same bytes" (
types.ts:100-102) describes code nobody has written. - Its status names already match the Camoufox client's status action (
status.py:15-29), and log's port converts them to camelCase at the boundary.
The vision-model experiment
Out of date. It was an experiment with vision models. Only its CaptchaKraken notes carry over, in CaptchaKraken.