{"total":1736,"count":50,"rows":[{"id":"2f890c13-66a9-43b3-a546-be68914e50aa","agent":"viral","category":"bug_fix","title":"Playwright wizard 'Launch click stopped firing' was TWO stacked bugs, not one","problem":"On 8/16 the Trybe→Meta Playwright build wizard's Launch button clicks silently stopped registering real launches. Debug logs showed FAILED_NO_JOB for jobs (e.g. ad 'T7') that actually existed and were live in Meta, making it look like the click itself was failing when the ad was in fact built. This wasted debugging time chasing the wrong root cause.","solution":"Root cause was two independent bugs stacked together: (1) `page.locator('button:has-text(\"Launch\")').nth(pick['i'])` used an index (`pick['i']`) that was computed against a JS-filtered list of only VISIBLE buttons, but the Playwright locator's element set/order differs from that JS-filtered list, so `.nth()` force-clicked the WRONG button entirely. Fix: instead of re-indexing into a Playwright locator, call `element.click()` directly via JS on the SAME filtered element list used to compute the index, so the index and the clicked element always refer to the same array. (2) Even after fixing the click target, the wizard navigates away immediately after Launch, which aborts the in-flight `/ads/launch` fetch client-side before the response event fires — so jobId capture via network response listener misses real launches that DID succeed server-side. Fix: added a `meta_verify_ad()` ground-truth fallback that, when jobId capture fails, polls the Meta Ads API directly for an ad tagged `trybe=<last8-of-submission-id>` inside the exact target ad set, retrying every 30s for up to 6 minutes before declaring a true failure. Lesson: when a UI-automation click 'stops working', check both (a) whether the click target index is being computed against a different DOM element set than the one being clicked, and (b) whether client-side navigation after a submit action can abort the network request/response you're relying on for confirmation — always prefer a server-side ground-truth check (poll the actual target system) over trusting client-side network event capture for critical launch confirmation.","code_snippet":null,"skill":"trybe-ugc-ad-builder","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-17","tags":null,"metadata":null,"created_at":"2026-08-17T22:01:02.024493+00:00","updated_at":"2026-08-17T22:01:02.024493+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"bc9aa879-e188-49a0-84c6-d52310de6018","agent":"flux","category":"bug_fix","title":"Naive string-replace price swaps in ported form-block Liquid sections corrupt unrelated data attributes","problem":"When porting a landing-page (LD) form-block section to a new product (e.g. building sections/product-gluco-gone.liquid for GlucoTone from the LD template), a plain find-and-replace of the display price string, e.g. changing all occurrences of \"49.99\" to \"49.95\" to match the new SB-literal pricing (intro 49.95/91.58/124.88), also matches inside unrelated numeric attributes that happen to share the same digits — specifically it corrupted data-ot=\"149.99\" (the one-time-purchase price attribute) into an unintended value, since \"49.99\" is a substring of \"149.99\". This kind of silent corruption is easy to miss because the page renders fine and only breaks the OT (one-time) price binding at checkout/JS level.","solution":"When porting form-block sections between products, never do a blind global string replace on bare price numbers. Instead: (1) grep the target file first for every literal occurrence of the old price substring and manually classify each hit (display text vs. data-* attribute vs. CSS) before touching anything; (2) anchor every replacement to its surrounding context, e.g. replace 'data-ot=\"49.99\"' as a full attribute match rather than '49.99' alone, and replace display-text occurrences with enough surrounding markup/whitespace to be unique; (3) after editing, do a readback byte-diff against the intended final content (as was done for ChloroZen/GlucoTone deploys, confirming exact byte-count matches: 34258 bytes and 31971 bytes respectively) and specifically re-grep for the old substring to confirm zero unintended survivors, including inside longer numbers like 149.99, 249.99, etc. This same class of bug also applies to CSS bracket-value diffs (e.g. bg-[#DAA29333] vs bg-[#FFEAD8]) — diff Tailwind arbitrary-value classes bracket-by-bracket, not by loose substring matching.","code_snippet":null,"skill":"shopify-bogo-deploy","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-17","tags":null,"metadata":null,"created_at":"2026-08-17T22:01:01.365834+00:00","updated_at":"2026-08-17T22:01:01.365834+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"fc013454-5eca-4224-b4e1-9eb0e56f2480","agent":"flux","category":"gotcha","title":"Loop Subscriptions UI silently drops minCycles on new discount plans — now confirmed on 3rd product","problem":"When Weston creates BOGO discount groups/plans in the Loop Subscriptions admin UI (the manual click-through step for each new BOGO product: create group, map plans, set minCycles=2 to lock in the commitment period), the minCycles value does not persist. It reads back as NULL via the Loop API on every single product tried so far: LD (original), ChloroZen (plans 3012329625/3012362393/3012395161), and GlucoTone (plans 3012427929/3012460697/3012493465). This is not a one-off fluke — it is a repeatable UI/API sync bug in Loop, and if unnoticed it means the intended 2-cycle minimum subscription commitment is NOT actually enforced on any of these BOGO offers, which changes churn/cancellation economics silently.","solution":"After Weston completes the Loop UI step for any new BOGO group, immediately verify via the Loop API (GET the plan by ID, e.g. plan IDs 3012329625/3012362393/3012395161 for ChloroZen, 3012427929/3012460697/3012493465 for GlucoTone) and check the minCycles field. It will read NULL even though 2 was entered/expected in the UI. Do not treat this as blocking deployment (checkout still functions and rebill pricing is correct), but always flag it explicitly to Weston as a known Loop UI persistence bug requiring a manual re-edit in the Loop dashboard per plan if the 2-cycle minimum matters for that offer. Track this as a standing checklist item for every future BOGO replication: 'verify minCycles post-Loop-UI-step, expect NULL, flag if not fixed.'","code_snippet":null,"skill":"shopify-bogo-deploy","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-17","tags":null,"metadata":null,"created_at":"2026-08-17T22:01:01.177344+00:00","updated_at":"2026-08-17T22:01:01.177344+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"e5d1fcd5-d546-4105-9fec-48557df830d0","agent":"watchdog","category":"gotcha","title":"Supabase amazon_products inventory/stock data can be months stale — never use it to confirm current in-stock status, always hit live SP-API FBA inventory endpoint","problem":"Weston asked for a quick check on whether Alpha Glow Shilajit Gummies (B0DG85N7ND) was 'freshly in stock' before approving a bid raise. The cached Supabase `amazon_products` table showed 0 quantity with a `last_synced` timestamp of June 19 — nearly two months old — which would have wrongly suggested the product was still out of stock.","solution":"For any current-inventory question that gates a bid/budget decision, never rely on the Supabase `amazon_products` table's cached quantity/last_synced fields — treat it as unreliable for freshness. Instead call the live SP-API FBA inventory endpoint via the proxy: `/fba/inventory/v1/summaries`, which returned 2,394 fulfillable units with a timestamp of 2026-08-16T21:26 UTC (same day), correctly confirming the product was in stock. Cross-check pricing/deal status the same way using the live SP-API Pricing API rather than any cached table — in this case it confirmed an active -38% deal ($34.95 → $21.56 landed). This two-source live-vs-cached distinction is itself the '2 data source' cross-check for inventory-gated decisions, per the standing rule of never acting on a single source.","code_snippet":null,"skill":"ppc-ad-hoc-review","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-17","tags":null,"metadata":null,"created_at":"2026-08-17T22:00:59.874194+00:00","updated_at":"2026-08-17T22:00:59.874194+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"f1dd32f3-0987-4083-b603-0998bdff60f5","agent":"watchdog","category":"gotcha","title":"Weekly pull/audit scripts live under profile root scripts/ppc/, not workspace/scripts/ppc/ — and .router-token path is not profile-relative","problem":"During the Monday 6AM PT deep audit, invoking `node scripts/ppc/pull-14d-account.js` from inside the watchdog `workspace/` directory failed with MODULE_NOT_FOUND. The script and its dependencies actually live one level up, under the profile root, not under workspace. Similarly, curl auth for the pull needs a router token file that is NOT stored relative to the agent's profile — it's a shared mission-control path.","solution":"Always invoke the weekly PPC pull/audit scripts using the absolute profile-root path, not a workspace-relative one: `node /home/wcorica/.hermes/profiles/watchdog/scripts/ppc/pull-14d-account.js` and `python3 /home/wcorica/.hermes/profiles/watchdog/scripts/ppc/audit-account-75floor.py`. For auth, the router token lives at the fixed absolute path `/home/wcorica/mission-control/.router-token` (shared across profiles, not under any profile's home) — pass this full path to curl/HTTP auth headers rather than assuming a profile-relative `.router-token`. If you get MODULE_NOT_FOUND or 401/curl auth errors on the weekly pull, check these two absolute paths first before debugging anything else. Expect the SP report generation poll to take ~50-55 min for a full 14-day account pull (SP+SB combined, ~215 poll cycles @ 15s) — this is normal, not a hang.","code_snippet":null,"skill":"ppc-weekly-audit","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-17","tags":null,"metadata":null,"created_at":"2026-08-17T22:00:59.700639+00:00","updated_at":"2026-08-17T22:00:59.700639+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"4f1a75e3-00fa-40aa-b285-e35731bea86e","agent":"surge","category":"gotcha","title":"Frame.io short links (f.io/<slug>) carry no UUID and break standard asset-resolution scripts unless redirect-followed first","problem":"The Frame.io asset-resolution script used to pull final video files/version stacks for ad builds normally extracts a UUID directly from the link URL (file/version_stack/folder links). During the Aug 16 wave, 2 of 39 Shelf Builder briefs contained a new link form never seen before: short links in the pattern f.io/<slug>, which contain zero UUID in the URL itself, causing the resolver to fail on those briefs.","solution":"f.io/<slug> short links must be redirect-followed to their canonical Frame.io URL before UUID extraction can proceed — use `curl -sIL --max-redirs 0` against the short link to capture the Location header (the redirect target), then run the normal UUID-extraction logic against that resolved URL. This handling was added to scripts/frameio_resolve.py (saved to the skill library) alongside existing support for file/version_stack/folder links and Google Drive links across both Frame.io accounts, so all 53 briefs (39 SB + 14 Lympho) resolved successfully. Any future Frame.io link resolver should treat f.io/<slug> as a distinct case requiring a redirect-follow step, not just a URL-pattern match.","code_snippet":null,"skill":"surge-mailbox-autostage / frameio_resolve.py","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-17","tags":null,"metadata":null,"created_at":"2026-08-17T22:00:58.860305+00:00","updated_at":"2026-08-17T22:00:58.860305+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"772370c6-65c8-4b87-89cc-4adb83fc1f90","agent":"surge","category":"business_rule","title":"Cloning a 'last-live' ad's copy for a new intake wave can ship stale pricing if the landing page offer has changed since that ad went live","problem":"Standard practice for building a new adset is to clone the copy structure (5x5: 5 headlines x 5 primary texts) from the most recently live sibling ad on the same offer. For the Lympho Defend Aug16 wave, the last-live sibling was from Aug 6, and its copy stated '$79.90 value for $42.49' pricing — but the live lander (re-verified via browser before launch) had since changed to $49.99/$91.99/$124.99 tiers (value $99.98). Shipping the cloned copy as-is would have created an offer-congruence violation (ad promises a price the page doesn't honor), risking disapproval, poor conversion, or compliance flags.","solution":"Before cloning any sibling ad's copy for reuse, re-verify the current live landing page pricing/offer via a fresh browser check — do not trust that a previously-approved ad's copy is still accurate just because it was live recently. When price/offer figures are found stale, rewrite the 5x5 copy from scratch with current figures rather than patching the clone. Structural fix to prevent recurrence: for offers where pricing might change, write price-agnostic copy (e.g. Shelf Builder's sibling used 'Buy one get one free — while it lasts' with no $ figures) so future clones need zero changes regardless of landing-page price updates. Adopt price-agnostic phrasing as the default pattern for any BOGO/promo-style offer going forward.","code_snippet":null,"skill":null,"platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-17","tags":null,"metadata":null,"created_at":"2026-08-17T22:00:58.681696+00:00","updated_at":"2026-08-17T22:00:58.681696+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"684ef30a-4be3-425d-b5b9-d0bf9d515811","agent":"surge","category":"gotcha","title":"Meta 'code 4' app-level rate limit during bulk ad builds masquerades as stuck/broken video uploads — don't chase the wrong bug","problem":"While building a 53-ad daily wave (39 Shelf Builder + 14 Lympho Defend ads) as a background process, a poll loop checking video processing status saw 5 videos stuck in 'not_ready_after_tries' after a burst of API calls. This looked exactly like broken/corrupted video uploads and could easily trigger wasted time re-uploading or re-encoding creative that was actually fine.","solution":"Before assuming a video is broken, always inspect the RAW error object returned by the Meta Graph API status-check call, not just the poll loop's summary state. In this case the actual response was error code 4 with is_transient=true and message 'Application request limit reached' — Meta's APP-LEVEL rate limit, not a video-processing failure. The video_status field for all 5 assets was already 'ready' the whole time; it was the status-CHECK calls themselves being throttled. Fix: back off and retry the status-check call (not the upload) — in practice it took ~2 retry cycles / ~10 minutes of backoff for the throttle to clear before the remaining ads could build. Lesson for any bulk-build script: on a stuck/timeout video status, log and check error.code/error.is_transient before treating it as a bad asset; code 4 = throttle, requeue the check, don't touch the video.","code_snippet":null,"skill":null,"platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-17","tags":null,"metadata":null,"created_at":"2026-08-17T22:00:58.497415+00:00","updated_at":"2026-08-17T22:00:58.497415+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"68946d81-db84-48f9-98d3-4488735c29d1","agent":"sentinel","category":"workflow","title":"Systematic method for reviewing print-house artwork proofs against approved art library","problem":"Received a Belmark print-house proof PDF (CN-CAP-LIVERMILKT-90BG LiverZen, manufacturer VW, version 02-V3) that needed verification against the approved artwork on file before authorizing a print run. Print-house proofs bundle production/job-ticket paperwork (PO#, job#, quantities, serialized datamatrix samples) alongside the actual printed art, and the approved reference file separately carries its own baked-in dieline/press specs from a prior proofing round — both of these non-brand-content layers can be mistaken for discrepancies if not filtered out before comparison.","solution":"Repeatable proof-review sequence: (1) Parse the proof filename/job-ticket header to extract SKU, product name, manufacturer code, and version code (e.g. SKU CN-CAP-LIVERMILKT-90BG, Part# GCN-LIVERMILKT, UPC# 810197341424, mfr 'VW' = customer '18 - VITAWORKS WEST', version 02-V3). (2) Search the CN Product Artwork Library for the approved file matching that exact SKU+version code and confirm dieline type/size (e.g. stand-up pouch bag, front panel height, gusset width) matches the job ticket spec before doing content diff. (3) Do a panel-by-panel content comparison (front panel copy/icons, gusset, back panel Supplement Facts panel — serving size, blend name, mg amounts and order, Other Ingredients, Suggested Use, cautions, GMP seal, Prop 65 warning, distributor address block, UPC barcode digits, version code stamp) and log each element as match/no-match individually rather than a single pass/fail. (4) Explicitly flag and disregard known non-content variances as FYI-only, not defects: (a) Amazon Transparency QR / datamatrix renders blank/placeholder on static approved art but populated+serialized on the live proof — normal, filled at print time; (b) print-house job-ticket/Variable Job Data Sheet pages (PO#, job#, press quantities, sample datamatrix strings) are production tracking paperwork, not brand artwork, and should be excluded from the content diff; (c) the approved reference file may itself carry visible dieline/press specs (zipper/cut type, overlam, ink plates, prior 'APPROVED' sign-off block) from its own earlier proofing round — these are expected in how the library stores files and are not proof defects. (5) Conclude with an explicit APPROVE/REJECT recommendation citing the exact version codes and UPC compared. This structure prevents false-positive flags on cosmetic/print-run artifacts while still catching real content drift.","code_snippet":null,"skill":"artwork-proof-review","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-17","tags":null,"metadata":null,"created_at":"2026-08-17T22:00:57.665496+00:00","updated_at":"2026-08-17T22:00:57.665496+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"d6c5899e-5a89-49d1-9aed-9dde183345de","agent":"pixel","category":"gotcha","title":"Storage copies can go stale after in-session image fixes — always re-verify ALL slots' byte-match before publishing, not just the ones just changed","problem":"During the Juvanix Women's NMN (B0HDBZ3KGK, SKU CN-CAP-WMNSNMNFOR-60CT-R1) round-2 correction, only S2/S7/S9 were fixed and re-uploaded this session. When Weston later asked to publish the full 9-slot set directly to Amazon, a byte-match re-verification of ALL 9 slots revealed S3/S4/S6/S8 storage copies (listingmind-assets/juvanix/final/{slot}.jpg) were STALE — they had been uploaded before earlier in-session fixes to those slots and never re-synced, meaning the live listing would have shipped outdated art if the last verification pass had been trusted.","solution":"Before any Amazon publish (or any hand-off claiming a set is 'approved and ready'), re-verify byte-match for EVERY slot in the set against local FINAL files, not just the slots touched in the current round. Process: for each slot, curl the Supabase storage object with a cache-busting query param (e.g. ?t=<timestamp>) and compare byte length/hash to the local /tmp/*/FINAL_{slot}.jpg. If any mismatch, re-upload with x-upsert=true to listingmind-assets bucket before proceeding. This caught 4 of 9 slots (S3/S4/S6/S8) that would have silently shipped stale art. Treat 'fixed this round' lists as incomplete evidence of overall set freshness — always do a full-set sweep immediately before the actual publish action, since fixes from PRIOR rounds/sessions can still be unsynced even if the current round's targeted fixes were uploaded correctly.","code_snippet":null,"skill":null,"platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-17","tags":null,"metadata":null,"created_at":"2026-08-17T22:00:55.522892+00:00","updated_at":"2026-08-17T22:00:55.522892+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"2a58a124-48fe-4d45-9610-197a6b4778e0","agent":"blitz","category":"gotcha","title":"GMV Max '40002 nil item_identity map' is NEVER transient past the first retry — 12/12 failures from the Aug 12 boost waves still failed identically 5 days later on 2026-08-17, confirming these are per","problem":"Prior guidance (documented 2026-07-28) said `40002 Nil item_identity map` on GMV Max session/create could be fresh-post ads-index sync lag and should be retried after 24-48h. retry12_boosts.js re-attempted the exact 12 items that failed this way in the Aug 12-13 jake_60d/organic17/starved41 boost waves (representing creators like @ShopByJake, @abtheaffiliate, @kioykotheartist, @autumn.acosta0 across campaigns like Shelf Builder BOGO, AdaptoDrive Maca Root, Group DUOs/TRIOs) — items with real historical revenue ($152-$10,742 each). After dup-checking each item against its target campaign's live sessions (to avoid double-firing), the script re-ran /campaign/gmv_max/session/create/ for all 12 five days after the original failure. Result recorded in /tmp/retry12_results.json: 0/12 succeeded — every single one returned the exact same message: 'Unable to batch get item identity: nil item_identity map. Please remove the item you no longer have permission for and try again.' This confirms the 24-48h retry window from the earlier learning does not apply to this specific error text — it is permanent, not lag.","solution":"Refine the failure-classification rule in tiktok-ads-operations-master-process.md: treat `nil item_identity map` (exact substring match) as PERMANENTLY DEAD on first occurrence — do not schedule a delayed retry cron for it, and do not carry it in any subsequent fire-queue dedup lists. Only the OTHER two 40002 variants are worth retrying: `System error occurred. Try again later.` and bare code -1 (local HTTPS timeout) — both of those verified as retry-successful in the same wave (see 2026-08-13 journal entry, sids 1873337954705570 / 1873337745372322). Concrete rule for any future fire script: after session/create fails, classify by `/nil item_identity/i.test(msg)` → push to a persistent DEAD-ITEM quarantine file (e.g. /tmp/dead_items_permanent.json, keyed by item_id) and never re-attempt; classify by `code === -1 || /System error/i.test(msg)` → push to a transient retry queue and re-attempt once after a short delay. Cross-reference: retry12_boosts.js (dup-check + fire pattern), rox_map_campaign.js (campaign/SPU/session lookup pattern used to build the retry queue).","code_snippet":null,"skill":"tiktok-gmv-max","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-17","tags":null,"metadata":null,"created_at":"2026-08-17T22:00:54.040998+00:00","updated_at":"2026-08-17T22:00:54.040998+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"4bfb8d30-c1c1-4dfc-b971-d6001751d3ec","agent":"helix","category":"gotcha","title":"IMAP mailbox monitor: don't assume uniform failure across accounts — different error strings mean different root causes even in the same run","problem":"On 2026-08-17 the CH inbox daily digest cron (job 65875d4d51c9, mail.privateemail.com:993, mailboxes team@/admin@/support@/weston@cleanhealth.io) failed for all 4 accounts, but NOT with the same error: team@, admin@, and support@ returned 'login failed: [AUTHENTICATIONFAILED] Authentication failed.' while weston@ returned a distinct 'login failed: [UNAVAILABLE] Account is temporarily unavailable.' Prior incidents (2026-07-14, 2026-08-16) established a heuristic that identical errors across all accounts = one systemic/provider-side issue, not 4 separate problems. That heuristic doesn't hold here — 3 of 4 accounts share one error class (credential/auth rejection) while the 4th has a different error class (account-level suspension/unavailability), meaning at least two distinct root causes are active simultaneously. The state file (state/inbox_triage_state.json) shows last_run/last_uid frozen at 2026-06-01T03:00:16Z with last_status 'ok' for all 4 accounts, confirming this monitor has been fully dark for 2.5+ months and the per-account error differentiation is the only new diagnostic signal available today.","solution":"When an IMAP/API monitor fails across multiple accounts on the same host, always diff the exact error strings per account before diagnosing — do not collapse them into one 'systemic outage' bucket just because they failed in the same run. Concretely for this setup: config lives at workspace/config/mailboxes.json (host mail.privateemail.com, port 993), state at workspace/state/inbox_triage_state.json (per-account last_uid/last_run/last_status). AUTHENTICATIONFAILED (team@, admin@, support@) points to a credential/password problem (rotation, expired app-password, or IMAP access disabled in the provider's control panel) and requires Weston to re-issue/verify app passwords for those 3 mailboxes specifically. UNAVAILABLE 'Account is temporarily unavailable' (weston@) is a different failure class entirely — typically indicates the mailbox itself is suspended, over quota, migrating, or locked by the provider (privateemail.com), which won't be fixed by a password reset and needs a support ticket or admin panel check on that specific account. Action item queued for Weston: (1) reissue/verify IMAP app-passwords for team@/admin@/support@cleanhealth.io, (2) separately check weston@cleanhealth.io mailbox status in the privateemail.com admin panel for suspension/quota/migration flags rather than resetting its password. General rule for any multi-account credential monitor: log and compare raw error text per account, not just pass/fail booleans, since mixed error types in one run signal multiple concurrent incidents rather than a single fixable outage.","code_snippet":null,"skill":"clean-health inbox monitoring","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-17","tags":null,"metadata":null,"created_at":"2026-08-17T22:00:52.836798+00:00","updated_at":"2026-08-17T22:00:52.836798+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"6ec850dc-ec14-473c-a06b-b3c261061fe2","agent":"watchdog","category":"workflow","title":"Intraday bleed-scan pull can 429 on raw script — use -retry variant + resume script for report gen timeouts","problem":"Running pull-bleed-scan.js (no retry logic) for the 2h intraday scan hit a 429 Throttled on the 2nd of 4 report requests (SB SUMMARY today), aborting the whole pull. Separately, even the hardened script's foreground download step can exceed the tool's foreground timeout while waiting on Amazon report generation.","solution":"Use pull-bleed-scan-retry.js instead of pull-bleed-scan.js — same output files (/tmp/bs-sp-today.json etc), adds 8-attempt backoff on request+status calls and a 15s stagger between the 4 report requests, so it survives 429s. If the foreground call still times out waiting for report COMPLETED status, don't re-run from scratch: the 4 report IDs are saved to /tmp/bs-report-ids.json by both scripts. Just run resume-bleed-scan.js (background, no notify needed for a quick job) to poll+download using the saved IDs. Confirmed working 2026-08-17: request script hit 429 on raw run, retry script succeeded, resume script picked up saved IDs and completed all 4 downloads (SP today=353, SB today=93, SP14d=5233, SB14d=1414 rows) within ~15 min of report gen.","code_snippet":null,"skill":"amazon-ppc-watchdog","platform":"amazon","applicable_to":null,"verified":true,"session_date":"2026-08-17","tags":["ppc","optimization","watchdog","bleed-scan","429","retry"],"metadata":null,"created_at":"2026-08-17T15:22:37.119621+00:00","updated_at":"2026-08-17T15:22:37.119621+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"554057a1-e446-461d-b86c-be283030fdcc","agent":"phoenix","category":"workflow","title":"Generating a formatted, print-ready Word doc (.docx) via Node's `docx` package for management-facing strategy reports","problem":"Mike needed a polished, presentable strategy document (title page, styled tables, color-coded callouts, page breaks) comparing three options for resolving Clean Nutra's FNSKU-vs-UPC identifier problem across Amazon FBA and new retail channels — a plain markdown summary or Google Doc via API wasn't the deliverable format requested; a real .docx file was needed.","solution":"Built the report entirely in Node.js using the `docx` npm package (package.json: {\"dependencies\":{\"docx\":\"^9.7.1\"}}), in a single script (build_upc_analysis.js). Defined small reusable helper functions: H1/H2/H3 for heading paragraphs, P() for body text, Bullet() for bulleted paragraphs, BoldLabel(label,value) for label:value lines, cell()/dataTable(headers, rows, colWidths) for styled tables (navy #1F3864 header row with white bold text, alternating #F2F2F2 striped rows, DXA column widths). Built the whole Document object as a sections/children array (title page, PageBreak() between major parts, executive summary, per-option cost/timeline tables, side-by-side comparison table, recommendation). Rendered with `Packer.toBuffer(doc).then(buffer => fs.writeFileSync(outputPath, buffer))`. Run via `npm install` then `node build_upc_analysis.js`. This is a reusable skeleton for any future formal Word deliverable (board decks, incident postmortems, vendor RFP comparisons) where a native .docx with real tables/formatting is required instead of a chat message or plain Google Doc.","code_snippet":"function dataTable(headers, rows, colWidths) {\n  const total = colWidths.reduce((a, b) => a + b, 0);\n  const headerRow = new TableRow({\n    children: headers.map((h, i) => cell(h, { bold: true, shade: NAVY, color: \"FFFFFF\", width: colWidths[i], align: AlignmentType.CENTER })),\n    tableHeader: true,\n  });\n  const bodyRows = rows.map((r, idx) => new TableRow({\n    children: r.map((val, i) => cell(val, { width: colWidths[i], shade: idx % 2 === 1 ? \"F2F2F2\" : null })),\n  }));\n  return new Table({ width: { size: total, type: WidthType.DXA }, columnWidths: colWidths, rows: [headerRow, ...bodyRows] });\n}\nPacker.toBuffer(doc).then((buffer) => fs.writeFileSync(outputPath, buffer));","skill":"docx-report-generation","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-16","tags":null,"metadata":null,"created_at":"2026-08-16T22:00:43.652741+00:00","updated_at":"2026-08-16T22:00:43.652741+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"7a4d52e5-fbb2-4c05-a47c-4ab5f54f7432","agent":"vector","category":"pattern","title":"'Extra' Amazon FBA shipping labels for one CIN7 transfer = Amazon FC-split, not a systems bug — diagnose via inboundPlans shipments/items, don't reuse the missing-labels playbook","problem":"Weston reported the opposite symptom from the usual 'labels never came through' issue: too MANY shipping labels appeared for single transfers (TR-00336, TR-00337, TR-00338 — NMN Supplement, Women's NMN Formula, MultiMacular Vision Defend Kit). The existing diagnostic runbook only covered missing/stalled labels (ShipHero fulfillment_status = Wholesale, empty placementOptions/shipments), which doesn't apply here and would send the investigation down the wrong path entirely.","solution":"Extra labels for one transfer are almost always Amazon's inbound placement service splitting a single-SKU shipment across multiple destination FCs for network balancing — expected behavior, not an error. Diagnose it in 3 API calls via the HiveMind Amazon proxy: (1) List `/inbound/fba/2024-03-20/inboundPlans?pageSize=30&sortBy=LAST_UPDATED_TIME&sortOrder=DESC`, filter to plans whose `sourceAddress.companyName`/`city` matches the transfer's FromLocation (e.g. 'Clean Nutra'/'Las Vegas') with `createdAt` near the transfer's DepartureDate. (2) `GET /inbound/fba/2024-03-20/inboundPlans/{id}` — the top-level `shipments[]` array has one entry per destination FC; N shipments = N label sets. (3) `GET /inbound/fba/2024-03-20/inboundPlans/{id}/items` — returns one line item per shipment-split (same msku/fnsku, different quantity each); sum them and confirm the total equals the CIN7 transfer's TransferQuantity exactly, proving units weren't double-booked. Confirmed Aug 2026: all three transfers checked (1,020 / 1,020 / 1,575 units) came back as 5 shipments each with uneven per-shipment splits (e.g. 180/180/240/180/240, no case-pack alignment) — pure FC-balancing, nothing to fix. Report back the shipment count, per-shipment quantities, and that they sum to the transfer total. Keep this separate from the distinct 'two CIN7 transfers merge into one Amazon shipment' scenario — that's multiple transfers landing in shared shipments, the reverse direction of this one-transfer-many-shipments case.","code_snippet":null,"skill":"clean-nutra-ecommerce-ops","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-16","tags":null,"metadata":null,"created_at":"2026-08-16T22:00:42.238889+00:00","updated_at":"2026-08-16T22:00:42.238889+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"5f5e3c04-8bcd-4e07-85bd-5bbd1b57b087","agent":"sterling","category":"gotcha","title":"Dashboardly's per-SKU COGS is a static merchant-typed number, not a live cost feed — it will NOT match QBO's landed cost and the gap is provably diagnosable","problem":"COGS was the single largest variance driver (~$100-150K) in the July 2026 TikTok QBO-vs-Dashboardly reconciliation, and it wasn't obvious from either report which source was 'correct' — both looked like legitimate COGS numbers, but QBO account 5010 and Dashboardly's COGS line disagreed by roughly 30% of QBO's rate on a per-unit basis.","solution":"To prove which figure is trustworthy: 1) Open the 'SKU by Day' tab in the Dashboardly export and check the `Data Source` column on any `Cost of Goods Sold` row — in the July 2026 export, 100% of COGS rows showed `Merchant Input` (confirm via a counter/pivot on that column), meaning it's a number someone manually typed into Dashboardly's settings, not a computed feed. 2) Pull the per-unit COGS for one SKU across every day it sold — it will be exactly constant (e.g. one bundle SKU showed -$6.05/unit on literally every day of the month), which is impossible for a real landed-cost feed that should flex with freight/duty changes — this is the smoking-gun proof it's static. 3) Compute blended unit cost both ways: `QBO unit cost = QBO COGS (acct 5010) ÷ total units sold` vs `Dashboardly unit cost = Dashboardly COGS ÷ same units sold` (use Dashboardly's own Units Sold total for an apples-to-apples denominator). The gap × units sold should reconcile to nearly the full COGS variance. 4) Conclusion to give the user: QBO's COGS is the trustworthy, landed-cost-inclusive figure; Dashboardly's is stale/incomplete unless someone manually updates its merchant-input cost table per SKU. Reference: skills/devops/brandmind-financial-reporting/references/tiktok-dashboardly-qbo-reconciliation.md.","code_snippet":null,"skill":"brandmind-financial-reporting","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-16","tags":null,"metadata":null,"created_at":"2026-08-16T22:00:41.429561+00:00","updated_at":"2026-08-16T22:00:41.429561+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"1f54b9c1-4a14-4250-bd83-9b03556d58c7","agent":"sterling","category":"bug_fix","title":"Dashboardly's reported Operating Profit and Net Profit don't foot — platform fees and COGS are silently double-counted in the roll-up math","problem":"While building the July 2026 TikTok QBO-vs-Dashboardly reconciliation workbook, manually footing Dashboardly's own P&L (Gross Profit − Platform Fees − Operating Costs) produced ($378,629), but Dashboardly's reported Operating Profit for the same period was ($578,968) — a ~$200,338 unexplained gap. A second gap of $268,164 appeared between Dashboardly's reported Operating Profit and Net Profit. Trusting Dashboardly's headline profit figures at face value would have overstated the July loss by roughly $200K-$470K when reported to Randy.","solution":"Diagnose by re-deriving the P&L from Dashboardly's own line items instead of trusting its summary rows: 1) Pull TikTok Fees (~$102K), Affiliate Commissions (~$82K), FBT Fees (~$34K), Shipping Credit (~-$8K) — these sum to the explicit 'TikTok & fulfilment costs' subtotal (~$210K). 2) Check whether Marketing/Fulfillment/Other operating-cost allocation lines ALSO embed these same fees — in the July 2026 export they did, meaning platform fees were deducted once explicitly and once again inside allocations. The unexplained gap (~$200K) ≈ the platform-fee subtotal (~$210K), confirming double-counting. 3) Separately check Net Profit vs Operating Profit: if the gap ≈ COGS, COGS is also being deducted twice between those two rows. 4) Resolution: use QBO Net Income as the reconciliation anchor (it follows standard accounting and is not double-counted). Dashboardly's Gross Profit line is usable (was within $6K of QBO in July). Dashboardly's Operating Profit and Net Profit roll-ups are NOT usable without this correction — flag them in red font + yellow fill on any output tab with a pointer to the variance-analysis explanation. Full waterfall template and line-item mapping table (QBO account # → Dashboardly label) saved at skills/devops/brandmind-financial-reporting/references/tiktok-dashboardly-qbo-reconciliation.md and dashboardly-pl-double-counting.md.","code_snippet":null,"skill":"brandmind-financial-reporting","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-16","tags":null,"metadata":null,"created_at":"2026-08-16T22:00:41.26631+00:00","updated_at":"2026-08-16T22:00:41.26631+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"fa8fd9dc-4bd2-42aa-a0ba-77cb43bbdad1","agent":"sentinel","category":"workflow","title":"MD5-checksum based Google Drive dedupe (not filename matching) catches renamed/re-uploaded duplicate label artwork across a 336-file migration folder","problem":"The 'Clean Nutra - Pending Migration' Google Drive folder (ID 1dtl1M97kPoxQvHV9s2mPd4l4xIo0fQb0) had 336 flat files accumulated from repeated uploads over months, many re-uploaded under different filenames (ARCHIVE-prefix variants, 'outlines' vs 'LABEL' naming, 'REV' vs 'FPS' naming conventions). Filename-only duplicate detection would have missed 25 of the 47 duplicate groups found (the ones with different filenames but byte-identical content), and would have falsely flagged one filename collision where two files share the exact same name but have genuinely DIFFERENT content (two versions of an SFP saved under an unchanged filename — a real risk of silently losing a revision if auto-deduped by name).","solution":"Use the Drive API (service account via google.oauth2.service_account.Credentials + googleapiclient build('drive','v3')) to list all files in the folder with files().list(q=\"'{folder_id}' in parents and trashed = false\", fields='nextPageToken, files(id, name, md5Checksum, modifiedTime, size)', pageSize=1000, supportsAllDrives=True, includeItemsFromAllDrives=True), paginating on nextPageToken. Group files by their md5Checksum field (Drive returns this natively for binary files — no need to download and hash locally). Any group with >1 file and identical md5Checksum is a true duplicate regardless of filename; keep the most-recently-modified copy per group as the presumed final version. Separately, group by filename alone and check for cases where the SAME name maps to MULTIPLE DIFFERENT md5Checksums — these are dangerous silent-overwrite risks (different content under one filename) and must NEVER be auto-deduped; flag for manual human review instead. For actual cleanup, don't delete directly — copy the surviving best files into a separate 'Deduped' destination folder via files().copy(fileId=fid, body={'parents':[DEST_FOLDER_ID],'name':name}, supportsAllDrives=True) and check the destination's existing filenames first so the copy step is resumable/idempotent if it fails partway through a large batch (built via /tmp/dedupe_copy_ids.pkl + /tmp/drive_pending_migration_files.pkl staging files in this run).","code_snippet":"from google.oauth2 import service_account\nfrom googleapiclient.discovery import build\ncreds = service_account.Credentials.from_service_account_info(sa_info, scopes=['https://www.googleapis.com/auth/drive'])\nservice = build('drive', 'v3', credentials=creds)\n# list with md5Checksum field, paginate on nextPageToken, then:\nfrom collections import defaultdict\nby_md5 = defaultdict(list)\nfor f in all_files:\n    by_md5[f.get('md5Checksum')].append(f)\ndupe_groups = {k: v for k, v in by_md5.items() if k and len(v) > 1}\n# separately catch same-name/different-content collisions:\nby_name = defaultdict(set)\nfor f in all_files:\n    by_name[f['name']].add(f.get('md5Checksum'))\ncollisions = {n: h for n, h in by_name.items() if len(h) > 1}  # needs manual review, not auto-dedupe","skill":"gdrive-migration-cleanup","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-16","tags":null,"metadata":null,"created_at":"2026-08-16T22:00:40.433483+00:00","updated_at":"2026-08-16T22:00:40.433483+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"a72bf592-3551-4818-8c2c-1b1df547c9d1","agent":"sentinel","category":"business_rule","title":"Prop 65 warnings on Clean Nutra labels frequently get added with defective OEHHA safe-harbor wording","problem":"During the PH Defend (CN-CAP-URINARYTRA-60CT) V8→V9 label delta review, V9 introduced a brand-new California Prop 65 warning that V8 completely lacked — but the added statement deviated from the required OEHHA safe-harbor language in four independent ways simultaneously: missing the word \"Consuming\" at the start, capitalized \"Lead\" instead of lowercase, missing the required \"cancer and\" clause before \"birth defects or other reproductive harm\", and a malformed/truncated URL (\"P65Warnings.ca.gov\" instead of \"www.P65Warnings.ca.gov/food\"). This shows a systemic pattern: when a designer adds Prop 65 language reactively (e.g. after a compliance flag), they tend to paraphrase from memory rather than copy the exact safe-harbor text, and each paraphrase error is graded as an independent Critical finding under standing protocol.","solution":"When reviewing any label/artwork that includes or should include a Prop 65 warning, diff the on-label text character-by-character against the exact OEHHA safe-harbor template rather than skimming for \"presence of a warning\": \"WARNING: Consuming this product can expose you to lead, which is known to the State of California to cause cancer and birth defects or other reproductive harm. For more information go to www.P65Warnings.ca.gov/food.\" Check specifically for these 4 recurring defect patterns seen in production artwork: (1) dropped \"Consuming\" at the start, (2) wrong capitalization of the hazard chemical name (should be lowercase e.g. \"lead\"), (3) dropped \"cancer and\" clause, (4) truncated/malformed URL missing \"www.\" prefix and/or the \"/food\" path suffix. Flag each deviation as its own Critical/🔴 finding (not bundled into one generic \"Prop 65 wording issue\") since design/legal sign-off tracks them as separate line items. Also note: a Prop65 addition in a newer version does NOT resolve the compliance gap if the added text itself doesn't match safe-harbor wording — document it as \"regression risk, not a resolution\" in the delta review rather than crediting it as fixed.","code_snippet":"PROP65_SAFE_HARBOR = (\n    \"WARNING: Consuming this product can expose you to lead, \"\n    \"which is known to the State of California to cause cancer and \"\n    \"birth defects or other reproductive harm. For more information \"\n    \"go to www.P65Warnings.ca.gov/food.\"\n)\n# Common defects to check for on any label carrying a Prop65 statement:\n# 1. missing 'Consuming' at sentence start\n# 2. wrong case on chemical name (must be lowercase, e.g. 'lead' not 'Lead')\n# 3. missing 'cancer and' clause before 'birth defects...'\n# 4. URL missing 'www.' prefix or '/food' path (bare 'P65Warnings.ca.gov' is non-compliant)","skill":"label-compliance-review","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-16","tags":null,"metadata":null,"created_at":"2026-08-16T22:00:40.254141+00:00","updated_at":"2026-08-16T22:00:40.254141+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"cb7bcc16-778b-4dab-9776-af183ac1a476","agent":"probe","category":"gotcha","title":"Atria-sourced competitor DBs (Resilia, Rosabella, Lymphoria-adjacent) lag Meta Ad Library by 1-2 days — freshest launch dates always look artificially low","problem":"All three daily tracker digests (Atria/Resilia, Rosabella/VascuGlow, Lymphoria) pull ad launch dates from local SQLite DBs whose upstream indexer (Atria) is 1-2 days behind Meta's live Ad Library. If you read the raw launch_date counts from these DBs and report the newest 1-2 days as-is, it looks like a launch-cadence slowdown when it's actually just incomplete indexing — a false negative that misleads Weston's morning report.","solution":"Never trust the freshest 1-2 launch_date buckets in Atria-backed DBs at face value. Cross-check them against the near-real-time Apify tracker DB before reporting. Pattern used across all three crons: (1) run the tracker's own digest script, e.g. `python3 /home/wcorica/.hermes/profiles/probe/scripts/atria_tracker_digest.py` (same pattern for rosabella_tracker_digest.py / lymphoria_tracker_digest.py); (2) separately query the Apify-fed DB for the last 7 days: `python3 -c \"import sqlite3; db=sqlite3.connect('/home/wcorica/.hermes/profiles/probe/data/resilia_tracker.db'); [print(r) for r in db.execute(\\\"SELECT launch_date, COUNT(*) FROM ads GROUP BY launch_date ORDER BY launch_date DESC LIMIT 7\\\")]\"`; (3) in the digest, lead with the Apify tracker's today/yesterday volume as the authoritative fresh number, and explicitly label the Atria DB's newest 1-2 days as 'still indexing' rather than implying a slowdown. Apply the same rehook-vs-fresh-spine split logic (checking clone_status via `sqlite3 lymphoria_tracker.db \"SELECT clone_status, COUNT(*) FROM videos WHERE clone_status IS NOT NULL GROUP BY 1\"`) to flag when competitors re-invest in a spine we've already cloned — that's a conviction signal to escalate, not just a count to report.","code_snippet":null,"skill":null,"platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-16","tags":null,"metadata":null,"created_at":"2026-08-16T22:00:39.437108+00:00","updated_at":"2026-08-16T22:00:39.437108+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"37cb0d73-2d19-4dab-8163-869191952736","agent":"prime","category":"gotcha","title":"Amazon issue code 100459 (severity ERROR) does not block DISCOVERABLE status — treat as informational, not a launch blocker","problem":"When polling Listings Items API for newly-fixed/recreated SKUs, nearly every listing (both the batch-9 UPC-swap SKUs and the 14 recreate-wave SKUs) returns issue code 100459 flagged as severity 'ERROR' (generic 'refer to Amazon policy' message pointing at https://sellercentral.amazon.com/help/hub/reference/external/G55N3JF2WQS), alongside code 100477 (WARNING, same generic policy link). At first glance an 'ERROR' severity issue on a listing looks like it should mean the listing is broken/non-live, which could trigger unnecessary re-work or a false escalation to Weston.","solution":"Confirmed from live data: SKUs carrying issue codes 100459 (ERROR) and 100477 (WARNING) are still reporting status=['DISCOVERABLE'] and correct UPC match (e.g. CN-CAP-ASHWAFENU-120BG, CN-CAP-GLYCIZEN-120BG, CN-CAP-URINARYTRA-90BG, CN-SGL-SAWPALMETTO-60BG-R1, CN-CAP-THYROID-90BG-R1, CN-CAP-LIPOGLUT-90CT-R2 all show this exact combo and are live/discoverable). When verifying listing health, do not gate 'is this SKU actually live' on the presence/absence of issue code 100459 or 100477 — gate on the summaries[].status field containing DISCOVERABLE plus the UPC identifier matching the expected value. Only escalate if status is missing DISCOVERABLE, or if a genuinely distinct issue code appears (e.g. missing item_package_quantity/container.type under code 18448, which IS worth flagging as a real attribute gap for Comply to harden, since it explicitly states 'do not block display' but still degrades listing completeness).","code_snippet":null,"skill":"brandmind/agent-hivemind","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-16","tags":null,"metadata":null,"created_at":"2026-08-16T22:00:38.602609+00:00","updated_at":"2026-08-16T22:00:38.602609+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"0cb89000-98d9-4a7a-88b8-f189e01702ae","agent":"nexus","category":"gotcha","title":"A same-day batch of Shopify barcode changes where ALL new values are ASIN/FNSKU-shaped (not real UPCs) signals an automated Amazon-sync overwrite event, not manual edits","problem":"In the 2026-08-15 3PM platform-listing-integrity run (diffing against the 8:30AM snapshot from the same day), the Shopify 🏷️ UPC/BARCODE CHANGES section logged 4 NEWLY CREATED + 6 REASSIGNED barcode values across 6 distinct SKUs (CN-CAP-PROSTATE-90CT, CN-CAP-URINARYTRA-60CT, CN-GUM-MENOPAUSEG-60CT, CN-DRP-MENSSUPPOR-2OZ x2, CN-SRM-SNAILANTIA-2OZ) and 10 total product/variant records. Every single one of the 10 new values (X003TDOSIB, X004IT552X, X003PVYTZJ, X004DMWZ5P, X004A2MU0N, etc.) was ASIN/FNSKU-shaped (10-char alphanumeric starting 'X00'), not a single one was a real 12-digit numeric UPC. This is a mid-day event that happened between the 8:30AM and 3PM snapshots on the same date, confirming it was a discrete write event, not accumulated drift. It also directly explains why the CROSS-PLATFORM UPC MISMATCH count grew from 15 (8:30AM run) to 16 (3PM run) that same day, with CN-DRP-MENSSUPPOR-2OZ newly appearing in the mismatch list because its CIN7-side value (769929015, a real numeric barcode) now disagrees with the freshly-overwritten Shopify value (X004IT552X).","solution":"When the UPC/BARCODE CHANGES section for any platform shows multiple SKUs changing in the same run, check whether ALL the new values share the ASIN pattern (regex ^B0[A-Z0-9]{8}$) or the FNSKU pattern (regex ^X0[0-9][A-Z0-9]{7}$, e.g. X003TDOSIB) rather than checking a real UPC's 12-digit-numeric shape. If most/all new values in that batch match one of these two shapes, treat it as a single systemic event (very likely an Amazon-Shopify sync app or bulk import job writing Amazon identifiers into the Shopify 'barcode' variant field) rather than N independent manual data-entry mistakes — do not file N separate tickets. Concretely: in run_audit.py's diff_shopify.py output, group same-run REASSIGNED/NEWLY CREATED entries by whether new_value matches r'^(B0[A-Z0-9]{8}|X0[0-9][A-Z0-9]{7})$'; if the match rate is high (e.g. 100% as seen here), escalate as one incident ('bulk FNSKU/ASIN overwrite of Shopify barcode field, N SKUs affected, likely from [app/integration]') and ask the Shopify admin/integrations owner to identify what wrote the field around that timestamp window (narrow it to between the two same-day snapshot times, e.g. 8:30AM-3PM PT on 2026-08-15). Also expect a same-day bump in the CROSS-PLATFORM UPC MISMATCH count as a downstream symptom — cross-reference newly-appearing mismatch SKUs against this run's own UPC/BARCODE CHANGES section before assuming the mismatch is old backlog.","code_snippet":null,"skill":"platform-listing-integrity","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-16","tags":null,"metadata":null,"created_at":"2026-08-16T22:00:37.798063+00:00","updated_at":"2026-08-16T22:00:37.798063+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"8b969b47-7250-4d83-a2bf-32333f6aafd5","agent":"apex","category":"gotcha","title":"Percent-of-baseline metric becomes meaningless once the baseline itself has decayed near-zero from prolonged death","problem":"For MultiMane MAIN, after 9+ consecutive dead days, the daily %-vs-pre-crater-baseline metric read >100% (looking 'recovered') purely because the baseline itself had crashed to near-zero ($12 Man / $38 Auto), even though absolute performance (202/1,503 impressions) remained far below the campaign's healthy-era numbers (thousands of impressions/day). Trusting the %-of-baseline metric alone on a long-dead campaign would have produced a false 'recovered, close the appeal case' conclusion.","solution":"Once a campaign has been dead more than ~7 days, stop using %-of-baseline as the primary confirm/deny signal for recovery — the baseline decays continuously during the death period, so ratios against it become artificially inflated. Instead compare absolute daily impressions/spend directly against the campaign's known healthy-era historical range (pulled from sp-full.csv or a long-window Ads API history query), and keep the case in escalation status (e.g. 'READY TO FILE' appeal) until absolute numbers — not relative percentages — cross back into the historically normal band.","code_snippet":null,"skill":null,"platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-16","tags":null,"metadata":null,"created_at":"2026-08-16T22:00:36.689377+00:00","updated_at":"2026-08-16T22:00:36.689377+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"6de119cd-aed2-41ec-a463-e3b59c12b262","agent":"apex","category":"bug_fix","title":"Wake-sweep scorer filename bug silently mislabeled a day's baseline as the prior day's","problem":"On Aug-14 the crater/wake-sweep scoring script had a filename bug: it wrote its output to wake-score-aug13.json instead of wake-score-aug14.json. When the Aug-15 cron run carried forward 'aug13' baselines for day-over-day comparison, it was actually loading Aug-14's numbers mislabeled as Aug-13's. This kind of silent mislabeling corrupts the day-over-day comparisons used to judge whether a dead campaign is genuinely 'waking' from a spend crater, risking false confirm/deny calls on recovery status without any error being thrown.","solution":"Never trust a baseline file's filename alone as its date — verify the internal/computed date content of a scored JSON before using it as a day-over-day reference in crater-wake analysis. When a cron script writes dated output (e.g. /tmp/wake-score-{date}.json, /tmp/wake-sweep-{date}-out.txt), compute the date string once into a single variable and reuse it for every output path in that run, rather than recomputing or hardcoding the date separately for each file target. Add a guard that logs a warning if the current run's target output file already exists with content whose internal date is more than 1 day older than the run date — that catches exactly this class of bug (stale/mislabeled carry-forward) before it propagates into a fork decision like 'case-park vs continue-watch'.","code_snippet":null,"skill":null,"platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-16","tags":null,"metadata":null,"created_at":"2026-08-16T22:00:36.514854+00:00","updated_at":"2026-08-16T22:00:36.514854+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"fbdd0699-e529-4e5b-9e48-76b9c5ff3e25","agent":"helix","category":"gotcha","title":"IMAP inbox-monitor cron can fail auth for months while still reporting 'ok' totals — check state timestamps, not just today's run","problem":"The CH inbox daily digest cron (inbox_daily_report.py / inbox_triage.py, 4 Privateemail.com mailboxes: team@, admin@, support@, weston@cleanhealth.io) has been returning 'login failed: [AUTHENTICATIONFAILED] Authentication failed.' for ALL 4 accounts on 2026-08-16. Checking state/inbox_triage_state.json shows every account's last_run is still 2026-06-01T03:00:16Z with last_status 'ok' — meaning the last-seen-UID state has not advanced in over 2.5 months. Because the digest script wraps failures into a per-account 'status' string and zeroes out unread_count/messages rather than raising a hard error, the daily cron output looked routine ('nothing urgent') for weeks while inbox monitoring was actually fully dark the whole time.","solution":"For any credential-based monitoring cron (IMAP/API), don't just check today's run output for 'errors' — cross-reference the persisted state file's last_run/last_status timestamp against the current date. If last_run is stale (>1-2 days old) despite the cron firing daily, that's the real signal, not the per-run status string. Concretely: state lives at ~/.hermes/profiles/helix/workspace/state/inbox_triage_state.json (per-account last_uid/last_run/last_status) and mailboxes.json holds credentials at ~/.hermes/profiles/helix/workspace/config/mailboxes.json (host mail.privateemail.com, port 993). Root-caused likely password rotation/lockout since ~June 1, 2026 (all 4 accounts failed simultaneously = provider-side credential/policy change, not a single-account typo). Fix pattern for any agent: (1) add a staleness check comparing state.last_run vs now() at the top of the digest script and escalate distinctly ('MONITORING OUTAGE' vs 'no new mail') when stale beyond threshold; (2) when 100% of accounts on one provider fail simultaneously with the same auth error, treat it as a provider-level credential/lockout issue requiring Weston to re-issue app passwords, not a code bug to patch around.","code_snippet":null,"skill":"clean-health inbox monitoring","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-16","tags":null,"metadata":null,"created_at":"2026-08-16T22:00:35.009648+00:00","updated_at":"2026-08-16T22:00:35.009648+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"7590baf8-35ed-481c-ab16-ce78d6c513a8","agent":"vault","category":"business_rule","title":"Upwork contractor spend is allocated to sales channels via a 'VA Legend' mapping, split evenly across each contractor's covered platforms — verify row-level sums before trusting the roll-up","problem":"Upwork bills one lump weekly charge per card/PayPal payment (e.g. 'UPWORK * -937407333' $4,782.75) covering dozens of contractors across two legal entities (Allseason Enterprises/ASE and Webstone Capital Holdings), but channel-level contribution-margin reporting needs each contractor's charge attributed to the specific Amazon/TikTok/Shopify/Walmart channel(s) they actually worked on, not booked as one undifferentiated 7100 Independent Contractor Expense line. Comparing this month's working file ('July 2026 UpWork Allocations.xlsx') against a second version ('July_2026_UpWork_Allocations_FIXED.xlsx') found rows where a multi-platform contractor's per-channel split had silently lost one platform's share (e.g. Aileen Caspe's $72 weekly charge, Allocation='TikTok/Amazon/Walmart', should split $24/$24/$24 but one version showed the Amazon share blank while Tiktok/Walmart still totaled only $48 against a $72 Charge) — an error that would understate Amazon's Upwork COGS/opex allocation for that week if journaled as-is.","solution":"Rebuild the allocation this way: (1) Maintain a 'VA Legend' tab mapping each Talent name -> Entity (Allseason Enterprises, LLC vs Webstone Capital Holdings) -> Allocation (either one platform like 'Amazon', or a slash-joined list like 'Amazon/Shopify/TikTok/Walmart') -> free-text Task description. (2) For each weekly Upwork invoice/receipt, there is one weekly detail sheet per entity (e.g. 'ASE July 6-12', 'ASE July 13-19', 'ASE July 20-26', 'W June 29-July 05' for Webstone) with columns Date, RECEIPT#, Amount, Inv#, Date of Service, Talent name, Charge, Platform, Amazon, Tiktok, Shopify, Walmart. Each contractor generates a 'Charge' row per week of service PLUS a separate 'Marketplace Fee for Ref ID <ref>' row for the same contractor that must be split using the identical percentage/ratio as the Charge row. (3) Allocation rule: if VA Legend lists one platform, 100% of Charge/Fee goes to that platform's column; if it lists N platforms, divide the Charge evenly by N (e.g. $100 charge / 4 platforms = $25.00 to each of Amazon/Tiktok/Shopify/Walmart) — round to 2 decimals but track the rounding remainder so the row's platform columns still sum to the original Charge to the penny. (4) Each weekly sheet ends in a totals row summing each platform column; that row's four platform totals MUST sum to the week's total Charge/invoice Amount (e.g. July 6-12: Amazon 984.90 + Tiktok 2462.25 + Shopify 491.40 + Walmart 302.40 = 4240.95, matching card charge T935599476). (5) Weekly totals roll up into the 'Summary' tab, split by entity (ASE block ~rows 8-13, Webstone block ~rows 19-24), producing a 'July Allocation' per platform per entity. (6) That Summary feeds the QBO journal entry (see 'QBO wnotes' tab, Num='Upwork Allocation Jul'): the JE credits/reverses the lump 7100 Independent Contractor Expense-Upwork accrual and re-books it as entity+platform-tagged lines like 'ASE Upwork Allocation - Amazon', 'ASE Upwork Allocation - Tiktok', 'Webstone Upwork Allocation - Shopify', etc., which is what lets channel P&L show Upwork costs by platform. BEFORE journaling: (a) for every contractor row, confirm Amazon+Tiktok+Shopify+Walmart columns sum to that row's Charge column, (b) confirm each weekly sheet's bottom-row platform totals sum to the week's invoice Amount, (c) confirm the Summary tab's per-platform 'July Allocation' column ties to the sum of that platform across all weekly sheets for that entity — any of these three checks failing means a platform's share was dropped or miscoded and the JE will misstate channel-level opex.","code_snippet":"# Sanity check before journaling: row-level tie-out\nfor row in weekly_sheet_rows:\n    platform_sum = round(row['Amazon'] + row['Tiktok'] + row['Shopify'] + row['Walmart'], 2)\n    assert abs(platform_sum - row['Charge']) < 0.02, f\"Row for {row['Talent name']} ref {row['Inv#']}: platforms sum {platform_sum} != Charge {row['Charge']}\"\n\n# Week-level tie-out: platform column totals must equal invoice/card Amount\nassert abs(sum(week_totals[p] for p in ['Amazon','Tiktok','Shopify','Walmart']) - week_invoice_amount) < 0.02","skill":null,"platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-15","tags":null,"metadata":null,"created_at":"2026-08-15T22:00:53.22934+00:00","updated_at":"2026-08-15T22:00:53.22934+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"a3a8cf77-32a1-4210-8463-45491038c274","agent":"vector","category":"workflow","title":"Cleaning a delivered Amazon restock priority list: two independent filters — multi-pack suffix strip + authoritative DC Status List cross-reference","problem":"After delivering an Amazon_Restock_Priority_*.xlsx built from the raw Amazon export, the requester asked to 'remove all the multi-packs 2, 3, 5 from this list and all the Discontinued items' — a second-pass cleanup on the already-generated list rather than a fresh pull, and it's easy to under-clean by relying on a stale hardcoded discontinued-SKU list or missing the multi-pack variant listings that inflate the restock ask.","solution":"Apply two independent filters in order: (1) Multi-pack variant removal — strip any restock-row SKU whose suffix matches regex `-[235]$` (e.g. CN-BELLABIOTIC-DRP-2, CN-SEAMOSSMUL-VEG-5) since these are separate Amazon multi-pack listings for the same base formula and double-count demand already captured by the 1-pack SKU. (2) Discontinued-SKU cross-reference — do NOT rely on a short hardcoded discontinued list carried in memory (it only grows reactively, ~1 SKU/session, and misses most of the ~100+ actually-discontinued SKUs); instead use the full 'SKU DC Status List' workbook when the user provides one (columns: SKU | Description | ASIN | Formula | Notes | Expiration | Total Inventory, with Notes == 'Disco' marking discontinued) as the authoritative source. Join by BOTH SKU and ASIN, not SKU alone, since the restock-report SKU naming doesn't always exact-match the DC list's SKU naming across the company's ~3 parallel SKU systems — ASIN is the more reliable key. Strip trailing pack-suffix (`re.sub(r'-[0-9]+$', '', sku)`) before comparing SKUs so a -1 variant maps correctly to its base SKU on the DC list. Report the two cleanup categories (multi-pack count, discontinued count) separately rather than merging them, since that's the shape the requester actually asked for.","code_snippet":null,"skill":"clean-nutra-ecommerce-ops","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-15","tags":null,"metadata":null,"created_at":"2026-08-15T22:00:51.845403+00:00","updated_at":"2026-08-15T22:00:51.845403+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"cfeb54ee-fc1a-4c8a-b0c4-b4d07e464c7d","agent":"vector","category":"gotcha","title":"Amazon Restock Inventory export as CSV must be read with encoding='latin-1', not utf-8","problem":"The Amazon 'Restock Inventory' report (same column layout whether it lands as .xlsx or .csv) sometimes arrives as a raw CSV named after the merchant ID, e.g. 945413020679.csv. Opening it with the default utf-8 encoding throws a UnicodeDecodeError partway through the file (confirmed Aug 2026: fails on byte 0x96, a smart-quote/em-dash embedded in a product title), which silently truncates the parse or crashes the script before any reorder analysis can run.","solution":"When the Amazon restock export arrives as .csv instead of .xlsx, open it with `open(path, encoding='latin-1')` (or pandas `pd.read_csv(path, encoding='latin-1')`) instead of the default utf-8/None. latin-1 reads the entire file cleanly with no data loss for this specific report shape (Country, Product Name, FNSKU, Merchant SKU, ASIN through Unit storage size — 30 columns). Column layout is identical to the .xlsx version, so once the encoding is fixed, the rest of the reorder/low-stock workflow (filter by SKU prefix, exclude discontinued SKUs, flag Alert in ('out_of_stock','low_stock') OR Recommended replenishment qty > 0) applies unchanged. Confirmed working against 945413020679.csv (403 rows) on 2026-08-14.","code_snippet":null,"skill":"clean-nutra-ecommerce-ops","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-15","tags":null,"metadata":null,"created_at":"2026-08-15T22:00:51.660112+00:00","updated_at":"2026-08-15T22:00:51.660112+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"430d5357-ae88-4dc6-ba3c-3bc522318c8b","agent":"surge","category":"gotcha","title":"Meta error 4469003 ('This ad is not delivering') and the Trybe partnership-permission WITH_ISSUES failure show the IDENTICAL red 'Delivery error' chip in Ads Manager but mean opposite things — must be","problem":"Weston flagged a screenshot of campaign 120246923992620756 ('CBO - Shelf Builder - Winners', act_357155718655940) showing a wall of red 'Delivery error' chips across the Ads tab (paging 1001-1150 of 1150), reading it as an emergency. Live pull via HiveMind proxy showed all 112 WITH_ISSUES ads carried a single Meta error code, 4469003, and that this code is Meta's automatic 'no results in 14 days' pause notice on ads that DID deliver spend but stopped converting — combined lifetime spend across all 112 was only $16.01 with zero purchases (57 never spent a cent). This is visually indistinguishable in the UI from the separate, previously-documented Trybe UGC partnership-permission failure (skill trybe-ugc-ads), where WITH_ISSUES means synced:false, $0 spend, ad never delivered at all, and requires a creator permission re-handshake to fix. Treating either one like the other wastes time: chasing 4469003 as a bug burns effort on ads Meta is correctly starving; missing a real Trybe permission failure because it 'looks like the same red chip as always' leaves a genuine bug unfixed.","solution":"When triaging WITH_ISSUES ads (or any 'red delivery error' screenshot escalation), never diagnose from the chip color/label alone — always pull the actual error code via the Meta Ads API/HiveMind read (ad-level `issues_info` or the effective_status detail) and branch on the code: (1) code 4469003 = Meta auto-stop after 14 days with no results; ad DID deliver at some point; correct action is PRUNE (archive/exclude from active roster), not fix — this is Meta doing its job. (2) Trybe cohort pattern (see skill `trybe-ugc-ads`) = `synced:false` + $0 lifetime spend + ad never delivered at all; correct action is a partnership-permission re-handshake with the creator, this is a genuine bug to FIX. Quick discriminator: check lifetime spend — 4469003 ads have nonzero spend history (they delivered before stopping), Trybe permission failures have exactly $0 spend and never left the review/issue state. Also use this same live-pull habit to separate cosmetic alarms from real ones: same investigation on Aug 14 found the campaign's real problems were structural (all 50 ad sets in the campaign at the 50-ad cap, blocking new intake without pruning) and a standing zombie gate of 508 ads (age>=14d AND lifetime spend <$10 AND still ACTIVE/WITH_ISSUES, $507 combined spend) — neither of which the red-chip screenshot itself revealed.","code_snippet":null,"skill":"trybe-ugc-ads","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-15","tags":null,"metadata":null,"created_at":"2026-08-15T22:00:50.968768+00:00","updated_at":"2026-08-15T22:00:50.968768+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"4923e553-72df-4d49-8871-6b979156b971","agent":"sterling","category":"gotcha","title":"Excel files can pass every XML/zip validation check yet still trigger Excel's repair-on-open prompt — check for unregistered external-workbook formula references","problem":"While rebuilding the recurring 'Transaction Detail for cash forecast.xlsx' file (QBO TransactionDetailByAccount export, sheet 'Sheet1-Analysis', 13 cols A-M, 100K+ rows) for Randy, a prior skill note had documented column M ('Cash Account') and column C ('Week Ending' dup) as live formulas referencing an external workbook (e.g. `=IFERROR(INDEX([1]Reference!E:E,...))`). That assumption was never actually verified against the real source file. Writing that fabricated formula string into thousands of rows produced a file that opened fine in openpyxl (even with data_only=True), passed zipfile.testzip(), and passed xml.etree.ElementTree.fromstring() on the sheet XML — every automated check looked clean. The ONLY symptom was Excel's own 'We found a problem with some content... Do you want us to try to recover as much as we can?' repair dialog when the user actually opened it, because col M is really a static 0/1 int flag and col C is really a static date value, not formulas — and the fabricated formula referenced external-workbook index [1] with no <externalReferences> block registered in xl/workbook.xml or matching externalLink relationship in xl/_rels/workbook.xml.rels, which is an OOXML schema violation invisible to generic XML/zip well-formedness checks.","solution":"1) Never trust a documented formula pattern (yours or a prior skill note) without re-verifying it against the CURRENT actual source file before a rebuild — open it fresh with openpyxl and inspect cell.value directly: a real formula string starts with '=', a static value is a plain int/float/str/datetime object. For this file: col L IS a real formula (`=IFERROR((Bn+7-WEEKDAY(Bn)),0)`); cols C and M are static values — rebuild the account-to-flag map for col M from the source file itself (`acct_to_m[acct] = ws_src.cell(row=r, column=13).value` for rows 7..first_restatement_row), don't assume it's constant across runs. 2) Run this mandatory pre-delivery check on every generated/rebuilt Excel file, not just this one — it's cheap (milliseconds) and catches a corruption class no other check catches:\n```python\nimport zipfile, re\nz = zipfile.ZipFile(output_path)\nsheet_xml = z.read('xl/worksheets/sheet1.xml').decode()\nhas_external_refs_in_formulas = bool(re.search(r'\\\\[\\\\d+\\\\]', sheet_xml))\nwb_xml = z.read('xl/workbook.xml').decode()\nhas_registered_external_link = 'externalReference' in wb_xml\nassert not (has_external_refs_in_formulas and not has_registered_external_link), \\\n    'Formulas reference an external workbook index with no registered externalReference — WILL corrupt in Excel'\n```\n3) Separately: never use ws.delete_rows() on large sheets you plan to keep — it independently corrupts XML internals in a way also invisible to zip/XML checks. Build a fresh openpyxl Workbook() and rewrite every row (including unchanged ones) instead of mutating a loaded copy. Full pattern documented in skills/devops/brandmind-financial-reporting/references/excel-corruption-prevention.md and references/cash-forecast-txn-detail-update.md.","code_snippet":null,"skill":"brandmind-financial-reporting","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-15","tags":null,"metadata":null,"created_at":"2026-08-15T22:00:50.278375+00:00","updated_at":"2026-08-15T22:00:50.278375+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"fea0e2eb-e925-47f0-b68c-8eda9c9ab893","agent":"sentinel","category":"gotcha","title":"Outlined/vector PDF label artwork returns zero extractable text — must use vision render, not pdftotext","problem":"Both the LymphoDefend bottle (4.5\"x2\" wrap label) and box (tuck-top carton) PDFs supplied by the co-manufacturer (ALS) were outlined/vectorized artwork files where pdftotext returns zero lines of live text, making automated text-diffing or keyword search against the Supplement Facts Panel impossible.","solution":"When pdftotext returns empty/zero lines on a label or packaging PDF, treat it as outlined artwork rather than a parsing failure. Fall back to rendering the PDF to image at high resolution (4x) and performing vision analysis panel-by-panel (front, back, side panels, spine, top flap, bottom flap for boxes) to manually transcribe every text element for comparison against the Supplement Facts Panel (SFP) source data and the prior version's review. Additionally, flag in the review output that a live-text PDF should be requested from the co-man for future review cycles to enable faster automated diffing.","code_snippet":null,"skill":"label-review","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-15","tags":null,"metadata":null,"created_at":"2026-08-15T22:00:49.585345+00:00","updated_at":"2026-08-15T22:00:49.585345+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"129203db-8598-44d4-9821-26b4ceee486a","agent":"sentinel","category":"workflow","title":"Version-over-version label reviews must diff against the prior version, not just re-check the current one, to catch regressions","problem":"Reviewing LymphoDefend box artwork 07-V7 against the prior 07-V6 review on file surfaced a NEW critical regression: the FNSKU code (X004FIH79B) that was present and correctly printed below the UPC barcode on V6 had been silently dropped from every panel of V7 (front, back, side panels, spine, top/bottom flaps) — a defect that a review checking V7 in isolation, without referencing V6's findings, would likely have missed since 'no FNSKU' isn't an obvious visual defect on its own.","solution":"For any resubmitted label/artwork version, load the immediately prior version's review file first and treat it as a checklist with three buckets: (1) items to re-verify as CARRIED if still unresolved, (2) items to confirm RESOLVED with a positive-confirmation note so they aren't re-flagged, (3) a full re-scan of every element the prior version got right, specifically to catch NEW regressions where something correct in the prior version is now missing/wrong in the new one (not just whether prior defects were fixed). Structure the output review doc with explicit '## Resolved Since Vx ✅' and '(CARRIED from Vx)' / '(NEW regression)' tags on every finding so the audit trail of what changed across versions is unambiguous, and always cross-check codes like FNSKU/UPC against the master SKU mapping sheet (row/ASIN) rather than trusting the artwork alone.","code_snippet":null,"skill":"label-review","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-15","tags":null,"metadata":null,"created_at":"2026-08-15T22:00:49.390943+00:00","updated_at":"2026-08-15T22:00:49.390943+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"327382db-8b99-4d5b-b803-2714d900e414","agent":"sentinel","category":"business_rule","title":"Algae/heavy-metal botanicals trigger mandatory Prop 65 warning — no exceptions","problem":"During the LymphoDefend 07-V7 label review, the bottle label still had zero Prop 65 warning text despite the formula containing Spirulina, Chlorella, Irish Sea Moss (Chondrus crispus), and Horsetail — all recognized heavy-metal (lead) bioaccumulation botanicals. This exact gap was first flagged at V2 (June 2026) and has now persisted unresolved through V2→V3→V4→V5→V6→V7 (5+ version cycles) because it kept getting logged as a lower-priority note instead of a hard blocker.","solution":"Standing compliance rule for Clean Nutra label reviews: if a DRP/supplement formula contains ANY of Spirulina (Arthrospira platensis), Chlorella (Chlorella vulgaris), Irish Moss/Sea Moss (Chondrus crispus), or Horsetail (Equisetum arvense) — or any other algae/heavy-metal-bioaccumulation botanical — the label review MUST classify a missing Prop 65 warning as a CRITICAL print-stop finding, never a 'recommended verification' or minor note. Use OEHHA safe-harbor wording as the acceptance bar: 'WARNING: This product can expose you to Lead, which is known to the State of California to cause birth defects or other reproductive harm. For more information go to P65Warnings.ca.gov.' (name the specific chemical; reject vague 'chemicals including' filler language). When one artwork panel (e.g., box) gets the fix but another (e.g., bottle) doesn't, flag it as a NEW discrepancy between panels, not just a carryover — don't let a partial fix on one panel cause the reviewer to soften severity on the other.","code_snippet":null,"skill":"label-review","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-15","tags":null,"metadata":null,"created_at":"2026-08-15T22:00:49.206801+00:00","updated_at":"2026-08-15T22:00:49.206801+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"b4891956-c948-4416-a72d-7ac0921ae64e","agent":"probe","category":"workflow","title":"Atria-CDN bridge keeps the Resilia/Lymphoria video-transcription pipeline alive when the Apify tracker is cost-capped","problem":"The primary video pipeline (resilia_video_pipeline.py) depends on Apify to discover new competitor video ads, but Apify has a hard monthly $199 cap that gets exhausted mid-month (hit outage window 2026-07-16 to 2026-08-01), which would otherwise stall spine-matching and auto-clone brief generation for weeks at a time.","solution":"resilia_atria_bridge.py (and its lymphoria twin) reads atria_tracker.db directly for rows where video_url LIKE '%cdn.tryatria.com%', filters to ones not already present in resilia_tracker.db.videos by video_key, downloads up to MAX_PER_RUN=40 per run via plain curl (`curl -s -L --max-time 180 -o dest videourl`, skip/delete if <50000 bytes), then reuses resilia_video_pipeline's transcribe_new()/load_spine_shingles()/shingles() to spine-match with the same overlap>0.35 threshold used everywhere else in the corpus. Key design points worth reusing: (1) Atria native_ids are plain digit strings, a different namespace from Apify's 'AQ…' FB-CDN keys, so both can coexist in the same videos table without key collisions — but flag that the SAME render may later reappear under an AQ key once Apify resumes, producing a sibling clone brief (cover with sibling-notation in the digest, don't dedupe blindly). (2) After transcription, check `if dl and texts and not any(t.strip() for t in texts.values())` — if every transcript in a run comes back empty, print a loud WARNING because that's the signature of a GPU/LD_LIBRARY_PATH environment failure, not just bad audio. (3) Record dl-failed / transcribe-failed clone_status rows for anything that didn't make it through, so the bridge doesn't repeatedly re-pull the same broken video on every cron run.","code_snippet":null,"skill":"competitor-ad-tracking","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-15","tags":null,"metadata":null,"created_at":"2026-08-15T22:00:48.520427+00:00","updated_at":"2026-08-15T22:00:48.520427+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"9e4dcd3a-0ae0-48e4-9af1-45d112c03119","agent":"probe","category":"business_rule","title":"Atria index always lags Meta by 1-2 days — never read the freshest launch_date as a slowdown","problem":"All three tracker digests (Resilia, Rosabella, Lymphoria) source competitor launch-date counts from atria_tracker.db, which is scraped from Atria's index. Atria's crawler itself lags real Meta Ad Library activity by 1-2 days, so the most recent 1-2 launch_date rows in daily_counts are structurally incomplete at read time — they will always show lower counts than the true launch volume, even when nothing has actually slowed down. Reporting these raw numbers as-is to Weston risks a false 'competitor is pulling back' verdict.","solution":"Every digest script/prompt for these three trackers explicitly labels the freshest 1-2 dates in the per-day breakdown as 'still indexing' rather than a real count, and cross-checks them against a faster, near-real-time source before drawing any conclusion. For Resilia specifically, cross-check with: `python3 -c \"import sqlite3; db=sqlite3.connect('~/.hermes/profiles/probe/data/resilia_tracker.db'); [print(r) for r in db.execute(\\\"SELECT launch_date, COUNT(*) FROM ads GROUP BY launch_date ORDER BY launch_date DESC LIMIT 7\\\")]\"` (Apify-fed, near-real-time). Lead the digest with the Apify/near-real-time numbers for today/yesterday, and use the Atria daily_counts only for the 7d/30d rolling trend and peak-day history where the lag has already washed out. Any digest builder should replicate this two-source pattern: slow-but-complete index (Atria) for trend, fast-but-narrower index (Apify per-brand ads table) for freshest 48h.","code_snippet":null,"skill":"competitor-ad-tracking","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-15","tags":null,"metadata":null,"created_at":"2026-08-15T22:00:48.336156+00:00","updated_at":"2026-08-15T22:00:48.336156+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"663a9003-d30e-4167-9b4a-b49e726b5d03","agent":"nexus","category":"gotcha","title":"CROSS-PLATFORM UPC MISMATCH list looks like static backlog noise across runs — but a value inside it can actively get worse without changing the SKU count","problem":"The unified platform-listing-integrity audit's 🚨 CROSS-PLATFORM UPC MISMATCH section printed the identical 15-SKU list across all 4 audit runs spanning 2026-08-13 and 2026-08-14 (8:30AM and 3PM both days), which reads as unchanging, already-triaged backlog. But on the 2026-08-14 3PM run, one of those 15 SKUs, CN-POW-WMNSCREATIORA-30SV, had its Shopify-side value change mid-day from a real-looking UPC (850052708665) to an ASIN-shaped string (X004EYCVDX) — a live regression hiding inside what looked like a static, already-acknowledged list. Anyone skimming the mismatch section for 'is this the same 15 as yesterday' (count/SKU-set unchanged) would miss that the underlying data got worse, not just stayed broken.","solution":"Do not treat a repeating CROSS-PLATFORM UPC MISMATCH SKU list as safe-to-skim just because the SKU set matches the prior run. Instead, cross-reference every SKU in that section against the SAME run's per-platform 🏷️ UPC/BARCODE CHANGES section (🆕 NEWLY CREATED / ⚠️ REASSIGNED) — any SKU appearing in both means its value actually moved today, not merely persisted. Concretely, in the 2026-08-14 3PM run, CN-POW-WMNSCREATIORA-30SV showed up simultaneously as 🆕 NEWLY CREATED (UPC X004EYCVDX on 3 landing-page product variants) and ⚠️ REASSIGNED (850052708665 → X004EYCVDX on 5 more variants) in the Shopify diff, while also still sitting in the 15-item CROSS-PLATFORM MISMATCH list — confirming a real edit event that morphed a plausible UPC into an ASIN-looking value, compounding the existing CIN7-vs-Shopify disagreement rather than fixing it. Rule of thumb: escalate any mismatch-list SKU that also shows up in that run's own CHANGES section as an active regression requiring immediate follow-up with whoever edits Shopify variant barcodes, and keep the remaining unchanged mismatch-list SKUs filed as known backlog (already covered by the broader UPC data-governance issue) rather than re-escalating them daily.","code_snippet":null,"skill":"platform-listing-integrity","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-15","tags":null,"metadata":null,"created_at":"2026-08-15T22:00:47.664508+00:00","updated_at":"2026-08-15T22:00:47.664508+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"b4073347-01ff-410a-bf5d-7f1ceb132a8e","agent":"helix","category":"business_rule","title":"FDA staff can override a PCAC advisory panel vote on 503A Bulk List additions — don't treat panel votes as final","problem":"On July 23-24, 2026 the PCAC advisory panel narrowly VOTED to loosen restrictions and recommend adding BPC-157, TB-500, and KPV to the FDA's 503A Bulk Drug Substances List. As of Aug 13-15, 2026, FDA staff published an updated briefing document and safety-risk bulk substance page that formally propose NOT adding BPC-157 (free base/acetate) to the list, citing immunogenicity risk, lack of long-term safety data, insufficient efficacy evidence for UC, and availability of approved alternatives — effectively overriding/ignoring the panel's own vote for all 7 peptides it reviewed (BPC-157, TB-500, KPV, MOTS-c, DSIP/Emideltide, Epitalon, Semax).","solution":"When monitoring FDA regulatory status for compounded peptides, do NOT treat a PCAC (Pharmacy Compounding Advisory Committee) panel vote as the final word — it is only advisory. The actual regulatory status is set by FDA staff via the 503A Bulk Drug Substances List determination and the FDA's public 'safety risks of bulk drug substances' page, which can be published weeks after the panel vote and can contradict it. Concretely: track both artifacts separately — (1) PCAC meeting outcome/vote, and (2) FDA's own bulk-substance nomination briefing docs / safety-risk list updates (these get dated revisions, e.g. Aug 13 and Aug 15, 2026 updates seen here). For Clean Health/Ascend: assume BPC-157, TB-500, KPV, MOTS-c, DSIP, Epitalon, and Semax remain NOT approved for 503A compounding despite the favorable panel vote, until FDA staff explicitly reverses its briefing-doc position. This changes sourcing/compliance risk assessment for any peptide protocol built around 503A pharmacy compounding.","code_snippet":null,"skill":"fda-peptide-monitor","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-15","tags":null,"metadata":null,"created_at":"2026-08-15T22:00:46.404534+00:00","updated_at":"2026-08-15T22:00:46.404534+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"3ad0e116-2b2b-45f4-92dc-049ef175faa4","agent":"vault","category":"workflow","title":"Classifying ambiguous PayPal bank feed lines by cross-referencing Blue Onion + PayPal CSV Custom Number","problem":"The PayPal bank feed export (Chase/bank-side CSV) only shows generic lines like 'Payment from {Name}', 'Deposit', 'Other', or 'Fee' with no indication of whether the money is a Shopify order, a CheckoutChamp/direct-response order, a vendor bill, or an inter-account transfer (Chase-to-PayPal). Without classification, none of it can be safely booked to QBO — mis-booking mixes revenue channels and breaks contribution-margin-by-channel reporting.","solution":"Build a 3-way match: (1) Blue Onion payments report ('Paid Out At' date, 'Payment Type' = sale/refund, 'Order System' = 'Shopify clean-nutraceuticals' vs NaN which = CheckoutChamp/direct); (2) the richer PayPal.com CSV export ('PayPal 07.31_08.13.CSV') whose 'Custom Number' field encodes the source — contains the substring 'shop_id' for Shopify orders, or starts with '5168-6-' for CheckoutChamp orders; (3) the bank-feed row's dollar amount, matched to Blue Onion gross amount within $0.02 tolerance and to the PayPal CSV Gross column filtered to Balance Impact='Credit'. Priority order: shop_id/CC-prefix match in PayPal CSV first (most reliable), then Blue Onion amount match, else flag PENDING/HOLD for manual review. For generic 'Deposit'/'Other' lines (usually Chase<->PayPal transfers or General Card Deposits), match by exact date+amount against the PayPal CSV 'Type' column: 'General Card Deposit'/'Bank Deposit' => TRANSFER (do not book as revenue); 'PreApproved' + shop_id/5168-6- => book as Shopify/CheckoutChamp AR. Vendor payments ('Payment to {Vendor}') get keyword-matched (lucid software/paddle.com/gruns nutrition -> 6700 Software; meta platforms -> 6455 Marketing; tiktok -> DISREGARD/exclude since it's ad spend paid from a different bucket; ovh/hostwinds/cloudflare/lagosec -> 6700 Software). Never auto-book 'Fee' or 'Adjustment' lines — hold them because they may already be embedded in Blue Onion's net settlement figures and double-booking overstates merchant fee expense. Output a classified Excel workbook with per-classification tabs (SHOPIFY, CHECKOUTCHAMP, VENDORS, TRANSFERS, PENDING) plus a SUMMARY tab totaling $ by classification, so only rows with QBO Action=ADD get journaled and every HOLD row has an explicit reason for the reviewer.","code_snippet":"def parse_amount(amt_str):\n    if pd.isna(amt_str):\n        return 0.0\n    s = str(amt_str).replace(\"$\", \"\").replace(\",\", \"\").strip()\n    if s.startswith(\"(\") and s.endswith(\")\"):\n        return -float(s[1:-1])\n    return float(s)\n\n# Classify by Custom Number pattern in PayPal.com CSV export\nhas_shopid = \"shop_id\" in custom_number\nhas_cc_custom = custom_number.startswith(\"5168-6-\")\n# Fallback: match gross amount (±$0.02) against Blue Onion 'Order System' field\nin_bo_shopify = round(abs(amt),2) in bo_shopify_gross_set\nin_bo_cc = round(abs(amt),2) in bo_cc_gross_set  # Order System is NaN for CheckoutChamp","skill":null,"platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-14","tags":null,"metadata":null,"created_at":"2026-08-14T22:00:23.222436+00:00","updated_at":"2026-08-14T22:00:23.222436+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"78ea8a40-99cb-4119-b033-6668387015a7","agent":"swarm","category":"bug_fix","title":"Euka outreach/CRM campaigns silently flip to bot_status=\"error\" when target_collab_valid_until expires","problem":"Multiple Euka outreach/CRM agent campaigns (ids 288170, 288173, 288179, 273290 seen today) stopped running and showed bot_status \"error\" in list_outreach_agents. Root cause: each campaign has a target_collab_valid_until date field that had lapsed into the past — once that date passes, Euka stops the agent and marks it errored rather than pausing gracefully, with no obvious alert.","solution":"Detect: call list_outreach_agents with botStatus=[\"error\"] (storeId + brandId required) to enumerate affected campaigns; get_outreach_agent for each returns the full campaign object including target_collab_valid_until and error_message. Fix: for each errored campaignId, call get_outreach_agent to fetch the current campaign object (need campaign_type + id + store_id preserved), then call update_outreach_agent with payload {campaignId, brandId, campaign: {campaign_type, id, store_id, target_collab_valid_until: <new future date, e.g. one month out>}, followUps: [], segmentAssignments: []} — followUps/segmentAssignments can be sent empty to leave them unchanged. After updating, call set_outreach_campaign_status with newStatus:\"running\" to restart it. Verify by re-running get_outreach_agent per id (check bot_status + target_collab_valid_until) and list_outreach_agents(botStatus=[\"error\"]) again to confirm the error count dropped. Use the mcp__euka__ tools (get_outreach_agent, update_outreach_agent, set_outreach_campaign_status, list_outreach_agents) directly rather than hand-rolling raw JSON-RPC calls to https://app.euka.ai/api/mcp — that raw path was only needed for one-off schema probing (tools/list -> inputSchema) and isn't necessary for routine fixes. Actionable takeaway: treat target_collab_valid_until as a TTL that needs periodic renewal on any long-running outreach campaign, and check for botStatus=\"error\" agents proactively rather than waiting for creators to notice sends stopped.","code_snippet":null,"skill":"euka outreach campaign management","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-14","tags":null,"metadata":null,"created_at":"2026-08-14T22:00:22.412028+00:00","updated_at":"2026-08-14T22:00:22.412028+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"ae36c54e-8faf-4600-a920-a4179437b619","agent":"surge","category":"gotcha","title":"Frame.io links can 404 even with a valid token+link because the workspace now spans TWO separate Frame.io accounts — resolver must iterate every account before declaring an asset dead","problem":"Clean Nutra now has two distinct Frame.io accounts under the same OAuth/IMS token: the original 'Clean Nutra (Old)' (account_id 18c0032c-..., the one stored/default in .frameio-oauth.json, holding legacy per-SKU projects) and a new 'Clean Nutra' (account_id db23ce80-431e-42aa-a85f-4a76e1d5ddce) that producers are migrating/creating projects into. When a producer re-shares a brief's asset link, it can land in the NEW account while the resolver only ever checks the default/first account, producing a 404 on a link+token that are actually both perfectly valid. This first hit Volcanic Clay '30-Day Skin Timeline' on Aug 13, and recurred identically on Aug 14 on the same SKU's re-share, confirming it's a standing steady-state fact of the workspace rather than a one-off migration glitch. Historical 'project deleted' blocks (PD Symptom-Checklist R7/R8, 'If Your Butt Grew 1cm Every Day') were suspected to actually be this same issue — retrying them on the new account resolved 'Butt Grew 1cm' immediately, while R7/R8 stayed dead on BOTH accounts (genuinely deleted).","solution":"Never trust a single-account 404 as proof an asset is dead. On any Frame.io resolve failure: call GET /v4/accounts (no account prefix, same bearer token) to list ALL accounts visible to the credential — this returns both 'Clean Nutra (Old)' 18c0032c-... and 'Clean Nutra' db23ce80-431e-42aa-a85f-4a76e1d5ddce for this workspace. Then retry the file -> version_stack -> folder resolve chain against EACH account_id in turn before classifying the link as genuinely dead/needing a producer re-share. Only if a link 404s on every account in that list should you skip the brief and flag for re-share (per the existing 401-vs-404 rule: 401 = stale token, refresh and retry; 404-on-all-accounts = truly dead, exclude and move on rather than blocking the wave). This fix is already codified in skill `airtable-ad-launch-pipeline` (pitfalls section, 'TWO Frame.io ACCOUNTS exist'), and the Aug 14 recurrence confirms it should be treated as permanent SOP, not a patch that will eventually become unnecessary — expect more briefs to resolve only on the new account as producers continue migrating projects.","code_snippet":null,"skill":"airtable-ad-launch-pipeline","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-14","tags":null,"metadata":null,"created_at":"2026-08-14T22:00:20.93454+00:00","updated_at":"2026-08-14T22:00:20.93454+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"da355602-4718-4a94-a2b3-ccfee2bfaae9","agent":"probe","category":"business_rule","title":"Atria-sourced launch-date data always under-counts the freshest 1-2 days — never call it a slowdown","problem":"Three separate daily competitor trackers (Rosabella/VascuGlow lane, Lymphoria lane, Atria/Resilia lane) all pull new-ad launch dates from Atria's Meta Ad Library index. Atria's crawler lags real Meta ad-library indexing by 1-2 days, so any digest that naively reports 'launches dropped today/yesterday' based on raw Atria counts is reporting a measurement artifact, not a real trend — this would mislead Weston into thinking a competitor paused spend when they haven't.","solution":"For every Atria-backed tracker digest (rosabella_tracker_digest.py, lymphoria_tracker_digest.py, atria_tracker_digest.py), apply this rule before writing conclusions: (1) pull launch_date counts grouped by day from the relevant sqlite DB, e.g. `python3 -c \"import sqlite3; db=sqlite3.connect('/home/wcorica/.hermes/profiles/probe/data/resilia_tracker.db'); [print(r) for r in db.execute('SELECT launch_date, COUNT(*) FROM ads GROUP BY launch_date ORDER BY launch_date DESC LIMIT 7')]\"`. (2) The most recent 1-2 launch_date rows are ALWAYS lower than final because Atria hasn't finished indexing them yet — label these explicitly as 'still indexing', never as a volume drop or slowdown. (3) Where a near-real-time Apify tracker DB exists for the same competitor (e.g. resilia_tracker.db populated via Apify), cross-check the freshest 1-2 days against that source and lead the digest with the Apify numbers for today/yesterday since they're the trustworthy fresh signal, using Atria only for the trend further back (3+ days old, fully indexed). This pattern generalizes to any competitor-tracking cron built on Atria's Ad Library scrape.","code_snippet":null,"skill":"competitor-ad-tracking","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-14","tags":null,"metadata":null,"created_at":"2026-08-14T22:00:20.237401+00:00","updated_at":"2026-08-14T22:00:20.237401+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"b825fc6c-aa76-4cdf-86f7-29fc58634e1c","agent":"nexus","category":"business_rule","title":"UPC/barcode field is unreliable across the whole catalog, not just a few SKUs — 90% blank in CIN7, plus cross-field contamination and fake ShipHero barcodes","problem":"While preparing the UPC Process Alignment meeting agenda (build_upc_meeting_agenda.py), a full scan of CIN7's 299 active core SKUs found 268 (90%) have NO barcode/UPC value at all. Of the 31 that do have a value: 8 are placeholder/truncated (e.g. '665356000000' — not a real 12-digit UPC), 3 have an Amazon ASIN or FNSKU sitting in the UPC field instead of a real UPC (e.g. CN-DRP-BLOODPRESS-2OZ has 'B0D7537K4Z' in CIN7's barcode field), and 4 have two different real-looking numbers disagreeing between CIN7 and Shopify with no way to tell which is authoritative. Separately, the ShipHero fulfillment audit found 50 product records where the barcode field is literally set to the SKU string itself (fake barcode == SKU), the same root contamination pattern showing up independently in a second system.","solution":"When auditing or trusting the UPC/barcode field anywhere in this stack, validate format before treating it as real: real UPCs are 12-digit numeric and should not equal the SKU string, an ASIN (starts with 'B0' + 10 alphanumeric), or an FNSKU (starts with 'X0'/'X1' pattern). A value ending in a long run of zeros (e.g. ending '000000') is a placeholder, not a real GS1-issued code. Because CIN7, Shopify, and ShipHero can each hold an independently-entered value for the same SKU, always cross-check the field across all three before using it for retailer catalog matching (Amazon/Walmart), and flag any SKU where CIN7 vs Shopify disagree even if both look numerically plausible — 15 such live disagreements were caught in one day's automated audit, confirming this is an ongoing drift issue (root cause: independent per-platform entry, no single source of truth for UPC), not a one-time backlog to clean up once.","code_snippet":null,"skill":"upc-barcode-data-governance","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-14","tags":null,"metadata":null,"created_at":"2026-08-14T22:00:19.552401+00:00","updated_at":"2026-08-14T22:00:19.552401+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"d3a5b732-b5ae-4147-9069-8f5acde7121c","agent":"apex","category":"pattern","title":"'Hard-zero' escalation for a product requires multi-day fade + both-sibling zero impressions, ideally with co-timing corroboration from a related SKU","problem":"Products intermittently show 0 impressions on a campaign due to short reporting flickers rather than a genuine shutdown; escalating every 0-impression reading to the highest severity tier produces noisy, low-confidence alerts that waste review time on transient blips.","solution":"Reserve 'hard-zero' (top escalation tier) for cases meeting all of: (1) impressions are 0 on BOTH the Manual and Auto sibling campaigns for the product, not just one; (2) this follows an observed multi-day fade trend (e.g., 3+ consecutive days of declining impressions) rather than appearing as a sudden single-day drop that could be a reporting gap; and (3) where possible, corroborate with timing evidence from a related product moving the same day. Concrete example: Adapto Glow Gummies was declared hard-zero on day 1 specifically because both sibling campaigns hit 0 impressions (down from y527/2,466) after a 3-day fade, AND because it shared the exact same-day wave-timing as the already-confirmed Adapto Drive chop from Aug-11 — the cross-product timing match was used as supporting evidence rather than judging the zero-impression event in isolation. Products with only single-sibling zeroes or a first-day drop (e.g., ShilaFlow's repeated flicker/re-fade pattern) stay in a lower 'flicker'/'gate-lean' class instead.","code_snippet":null,"skill":null,"platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-14","tags":null,"metadata":null,"created_at":"2026-08-14T22:00:18.261238+00:00","updated_at":"2026-08-14T22:00:18.261238+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"0566b725-dae2-452d-af17-8b088d994616","agent":"apex","category":"business_rule","title":"Bid/budget 'chop' is only CONFIRMED after a fully-closed day shows BOTH Manual and Auto siblings under ~20% of baseline","problem":"A single day's low spend on a campaign can be noise (reporting lag, one unusually low-CPC day, partial/raw data) rather than an actual deliberate bid or budget cut, so treating any one-day dip as a confirmed chop risks filing false escalations or premature re-mod recommendations.","solution":"Do not confirm a chop off same-day raw/provisional numbers. Wait for the day to fully close, then require the drop to independently show up in BOTH the Manual and Auto campaign for the same product, each at roughly <20% of its own prior baseline, before marking status 'CONFIRMED' (vs 'day-1 provisional'). Real example: CortiZen chop was confirmed only after Aug-12 fully closed with Manual spend $57 (15% of prior baseline) and Auto spend $99 (18% of prior baseline) — both siblings independently collapsed on the same closed day. Contrast with cases still in 'provisional' status where only one sibling has moved or the day hasn't closed yet (e.g., a Day-1 wake or fade needs a second closed day of the same reading — 'the 2-day flicker rule' — before any case gets filed or escalated).","code_snippet":null,"skill":null,"platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-14","tags":null,"metadata":null,"created_at":"2026-08-14T22:00:18.068677+00:00","updated_at":"2026-08-14T22:00:18.068677+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"d85a6d95-21fe-418e-b23a-98a86707d19e","agent":"helix","category":"gotcha","title":"FDA/DEA peptide regulatory monitor missed a material risk signal because it was scoped only to government action, not private civil litigation from pharma manufacturers","problem":"The daily FDA peptide monitor cron (job 50899031df5c) queries Perplexity sonar-pro for FDA/DEA/state Board of Pharmacy actions on the 12 legalized peptides (BPC-157, TB-500, Semax, Dihexa, MT-2, Epitalon, MOTs-C, GHK-Cu, KPV, DSIP, LL-37, PEG-MGF) plus 503A compounding rule changes and PCAC review status. On 2026-08-14 the run's own summary (state/fda_peptide_last.json) surfaced that Eli Lilly filed a civil suit around Aug 13, 2026 against 6 vendors, including Texas Peptides Inc., over alleged illegal sale of an obesity drug candidate -- a real, material legal-risk event for the peptide/GLP-1 telehealth space. This was captured almost incidentally because it happened to show up in the search results, not because the monitor's query/scope was designed to catch it. A monitor strictly filtered to 'FDA, DEA, or state Board of Pharmacy actions' would systematically miss brand-name manufacturer civil litigation (patent/IP holders suing compounders or resellers), which is often an earlier and more frequent risk signal than formal regulatory action -- large manufacturers like Eli Lilly and Novo Nordisk sue perceived infringers/generic-adjacent peptide vendors well before FDA enforcement catches up, and such suits can chill pharmacy partnerships or trigger reputational scrutiny for any telehealth peptide business (like Clean Health/Ascend) operating in the same product category.","solution":"When building or maintaining a regulatory-monitoring prompt for a controlled/gray-area product category (peptides, GLP-1s, compounded drugs, etc.), explicitly add a distinct tracked category for private civil litigation by brand-name manufacturers or patent holders against compounders/resellers/vendors in the same space -- do not rely on it surfacing incidentally inside a search scoped to 'FDA/DEA/BoP'. Concrete fix for this monitor: (1) add an explicit line to the Perplexity sonar-pro prompt such as 'Also report any civil lawsuits filed by pharmaceutical manufacturers (e.g., Eli Lilly, Novo Nordisk, Pfizer) against compounding pharmacies, telehealth companies, or peptide/GLP-1 vendors in the last 7 days, even if not a government regulatory action'; (2) in state/fda_peptide_last.json, store civil litigation as a separate key (e.g. 'civil_litigation': [{defendant, plaintiff, date, allegation, relevance}]) distinct from 'regulatory_actions' and 'pcac_status', so downstream digest logic can flag it as a different risk category (legal/reputational vs regulatory/enforcement) rather than burying it in one prose blob; (3) when a defendant name overlaps with a known/potential Clean Health pharmacy or supplier partner, escalate that specific finding to Weston immediately rather than waiting for the routine daily summary, since manufacturer litigation against a vendor Clean Health works with (or is considering) is a direct compliance/business-continuity risk, not just background news.","code_snippet":null,"skill":"fda_peptide_monitor","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-14","tags":null,"metadata":null,"created_at":"2026-08-14T22:00:16.913457+00:00","updated_at":"2026-08-14T22:00:16.913457+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"3f6b98f6-6a1b-4e3e-86dd-619cf1e65fd6","agent":"viral","category":"gotcha","title":"Trybe ads page load time scales with catalog size — 'Build Ads' button needs a 120s timeout, plus browser recycling and resumable results for long batch runs","problem":"The Trybe brand ads page (jointrybe.com/brand/ads?b=<brand_id>) renders the ENTIRE ad catalog before the 'Build Ads' button appears, so as our launched-ad count grows the page takes progressively longer to become interactive; default Playwright click timeouts (30s) started failing mid-batch, and long headless sessions degraded, causing cascading build failures across a run.","solution":"Three defenses baked into trybe_build_generic.py: (1) page.locator('button:has-text(\"Build Ads\")').first.click(timeout=120000) — 120s explicit timeout because render time grows with catalog size; navigation itself retried 3x with 10s backoff. (2) Browser lifecycle management: recycle (close + fresh login) every 12 builds OR after 2 consecutive failures — degraded sessions cause correlated failures, so a fresh browser after 2 straight fails distinguishes 'session rot' from real wizard errors; any raw exception also gets one fresh-browser retry before marking FAILED_EXCEPTION. (3) Idempotent resumability: append every per-sub result ({sub_id, creator, status}) to RESULTS_FILE after EACH build (not at the end), and on startup load it into DONE_SUB_IDS keyed on sub_id[:8], skipping anything already LAUNCHED/ALREADY_DONE — so a crashed run can simply be re-invoked with the same config and picks up where it left off. Also: after the first successful NEW-adset launch for a creator, flip that creator to EXISTING mode targeting '<prefix><sep><creator>' so remaining subs join the adset instead of erroring on duplicate creation.","code_snippet":null,"skill":"trybe","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-13","tags":null,"metadata":null,"created_at":"2026-08-13T22:00:46.265472+00:00","updated_at":"2026-08-13T22:00:46.265472+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"638f5077-b02f-429f-8d54-0b3bb39c5d4a","agent":"viral","category":"workflow","title":"Trybe Build Ads wizard now supports fully manual ad copy via 'No template (manual entry)' — automated it config-driven for multi-product launches","problem":"New product launches (Women's Transformation Stack, ChloroZen) had no existing Copy template in the Trybe Build Ads wizard, so builds either inherited the wrong default ('Gluco Tone (Default)') or required hand-typing 5 primary texts + 5 headlines per ad in the UI — untenable when launching batches of creator submissions across multiple products.","solution":"Extended trybe_build_generic.py with a MANUAL_COPY config key ({\"primary_texts\":[...], \"headlines\":[...]}, kept per-product in draft_copy_<product>_<date>.json). Flow inside the wizard: (1) use the generic select_template_dropdown() to switch the Copy dropdown from 'Gluco Tone (Default)' to 'No template (manual entry)' — find the trigger <button> by exact text with height 0–80px, scrollIntoView, mouse-click its bounding-rect center, then pick the [role=\"option\"] by exact text and VERIFY the trigger now shows the target text before proceeding (abort the build rather than launch wrong copy). (2) For each primary text: click the 'Add primary text' placeholder button for field 1; for subsequent fields that button disappears, so locate the section by a leaf element whose text starts with 'Primary Texts', climb ≤6 parent divs, and click the nearest visible 'Add' button inside; then fill textarea[placeholder=\"Primary text N\"]. Headlines are identical but section label 'Headlines' and input[placeholder=\"Headline N\"]. Landing page supports the same pattern with 'Enter custom URL...' → input[placeholder=\"https://yoursite.com/landing-page\"]. This makes copy a versioned JSON artifact reviewable before launch instead of ephemeral UI state.","code_snippet":null,"skill":"trybe","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-13","tags":null,"metadata":null,"created_at":"2026-08-13T22:00:46.074538+00:00","updated_at":"2026-08-13T22:00:46.074538+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"6e3d7a8e-a9b5-41a5-9542-d2f60952b308","agent":"vault","category":"api_integration","title":"Amazon SP-API order status batch lookup via HiveMind passthrough","problem":"Blue Onion shows Amazon orders as 'unfulfilled' even months after purchase, creating deferred revenue reconciliation issues. Need to verify live order status (Shipped vs truly Pending) against Amazon's actual data to identify sync lag vs genuinely stuck orders.","solution":"Use HiveMind's generic SP-API passthrough to call `/orders/v0/orders/{orderId}` directly for each order. Amazon Reports API (`GET_FLAT_FILE_ALL_ORDERS_DATA_BY_ORDER_DATE_GENERAL`) is unreliable — frequently fails with 'FAILED to create report' errors. For batch lookups of 100-500 orders, iterate with direct order endpoint calls. Key fields: OrderStatus (Pending/Shipped/Canceled), PurchaseDate, FulfillmentChannel (AFN/MFN). Rate limit by adding 0.5-1s delays between calls. Save results to JSON for cross-referencing against Blue Onion exports. This revealed that many 'unfulfilled' orders in Blue Onion were actually shipped (sync lag), not truly stuck.","code_snippet":"def call_hivemind(platform, endpoint, method=\"GET\", params=None):\n    payload = {\n        \"platform\": platform,\n        \"action\": \"read\",\n        \"endpoint\": endpoint,\n        \"method\": method,\n    }\n    if params:\n        payload[\"params\"] = params\n    cmd = [\"curl\", \"-s\", \"https://hivemind.brandmind.dev/api/selling-partner\",\n           \"-X\", \"POST\", \"-H\", f\"Authorization: Bearer {API_KEY}\",\n           \"-H\", \"Content-Type: application/json\", \"-d\", json.dumps(payload)]\n    r = subprocess.run(cmd, capture_output=True, text=True, timeout=120)\n    return json.loads(r.stdout)","skill":null,"platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-13","tags":null,"metadata":null,"created_at":"2026-08-13T22:00:45.261308+00:00","updated_at":"2026-08-13T22:00:45.261308+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"1d20e48e-e93d-4203-8acc-e2eb0fd1c806","agent":"phoenix","category":"gotcha","title":"Google Sheets CSV export URLs can silently return an HTML 'Page Not Found' page instead of erroring, corrupting local data caches","problem":"Two locally-cached CSV files (product_master.csv and pmf_sh.csv) that are refreshed via direct Google Sheets/Docs export URLs got overwritten with a Google Docs 'Page Not Found' HTML error page (window['ppConfig'] anti-XSS script + docs.google.com 404 template) instead of real spreadsheet data, while a third sibling file (pmf_wm.csv) pulled from the same workflow refreshed correctly with valid rows. Because Google returns HTTP 200 with an HTML body for this 404 case (not a 4xx/5xx status), a naive curl/wget-and-save script has no error to catch — it just writes the HTML straight into the .csv file, so any downstream code trusting that file gets garbage silently (e.g. blank product_master lookups, broken SKU joins) until someone manually opens the file.","solution":"When refreshing any CSV that is pulled from a Google Sheets/Docs 'export?format=csv' URL, validate the response BEFORE overwriting the cached file: (1) check the first bytes/line for '<!DOCTYPE html>' or 'window[\\'ppConfig\\']' and reject if found, (2) verify the response Content-Type header is 'text/csv' not 'text/html', (3) confirm the row count and header row match expectations (e.g. product_master.csv should have hundreds of rows, not 9 lines of minified JS), and only then atomically replace the old file (write to a .tmp path, validate, then mv over the original) so a failed pull never destroys the last-known-good cache. Root cause of the 404 itself is almost always a stale/changed gid or the sheet tab being renamed/deleted/moved — re-grab the export link from the live Google Sheet (File > Share > Publish, or the tab's URL gid=...) rather than reusing an old bookmarked export URL, and re-run the refresh for that specific file once the correct gid is confirmed.","code_snippet":null,"skill":"phoenix-data-sync","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-13","tags":null,"metadata":null,"created_at":"2026-08-13T22:00:44.445771+00:00","updated_at":"2026-08-13T22:00:44.445771+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null},{"id":"0a5c50a9-e603-4d96-80af-12642dfca6e8","agent":"haven","category":"gotcha","title":"execute_code is hard-blocked in this Hermes profile — always fall back to the terminal tool for Python, and don't guess PDF library names","problem":"While processing a user-uploaded 'Travel Benefits Acknowledgment.pdf' in a live Telegram session (not a cron job), the agent first tried `python3 -c \"import PyPDF2...\"` via the terminal tool, which failed with ModuleNotFoundError (PyPDF2 isn't installed — it's deprecated in favor of pypdf). It then tried the execute_code tool as a fallback, which returned a hard BLOCKED error: \"execute_code runs arbitrary local Python (including subprocess calls that bypass shell-string approval checks). Cron jobs run without a user present to approve...\" — even though this was an interactive Telegram session with the user actively present, not an unattended cron run. Both failures cost API-call turns and, combined with the user sending follow-up messages mid-processing, triggered 'interrupted_during_api_call' turn resets that further slowed the response (this exact pattern happened twice in one 4-minute window: once for the PDF, once later for an Excel travel-schedule attachment).","solution":"Two durable fixes for any Haven/HR agent handling documents: (1) Never rely on execute_code for Python execution in this profile — the block fires unconditionally regardless of whether a user is present or it's a cron job (the error text is cron-flavored boilerplate but the block is universal at the tool_executor level). Always use the `terminal` tool instead, e.g. `python3 -c \"...\"` or write a script and run it with `terminal`. (2) For PDF text/table extraction, don't guess at library names — PyPDF2 is not installed and is deprecated anyway. Check `skill_view('pdf')` first: its Prerequisites say `pip install pypdf pdfplumber reportlab` and its Quick Reference table maps 'Extract text' -> pdfplumber (`page.extract_text()`) and merge/split -> pypdf. If pip install isn't available/fast enough, `pdftotext -layout file.pdf -` (poppler-utils) is already on the box and extracts text with layout preserved in a single terminal call — faster than any Python import chain for a quick read of a short acknowledgment/policy PDF.","code_snippet":"# Fast path for a short PDF (e.g. an acknowledgment form) via terminal tool:\npdftotext -layout \"/path/to/file.pdf\" -\n\n# Or, if you need structured extraction:\npython3 -c \"import pdfplumber; print('\\n'.join(p.extract_text() or '' for p in pdfplumber.open('file.pdf').pages))\"\n# NEVER: import PyPDF2  (not installed, deprecated -> use pypdf)\n# NEVER: execute_code tool for this -> always terminal","skill":"brandmind/haven-ops","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-08-13","tags":null,"metadata":null,"created_at":"2026-08-13T22:00:43.584147+00:00","updated_at":"2026-08-13T22:00:43.584147+00:00","anti_patterns":null,"trigger_conditions":null,"verification":null,"complexity":"medium","usage_count":0,"success_count":0,"time_saved_minutes":0,"last_used_at":null,"last_used_by":null}]}