Skip to content

project: store-production-launch-plan area: admin status: approved 2026-10-06 (owner: "Yes do recommend") updated: 2026-10-07


Store production launch: store.embeddedera.com, 15 Cricket units, live Stripe

Related: embeddedera-store · store-launch · store-launch-checklist · store-cricket-listing · cricket-product. Kanboard #272–#279 are the existing launch gates. Researched 2026-10-06 from the repo, notes, map cards, live DNS and the pinned plugin zips; nothing changed.

Findings that shape the plan

  1. Sandbox guards are structural. Most scripts/configure-*.php and the sync scripts refuse unless wp_get_environment_type()==='local'; mu-plugins/stripe-sandbox.php forces test mode; mu-plugins/sandbox.php blocks all mail and HTTP. Production needs a tier switch (EE_STORE_TIER) and its own mu-plugin set; it cannot be "dev with live keys".
  2. order_sync.py hard-codes INSTANCE="ee-store-sandbox"; the production identity (ee-store-production, ERP 8100) already exists.
  3. Stock race: publish-availability.py writes absolute stock while order-sync allocates nothing, so a timer would re-inflate stock until shipment. Launch rule: Woo stock is set once and is authoritative; no availability timer; manual fulfilment.
  4. Egress is Squid, HTTPS only, api.stripe.com only. Turnstile needs challenges.cloudflare.com; SMTP needs a small postfix relay sidecar.
  5. Access on /wp-admin* also covers admin-ajax.php: check anonymous features signed-out.
  6. Stripe Tax plugin 2.1.0 may reject the restricted rk_live key (validator wants sk_live_); spike in P6.
  7. Email DNS for embeddedera.com is already good (Google SPF, DKIM, DMARC p=none); the Workspace relay smtp-relay.gmail.com:587 is already used for invoices/tasks.
  8. Nothing exists for store.embeddedera.com yet (no DNS, tunnel ingress or Access app).
  9. The store DB is in no backup today. Add a store set to erp-backup before the 02:50 restic run.
  10. Policy pages exist with dev wording, a Raleigh/Clayton mismatch, and "batteries not returnable" (conflicts with Cricket).
  11. Host: plenty of RAM headroom; swap already full (watch it).

Design

  • Separate compose project embeddedera-store-prod, fresh WordPress install, deployed from a pinned git worktree of a new production branch; ingress on 127.0.0.1:8510; dev keeps running untouched on 8500. Wrapper scripts/prod/pcompose so nobody runs a dev command at prod.
  • Secrets (0700 dir, 0600 files, never in git): ~/.config/embeddedera-store-prod/{env,mail.env,stripe-webhook.env}; live keys stay in ~/.config/stripe/live-store.env and reach WordPress only via stdin.
  • Production mu-plugins: production-guards.php (HTTP allowlist, no file mods/auto-updates, XML-RPC off) and stripe-live-guard.php (live keys only, refuses enable unless pk/rk/whsec all valid). Sandbox mu-plugins are not mounted.
  • Seed by replaying product-URL, storefront (prod variant), policies (prod variant), search, performance and recommended-plugin scripts; never copy fictional products/customers/orders, test keys or the sandbox tax rate. New bootstrap-prod.sh: USD, store address Clayton, Stripe Tax on with an empty Woo tax table, only the Stripe gateway, guest checkout, zone "Contiguous US", flat USPS Ground Advantage $8.95, shipping taxable.
  • Admin: one named admin, TOTP 2FA enrolled by the owner, backup codes in Vaultwarden; Cloudflare Access (Google) in front of /wp-admin* and /wp-login.php.
  • Routing: onboard access-app for admin/login, then cf-host add store.embeddedera.com http://127.0.0.1:8510; static assets edge-cached only; cart/checkout/account/wc-api bypassed; blog_public=0 until go-live.
  • Payments: scripts/prod/stripe-live-gateway configure|enable|disable (stdin only, card only, automatic capture, logging off). Owner creates the live webhook https://store.embeddedera.com/?wc-api=wc_stripe (card events) and its whsec_ goes to stripe-webhook.env. Stripe Tax live: origin Clayton, txcd_99999999, prices tax-exclusive.
  • $1 test: hidden product EE-TEST-001, owner's own card, refund in Woo, webhook 200 in the Stripe dashboard, then delete the product (fees ~$0.30 not returned).
  • Email: postfix sidecar → Workspace relay; FluentSMTP to the sidecar; From store@/orders@, Reply-To hello@; verify SPF/DKIM/DMARC pass.
  • Inventory: ERP EE-A-0003-A (pk 245, not trackable: no serials), 15 in Finished goods; mirror price breaks in the ERP ($24.99, 2+ $22.49); registry entry with order sync/availability off; Woo stock set once (14 with 1 buffer); max 4 per order. Manual fulfilment: order email → pack → USPS label → tracking → ERP sales order shipped from Finished goods. Weekly dry-run drift check.
  • Lithium text (verify with USPS before stating): one installed 3.7 V 100 mAh (0.37 Wh) cell, UN3481, ground only within the contiguous US.
  • Policies (owner approves wording): remove dev wording, fix name/address, shipping (contiguous US, $8.95, ground, ship-within days), returns (fix the battery line; 30 days), warranty/product use (prank kindly, not a toy, 5 V charger).
  • Backups/monitoring: erp-backup store set (DB dump + uploads, age-encrypted, R2, 30 days) at ~02:30; restore drill on the empty stack and after the $1 test, weekly via erp-verify; 5-minute health check timer (home 200, admin→Access, webhook not 5xx, scheduler healthy, no failed actions, dump < 26 h) covered by job-failure; Uptime Kuma monitor.

