cross-post-bulk-to-google
pink
#199 / 601
NAME
cross-post-bulk-to-google — Operator selects N listings, Sakura adapts and pushes the batch. Per-listing failures don't abort the batch.
SYNOPSIS
id cross-post-bulk-to-google
tier pink
section §7. Pink — Sakura local (65 baseline, 131 post-matrix)
trigger operator-commanded
tools merchant_center/sync
source new
DESCRIPTION
Sakura's local 8B model handles this on-device. Free, private, Cortex-grounded. The 'I just know what you have' tier.
Operator selects N listings, Sakura adapts and pushes the batch. Per-listing failures don't abort the batch.
MECHANISM
;;~ title "Cross-post · bulk to Google Shopping" ;;~ author "lacuna" ;;~ version 1 ;;~ mode automation ;;~ flavor purple ;;~ id cross-post-bulk-to-google ;;~ touches (etsy-listings ebay-listings shopify-listings google-merchant) ;;~ summary "Operator-commanded. Operator selects N listings; cart adapts and pushes each in turn with rate-limit-aware pacing. Per-listing failures don't abort the batch — they're collected and surfaced at the end. Tier-gated to Magic+; drafts never sync." ;; Bulk cross-post. The operator selects a batch (in the listing grid ;; or via "all active") and confirms; the cart walks the batch, pushing ;; each listing in turn. ;; ;; Recursion shape: pop head off the input list, push, branch on result; ;; on success drop the head and recurse; on per-listing failure record ;; the failure and continue. The batch is allowed to partial-succeed — ;; the operator gets a final report of which landed and which didn't. (cart 'cross-post-bulk-to-google '((author . "lacuna") (version . 1) (touches . (etsy-listings ebay-listings shopify-listings google-merchant)) (read-only . #f))) ;; ── start ────────────────────────────────────────────────────── ;; ctx carries 'listings — a list of listing records (already fetched ;; by the operator surface; the cart doesn't fetch them itself). Empty ;; list = nothing to do; escalate calmly. (define (start ctx) (let ((listings (ctx-get 'listings ctx))) (cond ((null? listings) (escalate 'empty-batch null)) (else (next 'check-merchant (ctx-set 'failures '() (ctx-set 'successes '() ctx))))))) ;; ── check-merchant ───────────────────────────────────────────── ;; Verify the operator has the merchant center connected before we ;; start the batch — failing fast here saves N round-trips. (define (check-merchant ctx) (act 'merchant_center.status '() 'check-merchant-result)) (define (check-merchant-result ctx) (let ((status (ctx-result ctx))) (cond ((not (status-connected? status)) (escalate 'merchant-not-connected null)) ((status-pending-verification? status) (after 3600 'check-merchant ctx)) (else (next 'pop-and-push ctx))))) ;; ── pop-and-push — the loop head ────────────────────────────── ;; Peel one listing off the head, push it, branch in a named result ;; fn. Drafts in the batch are silently skipped (the operator surface ;; usually filters drafts out, but the cart's contract is "drafts ;; never sync" so we belt-and-suspender it). (define (pop-and-push ctx) (let ((listings (ctx-get 'listings ctx))) (cond ((null? listings) (next 'finalize ctx)) ((listing-is-draft? (car listings)) (next 'pop-and-push (ctx-set 'listings (cdr listings) ctx))) (else (act 'merchant_center/sync (list (car listings)) 'check-batch-result))))) ;; ── check-batch-result ──────────────────────────────────────── ;; Record the outcome, drop the head, recurse. Per-listing failures ;; are collected into 'failures so the operator can see what broke ;; at the end. We pace at 30s on rate-limit; otherwise hard-step. (define (check-batch-result ctx) (let* ((r (ctx-result ctx)) (listings (ctx-get 'listings ctx)) (head (car listings)) (rest (cdr listings))) (cond ((push-ok? r) (next 'pop-and-push (ctx-set 'listings rest (ctx-set 'successes (cons (push-product-id r) (ctx-get 'successes ctx)) ctx)))) ((eq? r 'rate-limited) ;; Keep the head — don't drop it; we retry after the backoff. (after 30 'pop-and-push ctx)) (else (next 'pop-and-push (ctx-set 'listings rest (ctx-set 'failures (cons (list (listing-id head) r) (ctx-get 'failures ctx)) ctx))))))) ;; ── finalize — operator-facing summary ──────────────────────── (define (finalize ctx) (let ((wins (length (ctx-get 'successes ctx))) (fails (length (ctx-get 'failures ctx)))) (sakura/say (string-append "Bulk cross-post complete · " (number->string wins) " ok" (if (> fails 0) (string-append " · " (number->string fails) " flagged") ""))) (done)))
EXAMPLES
Example forthcoming —
docs/automations/examples.json entry editable in place.TIER
pink
Sakura local 8B · free · on-device
FITNESS
Ready
Fits any shop today — this one has no sample-size floor.
SEE ALSO
HISTORY
canonical source: curator/docs/AUTOMATIONS-CANONICAL.md · §7-pink-sakura-local-65-baseline-131-post-matrix
last regenerated: 2026-06-05 20:31 UTC (mechanical — edits land in the MD or sidecars, not here)
KNOWN BUGS
(none recorded — flag in AUTOMATIONS-CANONICAL.md or open an issue against the cart file)