Marketing Command Center
Search the build
-
39fcdef2026-07-27 MCC bluesky: fail loud if image can't embed — never silently post text-only -
f4429c82026-07-27 auto-save: 2026-07-27T15:21:37 (1 files) — data/cc-warmup-seed-200.json -
a9436a52026-07-27 auto-save: 2026-07-27T14:51:27 (1 files) — data/cc-import-activities-audit.jsonl -
d530fe42026-07-27 auto-save: 2026-07-27T14:21:14 (2 files) — data/cc-import-fm-clients-hold.json data/cc-import-fm-clients-mailable.json -
747d83a2026-07-26 auto-save: 2026-07-26T04:40:35 (1 files) — public/data/clients-manifest.json -
38e14e72026-07-22 auto-save: 2026-07-22T12:44:11 (1 files) — package-lock.json -
85f35662026-07-22 chore: version bump (session close) -
5d1c41b2026-07-22 chore(mcc): move IG re-pull schedule from Mac2 launchd to Kamatera-local cron -
6b2db252026-07-22 auto-save: 2026-07-22T12:13:58 (1 files) — data/ig-repull-status.json -
21be5cc2026-07-22 feat(mcc): board shows IG re-pull health at a glance + Run-now fix button -
6d2f55b2026-07-22 chore(mcc): daily launchd job for the IG media re-pull (Mac2 → Kamatera SSH) -
214b1bb2026-07-22 fix(mcc): IG re-pull only touches at-risk images, never clobbers stable URLs -
971082a2026-07-22 feat(mcc): IG Graph API re-pull to restore durable thumbnails for published IG posts -
2ffb36f2026-07-22 fix(mcc): durable local cache for expiring IG/fbcdn post images + graceful placeholder -
243d9c52026-07-22 fix(mcc): guard panel-router race that blanked panels + untrack screen-record videos -
5a92b902026-07-22 auto-save: 2026-07-22T10:13:11 (5 files) — scripts/repro-run3.js screenrecord/DEBUG-REPORT.md scripts/probe-403.js scripts/verify-fix.js scripts/verify-fix2.js -
c073e612026-07-22 auto-save: 2026-07-22T09:43:01 (8 files) — public/app.js screenrecord/ scripts/nav-map.js scripts/probe-403-and-posts.js scripts/repro-race.js -
355b7302026-07-22 auto-save: 2026-07-22T08:42:49 (1 files) — package-lock.json -
0eff96e2026-07-22 chore: v1.7.0 (session close — Threads un-gate, image+SKU generation, honest postable status, /publish skip) -
29d1ff22026-07-22 channels: /publish skips non-postable + media-mismatched channels -
69af9fe2026-07-22 channels: add honest 'postable' status — connected != can-actually-post -
d84dc2a2026-07-22 engine: untrack runtime engine-queue.json + protect it on deploy -
d9e22d32026-07-22 engine: image-grounded captions (vision) + always include DW SKU -
7ff5d7c2026-07-22 deploy: ship Threads un-gate to Kamatera MCC (app 69 reloaded, health 200) -
25452732026-07-22 engine: stop birth-gating Threads suggestions (DW is a Meta tester, publishes without App Review) -
5459b682026-07-21 LinkedIn: filed Developer Support ticket CAS-11523819 to unblock CMA (auto-added defaults on app 257140099) -
aae7c9c2026-07-21 LinkedIn: record CMA self-serve blocker (auto-added default products grey Request access; app 257140099 created+verified, client 86z1h5ic86lw65, org 3090253) -
2becbe52026-07-21 submissions: Facebook pages_manage_posts App Review package (dashboard steps + use-case + screencast script + reviewer repro) -
eb16f002026-07-21 submissions: LinkedIn dedicated-app + Community Management API request package (new-app spec + email-verify + post-approval finish) -
4e9c4252026-07-21 submissions: TikTok production App Review package (fields + scope text + demo shot-list + submit steps) -
33d51742026-07-21 chore: v1.6.3 (session close — FB page names fix) -
3779b0f2026-07-21 engine: add LinkedIn as 7th channel — image-upload adapter (initializeUpload→PUT→post), status entry, trade-voice captions, channel backfill for already-generated days -
71a303c2026-07-21 5x: Engine panel verified clean on live marketing.dw (0 app defects; 2 harness fixes, 1 phantom discarded) -
024a26b2026-07-21 channels: show real Facebook Page names instead of raw IDs in status panel -
0210a802026-07-21 engine: contrarian fixes — YT approve defaults private, edit routed through CAS (double-post guard), TZ pinned PT, productUrl in outbound content (+bluesky budget), LLM claim-grounding -
0b738312026-07-21 engine: brain (candidates+llm+generate), scheduler (CAS tick+crash sweep), Engine panel UI -
0938d512026-07-21 engine: core module (store/config/skeleton) + channels.postLive + leak-deny lib -
e8501e82026-07-21 assets: local qwen2.5vl vision enrichment (aiTags) + search -
70479a42026-07-21 youtube: add force-ssl scope for videos.update (visibility control) -
e10410d2026-07-21 youtube: add /youtube/visibility endpoint (flip private↔public via videos.update) -
85d08d72026-07-21 youtube: build resumable videos.insert upload pipeline (postYouTube) + token refresh -
ed9e2e22026-07-21 channels: derive TikTok + YouTube account handles from env (accounts rail was blank for both) -
ca60a5c2026-07-21 chore: v-bump (session close — TikTok token-mirror fix) -
34ea50f2026-07-21 TikTok App Review: mark posting pipeline VERIFIED (creator_info ok); demo needs only a hosted video + gated test-post -
a9c34db2026-07-21 Add TikTok App Review submission package (production fields + demo script + posting-pipeline prereq) -
63d0cd02026-07-20 TikTok: mirror OAuth token into env on callback (fixes Bearer undefined on post) + demo recorder scaffold -
9a142fd2026-07-20 auto-save: 2026-07-20T15:02:48 (1 files) — package-lock.json -
76de9532026-07-20 chore: v1.6.1 (session close — Schumacher calendar filter) -
5ea8d242026-07-20 calendars: filter Schumacher (internal-only) out of both calendar layers at source -
540ae082026-07-20 chore: lint + refactor + v1.6.0 (session close — reel/FB-page picker hardening) -
97dcfe72026-07-20 Composer: add reel/story asset picker + Facebook Page selector -
02a76e02026-07-19 auto-save: 2026-07-19T07:55:24 (1 files) — package-lock.json -
947d85f2026-07-19 chore: v1.5.0 (session close — Clients & Prospects panel, Threads accounts, gated CC import staging) -
861cc692026-07-19 auto-save: 2026-07-19T04:54:45 (1 files) — public/data/clients-manifest.json -
d77d6ee2026-07-17 chore: lint, refactor (drop dead env()), v1.4.0 (session close) -
c1d7a1d2026-07-17 instagram: poll media container status until FINISHED before publish — fixes 'Media ID is not available' (Meta processes images async) -
47459ec2026-07-17 cc-import: stage gated FM-client → Constant Contact import (34,825 contacts; scraped list excluded); upload drafted to pending-approval (DTD verdict A) -
c3336242026-07-17 auto-save: 2026-07-17T09:46:05 (2 files) — modules/channels/index.js public/panels/composer.js -
19aea362026-07-17 bluesky: rich-text FACETS — #hashtags + links now clickable (UTF-8 byte-offset aware per docs.bsky.app) -
c16d1382026-07-17 Vendor IG: mark BN Walls 'none found' (no Business-Discovery-readable IG); drop orphan cache entry -
3c657302026-07-17 auto-save: 2026-07-17T09:16:00 (1 files) — package-lock.json -
01590c12026-07-17 chore: lint (with-open autofix), refactor (none needed), v1.3.0 → v1.3.1 (session close) -
959a2f02026-07-17 Vendor IG Reporting: wire never-expiring page token, page-token discovery, standalone refresh CLI; populate 33/34 vendors -
b69f9932026-07-17 MCC Clients: server-side notes store so notes sync across devices -
87381732026-07-17 deploy: exclude live-authoritative runtime data (outbox/tokens/pages/assets) from rsync — deploys were clobbering the live Meta token, staged posts, and UI-added assets -
2888aa42026-07-17 board UX: add 'Clear staged' — dismisses stale staged/pending/failed cards (keeps posted history) via new POST /api/channels/outbox/clear; Compose links to Composer -
075ec0c2026-07-17 Vendor IG Reporting: render last 3 posts inline per vendor (was 10-photo grid) -
922c32f2026-07-17 clients panel: chip clicks open target in a right-side drawer (website embeds live; LinkedIn/IG show one-click card since they block framing) -
2aac0932026-07-17 MCC Clients: per-contact notes field below each firm card -
30afe182026-07-17 MCC Clients: show full address + phone + real domain + best contact/follow per firm -
7877a922026-07-17 composer: banner preview respects template aspect-ratio (#2) + warns when no asset selected (#3) — contrarian findings. Font-fail finding (#1) verified PHANTOM (allow-same-origin loads fonts), dropped. -
01d65e12026-07-17 composer: Banners tab renders a LIVE banner preview (real /api/layouts/render HTML in the middle zone) instead of a dead deep-link -
a4a92b12026-07-17 layouts: fix malformed placeholder SKUs — DW-GL-001 → DWGL-100001 (real DW format DW<code>-######) -
48778372026-07-17 5x: contrarian gate resolved — generate+publish now functionally verified end-to-end -
f3e51292026-07-17 composer: Generate copy now sends kind=instagram (was 'social' → silently degraded to headline). Contrarian catch. -
27b2af22026-07-17 5x: composer clean-twice report -
917acca2026-07-17 5x sweep 4: add inline SVG favicon — kills the app-wide /favicon.ico 404 (last console error) -
c5779ce2026-07-17 5x sweep 2: composer drops broken asset thumbnails (dead URLs no longer 404-spam the console or show broken tiles) -
8b2fd012026-07-16 composer: new central board — combine asset/banner + copy → publish to all connected socials at once -
5e38c362026-07-16 clients panel: one-click 'Copy not-actioned emails' (respects search+filters, skips already-actioned) -
05fccc42026-07-16 clients panel: 'not actioned' filter + LinkedIn/Instagram actioned timestamp columns in CSV export -
88eb92d2026-07-16 clients panel: inline LinkedIn/Instagram Follow chips under each name + date-time stamp on Follow/message click (persisted per contact/channel) -
bb5f3042026-07-16 docs: IG fleet connect checklist (35 live + convert/link steps for the rest) -
bec82b02026-07-16 channels: add /ig-audit — enumerate ALL Instagram accounts (page-linked + business owned/client), not just the 34 page-linked -
a8a21662026-07-16 auto-save: 2026-07-16T16:43:46 (1 files) — package-lock.json -
25362f42026-07-16 chore: lint, refactor, v1.3.0 (session close) -
1473c682026-07-16 auto-save: 2026-07-16T12:13:12 (1 files) — public/data/clients-manifest.json -
57231262026-07-16 clients panel: coverage summary (contacts/emails/IG/LinkedIn across all groups); manifest carries per-group counts -
b1d1fc72026-07-16 clients panel: dedup + field-merge in All Prospects (LA groups overlap; keeps richest contact record) -
d544ba12026-07-16 clients panel: merged ★ All Prospects group (search/sort/filter/export across all ~5.9k firms; each tagged by city) -
63885bc2026-07-16 clients panel: quick filters (email/IG/LinkedIn/uncontacted), Copy-all-emails, IG+LinkedIn count pills, density toggle (completes sort+density rule) -
d8851312026-07-16 auto-save: 2026-07-16T11:43:05 (1 files) — public/data/clients-manifest.json -
1a674412026-07-16 clients panel: search across ALL fields + sort dropdown (name/city/state/has-email/IG/LinkedIn), persisted -
e0ae6f52026-07-16 clients: browser+local-model contact enrichment (direct Instagram/LinkedIn + email gap-fill) -
ad37a602026-07-16 refresh-clients.sh: read CSVs from project data/sources (launchd can't read ~/Downloads — TCC), nullglob rsync -
87dc4412026-07-16 calendars: decouple launched layer so it stays live where bulk_fivefield_worklist is absent (Kamatera) -
c1ee3c82026-07-16 clients panel: per-group CSV export (respects filter; includes emails + LinkedIn/IG URLs; local download, not CC) -
b50489a2026-07-16 refresh-clients.sh: mkdir lock (macOS has no flock) -
09230f02026-07-16 refresh-clients.sh: pin PATH for launchd (node/python/homebrew) -
2ba021c2026-07-16 register clients module in registry so the panel mounts in the nav -
0ed9d952026-07-16 clients panel: FM client DB (43k) + CSV prospect groups, LinkedIn+Instagram discovery -
bfbe2222026-07-16 followlist: add build:followlist refresh script + CRCP broker section -
e347c432026-07-16 linkedin panel: add Follow list mode (ported from li-clicklist.html) -
063dc282026-07-16 All Calendars: add Launched layer — already-live products on their launch date, own modal, v1.2.0 -
d28f0d12026-07-16 PR naturals mailer: SISAL search link to page 1 (verified 1002 results, sisal SKUs on top) -
b3300df2026-07-16 PR naturals mailer: point SISAL tile at sisal search (page 5), keep pr-naturals UTMs -
f2866e62026-07-16 chore: harden calendars module (pool error log, load() try/catch, fieldvis merge), v1.1.0 (session close) -
a83ad8f2026-07-15 Place All Calendars in the Planning nav group (next to Marketing Calendar) -
b93e4e12026-07-15 Untrack regenerable 13MB activation snapshot (gitignored — was committed by add -A) -
c3e1ea52026-07-15 All Calendars: front-end panel (facet rail + multi-layer month grid + drag reschedule) -
bd251862026-07-15 channels: resolve IG per-page (bulk field dead) + add /meta-token paste-ingest route to bypass blocked OAuth redirect -
fea8d922026-07-15 All Calendars module: backend (activation projection + shopify enrichment + marketing/campaign overlays) -
81c03cf2026-07-15 channels: Meta connect now upgrades short→long-lived token + re-caches all 80 Pages so the whole IG fleet comes back permanent (not 1-hour) -
9ef59f02026-07-15 Add deploy/apply-cutover.sh — paste-safe one-command nginx cutover/restore (backup+install+test+reload+verify) -
4a030442026-07-15 accounts panel: use live Meta token-health so a dead token flips to needs-creds instead of showing green -
a26137e2026-07-15 Fix staged vhost: MCC front door + public /reels/*.mp4 + /privacy + /terms passthroughs to reels app (:9848) — restores media/legal fetch after the domain flipped to the MCC without them -
49dbc5c2026-07-15 chore: v1.0.2 (session close — PR naturals draft iterations) -
eb096692026-07-15 Stage DW-marketing consolidation (DTD verdict A): nginx vhost swap → MCC on DW domain + agentabrams 301 + CUTOVER runbook (staged, not applied) -
98f32bf2026-07-15 PR naturals v3 design: uniform 620x620 tiles (grasscloth source was 527px, CDN can't upscale -> swapped to full-size original) -
e98234f2026-07-15 texture tiles → vendor+tag collection links (browser-verified via CTA agent) -
a71c5e92026-07-15 naturals mailer: swap hero to Bellagio Weave roomset (pattern repeat issue on flat swatch) -
a7aea852026-07-15 PR naturals v3 — logo masthead, 888 phone, appointment banner, uniform square tiles, minimal footer -
1a545542026-07-15 PR naturals v2 mailer — 6 material categories from the July drop, dark editorial layout -
778a3752026-07-15 Phillipe Romano naturals campaign draft — Bellagio roomset hero + 6-series grid -
b904d732026-07-15 grasscloth campaign draft — Zoffany Kensington hero + Designtex colorway grid -
0f129eb2026-07-15 add reels module — embed dw-marketing-reels console via same-origin proxy (Range passthrough for video seek); add fix-connie-creds.sh -
0f46bdd2026-07-15 auto-save: 2026-07-15T11:36:14 (1 files) — scripts/fix-connie-creds.sh -
f2636e32026-07-15 channels: Instagram-account selector — pick 1-to-all IG accounts -
df1e6232026-07-14 chore: lint fixes (jget r.ok guard, .env value quoting per unquoted-sourcing rule, comment typo), v1.0.1 (session close) -
f872cf02026-07-14 channels: three-valued is_valid guard so a Graph rate-limit/error can't false-flip a valid Meta token to needsReconnect -
c60d8ff2026-07-14 Expand Sounds catalog: +6 royalty-free libraries & SFX, live TikTok trending-sounds link, Grateful Dead essential-eras section (30 sources) -
8bb0bc52026-07-14 Add Sounds & Assets panel: curated free/royalty-free music + TikTok assets, brand-safety badged (Grateful Dead archive listen-only) -
fa0d1c12026-07-14 deploy: ship Accounts panel to Kamatera; fix .deploy.conf HEALTH_URL to :9662 (was pointing at vendor-scrapers on 9661) -
3b13d2e2026-07-14 Add Accounts panel: Amazon-style social-account filter rail with connection status + inline how-to-fix credentials -
897ebac2026-07-03 chore: macstudio3 migration — reconcile from mac2 + repoint paths (stevestudio2→macstudio3, node/npm/npx→/opt/homebrew) -
67b4c0c2026-06-30 auto-save: 2026-06-30T12:40:24 (1 files) — scripts/cc-agent-discover.js -
fa747ed2026-06-24 deploy.conf: fix MCC health URL to :9661 + guard gitignored follow-counts data from rsync --delete -
90978c92026-06-24 Add Followers/Following tab to DW Marketing Command Center -
eac09ca2026-06-24 CC module: proxy Connie cc-agent when no local CTCT token (DTD verdict C) -
88a51a72026-06-22 Social: real approval pipeline + instant posting + truthful live/simulated UX -
fe2afd82026-06-17 meta-token-health: fix dead George alert URL (HTTPS flip 2026-06-17) -
de6eb5f2026-06-16 Security: remove browser-shipped basic-auth password from client boot-error (was the now-rotated DWMarketing2026!) — gitleaks-flagged browser-shipped cred in public/app.js -
ad0fc2a2026-06-16 Add META token health canary (Council #4): GET /me liveness + expiry warn + heartbeat stamp; alerts CNCP+George on expiry (the silent failure that darkened the Vendor IG panel) -
938df842026-06-16 Code-health: gitignore *.bak + runtime social-queue.json (untrack it), add rel=noopener to activation-page link -
78312e82026-06-16 Vendor IG roster: add web-confirmed @schumacher1889 (was 'no IG found'); 34/38 now have IG -
96afa0a2026-06-16 Vendor IG roster: add last-10-posts grid per vendor via Instagram Business Discovery (server-side, cached, graceful degrade on expired token) -
8e940082026-06-15 Channels: add Bluesky (live, app-password) + Threads (review-gated) publishers; board shows them as real columns -
e76aecd2026-06-15 Add Meta App Review prep doc (System-User route + full review steps, paste-ready justifications) -
54357362026-06-15 Channels: add Facebook/Instagram Page picker — select WHICH of your 80 Pages (+ 11 linked IG) to post to. -
a32a8892026-06-15 Add Streams Board — Hootsuite-style column view (social channels + Constant Contact) -
535807b2026-06-15 MCC polish: publish-time caveats for half-wired channels (TikTok private-only, YouTube uploads-not-wired) on chips + confirm dialog, created date+time + sort on Channels outbox, float CC import+ranked-opens above mock live cards -
9968bde2026-06-14 MCC should-fix sweep: unify Kravet block (lib/kravet.js, server+client single source, caption/name-aware, button disabled on block), add CA §17529.5 subject/from screening, origin-absolute fetches in assets+CC panels, align OAUTH_REDIRECT_BASE default, add 5th LinkedIn template -
da767262026-06-14 MCC officer-review fixes: fail-closed auth (remove hardcoded password default), HttpOnly/Secure cookie, gitignore runtime-state files, fix Wallpaper->Wallcovering leak -
4715bb62026-06-14 MCC QA fixes: re-render #3 (Anemone Blush, was off-theme) + #5 (statement mural, was tiled); soften mural copy to drop false 'no repeats' claim -
2a5b5592026-06-14 Channels: bake the Meta 4-step setup doc into the Meta card (Ads-app gotcha + Other/Business → Instagram+FB Login products → App Review Standard Access → then mint token); Meta parked -
744335b2026-06-14 MCC: approve + schedule the 8 launch social posts (Bold Blush 6/15-16, Custom Mural 6/17-18); LinkedIn drafts marked ready (manual) -
e0f2be02026-06-14 MCC: swap in AI room-renders for the 8 launch drafts (Gemini 2.5 flash image, hosted on product galleries); drafts stay unapproved/unscheduled -
eaee6962026-06-14 MCC: attach on-theme product imagery to the 8 staged Bold Blush + Custom Mural drafts (Shopify CDN URLs, Meta-fetchable) -
5d49b722026-06-14 MCC: stage Bold Blush + Custom Mural launch posts — 8 social drafts (IG/Pinterest/TikTok/FB) + 2 LinkedIn trade drafts, all unapproved/unscheduled (scheduler-safe), awaiting imagery + Meta token -
54fd8bb2026-06-13 CC Phase 1 (DTD verdict A): CSV import (contacts/campaigns/opens, no token) + ranked opens (campaigns by open-rate, most-engaged contacts) — normalized ctct-store, same shape the live v3 API will write -
0c63f9f2026-06-13 Image Bank: finish Repost UI — gated modal (image preview, required @credit, rights-confirm + #ad, wallpaper-ban) → stages via /repost, never auto-posts -
923cb9c2026-06-13 LinkedIn module (DTD verdict A): draft-first composer w/ DW B2B templates + char counter + wallpaper word-ban + copy/deep-link manual post + saved drafts; gated official-API publish (never auto-fires, Phase 2 token) — nav under Social Media -
c04d2b72026-06-13 Image Bank: gated STAGE-ONLY /repost endpoint (rights-confirm + Kravet-block + auto-credit + wallpaper-word-ban + #ad) + /reposts list — UI button pending -
961a1bb2026-06-13 Image Bank: Instagram ingest — public-permalink og:image grab (no token, tagged uncleared) + our/vendor IG roster (33 accounts) with regram allow/review/block (Kravet-family blocked) + instagram source filter -
267a4bb2026-06-13 Asset Library = Image Bank: add GDrive (folder rclone-import + file share-link), Harvest-from-campaign-HTML, Constant Contact library (Phase 2 gated) ingesters + sort + source filter -
bdfc3522026-06-13 Channels: self-serve Activate page — paste app creds in-browser (written to .env, never echoed), copy redirect URIs, portal links, one-click Connect; /setup + guarded /config endpoints -
2b266332026-06-13 MCC shell: fetch against location.origin so embedded-URL credentials don't throw 'URL includes credentials' -
c79449d2026-06-13 MCC shell: fix auth-cookie so fetch() works (was hanging on Loading), grouped hamburger nav w/ Social Media leading, hardened boot + error states -
c6baf4f2026-06-12 social: add validated meta-go-live.sh (Graph-verify token → deploy to Kamatera → confirm backend:meta); gitignore meta-creds.env -
0f9d5962026-06-12 social: add free Meta Graph (IG+FB) publish backend behind ayrsharePost abstraction -
751cc472026-06-12 gitignore transient social-publish-log.json -
cfea2d72026-06-12 social: Ayrshare live-publish + scheduler scaffold (gated, off by default) -
a9053722026-06-10 backsync: pull Yuri's 10 CRM features from prod (RFM, send-times, ab-tests, profiles, segment-perf, templates, journeys, browse-abandon, copy-scoring, playbook) — Mac2 source now matches live 18-panel build -
57a572a2026-06-10 vendors module: prefer headless-verified follower counts + verifiedLive flag; BN Walls recheck -
1cc10062026-06-10 Vendor IG: headless-verified follower counts (33 live, real numbers) — Rebel Walls 361K, Phillip Jeffries 527K, exact small-account figures -
a57c5b82026-06-10 Channels OAuth connect machinery: per-platform connect/callback (YouTube/Google, TikTok, Meta FB+IG) + token store + one-click Connect buttons; reads app client creds from env -
b546fbd2026-06-10 Copy panel: Gemini path (preferred over dead Anthropic) → real AI copy via valid Gemini key -
a85f5b22026-06-10 Channels publishing engine: FB/IG/TikTok/YouTube adapters (real Graph/TikTok API behind credential gates) + Connections+Composer panel + staged outbox until accounts authorized. The posting-machine spine. -
dc913ce2026-06-10 Vendor IG Reporting panel: DW + 37 vendor Instagram accounts roster (33 found, ~4.5M combined reach), sortable, for social/competitive reporting -
d54875f2026-06-10 3 more panels (parallel agents): Performance dashboard (KPIs+SVG charts+GA4 path), Social Scheduler (queue board, gated publish), Audience Segments (RFM-ready rule builder) — 8 panels total -
7207cc92026-06-10 assets: full-catalog search via Shopify predictive search (suggest.json) -
06963492026-06-10 assets: image Asset Library module (upload / URL / live DW-catalog pull) + Layouts picker -
0f49e0b2026-06-10 load .env file in server.js so PORT/creds come from file (fixes prod EADDRINUSE on default 9661) -
4b6184a2026-06-10 deploy config → port 9662 (9660/9661 taken on Kamatera) -
e8363ce2026-06-10 4 panels built (parallel agents): Constant Contact (mock+gated sends), Marketing Calendar, Suggested Copy, On-Demand Layouts — all mount + smoke-tested -
de565762026-06-10 scaffold: modular marketing command center shell (express, basic-auth, panel loader, module contract)
Authors
- Steve166
- steve20
Agents used
Skills used
- /channels6
- /publish6
- /data5
- /clients-manifest5
- /fbcdn4
- /cache4
- /pages4
- /panels4
- /assets4
- /ig-repull-status3
- /media3
- /app3
- /clients-notes3
- /catch3
- /price3
- /color3
- /style3
- /book3
- /hue3
- /status3
- /upload2
- /com2
- /media-cache2
- /outbox2
- /config2
- /composer2
- /reels2
- /index2
- /follow2
- /state2
Creative ideas + design notes
39fcdef · 2026-07-27 · MCC bluesky: fail loud if image can't embed — never silently post text-only
postBluesky() previously swallowed any image fetch/uploadBlob failure and posted text-only anyway (embed left undefined), which slips past the route-level text-only guard (content.mediaUrl was truthy upstream) and violates Steve's 2026-07-17 'DW never posts text-only' rule — silently, on ok:true. Now aborts the post with a loud error on any fetch/upload failure. Found by /contrarian gating TK-8. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
5d1c41b · 2026-07-22 · chore(mcc): move IG re-pull schedule from Mac2 launchd to Kamatera-local cron
The re-pull's script AND data both live on Kamatera, so a Mac2 launchd job SSHing in was a needless cross-machine dependency (Mac2 asleep / SSH hiccup at 06:20 would silently skip it — the exact drift the job exists to prevent). Now a self-contained root crontab entry on Kamatera (06:40 daily). Removed the Mac2 wrapper + plist; added scripts/ig-repull-SCHEDULE.md documenting the cron. Verified: cron present, a manual Kamatera run wrote a fresh healthy status, Mac2 job booted out + gone. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
21be5cc · 2026-07-22 · feat(mcc): board shows IG re-pull health at a glance + Run-now fix button
The daily IG media re-pull (com.steve.mcc-ig-repull) had no surface — you couldn't see if it was running or stale. Now the Streams Board shows a health strip above the columns: green/amber/red dot + last-run time + result, and a "Run now" button. - ig-media-repull.js: writes data/ig-repull-status.json every run (ranAt/mode/ recovered/graphErr/downloadDead/unrecoverable/ok). - channels: GET /api/channels/ig-repull-status (reads it) + POST /api/channels/ ig-repull-run (triggers --apply as a guarded child; 409 if already running). - board.js: #bd-repull-health strip — amber if never-run or stale (>36h), red on graph/download errors, green when clean; "Run now" POSTs then refreshes. Verified locally: status write, GET, POST trigger, concurrent-run 409 guard, and the strip renders (green dot, timestamp, Run-now button). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6d2f55b · 2026-07-22 · chore(mcc): daily launchd job for the IG media re-pull (Mac2 → Kamatera SSH)
scripts/ig-repull-cron.sh: wrapper the LaunchAgent runs — SSHes to Kamatera and runs ig-media-repull.js --apply, logging to ~/.claude/logs/mcc-ig-repull.log. scripts/com.steve.mcc-ig-repull.plist: reference copy of the installed LaunchAgent (runs daily 06:20; the live copy lives in ~/Library/LaunchAgents). Matches the DW scheduled-ops pattern (launchd on Mac2 SSHing to Kamatera, like the canaries). Bootstrapped + kick-tested end-to-end: SSH → --apply → exit 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
214b1bb · 2026-07-22 · fix(mcc): IG re-pull only touches at-risk images, never clobbers stable URLs
The re-pull was over-eager — it would refresh a published-IG post even when its mediaUrl is already a durable cdn.shopify.com image, downgrading a full-res photo to a square IG thumbnail. Now it only targets items whose current mediaUrl is missing or an expiring ig/fbcdn signed URL; stable non-IG hosts are left alone. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
971082a · 2026-07-22 · feat(mcc): IG Graph API re-pull to restore durable thumbnails for published IG posts
scripts/ig-media-repull.js: for every outbox item successfully published to Instagram (detail[].id = media id, detail[].igId = business account), fetch the media object's CURRENT media_url/thumbnail_url from Graph v23.0 — a fresh live URL — and cache the bytes locally keyed by the STABLE media id, then rewrite the item's mediaUrl to the /cache/media path. Makes published-IG thumbnails durable regardless of future signed-URL rotation. Token resolved per-igId from meta-pages.json (per-Page token), falling back to META_ACCESS_TOKEN/IG_ACCESS_TOKEN. Dry-run by default (read-only Graph calls + report); --apply caches + rewrites the outbox after backing it up. Items with a dead IG url but NO media id (failed cross-posts) are reported unrecoverable — the placeholder tile is correct for those. lib/media-cache.js: factor out fetchImageBuffer + writeAtomic; add exported cacheAs(url, keyBase) for explicit-key (media-id) caching used by the re-pull. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2ffb36f · 2026-07-22 · fix(mcc): durable local cache for expiring IG/fbcdn post images + graceful placeholder
Board post thumbnails embed raw Instagram/fbcdn CDN URLs whose _nc_ohc/oh/oe
signature is time-boxed; once it expires the URL 403s for everyone (verified:
even a bare server-side fetch 403s), so the <img> failed and the old
onerror hid it -> blank tile ("posts all blank").
- lib/media-cache.js: localize() maps an IG/fbcdn URL to /cache/media/<sha1(path)>.<ext>,
serving a cached local copy if present and background-downloading (best-effort,
6s timeout, SSRF host-allowlist, atomic write) any missing one so the next load
self-heals. Cache key is the media PATH (not the rotating signed query) so one
media = one file. Never blocks the response.
- channels /api/outbox: localizeItems() the outbox before serving.
- board.js: on img error, swap to an inline branded "image unavailable" SVG tile
instead of hiding — a dead/uncached image now reads as intentional, not blank.
- .gitignore: public/cache/ (locally-cached media, not source).
Verified end-to-end: outbox GET returns the localized /cache/media path and the
file serves 200; download pipeline writes valid image bytes. Deploy held (gated).
Note: images whose URL already expired before caching can't be recovered here —
fully restoring those needs a re-pull of fresh media from the IG Graph API (follow-up).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
243d9c5 · 2026-07-22 · fix(mcc): guard panel-router race that blanked panels + untrack screen-record videos
route() in public/app.js had no concurrency guard across its two awaits (fetch panel HTML, load panel JS), so rapid panel switching interleaved two route() calls that both wrote the shared #panel — a stale one landed after the fresh render and wiped it (title stays, zero cards/API/errors = "posts all blank"). Added a latest-wins token: after each await, bail if a newer nav superseded. Verified via /screenrecord (5-run order-varied recording, run3 reproduced the all-blank cascade) + a fixed-app.js-over-live-data proxy A/B. Deploy held (gated). Also .gitignore screenrecord/rec/ + *.webm and untrack the 10 committed webm recordings (7.5MB) an auto-save hook had pulled into history going forward. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
29d1ff2 · 2026-07-22 · channels: /publish skips non-postable + media-mismatched channels
A full-fan-out publish used to FIRE every connected channel and collect guaranteed errors (FB App-Review #200, LinkedIn 401, TikTok/YouTube image-as- video). Now /publish consults the postable signal + media-type fit and SKIPS (stages with a clear reason) any channel that can't actually take this post, firing only the ones that will land. Result rows carry skipped:true + reason. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
69af9fe · 2026-07-22 · channels: add honest 'postable' status — connected != can-actually-post
The status endpoint reported connected:true for channels that cannot post (Facebook needs pages_manage_posts App Review; LinkedIn token is unscoped/dead; TikTok/YouTube are video-only), which misled callers twice. Added a POSTABILITY map encoding the known structural blocks and surface per channel: postable — connected AND not structurally blocked (the real signal) mediaTypes — what it accepts (image caller must check; TikTok/YouTube = video-only) postableNote — the why (App Review / dead token / video-only) Also corrected the stale Threads caveat (tester-mode posts live without App Review). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
d84dc2a · 2026-07-22 · engine: untrack runtime engine-queue.json + protect it on deploy
engine-queue.json holds live approve/arm/post status mutated only by the running MCC — it was git-tracked (a latent trap: every deploy/commit carried a stale snapshot). Untracked it + gitignored (its sibling engine-config.json already is), and added it + engine-config.json to .deploy.conf RSYNC_EXTRA_EXCLUDES so a --delete rsync can never clobber the live queue. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
d9e22d3 · 2026-07-22 · engine: image-grounded captions (vision) + always include DW SKU
Two changes to daily post-suggestion generation: 1. VISION-GROUNDED COPY — captionsFor() now runs a vision pass (qwen2.5vl:7b) on the actual product image first, producing a factual visual brief (motif, scale, color, texture) that grounds the qwen3:14b copy. Old captions were text-only from title/tags and read generic; new ones describe what the image actually shows. Graceful: image fetch / vision failure (incl. non-Mac hosts that can't reach ollama) resolves to '' and captions fall back to text-only. One vision call per product (not per channel). Source label gains '+vision'. 2. ALWAYS INCLUDE THE DW SKU — candidates.resolveSku() fills dwSku from dw_sku || sku || variant_sku (strips the '-Sample' suffix; dw_sku is often empty on the mirror). llm.withSku() then guarantees the SKU in every caption, sized so channel limits never truncate it, and strips any dangling '· SKU' tail when no code exists. SYSTEM/userPrompt updated to request it (belt) with withSku as the deterministic backstop (suspenders). Verified end-to-end on the real Onda image: vision read the wave motif, all 7 channels carried '· SKU DWTT-73419' within limits; empty-SKU + broken-image edge cases degrade cleanly. $0 (local ollama). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2545273 · 2026-07-22 · engine: stop birth-gating Threads suggestions (DW is a Meta tester, publishes without App Review)
Threads posts live in tester mode (proven: postThreads() succeeded this batch), but generate.js was stamping every new Threads item status:gated from the static GATED_CHANNELS map. Removed threads from that map so items are born 'suggested' and approvable; the per-post approve->schedule step is still the real publish gate. TikTok stays gated (genuinely SELF_ONLY until app audit). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5ea8d24 · 2026-07-20 · calendars: filter Schumacher (internal-only) out of both calendar layers at source
Schumacher is internal-only and must never surface on a front end. The All Calendars feed re-projects from dw_unified every ~10min, so scrubbing the JSON alone would be undone on the next regen. Added a HIDDEN_VENDORS source filter applied to the activation layer (after date projection, so other vendors keep their real go-live slots), the launched layer, and the snapshot-fallback path. Verified: live regen yields 0 Schumacher (activation 20304 -> 20034). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
97dcfe7 · 2026-07-20 · Composer: add reel/story asset picker + Facebook Page selector
- /api/composer/reels serves generated reels as selectable video post media - Reels tab (portrait video tiles) → sets videoUrl; video preview in composer - Facebook Page picker (reuses /api/channels/pages) shown when FB is targeted - postFacebook/postInstagram now publish video (FB /videos, IG REELS) + selected pages - publish body carries videoUrl + pages; verified headless (25 reels, 80 pages, 0 errors)
b69f993 · 2026-07-17 · MCC Clients: server-side notes store so notes sync across devices
- /api/clients-notes GET+POST in server.js (atomic JSON store at data/clients-notes.json), mounted directly (not via registry) so no phantom nav panel; behind the existing basic-auth - clients.js: pull+merge server notes on group open (server wins, local-only notes pushed up), debounced POST on each edit; localStorage stays the instant cache. Textarea hints 'syncs across devices' - protect data/clients-notes.json from rsync --delete on deploy (.deploy.conf RSYNC_EXTRA_EXCLUDES + .gitignore)
2aac093 · 2026-07-17 · MCC Clients: per-contact notes field below each firm card
- auto-saving, auto-growing textarea under each broker/listing card, persisted per group in localStorage (keyed by the same recId as contacted/actions) - gold 'has-note' highlight when a note exists; notes included in CSV export
30afe18 · 2026-07-17 · MCC Clients: show full address + phone + real domain + best contact/follow per firm
- backfill address/state/category/mapsUrl from the source Google-Places CSVs into the staged prospect JSONs (scripts/backfill-address-from-csv.py), with a cross-CSV host fallback for the group whose source CSV is absent - clients.js: render 📍 full address (Google Maps link), 📞 tap-to-call phone chip, replace generic 'site ↗' with the actual domain (bsbdesign.com ↗), and a ⭐ Best contact/follow recommendation (gold-rings the recommended channel) - carry the richer location fields forward in crawl-csv-emails.py so future re-crawls keep them; export CSV gains Address/State/Category/Best-contact cols
25362f4 · 2026-07-16 · chore: lint, refactor, v1.3.0 (session close)
Cosmetic-only cleanup of modules/calendars/lib.js (googleEvents stub params uncommented to match call site; split a lone double-declaration). No behavior change — smoke test green (activation+launched live, counts intact). Minor bump reflects this session's launched-layer-live-on-Kamatera capability.
e0ae6f5 · 2026-07-16 · clients: browser+local-model contact enrichment (direct Instagram/LinkedIn + email gap-fill)
- enrich-contacts-browser.mjs: headless Chromium (Playwright) renders JS-only sites the HTTP crawler misses; pulls direct instagram/linkedin/tel from the DOM + mailto; falls back to local Ollama (qwen2.5) to deobfuscate emails. $0. - Panel shows DIRECT IG/LinkedIn (gold ↗) when resolved, search (⌕) otherwise; CSV export carries the direct links. Wired into refresh-clients.sh weekly.
87dc441 · 2026-07-16 · calendars: decouple launched layer so it stays live where bulk_fivefield_worklist is absent (Kamatera)
Give the worklist (RR_SQL) query its own inner try/catch inside computeActivation so a missing bulk_fivefield_worklist (Mac-only table) no longer sinks the launched layer, which reads only shopify_products. When the worklist is absent, activation items come from the last-good snapshot while launched recomputes LIVE. Adds activation_live/launched_live flags; keeps top-level error null in that normal case so the 'Live catalog unavailable' banner stops mis-firing.
0ed9d95 · 2026-07-16 · clients panel: FM client DB (43k) + CSV prospect groups, LinkedIn+Instagram discovery
- Clients & Prospects panel (Audience nav): per-group tabs, lazy-load, name filter, email + website + LinkedIn(all-search) + IG(google) per contact, contacted tracking, 300-row cap. Reads data/clients-manifest.json. - crawl-csv-emails.py enriches Google-Places CSVs for contact emails locally (~70% pilot hit); build-clients-manifest.js scans staged data. - Fleet-managed via refresh-clients.sh + com.steve.mcc-clients-refresh (weekly). Big datasets gitignored (regenerated + rsynced). NOT uploaded to CC (gated).
bfbe222 · 2026-07-16 · followlist: add build:followlist refresh script + CRCP broker section
- scripts/build-followlist.js regenerates linkedin-followlist.json from live sources (li-clicklist.html + CRCP brokers-snapshot.json), falls back to the committed JSON where a source is absent (e.g. Kamatera). npm run build:followlist. - Adds 2,425 CRE brokers (deduped) as their own section; each is a LinkedIn name-search link (scraped linkedin field is an unreliable domain-guess). - Panel: renders 'search' + firm sub-line, caps big sections at 300 rows with a filter-to-narrow note, and updates checkmarks in place (no 2.6k-row rebuild).
e347c43 · 2026-07-16 · linkedin panel: add Follow list mode (ported from li-clicklist.html)
New segmented toggle in the LinkedIn panel: Compose a post | Follow list. Follow list is data-driven from public/data/linkedin-followlist.json (179 people across Hospitality/New-leads/Warm sections + 8 saved-searches), baked into the project so it deploys to Kamatera (the ~/li-*.json source files only exist on the Mac). Per-person 'followed' checkmarks persist to localStorage with a progress bar + name filter, mirroring the :9819 hub.
063dc28 · 2026-07-16 · All Calendars: add Launched layer — already-live products on their launch date, own modal, v1.2.0
New 4th layer 'launched' (slate #3f5a73): products ACTIVE + online_store_published with created_at_shopify in the last 30 days, placed on their real launch date (past-side companion to the future-side staged/activation layer). Reuses the same enrichment via new shared fieldsFrom() helper — carries vendor/material/price/color/style/book/hue, so the left-panel facets filter it too. Launched chips are NOT draggable (historical) and open their OWN modal (live product, Launched date, Shopify Admin + View-on-site links, no reschedule). Verified local: 2,570 launched items over the window, render as live-bordered chips on June days, legend toggles the layer, launched modal correct (tag/no-reschedule/live-links), 0 console errors across the render. Ships live on the next (gated) MCC deploy alongside the DB-env fix. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
f2866e6 · 2026-07-16 · chore: harden calendars module (pool error log, load() try/catch, fieldvis merge), v1.1.0 (session close)
Report-only lint+refactor review at close — all syntax valid, SQL parameterization safe,
no critical issues. Applied 3 safe non-behavioral robustness fixes:
- lib.js: pool 'error' handler now logs instead of silently swallowing
- calendars.js: load() wrapped in try/catch with Retry banner (was: spinner-forever on fetch fail)
- calendars.js: fieldvis prefs now MERGE over defaults (was: JSON.parse('') threw every fresh load)
Deferred cosmetic items (pad dedup, named SLOT_MIN constant client-side) — noted, not applied.
NOTE: local now 1 commit ahead of live; ships on the next (gated) MCC deploy.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
a83ad8f · 2026-07-15 · Place All Calendars in the Planning nav group (next to Marketing Calendar)
One-line app.js edit — Steve-authorized exception to the module-contract 'don't touch app.js' rule so the panel groups with the existing calendar instead of falling into More.
c3e1ea5 · 2026-07-15 · All Calendars: front-end panel (facet rail + multi-layer month grid + drag reschedule)
public/panels/calendars.{html,js} — scoped under #cwroot (MCC style.css also defines
.grid/.card/.chip). Ports the astek left-panel field-toggle rail (search + type-ahead +
one collapsed table per field: vendor/material/price/color/style/book/hue/build-step/
channel/status/month, click-to-filter + count) and the dw-activation-calendar month grid
(image chips + slot times + drag-to-reschedule + detail modal), restyled to MCC's luxe
theme. Legend toggles each layer. Density slider scales chip size + type.
Verified headless (chromium): 42 cells, 8 facet tables, 12.3k chips render, vendor filter
20,356→4,394, zero console errors. Gitignores the 13MB regenerable snapshot.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fea8d92 · 2026-07-15 · All Calendars module: backend (activation projection + shopify enrichment + marketing/campaign overlays)
New self-contained MCC module 'calendars' (📆 All Calendars) that merges 3 layers: - activation: 20,304 staged SKUs on durable go-live dates (rr projection ported from dw-activation-calendar/generate-schedule.js), enriched from shopify_products so each SKU carries material/price/color/style/book/hue for left-panel field toggles - marketing + campaign: reused from the existing calendar module via new shared modules/calendar/store.js (datesForMonth + campaign upsert — one source of truth) - google: fail-soft stub (returns [] until OAuth is wired, Steve-gated) Endpoints /events (?fresh=1) /refresh /move (drag reschedule → overrides file). Fail-soft: DB unreachable → mock mode, never crashes the shell. Adds pg dep. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
f2636e3 · 2026-07-15 · channels: Instagram-account selector — pick 1-to-all IG accounts
When only Instagram is ticked, the page picker reframes as 'Instagram accounts': rows show @handle first, All/None act on the visible (IG-filtered) list so All = all IG accounts, count reads 'N of M Instagram accounts', search matches @handle. Verified via Playwright (label/rows/All/None, 0 console errors). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
90978c9 · 2026-06-24 · Add Followers/Following tab to DW Marketing Command Center
New follow-counts module: per-DW-IG-account followers_count + follows_count
(COUNTS ONLY — official Instagram Graph API returns no username lists) with a
once-daily snapshot store for growth-over-time charting.
- modules/follow-counts/index.js: /accounts, /history, /snapshot, /roster.
Pulls counts via Norma instagram-agent (:9810) — reuses its IG token/IG_USER_ID,
honors simulation mode (labels 'awaiting live IG creds', never fakes live).
Daily-idempotent JSON snapshot store (data/follow-counts-history.json).
- public/panels/follow-counts.{html,js}: per-account count cards + delta +
dependency-free SVG growth chart (followers/following toggle).
- registry.js + app.js: mounted under Analytics nav group.
- scripts/follow-counts-snapshot.js + deploy/com.steve.dw-follow-counts.plist:
STAGED once-daily launchd capture (not loaded — gated on Steve).
- .env.example: NORMA_IG_* override docs + go-live note.
Surface chosen by DTD (3/3 unanimous): marketing-command-center.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
eac09ca · 2026-06-24 · CC module: proxy Connie cc-agent when no local CTCT token (DTD verdict C)
- /campaigns and /calendar now try the live Connie cc-agent (env CC_AGENT_*, else gitignored .cc-agent.json, else defaults) before falling back to mock - scoped self-signed-TLS https request (rejectUnauthorized:false per-request, never process-wide), 6s timeout so a DOWN agent degrades to mock cleanly - response carries source: ctct | connie | mock + fallback_reason on degrade - send/draft/schedule gates untouched (CAN-SPAM / CA-17529.5 / confirm) - .cc-agent.json gitignored; .env.example documents CC_AGENT_* vars Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
88a51a7 · 2026-06-22 · Social: real approval pipeline + instant posting + truthful live/simulated UX
Audit found posts were 'simulated' only because no social backend is connected (META token is a placeholder + expired; flags off) — the code can post for real. - backend: pipeline statuses (drafted→pending-approval→approved→posted), new /submit (Submit for approval) + /approve (Steve sign-off) + /status (live vs simulated truth) endpoints. /publish already does real Meta/Ayrshare posting + marks posted; instant = inline approve+confirm. - panel: board columns = the 4 pipeline stages; per-card action by status (Submit for approval → Sign off ✓ → Publish); ⚡ Instant post button on the form; banner now reads ● LIVE (backend connected) or ● SIMULATED from /status, with a real ⚠️ 'posts to the REAL account' confirm when live. Verified end-to-end on an isolated port: draft→submit→approve→publish stages correctly (no backend), instant path works. Goes LIVE the moment a valid Meta token + META_IG_USER_ID land (the only remaining blocker).
fe2afd8 · 2026-06-17 · meta-token-health: fix dead George alert URL (HTTPS flip 2026-06-17)
http://100.107.67.67:9850 now 400s; use the Tailscale MagicDNS host + raw
GEORGE_AUTH + body field (matching the working watchdog sender). The token
itself is dead ('Cannot parse access token') — rotation is Steve-gated. The
Vendor IG cache fallback already exists (panel renders from vendor-posts-cache).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
5435736 · 2026-06-15 · Channels: add Facebook/Instagram Page picker — select WHICH of your 80 Pages (+ 11 linked IG) to post to.
- /api/channels/pages discovers all Pages via META_ACCESS_TOKEN (/me/accounts), caches per-Page tokens + IG links to data/meta-pages.json (gitignored); tokens stripped before sending to browser. - postFacebook/postInstagram now post per-selected-Page using each Page's own token (legacy FB_PAGE_IDS path preserved as fallback). - Composer: searchable Page picker (All/None/IG-linked/refresh), selection persisted to localStorage, shown when FB/IG targeted; publish guard requires >=1 Page. - platformStatus marks FB/IG connected when META_ACCESS_TOKEN present. Tested locally: /pages 200 (80 pages, 11 IG, no token leak), no-auth 401.
0f9d596 · 2026-06-12 · social: add free Meta Graph (IG+FB) publish backend behind ayrsharePost abstraction
- backendName() picks ayrshare (paid) > meta (free) > null (stage-only) - metaPost(): IG 2-step container+publish, FB /photos|/feed, native Graph API - publish() dispatcher routes /publish route + scheduler to active backend - /connection reports backend + liveChannels; all confirm/approved gates intact - .env.example documents META_ACCESS_TOKEN/IG_USER_ID/FB_PAGE_ID (free path)
cfea2d7 · 2026-06-12 · social: Ayrshare live-publish + scheduler scaffold (gated, off by default)
- ayrsharePost(): one-key publisher to IG/TikTok/Pinterest/FB; mock/stage when no key. - /publish live branch now posts via Ayrshare ONLY when AYRSHARE_API_KEY set AND confirm:true AND post approved:true; otherwise stages, sends nothing (unchanged). - scheduler: 60s tick, fires due+approved 'scheduled' posts — DRY unless SOCIAL_SCHEDULER_LIVE=true + key present; marks posted, logs every action. - new posts carry approved:false; /connection + /publish-log surfaces; .env.example. - verified: no key => staged only, unapproved => rejected, nothing sent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
7207cc9 · 2026-06-10 · assets: full-catalog search via Shopify predictive search (suggest.json)
q= now hits /search/suggest.json (whole storefront) so deep lines like grasscloth/silk/cork are findable; falls back to cached browse-filter on rate-limit/non-JSON. normImg() handles protocol-relative cdn URLs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
0696349 · 2026-06-10 · assets: image Asset Library module (upload / URL / live DW-catalog pull) + Layouts picker
- new modules/assets backend: list/upload(base64)/add-url/catalog/save-catalog/delete/file - standalone Asset Library panel (public/panels/assets.*) — 3 add modes, created date+time chip, density slider - Layouts panel gains a 📎 Assets picker drawer (Library + DW Catalog tabs) feeding the image field - self-seeds 6 real DW catalog images; runtime data (data/assets*) gitignored so deploys don't clobber - server.js JSON limit 2mb→28mb for image uploads; registry +assets Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
File tree
- .deploy.conf
- .env.example
- .gitignore
- 5x/REPORT.md
- README.md
- data/ab-tests.json
- data/browse-abandon-config.json
- data/browse-abandon-events.json
- data/calendar-dates.json
- data/calendar-schedule.json
- data/cc-import-activities-audit.jsonl
- data/cc-import-fm-clients-hold.json
- data/cc-import-fm-clients-mailable.json
- data/cc-warmup-seed-200.json
- data/ig-repull-status.json
- data/journeys.json
- data/playbook.json
- data/segments.json
- data/vendor-instagram.json
- data/vendor-posts-cache.json
- demo/record-tiktok-demo.mjs
- deploy/CUTOVER.md
- deploy/apply-cutover.sh
- deploy/com.steve.dw-follow-counts.plist
- deploy/nginx/marketing.agentabrams.com.conf
- deploy/nginx/marketing.designerwallcoverings.com.conf
- docs/META-APP-REVIEW.md
- docs/ig-fleet-checklist.md
- docs/submissions/facebook-app-review.md
- docs/submissions/linkedin-app-review.md
- docs/submissions/tiktok-app-review.md
- docs/tiktok-app-review-package.md
- drafts/grasscloth-2026-07-15.html
- drafts/pr-naturals-2026-07-15.html
- drafts/pr-naturals-v2-2026-07-15.html
- drafts/pr-naturals-v3-2026-07-15.html
- lib/brand.js
- lib/kravet.js
- lib/leak-deny.js
- lib/media-cache.js
- modules/ab-tests/index.js
- modules/accounts/index.js
- modules/assets/index.js
- modules/board/index.js
- modules/browse-abandon/index.js
- modules/calendar/index.js
- modules/calendar/store.js
- modules/calendars/index.js
- modules/calendars/lib.js
- modules/channels/index.js
- modules/clients/index.js
- modules/composer/index.js
- modules/constant-contact/index.js
- modules/copy/index.js
- modules/engine/candidates.js
- modules/engine/config.js
- modules/engine/generate.js
- modules/engine/index.js
- modules/engine/llm.js
- modules/engine/scheduler.js
- modules/engine/store.js
- modules/follow-counts/index.js
- modules/journeys/index.js
- modules/layouts/index.js
- modules/linkedin/index.js
- modules/performance/index.js
- modules/playbook/index.js
- modules/profiles/index.js
- modules/reels/index.js
- modules/registry.js
- modules/segment-perf/index.js
- modules/segments/index.js
- modules/send-times/index.js
- modules/social/index.js
- modules/sounds/index.js
- modules/templates/index.js
- modules/vendors/index.js
- package-lock.json
- package.json
- public/app.js
- public/data/clients-manifest.json
- public/data/linkedin-followlist.json
- public/index.html
- public/panels/ab-tests.html
- public/panels/ab-tests.js
- public/panels/accounts.html
- public/panels/accounts.js
- public/panels/assets.html
- public/panels/assets.js
- public/panels/board.html
- public/panels/board.js
- public/panels/browse-abandon.html
- public/panels/browse-abandon.js
- public/panels/calendar.html
- public/panels/calendar.js
- public/panels/calendars.html
- public/panels/calendars.js
- public/panels/channels.html
- public/panels/channels.js
- public/panels/clients.html
- public/panels/clients.js
- public/panels/composer.html
- public/panels/composer.js
- public/panels/constant-contact.html
- public/panels/constant-contact.js
- public/panels/copy.html
- public/panels/copy.js
- public/panels/engine.html
- public/panels/engine.js
- public/panels/follow-counts.html
- public/panels/follow-counts.js
- public/panels/journeys.html
- public/panels/journeys.js
- public/panels/layouts.html
- public/panels/layouts.js
- public/panels/linkedin.html
- public/panels/linkedin.js
- public/panels/performance.html
- public/panels/performance.js
- public/panels/playbook.html
- public/panels/playbook.js
- public/panels/profiles.html
- public/panels/profiles.js
- public/panels/reels.html
- public/panels/reels.js
- public/panels/segment-perf.html
- public/panels/segment-perf.js
- public/panels/segments.html
- public/panels/segments.js
- public/panels/send-times.html
- public/panels/send-times.js
- public/panels/social.html
- public/panels/social.js
- public/panels/sounds.html
- public/panels/sounds.js
- public/panels/templates.html
- public/panels/templates.js
- public/panels/vendors.html
- public/panels/vendors.js
- public/style.css
- screenrecord/DEBUG-REPORT.md
- screenrecord/debug-log.jsonl
- scripts/backfill-address-from-csv.py
- scripts/build-clients-manifest.js
- scripts/build-followlist.js
- scripts/cc-agent-discover.js
- scripts/cc-import-fm-preview.js
- scripts/com.steve.mcc-clients-refresh.plist
- scripts/crawl-csv-emails.py
- scripts/enrich-contacts-browser.mjs
- scripts/fix-connie-creds.sh
- scripts/follow-counts-snapshot.js
- scripts/ig-media-repull.js
- scripts/ig-repull-SCHEDULE.md
- scripts/meta-go-live.sh
- scripts/meta-token-health.js
- scripts/nav-map.js
- scripts/probe-403-and-posts.js
- scripts/probe-403.js
- scripts/probe-business-discovery.js
- scripts/refresh-clients.sh
- scripts/refresh-vendor-posts.js
- scripts/repro-race.js
- scripts/repro-run3.js
- scripts/screenrecord-posts.js
- scripts/verify-fix.js
- scripts/verify-fix2.js
- scripts/verify-proxy.js
- server.js