Phases

Phase Work Who Effort
P0 Answer the questions below; carrier/lithium call Owner 1 h + USPS call
P1 Branch, tier flag, prod compose, guards, prod scripts, tests Helper 1–1.5 days
P2 Dark stack on :8510, seed, admin, shipping, tax Helper (owner: 2FA) 0.5–1 day
P3 Backup set, health check, first restore drill Helper 0.5–1 day
P4 Access apps, DNS/tunnel, cache rules, signed-out checks Helper after owner OK 2–3 h
P5 Mail relay, Turnstile, final policies, lithium text Helper drafts, owner approves 0.5 day
P6 Webhook (owner), live keys, Stripe Tax spike, $1 test + refund Owner approves each money step 0.5 day
P7 ERP price breaks, registry, Cricket listing, stock 14, max-qty Helper (owner approves ERP writes) 0.5 day
P8 Go-live checklist, release, indexing on, link from embeddedera.com Owner approves 2 h + 72 h watch

Each phase has a rollback: revert the branch; pcompose down; remove DNS/ingress or stop ingress; stripe-live-gateway disable / stop stripe-proxy; set the listing pending.

Phase status

  • P1 DONE 2026-10-06 (worker, Sonnet 5.5). Branch production in ~/embeddedera-store (worktrees ~/embeddedera-store-prod/work dev, ~/embeddedera-store-prod/src pinned at ee4d7b3). AGENTS.md lifts the clause for this branch only. EE_STORE_TIER + ee_store_tier() (shared mu-plugin ee-store-tier.php), deploy/prod/ stack, production-guards + stripe-live-guard, scripts/prod/*, env-driven order_sync INSTANCE, configurable purge host, theme review bar hidden in production. Tests: tests/run-production-guards.sh (3 tier modes, network off, 54 checks) + tests/test_production_topology.py (21) + existing 84 pass. Branch NOT pushed: the repo has no git remote (also true for sandbox).
  • P2 DONE 2026-10-06. Stack embeddedera-store-prod up on 127.0.0.1:8510 only; fresh install, replay of the approved scripts, one admin adiaz (password only in ~/.config/embeddedera-store-prod/env). Verified: tier production/env production, no sandbox mu-plugin loaded, sandbox leftovers [], blog_public=0, no test keys, gateway disabled and only WC_Stripe* gateways registered, tax table 0 rows, zone Contiguous US 49 locations + flat USPS Ground Advantage $8.95 taxable, zone 0 empty, one product (Cricket draft, hidden, 24.99, 2+ 22.49, max 4, stock 0, release pending, not purchasable), dev stack untouched. Memory after bring-up: wordpress 140/512 MiB, db 102/384 MiB; host swap was already 4095/4095 MiB before and unchanged.
  • P3 DONE 2026-10-06. erp-backup store (02:30 cron store-backup) + store-verify (also run by weekly erp-backup verify): first set 2026-10-06_2342 encrypted, local + R2; restore drill into --network none MariaDB PASS (users 1, products 1, options 405, tax rates 0, zones 1, blog_public 0, USD, US:NC, home 127.0.0.1:8510, uploads hold the Cricket image). ee-store-prod-health.timer every 5 min (local checks); jobs-index lists both; job-failure hook covers them.
  • Owner next: log in (SSH tunnel ssh -L 8510:127.0.0.1:8510 angel@192.168.5.15, http://127.0.0.1:8510/wp-login.php), enroll TOTP 2FA and store backup codes in Vaultwarden, move the admin password from the env file to Vaultwarden then change it in WordPress; supply the dedicated Workspace relay login for P5 (MAIL_RELAY_USER/PASSWORD in the env file); review draft policy wording (ship-within "3 business days" is a placeholder I chose).
  • Gaps for later phases: scripts/cloudflare-purge-worker.py and its timer still target dev.embeddedera.com (P4); the stored Stripe settings row does not exist (defaults come from the guard, which forces disabled); Woo "Coming Soon" option is unset (store pages visible once public); USPS/lithium sentence is still "(pending USPS confirmation)" in the shipping policy.

Go-live checklist

  • Stack healthy 48 h dark; backup nightly; restore drill passed after the test orders
  • Live gateway: webhook 200, $1 purchase and refund done, test product deleted
  • Stripe Tax: NC taxed, other states $0
  • Mail passes SPF/DKIM/DMARC; order emails delivered
  • No dev/sandbox/test wording anywhere; policies final
  • Lithium/shipping wording verified with USPS; dry-run label bought
  • Units packed and matching the ERP; 2FA enrolled; kill switches rehearsed
  • Owner approves price, text, region, release, indexing, public links

1 production branch yes · 2 the simple $1 test only · 3 Cricket on no other channel: 14 online, 1 buffer · 4 From orders@embeddedera.com via the Workspace relay with a dedicated relay login · 5 if Stripe Tax rejects the restricted key, a full secret key for the plugin · 6 EmbeddedEra LLC, Clayton NC; CPA review only · 7 contiguous US, flat $8.95, labels via Pirate Ship · 8 max 4 per order; 2+ at $22.49 each · 9 owner watches the first 72 h; no link from embeddedera.com until the first order ships cleanly · 10 returns: unused, original packaging, 30 days. Owner still to confirm with USPS that Ground Advantage takes devices with a contained lithium cell.

Questions for the owner (answered above)

  1. Cut a production branch and lift the repo's "no production" rule for it?
  2. Accept a real $1 test on production (hidden, refunded, deleted)? Also the optional physical $1 test (~$10, refunded; ~$0.60 fees lost)?
  3. Is Cricket sold anywhere else (decides the 1-unit buffer: 14 or 15 online)?
  4. Mail: From store@ or orders@; does the alias exist; dedicated relay user or reuse the existing app password (advised against); or an API service such as Brevo?
  5. If Stripe Tax rejects the restricted key: give the plugin a full sk_live key, or accept a manual NC rate?
  6. Legal name and address on policies/receipts (Clayton); lawyer review or CPA hour only?
  7. Contiguous US only; flat $8.95 OK; label tool (Click-N-Ship, Pirate Ship, other)?
  8. Cap 4 per order; 2+ at $22.49 each?
  9. Go-live date; who watches the first 72 h; link from embeddedera.com at launch?
  10. Approve the Cricket copy and a Cricket returns rule (unused, in packaging, 30 days)?
  11. 2026-10-07 P4 done: Access apps 'Store admin' (store.embeddedera.com/wp-admin) and 'Store login' (/wp-login.php), Google, adiaz only, registered in onboard APP_NAME_BY_TOKEN[engineering], access-sync in sync; cf-host add store.embeddedera.com http://127.0.0.1:8510 (tunnel ingress + proxied CNAME). WP home/siteurl = https://store.embeddedera.com; Woo coming-soon on; blog_public=0 + X-Robots-Tag noindex. Signed-out checks: / 200 coming soon, /wp-login.php and /wp-admin/ 302 to Access, /?wc-api=wc_stripe 400 (public, not Access, not 5xx), robots ok, cf DYNAMIC. Still to do from P4: add the store host to Cloudflare cache rules 2/3 and the public checks to health-check.sh. Admin access is now via https://store.embeddedera.com/wp-admin (the ssh-tunnel path no longer works since home is the public URL).

  12. 2026-10-07: Store mail live (P5 part): orders@embeddedera.com Google Group (owner), app password 'Store relay' (adiaz@) in ~/.config/embeddedera-store-prod/mail.env (0600, loaded by pcompose), mail-relay sidecar → smtp-relay.gmail.com:587 with TLS required (POSTFIX_smtp_tls_security_level=encrypt), FluentSMTP → mail-relay:25 forcing From 'EmbeddedEra orders@embeddedera.com', ee_store_mail_enabled=yes. Test to adiaz@: Inbox, SPF/DKIM/DMARC pass, TLS 1.3. Note: Google's relay records the home public IP in a Received header (visible to customers who read headers).

  13. 2026-10-07 P4 follow-ups DONE: Cloudflare ruleset http_request_cache_settings: Rules 2 and 3 now match http.host in {"dev.embeddedera.com" "store.embeddedera.com"}; Rule 1 (HTML cache) untouched, dev only. Verified via curl: / DYNAMIC, /wp-includes/.../style.min.css MISS then HIT, /cart/ DYNAMIC, /?wc-api=wc_stripe 400 DYNAMIC. health-check.sh gained public checks (home 200, wp-login 302 to Access, webhook endpoint not 5xx/not Access); production branch commits aead1b8 + follow-up, src re-pinned, timer run once: all ok.

  14. 2026-10-07 P5 partly done. Turnstile BLOCKED: the Cloudflare token in ~/.config/cloudflare/env returns 10000 Authentication error on the Turnstile widgets API (no Account > Turnstile > Edit scope). Owner: add that scope to the token, or create a widget for store.embeddedera.com (managed mode) in the dashboard and put TURNSTILE_SITEKEY= / TURNSTILE_SECRET= in ~/.config/embeddedera-store-prod/turnstile.env (0600). scripts/prod/configure-turnstile.php is ready (enables login/register/reset + Woo login/register/reset/checkout/account); squid already allows challenges.cloudflare.com; the unauthenticated-POST rejection test is still to run after keys exist. Policies: the five production pages were already rendered and wired (Woo terms=18, privacy=3); text matches the owner decisions; signed-out grep of home, shop, cart, checkout (302 to cart), my-account, quote and all policy pages for development/sandbox/fictional/test/preview/Raleigh: zero hits. Draft for approval: store-policies-draft. Found: menu links /about/ /contact/ /shop-help/ /shop-faq/ /shipping-information/ /payment-information/ /engineering-notes/ /business-account/ all 404 on the store (replayed dev menu); fix before go-live (repoint Shipping to /shipping-policy/ etc.).
  15. 2026-10-07: Fixed public links: primary menu and 8 option/content values pointed at the setup address http://127.0.0.1:8510 (search-replace, GUIDs and the audit log kept); theme helper ee_store_link() sends About/Contact/Notes/Feedback to embeddedera.com, Shipping to /shipping-policy/, and hides the dev-only help, FAQ, payment and business-account links in production (commits on production, src re-pinned). All links on home, shipping and returns pages return 200 (wp-login 302 to Access).
  16. 2026-10-07: Owner created the live Stripe webhook destination: https://store.embeddedera.com/?wc-api=wc_stripe, enabled, 14 card events, API version 2026-08-26.dahlia (latest; watch deliveries in the $1 test). Signing secret in ~/.config/embeddedera-store-prod/stripe-webhook.env (0600; a leading space was trimmed). Not yet loaded into the gateway (P6).
  17. 2026-10-07: Owner approved the policies (3 business days). Policy pages are public on the store (only the home page shows coming soon; shop has nothing purchasable, gateway off). Draft email to Yara (yrosado@) in Gmail drafts: review policies, check USPS for UN3481 contained-in-equipment by Ground Advantage + markings, proposal for shipping@embeddedera.com Google Group (Angel + Yara) for Pirate Ship/USPS accounts and adding Yara to orders@. To do: create shipping@ group, add Yara to orders@, give Yara the production/shipping department in the app (people.yml has sales, purchasing, facilities), write the order-fulfilment SOP.
  18. 2026-10-07: Store public (owner: go live with zero stock, no backorders). Woo coming-soon off, blog_public=1, noindex header removed from the prod Caddyfile (error pages keep it). Cricket product 25 published, visible, stock 0 managed, backorders no, purchasable=false, _ee_listing_release=pending (shows 'Coming soon — not available for purchase yet.'), Stripe gateway still disabled. Verified signed-out: product page shows $24.99, no add-to-cart button, add-to-cart attempt leaves the cart empty, no noindex, robots ok, health check 7/7 ok. Cosmetic to fix: tier-pricing shows an empty 'You save ( %)'.
  19. 2026-10-07: Google Groups ready: shipping@embeddedera.com (new; Angel owner + Yara; carrier accounts: Pirate Ship/USPS) and orders@ (Yara added); collaborative inbox on for both. Test mail from the store relay to both groups delivered to adiaz@ (Yara's copy to be confirmed by her).
  20. 2026-10-07: Pirate Ship WooCommerce connect: allowed exactly ship.pirateship.com in production-guards.php and the squid allowlist (other hosts still blocked, verified with example.com); guard now logs blocked hosts (host only) to the wordpress container log. Gotcha: single-file bind mounts (mu-plugins, squid.conf, Caddyfile) keep the old file after a git checkout of the src worktree; recreate the container (pcompose up -d --no-deps --force-recreate ) after changing them. Tests all pass (topology 21, guards).
  21. 2026-10-07: wc-auth callback failed with 'A valid URL was not provided' (wp_safe_remote_* resolves DNS itself; the container has no outside DNS by design). production-guards now sets reject_unsafe_urls=false only for https://ship.pirateship.com; verified wp_safe_remote_post reaches it (200) and example.com stays blocked.
  22. 2026-10-07: Order-fulfilment SOP written: SOP-044 Store Order Fulfilment (draft, owner Sales) at https://sop.embeddedera.com/SOP-044-store-order-fulfilment/ . Lithium handling and the two Pirate Ship box presets are marked PENDING (USPS answer; measuring). Cross-refs SOP-007, 014, 026, 030, 037, 038.
  23. 2026-10-07: 'You save ( %)' fixed by plugin setting: option tier_pricing_table_you_save_enabled=no (Tiered Pricing Table, You Save badge) on production. The badge was only CSS-hidden where the plugin stylesheet loaded; the shop archive does not load it, so the empty line showed there. Signed-out curl: 0 'You save' on /shop/ and the product page; the 2+ $22.49 tier table remains. Setting is in the DB only: re-apply on a rebuild (not yet in bootstrap-prod.sh).
  24. 2026-10-07: ERP price breaks for EE-A-0003-A (part 245): added sale-price breaks pk 10 qty 1 = 24.99 USD and pk 11 qty 2 = 22.49 USD (same shape as the LiPo cells); read back via /api/part/sale-price/?part=245. Stock untouched.
  25. 2026-10-07: Turnstile live on WP login/register/reset and Woo login/register/reset/checkout/account (widget 'EmbeddedEra Store', managed, keys in ~/.config/embeddedera-store-prod/turnstile.env 0600, applied with scripts/prod/configure-turnstile.php via docker exec --env-file). Verified: widget renders on /my-account/; secret accepted by siteverify through the proxy (invalid-input-response for a dummy token); a login POST without a token is refused with 'Please verify that you are human.' Remaining for the $1 test (P6): owner's card.
  26. 2026-10-07: Store listing text (product 25) rewritten to match the new /cricket/ page: ships switched off, no defaults, links the instructions; applied in the DB only (scripts/prod/configure-cricket.php still holds the older text — update it before any rebuild).
  27. 2026-10-07: shipping@ / orders@ did not receive outside mail (Pirate Ship's verification never arrived; mail sent inside the domain did). Cause: the Google Group posting/moderation settings. Owner fixed them in groups.google.com (who can post = anyone on the web, no moderation). Gotcha: a new Workspace group must accept outside senders before any service (Pirate Ship, Stripe, customers) can mail it. To test, send from an outside address, because internal tests pass either way.
  28. 2026-10-30: Status check (read-only). Owner: 5 Cricket units ready to sell (supersedes the 15/14 plan; set Woo stock from the ERP count, buffer TBD). Still open before first sale: $1 live-card test + refund (P6, gateway still off), Stripe Tax NC check, ERP Finished goods count = Woo stock, SOP-044 step 4 still says lithium PENDING though the 10-13 USPS finding answers it, UN 38.3 summary for the cell not on file, cell voltage 3.7 vs 3.3 V wording, box presets unmeasured, Pirate Ship Woo connection unconfirmed. Public API: product 25 $24.99, purchasable false.
  29. 2026-10-30: Owner: "do everything you can". Done: SOP-044 v1.2 (step 4 lithium rule from the 10-13 USPS check, UN 38.3 summary to open items), deployed. Production writes refused by the auto-mode classifier, so wrote owner-run ~/embeddedera-store-prod/launch-cricket.sh payments|release|off (outside git): payments = load live keys from the 0600 files via stdin + enable gateway + hidden virtual $1 item EE-TEST-001; release = Cricket stock 5, _ee_listing_release live, max 4, trash the test item; off = kill switch. ERP shows 15 in Finished goods (stock item 283) vs owner's 5 ready: not changed, owner to confirm.
  30. 2026-10-30: Checkout 504 / slow cart after enabling the live gateway. Cart/checkout took 22-30 s (Caddy response_header_timeout 30s → 504); product/shop pages 0.01 s. Cause: the restricted key rk_live lacks Accounts Read (connected_account_read); Woo Stripe 11.0.0 WC_Stripe_Account::cache_account() does not cache a failed /v1/account, so every call on a cart page re-hits Stripe (~200 proxy connections in TIME_WAIT per load × ~0.2 s). Fix: owner adds Accounts Read to the key in the Stripe dashboard (no rotation). Gotcha for any future restricted key.
  31. 2026-10-30: Owner added Accounts Read to rk_live …ljZy → cart 0.2-0.5 s (was 25-30 s). $1 test order #29 paid (pi …2fkeOH succeeded, events delivered, pending_webhooks 0) but no tax. Stripe Tax API itself returns 6.75% (7¢) for Clayton 27520; store cart via Store API still $0 for NC and CA. Cause: the Stripe Tax for WooCommerce 2.1.0 plugin only accepts an sk_live_ key (Validate::validate_live_key_format), so it was never configured (plan finding 6, owner decision 5 = give it a full secret key). Owner to paste a standard secret key into WooCommerce → Settings → Stripe Tax.
  32. 2026-10-30: Stripe Tax live. Owner created a Full access sk_live key 'Stripe Tax plugin' (Vaultwarden), stored via launch-cricket.sh tax in ~/.config/stripe/tax-plugin.env (0600) and set in the plugin (mode live, txcd_99999999, woocommerce_calc_taxes yes). Store API cart $1: Clayton NC tax 7¢, Raleigh NC 7¢, Sacramento CA $0; cart 0.4-0.8 s. Gotcha: in Stripe's new key dialog only 'Full access' gives sk_live_; 'Full access except sensitive' gives rk_live_. Next: owner refunds #29, runs release.
  33. 2026-10-30: Cricket LIVE. Owner refunded #29, set Stripe payment methods to card only (express checkouts off), ran release. Public check: product 25 purchasable, 5 in stock, max 4 (qty 5 refused), qty 2 = $44.98, NC 1x = 24.99 + 8.95 USPS Ground Advantage + 2.29 tax (6.75% incl. shipping) = $36.23; test item gone. Still open: ERP 15 vs 5, Pirate Ship connected + box preset, UN 38.3 summary, no embeddedera.com link until first order ships.
  34. 2026-10-30: Owner: allow Cricket backorders (supersedes 10-07 'no backorders'). Added launch-cricket.sh backorders (set_backorders notify), owner to run. Follow-up: SOP-044 'Out of stock' exception and the shipping policy's 3-business-day promise assume no backorders.

HANDOFF 2026-10-30 (chat over budget; continue in a new chat)

State: Store LIVE for the Cricket since 2026-10-30. Live Stripe card payments (card only, express checkouts off), Stripe Tax live (sk_live key in ~/.config/stripe/tax-plugin.env, 0600), Cricket product 25 purchasable, stock 5, max 4, 2+ at $22.49, NC 1x = $36.23 incl. $8.95 shipping and $2.29 tax. $1 test order #29 refunded, test item removed. Store key rk_live …ljZy now has Accounts Read (without it cart pages took 25–30 s). Owner-run script: ~/embeddedera-store-prod/launch-cricket.sh payments|tax|release|backorders|off (outside git; production writes are refused for Claude by the auto-mode classifier). off = kill switch. Checks passed: Store API cart tests (tax NC 7¢/$1, CA $0; qty 5 refused; qty 2 = $44.98), webhook events delivered (pending_webhooks 0), cart 0.2–0.8 s, product page 200. Pending owner actions: 1. DONE 2026-10-30: backorders on, wording live. 2. DONE 2026-10-30: Shop page published (main 46fae4c). 3. Pirate Ship: WooCommerce Connected, box preset "Cricket – 1 unit". 4. ERP: EE-A-0003-A shows 15 in Finished goods (stock item 283) vs owner's 5 ready; owner to say which is right. 5. UN 38.3 test summary from the cell supplier. Open questions: the 6:51 AM Stripe "parameter_unknown POST /v1/payment_intents" in Workbench (payment still succeeded; check after a real order); configure-cricket.php / bootstrap-prod.sh don't hold today's DB-only settings (rebuild would lose them). Next action: confirm item 1 output, then do item 2 on "publish". - 2026-10-30: Backorders live (owner ran backorders): Store API shows '5 in stock (can be backordered)'; Cricket short description has the 2–3 week line; /shipping-policy/ has the Backorders section. Handoff items 1–2 done. - 2026-10-30: Store icon set (owner ran launch-cricket.sh icon: embeddedera-site public/icon-512.png → site_icon); favicon.ico and now point at embeddedera-icon (webp sizes). Pirate Ship: WooCommerce integration 'EmbeddedEra Store' enabled, import grid opens (no orders). Box preset still to measure. - 2026-10-30: Owner: add the two LiPo cells (EE-C-0184 100 mAh $5.95/5+ $4.99, EE-C-0197 320 mAh $6.95/5+ $6.00) to the live store. Built ~/embeddedera-store-prod/batteries.php + launch-cricket.sh batteries (published, visible, "Coming soon", stock 0, ERP 198/220 mapped, real-sample photos) and batteries-live <n100> <n320> (counted stock, release live). Gates before batteries-live: bin recount (both ERP "about 85"), loose-cell USPS rule + packing/mark in SOP-044, UN 38.3 summaries. - 2026-10-30: Pub 52 read for loose cells (details in store-battery-1s): surface only, sealed packaging, Wh marking on each battery, UN3480 mark + surface-only text, ≤5 lb. Batteries stay "Coming soon" until supplier gives Wh-marked, sealed cells + UN 38.3. Flag: 349.241c Wh marking applies to "all" Li-ion cells — check whether the Cricket's internal cell shows Wh. - 2026-10-31: Customers could not create accounts. Live sign-in page links to /create-account/ and /track-order/; both 404 on production (pages and woocommerce_enable_myaccount_registration were only ever set by the dev scripts configure-shop-help.php / configure-storefront.php; bootstrap-prod.sh skips them). Read-only crawl of 15 public pages: those two are the only broken links. Fix written as owner-run store-accounts.php (registration on, email login, customer-chosen password, both pages); server writes refused for Claude. After it runs: re-crawl, then owner makes one real account and deletes it. bootstrap-prod.sh still lacks this (rebuild would lose it). - 2026-10-31: Account sign-up fixed on production (owner approved "you run all that"): store-accounts.php run via pcompose wp eval-file → pages 34 create-account, 35 track-order, registration=yes (email login, chosen password, Turnstile). Verified: re-crawl 15 pages 0 broken; /create-account/ shows email+password+Turnstile form on a 390 px phone; track-order form present. Store mail enabled (ee_store_mail_enabled=yes, from orders@embeddedera.com, order + new-account emails default on). Health-check addition (all home + sign-in links 200, sign-up form present) tested from the Mac (passes now, fails on a 404); edit on the server refused for Claude, so owner applies it. Still to do: add the accounts settings to bootstrap-prod.sh; owner makes one real account on a phone and deletes it. - 2026-10-31: Health check now also checks every home + sign-in link returns 200 and /create-account/ has the sign-up form (owner applied; committed on production, src re-pinned; all 9 checks ok). First version failed falsely: curl | grep -q under pipefail exits 23 when grep stops early, fixed by reading the page first. Lesson: test health-check snippets under the script's own set -uo pipefail. - 2026-10-31: Accounts config now in bootstrap: scripts/prod/configure-accounts.php, mounted in wpcli, run after configure-policies (owner applied, committed on production, src re-pinned; replay printed "already existed" twice + registration=yes, so idempotent). Store vs site look: same colours/fonts; differences are header (two tall rows vs slim mono nav), menu items, body weight 300 vs 400, plain Woo shop page, no seasons. Theme rework waits on owner allowing Claude server writes to the store sandbox. - 2026-10-31: Store redesign to match embeddedera.com on the sandbox (dev.embeddedera.com), branch store-redesign in ~/embeddedera-store (3b10660, from sandbox): slim 44 px header (mark + mono "EmbeddedEra Store", mono nav Products/Support/Notes/About/Contact, compact search, Account/Cart), 72rem centred column, product cards (surface, 1rem radius, copper mono price), body weight 400, mono eyebrows/footer headings, pill buttons, dark notices. Feedback/Business Account/My orders moved out of the top bar (footer + Support menu). Checked desktop 1440 + phone 390: no sideways scroll, phone menu and both Products menus open. Not live: production deploy = cherry-pick onto production (functions.php differs there, expect a header conflict) after owner OK. Seasonal themes not ported. - 2026-10-31: Owner saw the redesign half-applied on dev (old header HTML, new CSS): Cloudflare HTML cache (rule 1, dev) + WP Super Cache served stale pages; my screenshots used ?nocache= and missed it. Cleared supercache/dev.embeddedera.com and purged Cloudflare by host (hosts:[dev.embeddedera.com], token works); plain URLs now serve the new header. Any theme deploy: clear both caches, then check the plain URL, not a cache-busted one. - 2026-10-31: Redesign round 2 (owner: "are we 100% sure it matches?"). Measured computed styles site vs store (diff.py): header row, logo, colours identical; fixed letter-spacing (TT25 -0.1px), search to site's quiet bordered mono box, site pill size, footer rebuilt as the site's centred mono stack (mark, socials, © line, store links row, policy row, Appearance), menu centred via 1fr|auto|1fr grid (.ee-header-right wrapper), wordmark hidden 701–1000 px. Commit on store-redesign. Deliberate differences: store keeps a visible search field and phone search/Products rows; no socials in the header; no seasonal strip/icon; site footer tagline omitted. - 2026-10-31: Redesign live on store.embeddedera.com (owner: "put it live"). Ported to production in ~/embeddedera-store-prod/work (095eb51, then 1b02adc search width, 4ea3e3f placeholder "Search products"): theme.css + mark.png checked out from store-redesign; header/footer markup applied, then 12 links to dev-only pages routed through ee_store_link (prod hides Help/FAQ/Business account, maps About/Contact/Notes/Feedback to embeddedera.com). src re-pinned (rollback: git -C ~/embeddedera-store-prod/src checkout --detach bed4f66 + clear caches). Cleared WP supercache + Cloudflare purge by host. Verified plain URLs: new header/footer on /, /my-account/, /create-account/, /cart/, no review banner; crawl 15 pages 0 broken; health check all ok; phone menu opens; no sideways scroll 390–1440; search finds Cricket by name and SKU. - 2026-10-31: Seasonal themes on the store test copy (store-redesign): scripts/sync-seasons.sh copies the site's inlined bootSeason + SEASONS table from https://embeddedera.com/ (maps --color-copper → --ee-season-accent/hover) and season-icons.svg into the theme; wp_head inlines it; header icon after the wordmark, 2 px strip under the header, accent in dark theme only (light keeps #a8682c). Hero scenes and greeting line not ported. Verified: site and store pick the same season on 17 dates (Playwright clock), ?season=off works, light theme keeps copper. Live deploy refused by auto mode without an explicit owner go — awaiting it; port = checkout assets from store-redesign + run seasons-theme.py on prod work, re-pin src, clear caches. - 2026-10-31: Seasons live on store.embeddedera.com (owner: "put the seasons live"): production a26fe56, src re-pinned (rollback 4ea3e3f), caches cleared. Verified on the live store: same season as embeddedera.com on 17 dates (0 mismatches), today = Halloween pumpkin + #ff8a1f accent, light theme keeps #a8682c, icon sprite 200, crawl 0 broken links, health check 9/9 ok. - 2026-11-01: Owner: show the stock count, keep selling past it, never mention backorder; email buyers if one ships late. Added launch-cricket.sh quiet-backorders and ran it: Cricket backorders=yes (no notify), short-description 2–3 week line removed, shipping-policy "Backorders" section replaced by "Delays" (email a new ship date, cancel for full refund). Verified public: "5 in stock", no "backorder" on product or policy page. - 2026-11-01: Owner: no quantity on the page either. woocommerce_stock_format=no_amount (store-wide, in quiet-backorders); public page now says "In stock" only, verified.

  • 2026-11-01: Fixed order-notification routing at owner request. Order #37 headers showed direct delivery to adiaz@ (orders@ was only From); live Woo settings were absent and defaulted to admin_email. Set explicit recipient orders@embeddedera.com for new_order, cancelled_order, failed_order; a separate fresh WP invocation verified all three enabled and resolving to the group. No mail resent; delivery to each group member has not been tested.
  • 2026-11-01: Incident (Claude). To read live shipping settings Claude ran pcompose --profile tools run --rm wpcli wp eval … twice (~16:47–16:50). The wpcli service's entrypoint is bootstrap-prod.sh, so this replayed the whole bootstrap: home/siteurl reset to http://127.0.0.1:8510, blog_public → 0, all configure-*.php replayed; the run crashed in an eval at "line 91". Effect: /cart, /my-account, /create-account 301 to store.embeddedera.com:8510, Store API permalinks 127.0.0.1. Cricket still purchasable. Prod DB writes refused for Claude, so the owner gets the fix. Shipping decision same day: Ground Advantage 7.95 + 1.00 * ([qty] - 1) (owner approved), not yet applied.
  • 2026-11-01: Incident resolved. Owner ran the home/siteurl → https://store.embeddedera.com + blog_public 1 SQL, redis FLUSHALL and supercache clear. Verified: health check 9/9 ok, /cart /my-account /create-account 200, permalinks public, no noindex meta, Store API cart NC qty1 = 24.99 + 8.95 + 2.29 = $36.23 (matches 10-30), qty2 = 44.98 + 8.95 + 3.64. Replay stopped at configure-policies line 91 ("Managed policy was edited in WordPress") so accounts/search/recommended/performance/cricket did not rerun; product-urls and storefront did (production-safe paths). Batteries are not on the store (404, not in Store API or sitemap); nothing in the replay touches them, so most likely launch-cricket.sh batteries was never run — confirm with owner. A policy page was edited in WP: configure-policies will refuse until reconciled.
  • 2026-11-01: Shipping $7.95 + $1.00/extra item (owner approved). production 08fec14 (bootstrap cost 7.95 + 1.00 * ([qty] - 1), Shipping policy price text dated Nov 1 + the Delays section quiet-backorders had added live, tests 21/21). Formula checked with WC_Eval_Math from the pinned Woo zip: 1→7.95, 2→8.95, 3→9.95, 4→10.95. The "policy edited in WordPress" refusal was our own quiet-backorders Delays edit, not a hand edit. Owner-run: launch-cricket.sh shipping-qty (re-pins src to production, sets the rate, accepts the Delays baseline, reruns configure-policies, flushes caches). Not yet run.
  • 2026-11-01: Shipping change live + batteries listed. Owner ran shipping-qty and batteries. src = 08fec14. Verified via Store API (NC): 1 Cricket ship 7.95 tax 2.22 total 35.16; 2 → 8.95 / 57.57; 4 → 10.95 / 107.72; Alaska no rate. /shipping-policy/ dated November 1, 2026, new price text, Delays kept, no "8.95"/"Backorder". EE-C-0184 $5.95 and EE-C-0197 $6.95 pages 200, "Coming soon", not purchasable. Health check 9/9.
  • 2026-11-01: Connector confirmed from the Cricket 008 BOM/schematic: BT1 Molex 53048-0210 (PicoBlade, 1.25 mm; the 1.27 mm parts are M1/J1). Owner: listings say "2-pin Molex PicoBlade, 1.25 mm pitch" + note "Fits the Cricket battery header (Molex 53048-0210)." (owner: say only what it fits, no "not compatible" line). Edited ~/embeddedera-store-prod/batteries.php (both cells); owner reruns launch-cricket.sh batteries (keeps stock/release). Branding fixes (photo backgrounds, pinout redraw, Cricket-style layout, shorter summary) proposed, awaiting owner OK.
  • 2026-11-01: Battery branding preview built (not applied): grey-background photos (Cricket hero tone, edge-mask recolor), on-brand PicoBlade pinout diagram (SVG, store palette), Cricket-style layout (intro, spec table, Connector tiles, Battery and safety, Shipping), short text without repeated price. Files in session scratchpad prev/; comparisons sent to owner. Open: is the 320 mAh polarity the same as the 100 mAh (needed to show the pinout there)?

Handoff 2026-11-01 (battery listing branding) — read this and continue

Done today: shipping $7.95 + $1/extra item live (prod 08fec14, verified carts 1/2/4); site-URL incident fixed (gotcha on map card: never pcompose run wpcli without --entrypoint wp); batteries listed "Coming soon"; connector text live on both: "2-pin Molex PicoBlade, 1.25 mm pitch" + "Fits the Cricket battery header (Molex 53048-0210)" (owner: never list what it is NOT compatible with). Source: ~/embeddedera-store-prod/batteries.php (owner runs launch-cricket.sh batteries; idempotent, keeps stock/release). Preview (not applied): ~/embeddedera-store-prod/battery-preview/ — compare-100mAh.png, compare-320mAh.png (live vs proposed), recolor.py (white → Cricket grey, edge mask), pinout.svg/png (on-brand PicoBlade diagram), build.py (proposed short text + Cricket-style description: intro, spec table, Connector tiles, Battery and safety, Shipping). Images live in the theme: src/themes/embeddedera/assets/battery-ee-c-0{184,197}-studio-*.png. Owner feedback 2026-11-01 (open): (1) unsure about the grey background — ask: keep white studio photos, or grey? (2) 320 mAh photos should be rotated 90° counter-clockwise so both batteries face the same way (cell horizontal like the 100 mAh). (3) Owner resent the 4 original phone photos (wood table, warm light): saved in battery-preview/originals/ (ee-c-0184-label/reverse, ee-c-0197-label/reverse). Owner "not sure this is better"; recommendation given: keep the studio images (cleaner, same look as each other), only rotate the 320 ones CCW, owner to choose white vs grey. (4) Is 320 mAh polarity same as 100 mAh (black left, red right, latch up)? Needed before showing pinout/polarity row on it. Next action: rotate 320 images CCW, rebuild preview with owner's background choice, send comparisons; on approval put new assets in the theme (production branch commit, owner deploys) and update batteries.php descriptions/short text; owner reruns launch-cricket.sh batteries; verify both live pages (curl + headless chrome screenshot google-chrome --headless=new --screenshot). - 2026-11-01: Battery preview v3: 320 mAh studio photos rotated 90° CCW (label horizontal, board left, same as 100 mAh) as battery-ee-c-0197-studio-{label,reverse}-v2.png; white and grey sets in ~/embeddedera-store-prod/battery-preview/v3/, side-by-side compare-white-vs-grey-v3.png sent to owner. Grey recolor leaves white specks between the red/black wires; recommended white. Waiting on owner: white or grey; 320 mAh polarity same as 100 mAh? - 2026-11-01: Owner: white backgrounds; 320 mAh has the same PicoBlade connector and pinout as the 100 mAh. Built: production 530748b + 8fe6373 (320 mAh photos rotated CCW as studio-{label,reverse}-v2, shared on-brand battery-picoblade-pinout.png 1600×1040 transparent corners, .ee-product-figures grid + .ee-product-specs table CSS, figures keep natural aspect since a 800/520 crop cut the 320 connector). batteries.php: spec table with Polarity + pinout on both cells, short text without price, one meter line replaces the 'pitch alone does not guarantee' paragraph. launch-cricket.sh batteries now re-pins src to production first and clears WP caches. Rollback: git -C ~/embeddedera-store-prod/src checkout --detach 08fec14, restore battery-preview/batteries.php.bak-2026-11-01, rerun batteries. Owner to run; then verify plain URLs at 390/1440 px (dark mode pinout, shop grid hero). - 2026-11-02: First real order shipped (#37, Paul Hale). Pirate Ship label $6.09 bought 14:03 EST; the Woo integration worked end to end: order Processing → Completed, "Completed order" + "Customer note" emails sent, tracking note added (SOP-044 step 6 proven). But the label is UPS (1Z… tracking), not USPS Ground Advantage as SOP-044 step 5 requires and as the customer's shipping line says; owner told to drop it at UPS, not USPS. Open: decide whether UPS Ground Saver is allowed (update SOP-044) or set Pirate Ship to USPS only; ERP sales order + orders@ close still to do. - 2026-11-02: Owner asked (06:47) for Woo order -> ERP stock sync via the Pirate Ship/Woo flow: pending order subtracts ERP stock, cancel adds it back, so ERP is never entered by hand. Design only; no implementation logged. TODO: decide where it lives (registry order sync is off for Cricket). - 2026-11-02: Owner: don't name a carrier; buy the cheapest ground label (USPS or UPS). production c8ad8be: method title 'Standard shipping', Shipping policy 'a ground service, USPS or UPS; we choose the carrier' (dated Nov 2, stale 'pending USPS confirmation' dropped), tests 21/21. Owner-run launch-cricket.sh shipping-name (backup launch-cricket.sh.bak-2026-11-02) applies it live; not yet run. SOP-044 v1.4 deployed (step 5 cheapest ground, drop-off matches carrier; UPS lithium basis 49 CFR 173.185(c)).