{"total":3319,"count":50,"rows":[{"id":"3693ab34-dd9b-486a-99c9-79977a7a9460","agent":"viral","category":"gotcha","title":"New Meta ad account owned by an agency business (WT - Agency01) needs pixel + page sharing before Trybe can build ads","problem":"Weston asked for a ChloroZen Trybe campaign on a brand-new ad account act_2279224506258055 ('Clean Nutra (ChloroZen) - 02 Ares'), which is owned by business WT - Agency01 2198525764402961, not MAIN (Webstone Marketing 681450815374544). The account showed ACTIVE, funded and with ADVERTISE/MANAGE, but it had 0 campaigns, 0 pixels, 0 promote_pages and 0 audiences. Trybe had no connection for it, and once the connection was added Trybe returned PAGE_NOT_ADVERTISABLE because the agency business owned 0 pages, either directly or as client pages.","solution":"Pre-flight checklist for any new account before using the Trybe Build Ads wizard: (1) Read-only probe through the HiveMind proxy: account status/business/funding, then the /adspixels, promote_pages and customaudiences edges. (2) Check the Trybe brand portal connection roster for the account. If it is missing, ask Weston to connect it. For ChloroZen it showed up as de131dcb-13a6-4f43-ac43-5c1046abdee3. (3) Pixel: share the existing MAIN pixel (Clean Nutra Pixel - Primary 997334514499253) to the new account through HiveMind, then read back /adspixels on the new account to confirm. (4) Pages: partner-share the pages from MAIN into the agency business's client_pages (The Chlorophyll Lab 1358081254057293, Chlorophyll Secrets 1269032499625695, Clean Nutraceuticals 110559444780282), then read back client_pages. Assigning users worked only for Clean Nutraceuticals. The chlorophyll pages failed with code 10 / subcode 1690202. (5) Even after a confirmed share, promote_pages stayed [] and Trybe kept returning PAGE_NOT_ADVERTISABLE, so start a poller that re-checks promote_pages every 5 minutes for about 80 minutes (cz_pages_poll.py) and only build once the page appears. (6) Trybe connection settings only had GT/SB/VG copy and landing page templates. For a new product, plan manual copy plus a custom URL (/products/chlorozen-bogo). (7) Exclusions: there was no ChloroZen Klaviyo buyer list in any of the 10 Klaviyo accounts. The nearest option is MAIN 'CN | Shopify Purchasers 180d | customer list' 120251005010810756 (53.1k, all-store), but sharing an audience across businesses to the agency-owned account is unverified, so check it before relying on it.","code_snippet":null,"skill":"trybe-ugc-ad-builder","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-17","tags":null,"metadata":null,"created_at":"2026-10-17T15:00:44.970473+00:00","updated_at":"2026-10-17T15:00:44.970473+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":"6b7e9f59-cf02-4f18-a36a-764b3410248b","agent":"flux","category":"bug_fix","title":"Draft Shopify PDP clones can overwrite live donor sections: enforce theme-asset ownership before deployment","problem":"During the Oct 16 ASIN launch, the Internal Fire clone used section key internal-fire-pdp, which was already the LIVE Ashwa Maca donor's section key. Deploying the new product while it was DRAFT still overwrote the live donor buy box with new variant IDs for approximately six minutes. Product publication status did not isolate the shared theme assets.","solution":"Before deploying a clone, compare its section and template keys with the donor keys and existing theme assets; reject collisions, and refuse to overwrite repository files that are not owned by that clone. In this incident, stop the batch deploy, restore the donor GitHub-first (recorded recovery commit e9e004ce61), then PUT the restored asset to Shopify. After a 100-second hold, verify the live donor page contains its original variant 42888092352665 and no clone variant IDs. Rename the clone section to internal-fire-120-pdp and update the product template_suffix to match its renamed template. The clone engine at ~/work/asin_pdp_clone.py was hardened with section/template inequality assertions and repository-file ownership checks. Use count-qualified names such as -120-pdp or -90-pdp for new clones, but still check uniqueness: naming alone is not an ownership check. Run the donor-variant-residue assertion and rendered-page QA before resuming the batch. Audit orders across the incident window separately from page recovery; the recorded check examined 100 orders from 19:00–19:50Z and found none on the Internal Fire draft and no -120BG SKUs. Evidence and reusable implementation reference: memory/2026-10-16.md, refs/asin-to-shopify-pdp-clone.md, scripts/asin_clone/.","code_snippet":null,"skill":null,"platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-17","tags":null,"metadata":null,"created_at":"2026-10-17T15:00:44.475251+00:00","updated_at":"2026-10-17T15:00:44.475251+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":"7a363547-0eb9-4331-866b-730d6d3cb3bb","agent":"watchdog","category":"pattern","title":"Intraday prorated-pace bleed scans mislabel budget cap-outs as spikes; check for frozen spend across scans","problem":"The 2h intraday bleed scan flags a campaign when today-so-far spend is >150% of its prorated 7d daily average and >$50. On 2026-10-16 most flags sat around 1.5x, and some campaigns (MultiMane 120ct Manual Apex $294.59, Herbaplex Bag Manual EC $79.40) showed the exact same spend in every scan from 08:41 to 16:01 PT. These campaigns were not bleeding. They had used up their daily budget early in the morning. Prorating by the fraction of the day elapsed makes an early cap-out look like a 2.7x spike, so the alert fills up with false positives and real spikes are harder to see.","solution":"Split the scan into two pulls so it can run often and cheaply. (1) The first scan of the day does a full pull (pull-bleed-scan-retry.js: SP+SB today plus 14d baseline, ~40 min because Amazon holds reports in PENDING/PROCESSING). (2) Later scans run pull-bleed-scan-today.js, which refreshes only today's SP+SB campaign reports through the Supabase proxy and reuses the morning's 14d baseline. That dropped later runs to about 1-4 min. For each flagged campaign, sort it into one bucket: (a) CAPPED: spend is identical to the previous scan's value. Mark it 'capped since HH:MM', since it's a weekly-CSV budget candidate, not a bleed. (b) EARLY CAP-OUT: ratio is about 1.5x and today's spend is already at or above its full-day 7d average. (c) REAL SPIKE: ratio is still well above 1.5x late in the day and spend is still climbing (e.g. MiracleMin 1.92x, ShroomZoom 1.98x at 66.8% of the day). Also add a conversion watch line for campaigns that are under the threshold but have high clicks and no or few orders today (F&V Auto 0 orders/24 clicks, V-Biotic Auto EC 4 orders/112 clicks). Before POSTing, always check that SP total + SB total equals the analyzer's total, and spot-check 3 campaigns' spend/orders/clicks against the raw JSON. Make zero writes at T0.","code_snippet":null,"skill":"watchdog-bleed-scan","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-17","tags":null,"metadata":null,"created_at":"2026-10-17T15:00:43.61564+00:00","updated_at":"2026-10-17T15:00:43.61564+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":"238d8145-3730-4b5a-a8e8-4375fbe1cf66","agent":"vector","category":"workflow","title":"Re-request for a multipack TO: reuse the existing DRAFT TRs instead of making new ones, then check ShipHero cases after moving to In Transit","problem":"Weston asked for a new transfer of 50 each of the 5 GlycoAkk 2-6PK multipacks (FG-01971..FG-01975), saying the multipacks were 'unflagged'. Draft TRs TR-00545..TR-00549 from Oct 8 already existed for those same 5 SKUs: 50 each, lot 0426177, exp 2028-04-30, with kits on hand 50 each at Vegas and allocated 0. Creating new TRs would have doubled the transfer. After the existing TRs were put in transit, ShipHero showed no case data for the packs, and Amazon still had the listings at DISCOVERABLE, so they were not live yet.","solution":"1) Before creating any CIN7 Stock Transfer, list every TR modified since the original request date. Here that was 65 TRs since Oct 8. Grep their lines for the SKU family (GLP1/GlycoAkk FG-0197x). If DRAFTs already exist, report their numbers back and ask the requester to approve those instead of creating new ones. 2) Once approved ('move to in transit'), PUT each TR to In Transit with Departure date 2026-10-16 and SkipOrder. Then re-GET each TR to confirm the status actually changed. Done for TR-00545..00549. 3) Right after that, query ShipHero for each pack SKU. Here it showed on_hand 0 / allocated 0 / backorder 0, because the bridge orders had not synced yet, and cases:[] on all 5. Empty case data means a 'Product Data Needed' work order should be expected. The kits were also not built in ShipHero yet, so an ASSEMBLY work order needs to be watched for. Check again on the next turn. 4) Check the Amazon side separately with SP-API GET /listings/2021-08-01/items/{sellerId}/{sku}. The status was DISCOVERABLE, not BUYABLE, and there was issue 18448 (missing primary_supplement_type). That attribute must be added through a Listings PATCH or the listing stays unsellable even after the inventory arrives. 'Unflagged' in ShipHero does not mean the Amazon listing is ready.","code_snippet":null,"skill":"cin7-transfer-orders","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-17","tags":null,"metadata":null,"created_at":"2026-10-17T15:00:43.114566+00:00","updated_at":"2026-10-17T15:00:43.114566+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":"29e5378c-11b1-4f9f-a0d5-1c48bbefa62c","agent":"surge","category":"gotcha","title":"Dual-pixel Meta setup (Blotout routing by UTM): check ad names, audience ownership and both pixels' purchaser exclusions before launch","problem":"SB2 Arm B sends conversions to the Primary pixel through Blotout: if utm_campaign contains 'PX-Primary' the event goes to the Primary pixel, otherwise it goes to the SB pixel by SB2 utm_medium. The plan was to reuse the winner ads from the main-account Trybe CBO under 'PXP | <original name>'. Review found that all 200 of the 200 ACTIVE source ad names contain '=' (from '_trybe=xxxx'), which breaks utm_content. The planned audiences also could not be used as written.","solution":"Pre-launch checklist for any campaign that routes to a pixel by UTM: (1) Clean ad names before using {{ad.name}} in UTMs. Replace '=' (e.g. '_trybe=xxxx' becomes 'trybe-xxxx') and remove '+' and '#', since all of them break or truncate query params. (2) Check who owns each custom audience. The 14D engaged audience 120247453631750711 belongs to the LD account 314955010695474 and is not available on SB2. Use one that SB2 already owns instead: 'CN | All Site Visitors 14d' 120250076761110467 (Primary PageView 14d + FB/IG shop). (3) Purchaser exclusions must cover BOTH pixels. Arm A sends purchases to the SB pixel, so the Primary-only 'CN Purch180d' (120247435278350711) misses those buyers. Also exclude SB Purch180d 120250037880600467. (4) Make utm_source match the Triple Whale standard ('facebook', not 'meta'). (5) Copy from another buyer's source campaign read-only (here the main-account CBO 120246923992620756, Viral's lane), and only because Weston directed the reuse. (6) If the brief only covers the CBO, settle the PX-Primary routing for the ABO test sets too.","code_snippet":null,"skill":null,"platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-17","tags":null,"metadata":null,"created_at":"2026-10-17T15:00:42.637842+00:00","updated_at":"2026-10-17T15:00:42.637842+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":"bb40bb12-e950-4dee-8443-5febff0d5b15","agent":"surge","category":"api_integration","title":"Share an existing Meta pixel to another ad account via Graph API (shared_accounts)","problem":"Weston wanted the SB2 overflow account (act_1486216662942319) rebuilt on the Clean Nutra PRIMARY pixel (997334514499253) with raw Purchase optimization. SB2 could only see the Shelf Builder pixel (4138442776368031), so no ad set could select the primary pixel and the rebuild was blocked.","solution":"Share the pixel at the business level, not per campaign. Call POST https://graph.facebook.com/<ver>/997334514499253/shared_accounts with params account_id=1486216662942319 and business=681450815374544 (the business that owns the pixel), using a token that has admin rights on that pixel. Then check it two ways: (1) GET the pixel's shared/owner account list and confirm the count went up (it went from 10 to 11 here, with SB2 now included); (2) GET /act_1486216662942319/adspixels and confirm both the SB pixel and the Primary pixel are listed. Sharing does not change any live campaign. Existing ad sets keep the pixel they already use (the SB2 sets stayed on the SB pixel), so you still have to choose the new pixel when you build the new campaigns.","code_snippet":"POST /997334514499253/shared_accounts  {account_id: 1486216662942319, business: 681450815374544}\nGET  /act_1486216662942319/adspixels   -> expect SB px 4138442776368031 + Primary 997334514499253","skill":null,"platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-17","tags":null,"metadata":null,"created_at":"2026-10-17T15:00:42.52032+00:00","updated_at":"2026-10-17T15:00:42.52032+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":"ae327e7c-84d3-49c4-95e8-d6899f12e183","agent":"stockpile","category":"gotcha","title":"CIN7 delivered-product price lookup: SKU revision suffixes (-R1) hide the PO line, and AverageCost can disagree with the PO price","problem":"Weston asked what we paid per jar of InfinityOne (Urolithin A). The obvious SKU, CN-CAP-UROLITHINFOR-60CT, has never been on any PO. The jar that was actually bought is CN-CAP-UROLITHINFOR-60CT-R1, named differently ('Clean Nutra Urolithin A Supplement 60ct'), so /Product?Name= didn't find it. The product was already fully received, so there was no OnOrder and the productavailability shortcut to the supplier returned nothing. On top of that, CIN7 AverageCost ($6.18) didn't match the real PO price ($11.03).","solution":"1) Map the packaging word to the SKU suffix: 'jar'/'bottle' means -60CT/-90CT, and 'bag' means -60BG/-90BG (bag FILL SKUs look like -60BG-FILL). 2) Match PO lines on the lowercase SKU stem (e.g. 'urolithinfor'), not the full SKU, so revision variants like -R1 are included. 3) Delivered products have no OnOrder, so the productavailability→supplier shortcut fails. In that case run a full PO scan in the background: paginate GET /purchaselist?Page=n&Limit=100 from DATE_FROM=2000-01-01 and skip VOIDED. For each PO, call GET /purchase/order?TaskID=<id> for Advanced POs (GET /purchase?ID= returns 400 for these) or GET /purchase?ID=<id> for Simple POs. About 754 POs took ~12 minutes with frequent 429s. Back off and retry, and tell the requester it's running. 4) Ignore -LB (label, ~$0.11) and -TC (Transparency, $0.05) lines when giving the product unit cost. 5) Always raise two data-integrity flags. (a) Report the PO line Price as 'what we paid', and flag it when CIN7 AverageCost is different. Here $6.18 vs $11.03 means COGS is understated. (b) Check GET /advanced-purchase?ID= Combined{Invoice,Receiving,Payment}Status. PO-00379 (VitaWorks West, 8,369 @ $11.03 = $92,310.07, net 30) was FULLY RECEIVED and its memo said 'invoice 126121 matches', yet it still showed NOT INVOICED / UNPAID. Flag this as either an unrecorded payment or a bill that is still owed. Give the bag price for comparison (PO-00229, 60BG-FILL, 10,000 @ $11.86).","code_snippet":"stem = 'urolithinfor'\nfor po in purchaselist_all(date_from='2000-01-01'):\n    if po['Status'] == 'VOIDED': continue\n    d = get('/purchase/order', TaskID=po['TaskID'])  # Advanced; Simple -> /purchase?ID=\n    for ln in d.get('Lines') or []:\n        sku = (ln.get('SKU') or '').lower()\n        if stem in sku and not sku.endswith(('-lb','-tc')):\n            print(po['OrderNumber'], ln['SKU'], ln['Quantity'], ln['Price'])\n# then compare Price vs /Product?Sku=<sku> AverageCost and check /advanced-purchase?ID= statuses","skill":"clean-nutra-inventory","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-17","tags":null,"metadata":null,"created_at":"2026-10-17T15:00:42.03968+00:00","updated_at":"2026-10-17T15:00:42.03968+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":"405eb69c-1c7d-488a-bc23-dee36f21952d","agent":"sentinel","category":"business_rule","title":"Resolve every artwork finding to UPSTREAM-vs-ARTWORK per FIELD before writing the work order, and verify sibling claims against the sibling's artwork","problem":"A proof correction request that tells the designer to fix something the supplier's own spec specifies wrong will REGRESS on the next revision, and a request that tells them to revert something the spec is merely behind on will undo a deliberate harmonisation. On 16 OCT 2026 all three directions appeared in one batch: Renouth C-1/C-3 were upstream (the WBB SFP contains zero '%' anywhere and itself says 'Quercetin Anhydrous') while M-3/m-2 in the SAME PANEL were artwork-only (spec correctly reads 'Medicago sativa' with no author citation); Ginkseng was INVERTED — the WBB SFP (14AUG2026) shows eleven individual rows with no blend while the live 60CT already uses the blend, so the artwork moved ahead of its own spec and zero of its Critical/Major findings were designer-fixable. Compounding this, the Mello Mood 07-V6 review had asserted 'IMN 60CT declares Saffron (Flower) correctly, therefore WBB-specific' without ever opening the 60CT artwork — it actually reads 'Saffron (Crocus sativus L.) (Flower) Extract (30:1)', same wrong part plus an author citation, making the defect shared/upstream on a live Amazon FBA + Walmart ACTIVE listing.","solution":"Before writing any correction instruction: (1) grep the supplier's own SFP/spec document row by row for each finding's field — a spec with zero '%' tokens anywhere proves standardization omissions are upstream, not a designer oversight; (2) classify each finding into UPSTREAM (fix the spec first or the artwork regresses), ARTWORK (spec is right, artwork drifted), or INVERTED (artwork is ahead — update the spec, do NOT revert the label); (3) resolve ownership per FIELD, never per document — on PH Defend the strain codes/M-1/M-3 were artwork restores from the 60CT while the CFU declaration and silymarin % were shared spec gaps, and the capsule-shell declaration reversed (bag better than bottle); (4) BINDING RULE now in new-pitfalls-sep2026.md: a 'the sibling declares it correctly' exemption is a factual claim about another document and must be verified by opening THAT DOCUMENT'S ARTWORK at high zoom — never inherited from a prior review's summary. Applying this on PH Defend the same day caught the direction reversal that a copy-the-bottle instruction would have broken. Corollary trap: never version-triage off pdfinfo — all four WBB bags carry the identical embedded Title string '-08OCT2026-v7_UI8203_Proof' (WBB's internal job counter) while their true versions are V5/V8/V6/V7, and Ginkseng's real V7 agrees only by coincidence. Likewise a page-size delta is a QUESTION, not a finding: Age Defend's 522x234 -> 540x252 pt looked like a dieline change but registering two 600dpi renders aligned at exactly (+75,+75)px with zero residual = 18pt = 0.125in added bleed per side, trim slug 6.25 x 2.25 unchanged on both, so it graded RV.","code_snippet":null,"skill":"artwork-proof-review","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-17","tags":null,"metadata":null,"created_at":"2026-10-17T15:00:41.548517+00:00","updated_at":"2026-10-17T15:00:41.548517+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":"3cfa6463-6631-43e9-bc6e-62831f3ad56b","agent":"sentinel","category":"pattern","title":"Diff the baseline's PASS column and the disclosure structure — removal-class regressions are invisible to a findings-only diff","problem":"Version-over-version proof review normally diffs the prior review's FINDINGS list to see what got fixed. That method is blind to regressions where a previously-VERIFIED-GOOD element is deleted, and blind to restructuring that conceals a disclosure the prior version made. Both occurred on 16 OCT 2026: Renouth CN-CAP-POLYPHENOL-90BG 07-V5 DELETED its substantiated VEGAN badge (formula still supports it — HPMC shell plus '(vegetable)' mag stearate) and replaced it with unsubstantiable puffery 'PREMIUM QUALITY', while the 07-V3 baseline had logged 'Vegan badge accurate' as a verified PASS. Separately Ginkseng CN-CAP-GINSENG-90BG 07-V7 folded six individually-dosed rows into 'Clean Ginkseng(TM) Proprietary Blend 100 mg', concealing the 1 mg Longjack dose that V5 had disclosed, while TONGKAT ALI stayed a front-panel hero pill.","solution":"Add two mandatory passes to every version-over-version proof review. (1) BADGE/CLAIM ROW DIFF: extract the badge and certification row from BOTH versions as an explicit list and diff the lists, then ask the substantiation question in REVERSE — not only 'is this claim supported?' but 'was a supported claim removed?'. Pull the prior review's PASS column, not just its findings. Same supplier, same week, three different outcomes proves this must be re-run per file: Renouth deleted its Vegan badge, Mello Mood kept its, PH Defend correctly carries none at all. (2) DOSE-VISIBILITY ARITHMETIC: when a panel is restructured into a proprietary blend, sum the prior version's individual doses and compare to the new blend total — on Ginkseng 20+20+20+20+19+1 = exactly 100 mg, which simultaneously validated the stated total as honest, recovered the concealed 1 mg (~0.5% of the 200-600 mg clinical range), and confirmed total actives 1,505 mg on both versions = regrouping not reformulation. Grade on badge + token dose + REMOVAL OF THE DISCLOSURE, explicitly NOT on blend membership (blends are legal). Score dose visibility as a ratio (10/10 -> 5/10) so the regression is quantified in the work order.","code_snippet":"# dose-visibility check when a blend appears\nprior = {'Ginkgo':20,'Ginseng':20,'Rhodiola':20,'Bacopa':20,'Schisandra':19,'Longjack':1}\nassert sum(prior.values()) == 100   # == stated blend mg -> total honest, no reformulation\nhidden = {k:v for k,v in prior.items() if v < 0.05*sum(prior.values())}  # token doses now concealed","skill":"artwork-proof-review","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-17","tags":null,"metadata":null,"created_at":"2026-10-17T15:00:41.424016+00:00","updated_at":"2026-10-17T15:00:41.424016+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":"bb9196d0-9cae-4e33-8996-4b6b500c9593","agent":"sentinel","category":"gotcha","title":"Dieline guide-rule masks silently return ZERO: guide colour varies per piece AND the threshold predicate can exclude the real colour","problem":"Across five artwork reviews on 16 OCT 2026 the magenta colour-mask used to detect dieline guide rules crossing live copy returned ZERO hits on two different files, which reads as a clean 'no guides over copy' result and would have shipped an undetected overprint. Two separate root causes: (1) PH Defend CN-CAP-URINARYTRA-90BG's guides are BLUE RGB(46,48,146) because magenta is that package's own brand colour, so no magenta exists to find; (2) Age Defend CN-CAP-ANTIAGINGF-60CT's guides genuinely ARE magenta at RGB(236,0,140) but the mask predicate b>150 excluded them because b=140. A zero result from a colour predicate is indistinguishable from a pass.","solution":"Never reuse a colour predicate validated on a sibling piece. Per file: (1) render the page at 600dpi; (2) sample ACTUAL pixels — Counter() the RGB tuples of the suspect rule rows and print the top values before building any mask, rather than assuming the portfolio-standard magenta; (3) re-derive the mask bounds from the sampled tuple with slack on every channel (for RGB(236,0,140) use b>120, not b>150; for RGB(46,48,146) build a blue predicate entirely); (4) VALIDATE the rebuilt mask by reproducing a known-good coordinate from the logged baseline review — on Age Defend the rebuilt mask reproduced V1's logged rule position y1613-1614 exactly, which is what proved the mask was live rather than just differently broken. On PH Defend, once the blue mask was correct the guide positions came back identical to all three siblings, which converted nine per-SKU findings into one batch-level template condition. Treat ANY zero-hit colour mask as unproven until a baseline coordinate is reproduced. Same failure class as the AdaptoDrive green-channel retraction. Patched into keyline-clipping-and-trim-safety.md as a two-row failure-mode table.","code_snippet":null,"skill":"artwork-proof-review","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-17","tags":null,"metadata":null,"created_at":"2026-10-17T15:00:41.281778+00:00","updated_at":"2026-10-17T15:00:41.281778+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":"f5f77d98-e568-4201-b183-83a383d1bdab","agent":"scout","category":"gotcha","title":"skill_manage hard-caps each skill file at 100,000 chars — check size before appending, route overflow to a new reference file","problem":"During the same Oct 16 handoff-doc background-review pass, skill_manage writes failed twice back-to-back: first with \"SKILL.md content is 100,180 characters (limit: 100,000). Consider splitting...\" and then, after moving the content, again with \"references/jd-formatting-and-pdf-rendering.md content is 100,135 characters (limit: 100,000)\" — both files were already right at the cap from prior growth, so appending the new handoff-doc notes pushed each one over before the write was attempted, burning two extra API-call retries.","solution":"skill_manage enforces a hard 100,000-character ceiling per file (main SKILL.md and every references/*.md file individually). Before appending substantial new content to an existing skill/reference file, skill_view it first and check its returned size — if it's already near 100K, don't try to patch more into it. Instead create a new standalone references/<topic>.md file for the new content (this is exactly how recruiting-scout/references/leave-coverage-handoff-docs.md came to exist) and add a one-line pointer from SKILL.md to the new file rather than inlining it. A background-review pass that discovers the 100K error only after already drafting the content wastes a full retry cycle — check size proactively instead.","code_snippet":null,"skill":"recruiting-scout/SKILL.md","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-17","tags":null,"metadata":null,"created_at":"2026-10-17T15:00:40.81699+00:00","updated_at":"2026-10-17T15:00:40.81699+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":"c2fef8f0-266d-491c-a93c-180f7278775d","agent":"probe","category":"pattern","title":"Fallback for Meta Ad Library when Atria is down: capped Apify runs with a receipt file per run so a crash never pays twice","problem":"Atria went down through the HiveMind proxy with HTTP 403 (atria_tracker_scrape, lymphoria_atria_bridge, rosabella_tracker_scrape and all monitoring_census_* jobs failed). Competitor monitoring for 46 configured pages then depended on paid Apify scrapes. A cron that crashes or times out halfway through a paid actor launch can either launch the same run again and pay twice, or leave the run going with no record of it. A capped newest-first sample also must never be reported as full-library coverage.","solution":"Use actor curious_coder~facebook-ads-library-scraper and pin build 2.7.26. For each page, build the URL https://www.facebook.com/ads/library/?active_status=all&ad_type=all&country=ALL&view_all_page_id=<id>&search_type=page&media_type=all&sort_data={direction:desc,mode:recent}. Send the body {urls:[{url}],count:70,limitPerSource:70,scrapeAdDetails:true,'scrapePageAds.sortBy':'most_recent',proxy:{useApifyProxy:true},runTag:'probe-fallback-<uuid4hex>'} and the query params {build,timeout<=140,maxItems:100,maxTotalChargeUsd:'0.20'}. Run sequence: (1) Write a receipt JSON at state_dir/receipts/<tag>.json before doing anything else, and add charge_cap to a running 'reserved' total. Stop the batch once reserved+cap would go over the paid budget. (2) Set launch_uncertain=true and save. (3) POST /acts/<actor>/runs. (4) Save run_id, defaultDatasetId and status right away and set launch_uncertain=false, so the remote run ID is on disk before polling starts. (5) Poll GET /actor-runs/<id> every 2s until the status is SUCCEEDED, FAILED, ABORTED or TIMED-OUT, saving usageTotalUsd as you go. If the page deadline is within 18s, POST /actor-runs/<id>/abort. On resume: a receipt with run_id continues polling. A receipt with launch_uncertain=true and no run_id refuses to relaunch ('reconcile receipt manually'). A consumed receipt allows a new tag. Rotate pages starting from state['last_page'] so that capped batches cover the whole set over time. Write every result to monitoring_coverage with mode='apify-newest', partial=1 and reason 'bounded newest sample; NOT census'. Report coverage=PARTIAL, full_census=false. A watchdog cron counts pages without a successful receipt in the last 24h (today: 45/46). An ADS_NOT_FOUND result such as resilia/Dr-Michael-Rawlings m108432878597216 is labeled 'query unverified, not verified inactive' and is never treated as proof the page has stopped advertising.","code_snippet":"r.update(launch_uncertain=True,actor_input=body,actor_limits=params);save(path,r)\nd=api('POST','/acts/'+ACTOR+'/runs',params=params,body=body)['data']\nr.update(run_id=d['id'],dataset_id=d.get('defaultDatasetId'),status=d['status'],launch_uncertain=False)\nsave(path,r)  # remote identity BEFORE polling -> resumable without paying twice","skill":"probe/scripts/monitoring_apify_fallback.py","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-17","tags":null,"metadata":null,"created_at":"2026-10-17T15:00:40.293496+00:00","updated_at":"2026-10-17T15:00:40.293496+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":"ae4c95e5-3ac7-42db-a8f7-c7a57160274d","agent":"prime","category":"gotcha","title":"Non-square gallery image present in listing attributes but hidden on PDP: escalate re-upload, then 2000x2000 white pad","problem":"Cellnova NMN B0HDC2NQ4N (CN-CAP-NMNSUPP-60CT-R1): Weston reported that the 2-jar supplement-facts/back-label image (PT04, 71bkh74zuVL, 1280x1120) was missing from the live PDP. The catalog and listing attributes both still had it in slot 4 with issues=0, but the PDP image popup showed only 8 thumbnails. Every API-side check passed while the image was invisible to customers.","solution":"Trust a customer-side screenshot over the API readback. Escalate in two steps, as Weston directed: (1) re-upload the IDENTICAL bytes under a new Supabase path (prefix cellnova-pt04-reupload-20261017) and PATCH that slot only, so Amazon re-ingests the image. This did not fix it: the next PDP screenshot still showed 8 thumbs. (2) Pad the image to a 2000x2000 square on pure white (no resize of the artwork itself, just canvas padding), upload it under a new prefix (cellnova-pt04-square-20261017), and PATCH only that slot. During an image-only update, never touch title, bullets or description. The likely cause is the non-square 1280x1120 aspect plus a dimension under 1600px, which Amazon may silently drop from the PDP carousel even when the attribute is accepted. Ship gallery slots as square images of at least 1600px (we use 2000x2000) so this never happens. Ask Weston for a fresh PDP screenshot to confirm; we have no API signal for 'hidden on PDP'.","code_snippet":null,"skill":"brandmind/amazon-listing-launch","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-17","tags":null,"metadata":null,"created_at":"2026-10-17T15:00:39.815104+00:00","updated_at":"2026-10-17T15:00:39.815104+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":"0e43c05a-9e8f-44ce-9842-c9389ca08a07","agent":"prime","category":"gotcha","title":"Multipack gallery PATCH ACCEPTED but listing still shows old images while packs sit in 100521 review hold","problem":"On 2026-10-17 we pushed the 8-slot InfinityOne gallery to 5 new FBA multipacks (CN-KIT-CAP-UROLITHINFOR-60CT-2PK..6PK, ASINs B0HN9T31ZS/B0HN9FV6J1/B0HN9DHMYQ/B0HN9KZ1HR/B0HN97LZ1L). All 40 Listings API PATCHes came back ACCEPTED with issues=0. The listing attributes still returned the OLD 2-image gallery (slot1 41y7jhssJZL.jpg, slot2 51xSX0L70IL.jpg, slots 3-8 empty) and Catalog Items GET returned 404. Re-submitting looked like the obvious fix but was the wrong call.","solution":"Before you call a multipack image PATCH a failure, check the pack's issue codes with a read-only listing GET (gallery_batch_push.live_attrs). The packs carried 100459/100477 (supplement cGMP/TIC document gate) plus 100521 ('under review, up to 48h'). Catalog 404 means the ASIN is not published yet. While 100521 is open, Amazon accepts the PATCH and queues it, and other_product_image_locator_1..8 keeps showing the previous values until the review clears. Rules: (1) do NOT resubmit, because duplicate submissions can stack more review cycles; (2) save the push state (e.g. /tmp/gallery_push_<ASIN>.json with the Supabase URLs sent); (3) schedule a one-shot READ-ONLY cron about 6h out that reads slots 1-8 per SKU and reports how many slots are filled and differ from the old values, whether main slot0 is unchanged, and the current issue codes; (4) re-PATCH only if 100521 has cleared and the slots still hold old values. The single-pack parent (B0HDC2PQJQ, already published) took the same push immediately and content-verified (mean abs diff <0.7 on all slots). So the lag is caused by the pack's review state, not by the payload.","code_snippet":"import os, gallery_batch_push as g\nos.environ.pop('HIVEMIND_TOKEN', None)\nOLD = {1:'41y7jhssJZL.jpg', 2:'51xSX0L70IL.jpg'}\nfor sku in [f'CN-KIT-CAP-UROLITHINFOR-60CT-{n}PK' for n in range(2,7)]:\n    a = g.live_attrs(sku)  # read-only\n    # count slots 1..8 filled and not ending with OLD[i]; print issue codes","skill":"brandmind/amazon-listing-launch","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-17","tags":null,"metadata":null,"created_at":"2026-10-17T15:00:39.67307+00:00","updated_at":"2026-10-17T15:00:39.67307+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":"566622ad-b142-4d91-807c-ed77c5b1c673","agent":"nexus","category":"pattern","title":"CIN7 kit/multipack SKU creation pattern: AutoAssembly=False + BOMType=Assembly + explicit BillOfMaterialsProducts","problem":"Needed to create 22 new CN-KIT-GUM-* FBA multipack SKUs in CIN7 (2-6 packs of existing gummy root SKUs like CN-GUM-KIDSFOCUS-60BG) that behave as build-on-demand assemblies rather than auto-assembled kits, matching the existing sibling CN-KIT-GUM-* template conventions used elsewhere in the catalog.","solution":"Verified working CIN7 /product POST payload for this kit pattern: Type='Stock', Status='Active', BillOfMaterial=True, AutoAssembly=False, AutoDisassembly=False, BOMType='Assembly', AssemblyCostEstimationMethod='Average Cost', QuantityToProduce=1.0, with BillOfMaterialsProducts=[{ComponentProductID: <root product GUID>, Quantity: <pack count>, WastagePercent: 0.0, WastageQuantity: 0.0, CostPercentage: 0.0}]. Also set AdditionalAttribute3='Yes' and AttributeSet='Sales - Product Attributes' to match the attribute convention on sibling kit SKUs. Post-create verification via GET /product?ID=<id>&IncludeBom=true confirmed Status=Active, BillOfMaterial=true, and correct BOM component/qty -- this payload shape is the reusable template for any future CN-KIT-* multipack additions sourced from a single root SKU.","code_snippet":null,"skill":null,"platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-17","tags":null,"metadata":null,"created_at":"2026-10-17T15:00:39.180833+00:00","updated_at":"2026-10-17T15:00:39.180833+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":"0c8de352-874c-46a2-bd83-ae5342cb2d9d","agent":"nexus","category":"api_integration","title":"ShipHero product_create GraphQL mutation requires warehouse_products and rejects 'active' field","problem":"When creating new ShipHero SKUs for the CN-KIT-GUM-* Amazon FBA gummy multipacks (via create_gummy_kits.py), the initial product_create mutation call failed with HTTP 400 and two distinct schema errors: \"Field 'CreateProductInput.warehouse_products' of required type '[CreateWarehouseProductInput]!' was not provided\" and \"Field 'active' is not defined by type 'CreateProductInput'\". Because product_create failed, the product never existed in ShipHero, which then caused the follow-up assembly_add_components call to fail with 'Product <sku> not found' (code 5) -- a cascading failure that looks like an assembly-step bug but is actually rooted in the create step.","solution":"ShipHero's CreateProductInput schema mandates a non-empty warehouse_products array even for a brand-new, zero-stock SKU, and it does NOT accept an 'active' field at all (omit it entirely -- new products default active). Working mutation shape (see create_shiphero() in /home/wcorica/.hermes/profiles/nexus/workspace/create_gummy_kits.py): `product_create(data: { sku, name, barcode, country_of_manufacture: \"US\", customer_account_id, is_assembly: true, warehouse_products: [ { warehouse_id: \"<WAREHOUSE_ID>\", on_hand: 0 } ] })`. After create succeeds, set assembly BOM in two follow-up calls: (1) product_update with is_assembly: true, then (2) assembly_add_components(data: { assembly_sku, customer_account_id, components: [ { sku: <root_sku>, quantity: <qty> } ] }). Always verify with a product(sku:) query after each step -- if assembly_add_components returns 'Product not found', check that product_create actually succeeded first rather than assuming the assembly call itself is broken.","code_snippet":null,"skill":"create_gummy_kits.py","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-17","tags":null,"metadata":null,"created_at":"2026-10-17T15:00:39.046734+00:00","updated_at":"2026-10-17T15:00:39.046734+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":"ad3907a0-bf48-4052-acb9-caeff8ca6842","agent":"blitz","category":"gotcha","title":"997 of the 1,677 handle records in creatify-windows-fast.json have ONLY a d30 block — no d7/d14 — so any ad-hoc consumer doing `rec.d7.cost` throws on 59% of the index; the shipped report script silen","problem":"While auditing the 2026-10-16 report artifact I wrote ad-hoc checks that iterate the d7 block of every handle in data/creatify-windows-fast.json and they blew up on a large fraction of keys. Counted directly against the file: 1,677 records carry a `d30` block and only 680 carry a `d7` block — exactly 997 records are d30-only. The cause is that a record is only given a window block if that window's sweep produced rows for it, so handles whose spend/revenue all fell outside the trailing 7 and 14 days are built from a single window and the narrower keys are simply absent rather than present-and-zero. creatify_report_pv.js happens to guard every access, so the report prints fine and nothing in GATES complains — the breakage only appears in new scripts, which means every future ad-hoc audit rediscovers it the hard way. This is the same class of trap as the 2026-09-22 '30d baseline roster spend = $0.00' incident, where creatify-windows.json carried only d7/d14 keys and a missing-key read was mistaken for a real finding; here the asymmetry is inverted (d30 present, d7/d14 missing) which makes the stale muscle memory actively wrong.","solution":"Treat window blocks in creatify-windows-fast.json as OPTIONAL, always, in both directions — never assume d7/d14 exist because d30 does, and never assume d30 exists because an older artifact only had d7/d14. Concrete rules for any script reading this artifact:\n\n1. Read through a zero-filling accessor rather than dotting into the block: `const W = (rec, k) => rec?.[k] || { cost:0, rev:0, orders:0, items:0, org_rev:0, org_orders:0, org_items:0, total_rev:0, total_orders:0 };` then `const d7 = W(rec,'d7');`. Never `rec.d7.cost`.\n\n2. A missing block means 'no rows in that window', which is semantically zero — but distinguish it in output. If you are reporting coverage, count `withD7` separately from `withD30` and print both, because a d7-only-missing creator is a creator with no recent activity, not a data error.\n\n3. Sanity-check the artifact header before trusting any of it: creatify-windows-fast.json carries `generated_at`, `elapsed_sec`, `apiFail`, `incomplete`, `calls`, `rows`, `distinct_items` and an explicit `windows` map ({d7: 2026-10-09..10-15, d14: 2026-10-02..10-15, d30: 2026-09-16..10-15} on the 10-16 build). Assert apiFail === 0 && incomplete === 0 and that windows.d30.END is yesterday before reading a single handle — that is cheap and it is what makes a later 'the numbers are weird' investigation convergent.\n\n4. Verify the shape yourself instead of trusting a schema memory, in one command: `rg -c '^   \"d7\": \\{' data/creatify-windows-fast.json` vs `rg -c '^   \"d30\": \\{' data/creatify-windows-fast.json` (680 vs 1677 on the 2026-10-16 build). Do this FIRST on any new artifact build, because the ratio changes with every pull.","code_snippet":"// shape check, 2026-10-16 artifact (654 KB, 38,776 lines)\n// $ rg -c '^   \"d7\": \\{'  data/creatify-windows-fast.json   ->  680\n// $ rg -c '^   \"d30\": \\{' data/creatify-windows-fast.json   -> 1677\n// => 997 handle records are d30-only\n\n// WRONG — throws on 997 of 1677 records\n// for (const [h, rec] of Object.entries(art.handles)) spend7 += rec.d7.cost;\n\n// RIGHT — windows are optional in BOTH directions\nconst ZERO = { cost:0, rev:0, orders:0, items:0,\n               org_rev:0, org_orders:0, org_items:0,\n               total_rev:0, total_orders:0 };\nconst W = (rec, k) => (rec && rec[k]) || ZERO;\n\nlet spend7 = 0, withD7 = 0, withD30 = 0;\nfor (const [h, rec] of Object.entries(art.handles)) {\n  if (rec.d7)  withD7++;\n  if (rec.d30) withD30++;\n  spend7 += W(rec, 'd7').cost;\n}\nconsole.log(`coverage d7=${withD7} d30=${withD30}`);   // print it, don't hide it\n\n// and gate on the header before reading anything:\nif (art.apiFail !== 0 || art.incomplete !== 0) throw new Error('artifact incomplete');\nif (art.windows.d30.END !== yesterdayISO()) throw new Error('artifact stale');","skill":"creatify-reporting","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-17","tags":null,"metadata":null,"created_at":"2026-10-17T15:00:38.485818+00:00","updated_at":"2026-10-17T15:00:38.485818+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":"172f34f5-c794-4086-8749-59831b540771","agent":"blitz","category":"bug_fix","title":"The Creatify report understated Greg Dawson by 78x ($17.61/1.63x printed vs $1,379.24 -> $238.00 = 0.17 ROAS real) because his roster TikTok cell holds TWO handles and only one exists in the artifact","problem":"Weston asked 'Is this accurate?' of the 2026-10-16 Friday Creatify report (creatify-report-2026-10-15.txt). Everything about the data pull was clean: data/creatify-windows-fast.json was generated 2026-10-16T06:37:32Z with apiFail 0, incomplete 0, 8453 calls, 9291 rows, windows ending 2026-10-15, and the headline 30d ad spend $3,502.07 -> ad rev $11,412.35 (ROAS 3.26, 391 ord) ties to the artifact exactly, as do Kotera 5.91x / Lisa 5.87x / Kristina 0.37x per-handle. One line was still materially wrong: Greg Dawson printed at $17.61 spend / 1.63x. His real number is $1,379.24 spend -> $238.00 revenue = 0.17 ROAS on the 9 Lympho Defend boosts fired 2026-10-03, making him the WORST performer on a 56-creator roster rather than a rounding footnote. Root cause is a two-sided name join, not a bad API read. Row 13 of data/creatify-roster-latest.csv reads TikTok = '@gregdawsonbiz , @gregs.daily.deals'. The report's author-resolver (video page -> @handle) resolved all 9 boosted videos to gregdawsonbiz, and gregdawsonbiz is ABSENT from the artifact's handle index — zero keys in creatify-windows-fast.json contain 'dawson'. Only gregs.daily.deals ($17.61) was found and summed. The emitted CSV even prints Handle: \"gregdawsonbiz gregs.daily.deals\", so the join KNEW both names and still reported one. This is not a one-off: 19 of 60 roster rows carry multiple handles in the TikTok cell, and per-handle presence in the artifact is spotty across them (Lisa Chambers' simplylisalynn ABSENT, Kotera's kmwontmiss ABSENT, Angelique 3 of 4 ABSENT, Jennifer Revis 3/3, Erica Pfaff 3/3, Ana Pantoja 2/2, Carolina Vazquez 2/2). The lethal part is that 'ABSENT' is ambiguous — it means EITHER genuinely zero spend OR the resolver never produced that handle — and the report cannot tell the two apart. Greg proves the second case is real and expensive. Note the spend is NOT missing from the account totals (it lives inside the Lympho Defend lane rollup); it is missing from the creator's NAME, which is exactly what a per-creator accountability report exists to produce.","solution":"ADD A FIFTH GATE: BOOST-LEDGER RECONCILIATION. Freshness, apiFail=0, monotonicity and coverage all passed on a wrong report because they only interrogate the API pull. The join needs its own gate, and we already own the ground truth to build it: every boost we fire is written to a ledger (data/sheet-boosts-1003.json sessions[], data/adhoc-boosts.json batches[]) with {name, item_id, spu, campaign}. Rule: for every session fired inside the report window, that item_id's spend MUST appear under SOME roster handle in the final rollup. Any boosted item whose spend lands under no roster name is an attribution miss -> DO_NOT_PUBLISH, print the orphan item_ids. That is the boost/algo split line Weston mandated 2026-10-08 and it would have caught Greg on the first run.\n\nTO VERIFY A SUSPECT CREATOR LINE RIGHT NOW, bypass the handle index entirely and query the lane the items actually live in (pattern proven in greg_lane_check.js, which returned 2,278 rows / fails=0 / 9 of 9 Greg items present, each $122-$185 spend and $0-$58 revenue). Filter by the item_ids from the ledger, never by handle: POST hivemind-api {platform:'tiktok_ads', action:'read', endpoint:'/gmv_max/report/get/', method:'GET', params:{advertiser_id:'7340385658755891201', store_ids:['7495291933339519508'], data_level:'CREATIVE', dimensions:['item_id'], metrics:['cost','gross_revenue','orders','roi'], start_date:'2026-09-16', end_date:'2026-10-15', filtering:{campaign_ids:[CAMP], item_group_ids:[SPU]}, page_size:100}} paging on data.page_info.total_page, and abort on any r.code !== 0 (an empty list is only meaningful when code===0 — the 2026-10-03 phantom-zero rule). Greg's lane was campaign 1870816040131793 x SPU 1730131946467791380.\n\nRESOLVER FIX: when the roster cell yields N handles for one creator, the rollup must sum over ALL N and must additionally record which of them the artifact actually contained. Emit a per-creator `handles_found` / `handles_missing` pair in the CSV so 'zero spend' and 'handle never resolved' stop looking identical. A creator whose RESOLVED handle (the one the video page returned) is in handles_missing while the ledger says we fired boosts for them is a hard error, not a footnote.\n\nAlso immaterial but known: the report script sums slightly differently from the handle rollup (printed Kotera 168 ord vs artifact 152; Lisa $247.88/73 ord vs artifact $243.94/72). Do not chase those as bugs — they are a sum-order difference, unlike the Greg case which is a missing addend.","code_snippet":"// greg_lane_check.js — verify a creator by ITEM_ID against the lane, not by handle\nconst led = JSON.parse(fs.readFileSync(path.join(WS,'data/sheet-boosts-1003.json'),'utf8'));\nconst GREG = led.sessions.filter(s => s.name.includes('Greg'));\nconst WANT = new Set(GREG.map(s => s.item_id));          // ledger = ground truth\nconst SPUS = [...new Set(GREG.map(s => s.spu))];\n\nfor (const spu of SPUS) {\n  let p = 1, lp = 1;\n  do {\n    const r = await api('/gmv_max/report/get/', {\n      advertiser_id: ADV, store_ids: JSON.stringify([STORE]),\n      data_level: 'CREATIVE', dimensions: JSON.stringify(['item_id']),\n      metrics: JSON.stringify(['cost','gross_revenue','orders','roi']),\n      start_date: '2026-09-16', end_date: '2026-10-15',\n      filtering: JSON.stringify({ campaign_ids: [CAMP], item_group_ids: [spu] }),\n      page: p, page_size: 100 });\n    if (r.code !== 0) { fails++; break; }                // never fall through on non-zero\n    for (const row of (r.data?.list || [])) {\n      const id = String(row.dimensions?.item_id || '');\n      if (WANT.has(id)) hits.push({ id, cost:+row.metrics.cost, rev:+row.metrics.gross_revenue });\n    }\n    lp = r.data?.page_info?.total_page || 1; p++;\n  } while (p <= lp);\n}\n// result: 9 of 9 items present, $1,379.24 -> $238.00 (0.17x)\n// report printed $17.61 -> $28.70 (1.63x) from @gregs.daily.deals only\n\n// THE GATE that was missing (run before publish):\n// for (const s of ledgerSessionsInWindow) {\n//   if (!rollupItemIds.has(s.item_id)) orphans.push(s);   // spend with no roster NAME\n// }\n// if (orphans.length) throw new Error('DO_NOT_PUBLISH: ' + orphans.length + ' unattributed boosts');\n\n// roster row that caused it (data/creatify-roster-latest.csv line 13):\n// Greg Dawson,gldawson@gmail.com,...,\"@gregdawsonbiz , @gregs.daily.deals\",17k,265k,MO-INF-0010,...","skill":"creatify-reporting","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-17","tags":null,"metadata":null,"created_at":"2026-10-17T15:00:38.355734+00:00","updated_at":"2026-10-17T15:00:38.355734+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":"7d69094d-92aa-4f32-8e1b-e2cd7a6d4ba2","agent":"viral","category":"business_rule","title":"Automated +30% ABO scaling every 3h needs an intraday spend gate, so the same set isn't raised again before its new budget has been tested","problem":"Weston gave a standing order: run the Shelf Builder ABO scaling protocol every 3h from 6am to 9pm PT, +30% each time, with no per-run approval, and run a separate cut review twice a day. The 72h CAC gate (<$50, ≥5 sales, pacing ≥80% on yesterday) uses lagging data. Without another check, a set raised at 6am would still pass at 9am and noon and get compounding raises before it had spent anything at the higher budget.","solution":"Setup used: a no_agent cron (hourly at :05, acting only in the PT slots 6/9/12/15/18/21) calls scripts/sb_scale_auto.sh, which runs scale_auto_run.py. Window: 72h PT, with pacing measured on the previous full day. Gates: 72h CAC <$50, ≥5 sales, pacing ≥80%. Intraday gate: any set already raised today is only eligible again once today's spend reaches ≥80% of the new budget pro-rated to the current hour. Every raise is read back (e.g. 10/16 6AM: 19/19 read back, +$1,393.07/day), results are written to scale30_<TAG>_results.json and protocol_log.csv, and the cron output is the notification. The kill switch is the file SCALE_AUTO_STOP. Cuts are kept separate and review-only: a no_agent cron at :20 for PT slots 8 and 19 runs cut_auto_run.py and writes cut_<TAG>_recs.json. Pauses only happen after Weston says 'go': WESTON_APPROVED=1 python3 cut_auto_exec.py <TAG>. This follows his law of scaling only proven winners and never killing without approval.","code_snippet":null,"skill":"sb-abo-scaling","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-16","tags":null,"metadata":null,"created_at":"2026-10-16T15:01:17.132322+00:00","updated_at":"2026-10-16T15:01:17.132322+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":"a57258c1-b474-4a88-95b7-aaec526fe791","agent":"viral","category":"gotcha","title":"New Meta ad account owned by an agency business: Trybe connection alone isn't enough, so pages and pixel have to be shared and checked separately","problem":"Weston asked for a ChloroZen Trybe campaign on a brand-new ad account (act_2279224506258055, 'Clean Nutra (ChloroZen) - 02 Ares'). That account sits under the agency business WT - Agency01 (2198525764402961), not the main Webstone Marketing business (681450815374544). Once Trybe showed the connection as ACTIVE, the build still could not go ahead: the account had 0 pixels and 0 promote_pages, and Trybe returned PAGE_NOT_ADVERTISABLE. The agency business had 0 client or owned pages. Weston said the pages 'were added', but no page showed up through the API.","solution":"Checklist before any Trybe build on a new account. (1) Do a read-only probe through HiveMind: check account status, timezone, owning business, funding, and our ADVERTISE/MANAGE tasks, then count campaigns, pixels, promote_pages and audiences. (2) Check the Trybe brand-portal connections list for the account. Weston has to connect it on the Trybe side, and once he does a new connection UUID appears (here de131dcb-...). (3) Share the existing pixel to the new account through HiveMind. Here that was Clean Nutra Pixel - Primary 997334514499253. Read it back on the account's /adspixels edge. (4) Partner-share the brand pages into the agency business's client_pages: The Chlorophyll Lab 1358081254057293, Chlorophyll Secrets 1269032499625695, and Clean Nutraceuticals 110559444780282. Read client_pages back to confirm. (5) Assign users to each page. This worked for Clean Nutraceuticals but failed for both chlorophyll pages with code 10/1690202, so expect partial failures and report them. (6) Even after a confirmed share, promote_pages can stay [] and Trybe can keep returning PAGE_NOT_ADVERTISABLE while things propagate. Run a poller instead of retrying the build (cz_pages_poll.py checked every 5 min for up to 80 min). (7) Check Trybe connection settings for the product's copy template and LP. This connection only had GT/SB/VG templates, so plan manual copy plus a custom URL (/products/chlorozen-bogo). (8) Check the exclusion audience early. There was no product-specific Klaviyo buyer list in any of the 10 accounts. The nearest substitute is the main account's all-store 'CN | Shopify Purchasers 180d' (120251005010810756, 53.1k), but sharing a custom audience across businesses into an agency-owned account is unverified. Raise that with Weston before building. Create nothing until every blocker is cleared and launch is explicitly approved.","code_snippet":null,"skill":"trybe-ugc-ad-builder","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-16","tags":null,"metadata":null,"created_at":"2026-10-16T15:01:17.000001+00:00","updated_at":"2026-10-16T15:01:17.000001+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":"5f801bb0-c108-43a8-9724-b96661cb0854","agent":"vault","category":"business_rule","title":"'PAID' on a PO means bills-settled, not PO-fully-billed — without the Cin7 PO order value the fully-billed test cannot be run and must not be implied","problem":"Sara overwrote 21 rows I had marked PAID with PARTIALLY PAID. Tested live against QBO, every one of those POs showed billed == paid with zero open balance — e.g. PO-00479 billed $78,500.00, paid $78,500.00, open $0.00 — so on the arithmetic my verdict was correct. She was answering a different question: a $120,000 PO carrying one settled $78,500 invoice is PAID by Bill.Balance == 0 and partially BILLED in reality, with goods still outstanding. Both readings are true and only one answers 'where do we stand on this PO?'. The trap is that quoting the arithmetic silently resolves the ambiguity in your own favour, and the Cin7 export this report was built from carries no PO order value at all, so the fully-billed test cannot be run from the data in hand.","solution":"Before designing any PO status logic, check whether the source export actually carries a PO order value: hdrv = [h for h in her_header if h and re.search(r'PO (Total|\\$|Value)|Order \\$', h)] — on the 2026-10-16 Cin7 export this returned [], so the test was impossible and the honest output is to SAY the test cannot be run, request the Cin7 export carrying PO value, and then add explicit 'PO value' and '% billed' columns so bills-settled and fully-billed are separate concepts rather than one overloaded word. Keep the STATUS vocabulary strictly to payment states — PAID / PARTIALLY PAID / UNPAID / NOT FOUND — and push every reason, evidence grade and Cin7 state into the Match tier / Match reasoning columns; an entry like 'NO INVOICE RAISED (Cin7)' is an explanation wearing a verdict's clothes and drew 'why did you use this comment instead of Paid, Unpaid or not found', after which she hand-edited 52 rows to UNPAID. Adopt her vocabulary. Two related judgement rules confirmed the same day: (a) a '... DIFF' or '... ADJ' sibling of an already-paid DocNumber carrying a trivial open balance is a price-variance stub, not an unpaid invoice — bill 20975 $8,960.00 paid plus 20975 DIFF $280.00 open on PO-00291 ($46,432.98) is mechanically PARTIALLY PAID but she called it PAID and that is the sensible read; call the PO paid, note the residual and list the stub so someone can write it off. (b) Cin7's payment flag is not the system of record — all 9 Avery POs Cin7 showed UNPAID were fully settled in QBO, $52,625.35, every one via Bill.com Money Out Clearing; flag the staleness explicitly because it is actionable for whoever maintains Cin7. Where QBO contradicts her verdict, write the evidence into her reason column and tint it rather than changing the verdict: 'VERDICT PARTIALLY PAID (hers, untouched) | Basis: formal PO document — QBO: every bill on this PO is settled, billed 78,500.00, paid 78,500.00, open 0.00. Treat as partially BILLED, not partially paid.' 77 such Basis notes shipped across four categories with zero verdict edits.","code_snippet":"# gate the status design on whether PO order value even exists\nhdrv = [h for h in her_header if h and re.search(r'PO (Total|\\$|Value)|Order \\$', h)]\nif not hdrv:\n    note = ('Cin7 export carries no PO order value - whether each PO is FULLY BILLED '\n            'cannot be tested. PAID here means every bill raised against the PO is settled.')\n\n# price-variance stub: paid sibling + trivial open DIFF/ADJ => PO is PAID\nif re.match(rf'{re.escape(base_docnum)}\\s*(DIFF|ADJ)$', doc, re.I) and bal < 0.02 * po_total:\n    verdict, residual = 'PAID', bal","skill":"finance/po-payment-status-verification","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-16","tags":null,"metadata":null,"created_at":"2026-10-16T15:01:16.464385+00:00","updated_at":"2026-10-16T15:01:16.464385+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":"4d62ef89-71fc-4056-aaae-6a263a08417e","agent":"vault","category":"workflow","title":"When a reviewer returns your workbook with hand-typed verdicts: settle authorship from docProps/core.xml, normalise before counting, and never retract a correct finding to compensate for a wrong accus","problem":"Sara returned PO Status Cin7 V1.xlsx — one tab, 437 rows, 14 columns — with 'review and let me know if anything needs to be updated' and her own verdicts typed over mine. I got it wrong twice in opposite directions in a single session: first I diffed V1 against my pickled data, declared 'your edits contradict QBO' and listed her supposed errors; she replied 'you marked them'; I then re-diffed against the WRONG TAB (the merged workbook's first tab instead of All 437), got a 68% column match, concluded the whole file was mine and withdrew the entire finding; she replied 'I dont know what changed'. Compounding it, a raw cell-by-cell comparison reported 758 differences when only 77 were real edits, and a separate verdict diff reported 238 differences of which 160 were my own decorative '(AMAZON - disregarded)' suffix that she had simply stripped. Accusing the reviewer of my defect burned one turn; over-retracting a correct result burned another and destroyed a real finding.","solution":"Four mechanical steps, in order, before forming any opinion. (1) MD5 the returned file against every workbook in the workspace — byte-identical means she is pointing at CONTENT or version currency, not changes; different or a filename you never produced means she annotated it and her column is the priority input. (2) MD5 does not say WHO changed it — read the zip's docProps. zipfile.ZipFile(path).read('docProps/core.xml') and pull dc:creator, cp:lastModifiedBy, dcterms:created, dcterms:modified, plus <Application> from docProps/app.xml. dc:creator 'openpyxl' = your output; a human name + Application 'Microsoft Excel' + a later dcterms:modified = she opened and saved it. Hers read dc:creator 'Sara Skef', Excel 16.0, modified 2026-10-16T04:17Z. This is decisive, costs one call and needs no user input — when she says 'I dont know what changed' that is a prompt to resolve it from the artifact, not to ask again. (3) PIN THE COMPARISON BASIS — name the exact file AND tab on both sides and assert her_hdr == my_hdr before reading any diff. A sub-100% match on columns that should be identical means you picked the wrong tab; investigate the basis, never the data. (4) Normalise before counting or the diff lies by 10x: collapse whitespace, fold em/en dashes to '-', strip timestamps (Excel round-trips 2025-02-13 to 2025-02-13 00:00:00 — that produced 520 of the 758 noise diffs across Order Date / Last paid) and strip your own decorative suffixes. The four noise classes here were 520 date artifacts, 161 cells she cleared on Amazon rows, whitespace/dash variants, and then 77 genuine VERDICT edits. Report the noise classes separately so the real number is visible. Then diff verdicts and AMOUNTS separately — 238 verdict differences with 0 amount differences immediately rules out a figures problem and proves the dispute is semantic. Classify each surviving edit into exactly three buckets: adopt (she's right — 52 rows relabelled to UNPAID), semantic gap (she's answering a different question — name the ambiguity, fix neither side), contradicts the ledger (test live and report as a finding: her cited invoice 20-88630 does not exist in QBO under any spelling, which reframes as an A/P entry gap rather than 'you're wrong'). Distrust a zero-row result from a hand-typed vendor Id — querying VendorRef '3697' for Aegle returned zero bills and would have 'confirmed' her note was unsupported; the cached vendor map gave the real Id 605 with 10 bills. On the rebuild, build on HER returned file and annotate, never overwrite: copy every verdict cell through verbatim, write QBO's disagreement into her existing reason column and tint it, and assert zero verdict changes and zero amount changes before saving. Rule: a retraction needs the same evidence bar as the original claim.","code_snippet":"z = zipfile.ZipFile(path)\nx = z.read('docProps/core.xml').decode('utf8','ignore')\nfor tag in ['dc:creator','cp:lastModifiedBy','dcterms:created','dcterms:modified']:\n    m = re.search(rf'<{tag}[^>]*>([^<]*)</{tag}>', x); print(tag, m and m.group(1))\n# openpyxl => mine.  'Sara Skef' + Microsoft Excel + later modified => hers.\n\nassert her_hdr == my_hdr, (her_hdr, my_hdr)      # trips => wrong tab, not bad data\n\ndef norm(s):\n    s = re.sub(r'\\s+', ' ', str(s or '').strip()).replace('\\u2014','-').replace('\\u2013','-')\n    return re.sub(r'\\s*\\(AMAZON - disregarded\\)', '', s).strip().upper()\n\n# annotate-never-overwrite rebuild\nassert not [p for p in her_pos if norm(her[p]['VERDICT']) != norm(out[p]['VERDICT'])]\nassert not [p for p in her_pos if any(her[p][c] != out[p][c]\n            for c in ['Billed $','Paid $','Open $','Bill # (invoice no.)'])]","skill":"finance/po-payment-status-verification","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-16","tags":null,"metadata":null,"created_at":"2026-10-16T15:01:16.318965+00:00","updated_at":"2026-10-16T15:01:16.318965+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":"e0e79f5c-f7ba-4baf-b0e0-26f9a264f12e","agent":"vault","category":"business_rule","title":"An end-of-day investment sweep makes gross bank deposits meaningless to a lender — show the sweep as its own line and give a sweep-excluded operating subtotal","problem":"The Chase 2073 analysis across five cycles showed gross deposits of $293,273,752.25 against a business doing roughly $133M of annual revenue. From July 2026 the account runs a nightly end-of-day sweep into JPMorgan U.S. Treasury Plus, holding exactly $2,000,000.00 on ledger and sweeping the excess out and back each day, so $266,292,012.54 of those 'deposits' are the same money being redeemed over and over. Handing a lender the gross figure gets the file disbelieved or the business misread, and the ledger balance sitting at a flat $2,000,000.00 for three consecutive cycles is the only tell on the face of the statement.","solution":"Detect the sweep from the statement descriptor ('INVESTMENT SWEEP' / 'SWEEP' in the upper-cased description, JPMorgan U.S. Treasury Plus, Fund 3919, 'Redemption of Shares') and classify it as its own category ahead of Wire/ACH, because the descriptor also carries book-transfer language that would otherwise bucket it as a wire. Then present BOTH views in the By Type tab: gross money-in and money-out blocks, plus a separate sweep-excluded 'operating' block — here gross deposits $293,273,752.25, of which sweep redemptions $266,292,012.54, leaving operating money in $26,981,739.71 and operating net +$181,960.01 over the five cycles. Exclude the sweep from the Largest Items tab or it occupies every row. Report true cash as the ledger balance PLUS the swept position ($5,257,758.04 + $2,000,000.00 at 30 Sep 2026) — a lender reading only the ledger balance understates available cash by the swept amount. Classification order that worked, applied to the upper-cased statement descriptor: sweep first; then r'\\bBOOK TRANSFER\\b|\\bBOOK XFER\\b|\\bFEDWIRE\\b|\\bWIRE\\b' -> Wire; then the NACHA fields 'ORIG CO NAME' / 'ORIG ID' / r'\\bSEC:(CCD|PPD|WEB|TEL)\\b' -> ACH; then 'PAYMENT TO CHASE CARD' / 'EPAY' -> Card payment; then fall back on the statement SECTION. Direction comes from the section (DEPOSITS AND ADDITIONS is inflow, everything else outflow), never from the amount sign. Report counts alongside amounts — wire volume of 3 in / 3 out across five months IS the answer when a bank is pricing wire activity.","code_snippet":"u = desc.upper()\nif 'INVESTMENT SWEEP' in u or 'SWEEP' in u:                return 'Investment sweep'\nif re.search(r'\\bBOOK TRANSFER\\b|\\bBOOK XFER\\b|\\bFEDWIRE\\b|\\bWIRE\\b', u): return 'Wire'\nif 'ORIG CO NAME' in u or 'ORIG ID' in u or re.search(r'\\bSEC:(CCD|PPD|WEB|TEL)\\b', u):\n    return 'ACH'\nif 'PAYMENT TO CHASE CARD' in u or 'EPAY' in u:            return 'Card payment'\n\n# deliverable: gross AND sweep-excluded, side by side\noperating_in = gross_in - sweep_redemptions   # 26,981,739.71 of 293,273,752.25\ntrue_cash    = ledger_balance + swept_position","skill":"finance/lender-financial-statement-package","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-16","tags":null,"metadata":null,"created_at":"2026-10-16T15:01:16.197993+00:00","updated_at":"2026-10-16T15:01:16.197993+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":"98f70812-7ae5-40fd-942c-41717a950ebd","agent":"vault","category":"bug_fix","title":"Chase statement PDFs: three silent parser defects (atomised text, header-text sections, $-prefixed first row) — only a per-section footing gate catches them","problem":"Building the Bank Analysis tab of the AllSeason lender package from five Chase Platinum Business Checking 2073 statement cycles (May-Sep 2026), three separate parser defects each produced plausible-looking numbers and NONE raised an exception. (1) page.get_text() on a Chase PDF returns every label and figure as its own fragment, so a line-regex over the raw text matched ZERO transaction rows across all five statements. (2) Sectioning on visible header text mis-attributed 22 August ACH debits into FEES because a FEES header appeared mid-page while electronic withdrawals was still running — fees over-reported $180,444.16 and electronic withdrawals under-reported by the identical amount. (3) A money regex of ^-?[\\d,]+\\.\\d{2}$ skipped the FIRST row of every section because Chase prints that one row with a $ prefix, silently dropping $6,593,818.03 from September deposits.","solution":"Parse Chase statements from word geometry, not text lines, and gate every figure on the statement's own control totals before reporting anything. (1) Rebuild visual rows by y-clustering page.get_text('words') with ytol=2.0 — larger merges adjacent rows, smaller splits one row across two buckets. (2) Section off the embedded literal markers in the text layer, not the printed headers: regex r'\\*START\\*([A-Z0-9 &\\-]+?)(?: SECTION)?\\d*\\b' against the upper-cased line, map marker names to canonical sections, and leave `section` unchanged for POST/END prefixes. Skip DAILY ENDING BALANCE, CHECKING SUMMARY, SERVICE CHARGE SUMMARY and ACCOUNT ANALYSIS — they carry dated rows that are not transactions and will double-count. (3) Money regex must be ^-?\\$?-?[\\d,]+\\.\\d{2}$ and strip both ',' and '$'. (4) Start a record at a ^\\d{2}/\\d{2}$ date token, accumulate following lines into desc, close on the next date token or section marker, and if amt is still None take it from the continuation line — descriptions wrap and the amount sometimes lands on the wrap line. (5) THE GATE: parse the CHECKING SUMMARY block separately for both the COUNT and the AMOUNT per section (r'Deposits and Additions\\s+(\\d+)\\s+([\\d,]+\\.\\d{2})') and assert detail == header for both, per section per cycle, plus the balance roll beginning + deposits - (elec + other + atmdebit + fees + checks) == ending. The failure signatures are diagnostic: a count short by exactly one per section is the $-prefix bug; a mirrored over/under pair between two sections is section misattribution, not missing data. All five cycles x five sections footed to $0.00 before any figure was reported, and the reply said so. Also note on the deliverable that this is a statement analysis for lender review, NOT a bank reconciliation — it does not agree the statements to the GL. Separate premise check: 'pull it from QBO' cannot apply to bank statements — QBO holds the ledger, there is no endpoint returning a statement PDF; search workspace/_inbox/2026MMDD-statements-<last4>-.pdf, count the cycles you actually found (six asked, five existed, April 2026 absent) and report the shortfall in the first paragraph. Chase cycles are not calendar months (May ran 01-29 May, June ran 30 May-30 Jun) — label tabs with cycle dates.","code_snippet":"def page_lines(page, ytol=2.0):\n    ws = page.get_text('words')          # x0,y0,x1,y1,word,...\n    b  = collections.defaultdict(list)\n    for x0, y0, x1, y1, w, *_ in ws:\n        b[round(y0 / ytol)].append((x0, w))\n    return [' '.join(w for _, w in sorted(b[k])) for k in sorted(b)]\n\nMONEY = re.compile(r'^-?\\$?-?[\\d,]+\\.\\d{2}$')   # the $ is on the FIRST row only\n\nmk = re.search(r'\\*START\\*([A-Z0-9 &\\-]+?)(?: SECTION)?\\d*\\b', line.upper())\nif mk:\n    section = MAPNM.get(mk.group(1).strip(), section); continue\n\nfor sec, key in MAP.items():                     # footing gate - both count AND amount\n    n, tot = parsed[sec]\n    assert n == hdr_count[key] and tot == hdr_amount[key], (per, sec, n, tot)\nassert beginning + deposits - (elec + other + atmdebit + fees + checks) == ending","skill":"finance/lender-financial-statement-package","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-16","tags":null,"metadata":null,"created_at":"2026-10-16T15:01:16.072887+00:00","updated_at":"2026-10-16T15:01:16.072887+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":"cac453ac-be6a-43a3-9ad3-361d84db1f6c","agent":"flux","category":"gotcha","title":"Klaviyo rebill audits must inspect A/B-test sends and the actual trigger-event properties","problem":"An initial live-flow audit missed XTW4qW, a five-email subscription sales sequence, because its sends were nested inside completed ab-test nodes rather than top-level send-email actions. It reached 13 of 25 sampled rebill customers. Its Recharge-era rc_active_subscriber guard did not protect Loop subscribers. A proposed Shopify-tag exclusion also failed discovery: tags were empty on 100 of 100 sampled Klaviyo rebill events.","solution":"Paginate GET https://a.klaviyo.com/api/metrics/ and /flows/?page[size]=50, then fetch each live flow with GET /flows/{id}/?additional-fields%5Bflow%5D=definition. Inspect both top-level send-email/send-sms actions and ab-test.data.main_action plus current_experiment.variations[].actions; reconcile the map against real profiles' Received Email timelines. For Clean Nutra's Shopify Placed Order metric XNamTZ, the observed rebill discriminator was Source Name = subscription_contract_checkout_one. Append the metric-property exclusion below to the trigger's condition_groups while preserving existing filters; the API key is field, not property. The recorded repair cloned XTW4qW to V8iwK9 and parked the old flow in manual. Clone mechanics: back up the full definition, replace action IDs with temporary_id values, remap links and entry_action_id, and strip nested IDs from action data. Collapse an A/B node to its winner only after confirming the experiment is completed; the helper does not enforce that prerequisite. IMPORTANT: POST /flows/ inherited live action statuses and activated an earlier clone immediately, so do not run the helper as a draft-only operation or without publication approval. Read back both old and new definitions and action statuses. Ordered Product events did not carry Source Name: the four gift-app flows instead used a profile filter of Shopify Placed Order count equals 1 all-time while retaining ProductID triggers. That policy is first-ever-order-only, not merely non-rebill, and excludes returning purchasers too. Evidence: memory/2026-10-14.md entries for Oct 15; evidence/kl_rebill_sweep.py, evidence/kl_clone_exclude_rebill.py, and evidence/kl_first_order_only.py.","code_snippet":"{\"type\":\"metric-property\",\"metric_id\":\"XNamTZ\",\"field\":\"Source Name\",\"filter\":{\"type\":\"string\",\"operator\":\"not-equals\",\"value\":\"subscription_contract_checkout_one\"}}","skill":null,"platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-16","tags":null,"metadata":null,"created_at":"2026-10-16T15:01:15.560279+00:00","updated_at":"2026-10-16T15:01:15.560279+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":"98daf7e1-feb8-4641-a512-fda1d67144a6","agent":"vector","category":"workflow","title":"CIN7 shows 0 at Vegas while ShipHero has the stock: move the In_Transit batch with a TR instead of adding new stock","problem":"CIN7 showed Vegas = 0 for ST-8IN1IMMUNE-VEG (FEFO-tracked, ProductID abed5227-2eff-4689-a86a-9c2e29ebe69f), while ShipHero LV showed on_hand 24,574 / alloc 0. The CIN7 stock was still sitting at the virtual location 'Protein Research - In_Transit' as batch FEFO-CONV-20260910, qty 24,293, and was never received into Vegas. Adding stock at Vegas through a stock adjustment with IsNewStock would count the same units twice.","solution":"To sync CIN7 with the ShipHero physical count: (1) Pull CIN7 batch-level availability for the ProductID across all locations and find the In_Transit row (Protein Research - In_Transit, LocationID 81a1c1cc-66eb-4947-bc7a-dd89c687c66d). (2) Compare it to ShipHero LV on_hand, using the lot list to confirm it is the same physical stock. Here the gap was +281 (24,574 vs 24,293). (3) Create a CIN7 Stock Transfer from In_Transit to Vegas for the In_Transit qty, quoting the exact batch (FEFO-CONV-20260910). Leave it in DRAFT (example: TR-00600, TaskID 197a1b49-e4e1-4ae1-91c1-c3e4c214ceab) and get explicit approval before completing it. Only the leftover gap (+281) should be a stock adjustment. (4) Before creating the TR, scan for duplicates: list all TRs since about 30 days back (173 TRs since 9/15 here) for the same SKU and its siblings. Here, sibling CN-CAP-8IN1IMMUNE-60CT already had TR-00533 and TR-00462 IN TRANSIT. Only after the Vegas balance is right should the Vegas to FBA transfer be built.","code_snippet":null,"skill":null,"platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-16","tags":null,"metadata":null,"created_at":"2026-10-16T15:01:14.295084+00:00","updated_at":"2026-10-16T15:01:14.295084+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":"d83a2a5f-1e82-4f61-9d31-9c0e7e3eed2a","agent":"vector","category":"gotcha","title":"3PL-owned SKU to FBA: FNSKU==ASIN and MSKU missing from our seller account means a separate seller account, so ship it manually","problem":"Mike asked for ST-8IN1IMMUNE-VEG x3024 to move from Vegas to FBA. The ShipHero 3PL customer was Stamiron Labs, not Clean Nutra. The Amazon identifiers he gave were ASIN B093P29P79, FNSKU B093P29P79 (the same value as the ASIN) and MSKU Stamiron-8in1-1pack. That MSKU returned NOT_FOUND in the Clean Nutra seller account A2JBR7QMN8UCHF, and FBA inventory summaries came back empty, including with case variants. A similar Clean Nutra product (B095KWRDZL / CLN-IMMUNE-SUP-01-NEWSEAL / X002ZMRINT) could easily have been picked by mistake.","solution":"Before building any FBA transfer for a SKU whose ShipHero 3PL customer is not Clean Nutra: (1) Look up the given MSKU with the SP-API Listings Items call (GET /listings/2021-08-01/items/{sellerId}/{sku}) on A2JBR7QMN8UCHF, and with FBA inventory summaries (GET /fba/inventory/v1/summaries?sellerSkus=...). Try case variants too. (2) If the FNSKU equals the ASIN, the item uses the manufacturer barcode and is commingled, so no X00 label applies. (3) Use the Catalog API to confirm the brand and UPC (here: brand Stamiron, UPC 722649919283, 60ct). If the brand is a 3PL client and the MSKU is not in our account, the listing lives in that client's own seller account. Do not swap in a similar Clean Nutra ASIN or FNSKU. Do not let the CIN7 to Amazon bridge auto-create a shipment, because it would try to ship under the wrong account. Instead, confirm that the Amazon inbound will be created manually in the client account (Mike: 'We will do it manually'). Also check ShipHero case_quantity: it showed 1 here, but the sibling SKU ships 126/case (3024 = 24x126), so fix or override it before making labels.","code_snippet":null,"skill":null,"platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-16","tags":null,"metadata":null,"created_at":"2026-10-16T15:01:14.170833+00:00","updated_at":"2026-10-16T15:01:14.170833+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":"82e94186-148c-467b-99da-34050d00a3c6","agent":"stockpile","category":"api_integration","title":"CIN7 Advanced Purchase POs can be read through the API: use /purchase/order?TaskID=, because /purchase?ID= returns 400","problem":"Weston sent the ParaDefense SFP and asked what unit price we paid on the co-man PO. Calling GET /purchase?ID=<id> on that PO (PO-00844, an Advanced Purchase) returned HTTP 400. Because of that 400, we had long believed Advanced POs were 'API-blocked' and could only be priced in the CIN7 UI. Also, purchaselist InvoiceAmount is 0.0 on uninvoiced Advanced POs, so it can't be used as the cost.","solution":"Fast path, about 4 calls: 1) Resolve the SKU with GET /Product?Name=<brand>, trying both spaced and unspaced names ('Para Defense' and 'ParaDefense'). The CIN7 SKU can differ from the label's (CN-SGL-PARADEFENSE-90BG in CIN7 vs -90CT on the SFP). 2) GET /ref/productavailability?Sku=<SKU> returns OnOrder, NextDeliveryDate, and Location. The co-man in-transit location (e.g. 'Albert Max, Inc. - In_Transit') tells you which supplier to look for. 3) Paginate GET /purchaselist?Page=n&Limit=100 (about 6 pages, ~600 POs), filter on Supplier containing the co-man name, and skip VOIDED. 4) For Simple POs, call GET /purchase?ID=<id> and read Order.Lines. For Advanced POs, call GET /purchase/order?TaskID=<id> and read Lines[].{SKU,Quantity,Price,Total}; get the header, terms, and RequiredBy from GET /advanced-purchase?ID=<id>. Report PO#, supplier, order date, required-by date, quantity, unit price, total, deposit amount from the terms, and invoice/payment status. Result from this run: PO-00844 Albert Max, 25,000 units @ $5.20 = $130,000, 20% down ($26,000) with the rest net 30, due 2026-12-04, not yet invoiced. Before the SFP goes to the brand team, check that the footer SKU matches the CIN7 SKU, that no DRAFT stamp is left on it (this one was v0.4 DRAFT), the allergen line against the ingredient sources, and that serving size × servings equals the count. Paige reviews the SFP before it goes to Molly.","code_snippet":"GET /purchaselist?Page=1&Limit=100   # filter Supplier~'Albert Max', Status!='VOIDED'\nGET /purchase/order?TaskID=<PO TaskID>   # Advanced PO lines + Price\nGET /advanced-purchase?ID=<PO TaskID>    # header, terms, RequiredBy\n# NOT: GET /purchase?ID=<advanced id>  -> 400","skill":"clean-nutra-inventory","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-16","tags":null,"metadata":null,"created_at":"2026-10-16T15:01:13.650775+00:00","updated_at":"2026-10-16T15:01:13.650775+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":"cffe9bfd-c983-4c69-bc13-f10b55cc8bba","agent":"sterling","category":"gotcha","title":"Broker-returned Excel that msoffcrypto rejects with FileFormatError is IRM/DRM-protected (CDFV2 'DRMEncryptedDataSpace'), not password-protected — no library opens it; ask for an unprotected re-save i","problem":"The benefits broker returned '2026 Census Template_Clean Nutra_.xlsx' asking for dependents, Relationship codes and Waiver Reasons added. The file would not open in openpyxl; `file` showed a CDFV2 container; msoffcrypto-tool raised FileFormatError rather than prompting for a password, so it was unclear whether this was a password, corruption, or something else, and the task was blocked.","solution":"Diagnose with olefile: `python3 -c \"import olefile;print(olefile.OleFileIO(p).listdir())\"` — if the streams include '\\u0006DataSpaces/DataSpaceInfo/DRMEncryptedDataSpace' (and no 'EncryptionInfo' stream) the workbook is Microsoft IRM/Rights-Management protected. msoffcrypto, openpyxl, LibreOffice headless and pandas cannot decrypt it because the key lives in Azure RMS, not in the file; there is no password to guess. Correct move: immediately tell the requester it is DRM-protected and ask for an unprotected re-save (Randy sent his own 'COMPLETED v2' within the hour). Meanwhile keep working from our last clean copy (/tmp/census) so the merge is ready. Follow-on census rule from this round: insert the new columns (I Relationship, P Medical Waiver Reason) with openpyxl insert_cols and re-point any data-validation lists; add dependent rows under each enrolled employee per tier (16 rows across 6 enrollees); and do NOT infer waiver reasons (part-time, stipend, waiting period) — Randy explicitly reversed those; leave col P blank/orange for all waived employees until employee waiver forms arrive. Builder /tmp/census2/build_v3.py → ~/mission-control/uploads/2026 Census Template_Clean Nutra_COMPLETED v3.xlsx.","code_snippet":null,"skill":"bamboohr","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-16","tags":null,"metadata":null,"created_at":"2026-10-16T15:01:13.104268+00:00","updated_at":"2026-10-16T15:01:13.104268+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":"4862cdbc-a1a3-48f4-87f1-fb08b4b04720","agent":"sterling","category":"gotcha","title":"Retargeting a retailer kickoff PDF (Publix → Meijer) with PyMuPDF: redact the UNION of the word and the overlapping headline line, re-typeset with the brand TTF, and compare images with get_image_rect","problem":"Randy needed the two Acosta/Publix kickoff decks (VMS 24pp from LibreOffice, Active Nutrition 15pp from PowerPoint) re-issued for Meijer. 'Publix' appeared only on each cover slide in 52pt Poppins-Bold white, but a naive add_redact_annot on just that word clips neighbouring glyphs whose bboxes overlap (the headline below, and on a later slide-7 fix the 10.5pt eyebrow 'THE PORTFOLIO' overlapping the 33pt ascender box). Verification by page.get_images() count also falsely suggested images were lost after saving.","solution":"1) Locate text with page.search_for / get_text('dict'); redact the union rect of the retailer word AND any text line whose bbox overlaps it, then re-typeset both strings with page.insert_text(point, text, fontfile='~/.fonts/Poppins-Bold.ttf', fontsize=52, color=(1,1,1)) at the original baselines (measure widths with fitz.get_text_length to confirm they fit the original box, e.g. 681/452pt < 899). When a small eyebrow sits above a big headline, start the clip at eyebrow baseline + 1.5pt and re-typeset the eyebrow too, otherwise its descender area gets wiped. 2) Save with doc.save(out, garbage=4, deflate=True). 3) Verify: (a) 0 occurrences of the retailer name in raw bytes and in page text; (b) render pages 2+ at 40dpi before/after and assert pixel-identical pixmaps; (c) compare drawn images per page with page.get_image_rects(xref) / get_text('dict') image blocks — NOT len(page.get_images()), because garbage=4 strips unreferenced XObjects (55 in the VMS deck) and the count drops with zero visual loss. 4) Keep a backup copy in /tmp before overwriting the uploads filename when applying follow-up fixes (headline re-break 3→2 lines). Scripts /tmp/meijer_retarget.py and /tmp/fix_slide7.py; outputs ~/mission-control/uploads/Clean_Nutra_Acosta_Meijer_Kickoff_{VMS,Active_Nutrition}_10.15.2026.pdf. Also check the body for retailer-specific commercial terms before shipping — here pricing/SKUs were retailer-agnostic (Publix grid $5K/mo and $39/$32 mandate were never in the deck; Meijer grid = $2,500/4%).","code_snippet":"import fitz\ndoc = fitz.open(src); pg = doc[0]\nhits = pg.search_for('Publix')\nlines = [l for b in pg.get_text('dict')['blocks'] if b['type']==0 for l in b['lines']]\nclip = fitz.Rect(hits[0])\nfor l in lines:\n    if fitz.Rect(l['bbox']).intersects(clip): clip |= fitz.Rect(l['bbox'])  # union w/ overlapping headline\npg.add_redact_annot(clip); pg.apply_redactions(images=fitz.PDF_REDACT_IMAGE_NONE)\npg.insert_text((x0, baseline), 'Meijer', fontfile=FONT, fontsize=52, color=(1,1,1))\ndoc.save(out, garbage=4, deflate=True)\n# verify images via rects, not get_images() count\nassert [pg.get_image_rects(x[0]) for x in pg.get_images()] ","skill":"document-generation","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-16","tags":null,"metadata":null,"created_at":"2026-10-16T15:01:12.988468+00:00","updated_at":"2026-10-16T15:01:12.988468+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":"301cd266-8ee0-4225-a050-1a6409dc072d","agent":"sterling","category":"api_integration","title":"Pulling vendor invoice PDFs out of QBO: query Attachable ONE EntityRef at a time, download via TempDownloadUri with plain urllib (the proxy /download/{id} route 500s), then parse Taft law-firm invoice","problem":"Randy asked for Taft Stettinius legal invoice detail for the past 4 months with per-matter, per-attorney breakdown. QBO only holds the Bill header (5 bills, $121,700.31, VendorRef 194, 6/15-10/15); the line-item detail lives in the attached PDFs. Attachable queries with an OR across several EntityRef ids are not parseable by QBO, and the HiveMind proxy's /download/{id} endpoint returns 500 'No match for accept header', so the attachments looked unreachable at first.","solution":"1) List bills: QBO query `SELECT * FROM Bill WHERE VendorRef = '194' AND TxnDate >= '2026-06-15'`. 2) For EACH bill id separately run `SELECT * FROM Attachable WHERE AttachableRef.EntityRef.value = '<billId>'` — one id per query; OR/IN across ids fails to parse. 3) Each Attachable carries a TempDownloadUri (short-lived S3 link); fetch it with plain urllib.request.urlopen — no auth header needed — and skip the proxy /download/{id} route entirely (always 500). 4) Parse Taft PDFs: matter blocks start at 'Client/Matter # 117926/NNNNN'; time rows = date / timekeeper initials (regex `[A-Z]{2,5}\\d?` because MAM4 has a digit) / wrapped description lines / hours; costs sit under 'COSTS ADVANCED'. Taft prints NO hourly rates, so implied rate = matter fees ÷ matter hours. Timekeepers are initials only — resolve to names via names cited in descriptions plus taftlaw.com bios and record a Confidence column (e.g. AHW Amanda H. Wilcox Ptr, MFF Mark F. Fazio billing atty, PRB Philip R. Bautista TM co-chair). 5) Tie-out before delivering: sum of By-Category per bill minus QBO Bill.TotalAmt must be 0.00 for every bill (it was, ×5; 386 time entries 187.0 hrs $109,522 pre-discount, 62 costs $27,123, 136 matters all tying to the invoice summary pages). Note flat courtesy discounts appear as negative lines (−$6,000 on 7199734) and some bills are costs-only (7246069 = $15,123 costs). Scripts /tmp/taft4/{pull_bills,pull_att,dl,parse,tie,build}.py → ~/mission-control/uploads/Taft_Invoice_Detail_Jun-Oct_2026.xlsx.","code_snippet":"# one Attachable query per bill id — OR across ids does not parse\nfor bill_id in bill_ids:\n    q = f\"SELECT * FROM Attachable WHERE AttachableRef.EntityRef.value = '{bill_id}'\"\n    for att in qbo_query(q).get('Attachable', []):\n        uri = att.get('TempDownloadUri')  # short-lived; fetch immediately\n        if uri:\n            data = urllib.request.urlopen(uri, timeout=60).read()  # no auth header, no proxy /download\n            open(f\"/tmp/taft4/pdf/{att['Id']}.pdf\", 'wb').write(data)\n# tie-out: sum(parsed categories) - float(bill['TotalAmt']) must be 0.00","skill":"hivemind-api","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-16","tags":null,"metadata":null,"created_at":"2026-10-16T15:01:12.863642+00:00","updated_at":"2026-10-16T15:01:12.863642+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":"fc84fa7a-9c08-4677-a47f-f8d61f155663","agent":"sentinel","category":"workflow","title":"Hash and cross-read the source documents before grading: md5 catches byte-identical resubmissions and one-spec-serving-two-SKUs; the rolling GDoc is a disposition register, not a revision pointer","problem":"Two near-misses in one day came from grading artwork against the artwork alone. On Multimineral 102 03-V20 I had the Black Seed Oil / Black Cumin mismatch drafted as a Critical print-stop — it IS the Critical of record on the sibling 120BG SKU — but Paige's rolling review GDoc, updated the same day, recorded it as a formally dispositioned accepted risk: 'Weston/Mike approve to run as is and R&D a new source of black seed oil for future runs.' Grading off artwork plus sibling precedent would have handed the client a blocker her own leadership had already signed off. Separately, the NitroRush blend-name conflict (artwork 'Clean NitroRush' vs spec 'Vaso Ease') looked like an artwork error until hashing showed the VasoEase SFP and the 'NitroRush SFP' are literally the same file — md5 a8f8ead223c115c4f8ecf520ab0b1c4d, both BOM 60-00792, spec titled 'Vaso Ease Proprietary Blend', both products built under lot 260246. And the NitroRush 03-V2 submission itself was md5-identical (2153c4f313e6d167eebc006122ced2fb) to the LABEL, PROOF and outlines copies already in Drive — a byte-identical resubmission with all six prior directives unactioned.","solution":"Run this evidence-gathering pass BEFORE assigning any severity:\n1. `md5sum` every copy of the submitted artwork against all LABEL / PROOF / outlines copies already in Drive. An identical hash means a byte-identical resubmission — lead the deliverable with that fact and the unactioned-directive count rather than re-litigating individual findings.\n2. `md5sum` the SFP/FPS of record for the SKU against the SFPs of sibling and formula-adjacent SKUs. A collision means one spec file is doing duty for two SKUs (NitroRush/VasoEase, BOM 60-00792) — the finding is 'this SKU needs its own spec', routed to Q&R, not a designer edit. Identical formulas also make divergent badges diagnostic: VasoEase carries a VEGETARIAN badge and NitroRush does not, and the formula evidence (pectin, citrus fiber, microalgae) says NitroRush is under-claiming.\n3. EXPORT AND READ THE ROLLING REVIEW GDOC FIRST, every time, on every SKU that has one. It is the disposition register. Items with a named leadership disposition go into the deliverable as a quoted disposition and do NOT count toward severity totals. Then look for the value you can still add on top: the register named only the common-name mismatch, while the FORM mismatch (cold-pressed fixed oil vs a 30:1 dry seed extract — genuinely different raw material) is the substantive half and the actual reason an R&D re-source is the right fix.\n4. When a register asks for 'substantiation', test whether the honest answer is 'this cannot be substantiated' rather than hunting for documents. Vegan substantiation was requested at V18, V19 and V20 on a formula whose first-listed blend ingredient is Manuka Honey Powder, with a hero MANUKA HONEY badge on the same panel as the VEGAN badge — honey is bee-derived and predominance kills any trace argument, so the answer is a false affirmative claim and the fix is one word: VEGAN to VEGETARIAN.\n5. VERIFY WHETHER A SPEC EVEN CONTAINS A FORMULA SECTION before claiming reconciliation. The Age Defend VW FPS issued 16 OCT closed the 'no VW FPS exists' gap but has no formula section at all, confirmed by `pdftotext` AND a 3-page OCR sweep — so four material formula changes (HPMC shell, added MCC, Smoketree vs Fisetin, Barberry Bark vs Root) print with nothing authorizing them. Frame that as an upstream authorization gap, not a designer error, and state that section-3 reconciliation is structurally impossible rather than reporting it as passed.\n6. RE-VERIFY STALE FINDINGS instead of carrying them forward. AuraZen V5's standing Critical 'no -60BG row exists in CN SKU Mapping' was closable: the mapping sheet is still 404, but PMF 2.0 row 2031 now carries the SKU as Core Product / Active with a barcode matching the printed UPC exactly.","code_snippet":"# 1. byte-identical resubmission + shared-spec detection\nmd5sum _inbox/*.pdf drive_cache/**/LABEL*.pdf drive_cache/**/PROOF*.pdf | sort | uniq -D -w32\nmd5sum specs/*SFP*.pdf | sort        # collisions => one spec serving two SKUs\n\n# 2. does the spec actually contain a formula section?\npdftotext -layout fps.pdf - | grep -inE 'formula|ingredient|blend|mg\\b' || echo 'NO TEXT FORMULA'\n# text layer empty or no hits => OCR sweep every page before concluding\npdftoppm -r 300 -png fps.pdf /tmp/fps && for p in /tmp/fps-*.png; do tesseract \"$p\" - --psm 6; done | grep -inE 'formula|ingredient'","skill":"brandmind/label-review-protocol","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-16","tags":null,"metadata":null,"created_at":"2026-10-16T15:01:12.363752+00:00","updated_at":"2026-10-16T15:01:12.363752+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":"865e4e43-4b0b-44e7-8c93-5199ef485b6b","agent":"sentinel","category":"gotcha","title":"A directive closed by ingredient removal is not durably closed — and a persisting defect may mean the DIRECTIVE is wrong, not the designer","problem":"Three separate failure modes of the directive-tracking register surfaced across six label reviews in one day, all of which cause a reviewer to grade the wrong party. (1) AuraZen 03-V6: St. John's Wort was flagged M-3 at 03-V2 for having no drug-interaction warning, then marked resolved at V3 — but only because the ingredient was deleted from the formula, not because anyone wrote the warning. At V6 SJW is back at 40 mg standalone (std. 0.3% hypericin) and the warning is still absent; the existing caution covers 5-HTP/SSRI/MAOI only and never discloses the actual CYP3A4/P-gp induction risk. (2) AlphaGlow 03-V7: the 13 APR directive 'correct spelling: Shilajit (Asphaltum punjabinum)' was complied with, but shilajit is a mineral-organic rock exudate with NO botanical binomial, so both the artwork's punjabinum and the SFP's punjabianum are invalid and italicising either asserts a taxon that does not exist. (3) VasoEase 03-V25 and Multimineral 102 03-V20: a defect (Crataegus pinnatifidia Bunge; Elderberry/Ashwagandha source additions) lives in the register's Notes column while the adjacent checkbox reads 'Approved' — recorded in one field, passed in another, which is how the VasoEase botanical error survived four review passes.","solution":"Register-archaeology rules to run before grading any version-over-version finding:\n1. REOPEN-ON-RETURN. For every directive marked resolved, diff the ingredient list of the resolving version against the current one. If the item was resolved by removal and the ingredient has since returned, reopen the ORIGINAL directive at its original severity — do not treat it as a new finding and do not trust the 'resolved' status. Record the resolution mechanism (copy added vs ingredient removed) in the directive tally so this is checkable later.\n2. AUDIT THE DIRECTIVE ITSELF when a defect persists across versions after apparent compliance. Validate the instruction against the underlying science/regulation before blaming the designer; the correct output may be withdrawing the directive (shilajit) rather than another respelling.\n3. CHECK WHETHER THE SPEC CARRIES THE SAME ERROR. On AuraZen the Saffron '(Flower)' plant part, the author citations (Rhodiola rosea L, Hypericum perforatum L.) and all three bad %DV values were wrong in the SFP as well as the artwork. Fixing artwork against an uncorrected spec just re-breaks at the next revision — bundle these to Q&R as a single spec-correction package instead of issuing designer edits.\n4. READ NOTES AND CHECKBOX AS SEPARATE SIGNALS. Never accept a row's 'Approved' status without reading its Notes column; flag the mismatch explicitly to the register owner.\n5. EXPECT THE SPLIT. Directive tallies across VasoEase (9 of 11 unactioned), AlphaGlow (9 of 11) and AuraZen (7 resolved / 10 unactioned / 1 regressed) show the same pattern: typographic and data-entry directives get actioned, anything needing a regulatory decision, spec change or legal sign-off comes back unfixed. Use this to predict which items to escalate past the designer on the first pass.","code_snippet":null,"skill":"brandmind/label-review-protocol","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-16","tags":null,"metadata":null,"created_at":"2026-10-16T15:01:12.244723+00:00","updated_at":"2026-10-16T15:01:12.244723+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":"5752001c-7ac3-44ce-89e3-14987ef757f8","agent":"sentinel","category":"business_rule","title":"Blank Amazon Transparency well: measure the approved baseline and check SKU/listing status BEFORE grading it Critical","problem":"On the NitroRush 03-V2 and Age Defend 02-V1 label reviews I graded the empty Amazon Transparency code well as a Critical print-stop on the strength of a hard pixel measurement (0 of 91,809 interior pixels non-white, plus a 'Scan with...' placeholder caption). The number was correct but the severity was wrong: a blank well is NORMAL on static artwork because the Transparency code is applied per print run, not baked into the dieline. I had to self-correct two already-built deliverables, withdraw a designer ticket and withdraw a portfolio-wide artwork-sweep recommendation. Rigorous measurement of the wrong question produced a false blocker twice in one morning.","solution":"Binding pre-check before grading any blank placeholder box (now patched into skills/brandmind/label-review-protocol/references/artwork-proof-review-workflow.md):\n1. MEASURE THE BASELINE FIRST. Pull the approved in-market incumbent version of the same SKU (e.g. Age Defend 01-V8, VasoEase 03-V24, AuraZen 03-V2/V3/V5) and run the identical interior-pixel measurement on its Transparency well. Crop the well interior with a 3-5px inset on all sides to avoid keyline bleed, then count non-white px: `px = img.crop(box).convert('L'); nonwhite = sum(1 for p in px.getdata() if p < 250)`. Verify at 3-4 different insets so a keyline does not create a false positive. If the approved baseline returns the same 0.0000%, the blank is the portfolio norm and CANNOT be Critical.\n2. CHECK PRECEDENT. Portfolio disposition for blank Transparency wells is RV or Major, never Critical.\n3. CHECK SKU/LISTING STATUS before writing it up at all. Look up the SKU in PMF 2.0 and Product XREF - AMZ. If Amazon Status/ASIN/FNSKU/UPC are all N/A (AuraZen -60BG, PMF row 2031), a blank FNSKU box plus a blank UPC box is ONE ops/listing gap, not two artwork defects. If an Active `-TC` packaging SKU exists, it stays RV (Multimineral 102).\n4. STATE THE ASYMMETRY in the fix: a printed UPC that already exists and check-digit validates (e.g. 810197340663) can be populated now; the FNSKU genuinely cannot until the listing is live.\n5. CORRECT ASK is not a designer edit — it is an ops confirmation of inline serialization plus a first-article scan of 3-5 units that both scan and differ from each other.","code_snippet":null,"skill":"brandmind/label-review-protocol","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-16","tags":null,"metadata":null,"created_at":"2026-10-16T15:01:12.118077+00:00","updated_at":"2026-10-16T15:01:12.118077+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":"fd9dfa65-385d-4a3a-b52d-019f77a23053","agent":"probe","category":"gotcha","title":"Apify fallback cron says 'script failed' when 45 of 46 pages succeeded: how to triage exit 1","problem":"On 2026-10-16 the competitor-pages Apify fallback cron (4e88910288c1) reported 'script failed, exit code 1'. Atria through the HiveMind proxy still returned 403, so Apify was the only collection path. Reading that status as a fleet outage would lead to wasted reruns or a false escalation. In fact last_24h_successful was 45 of 46 pages. The only error came from one Resilia page (Dr-Michael-Rawlings, m108432878597216). Its actor run SUCCEEDED, cost $0.00005, and returned one row with accepted=0.","solution":"monitoring_apify_fallback.py ends with return int(bool(report['errors'])), so a single bad page turns the whole cron red. To triage: (1) Open the newest data/monitoring_apify/batches/<uuid>.json and compare last_24h_attempted with last_24h_successful. Then read daily_missing_or_failed[], which gives the lane, page_id, receipt path, error, status, returned and accepted for each problem page. (2) If a page shows status=SUCCEEDED, returned=1, accepted=0, usage about $0.00005 and the error 'no matching actual ads; empty/sentinel/mixed-host query unverified', the actor finished cleanly but sent back a single non-ad or sentinel row. That means the page has no ads in the library, or the page_id is stale or wrong. It is not a transport failure, so rerunning won't help. Check it by hand at facebook.com/ads/library/?view_all_page_id=<id without the m prefix>&active_status=all&country=ALL. Then fix or remove the entry in the page config. (3) A large rejected count is normal and is not an error. normalize() drops rows when any link_url fails allowed_destination(lane,url). Example: Lymphatic-Wellness-Explained returned 70 rows, accepted 33, rejected 37. On shared or advertorial pages, most of the newest-70 sample can be ads that point to other brands, so the useful sample is smaller than count=70. (4) dataset_item_count below the returned count (70/51, 30/24, 14/11 today) is GET /datasets/<id> metadata lag. The script logs it as a warning only. It raises an error only when itemCount is greater than the rows retrieved. (5) Raise a problem only if an Atria 403 continues and the failed list grows past single pages.","code_snippet":"r=json.load(open(sorted(glob('data/monitoring_apify/batches/*.json'),key=os.path.getmtime)[-1]))\nprint(r['last_24h_attempted'],r['last_24h_successful'],r['atria']['http_status'])\nfor p in r['daily_missing_or_failed']: print(p['label'],p['page_id'],p['last'])","skill":"competitor-ad-monitoring","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-16","tags":null,"metadata":null,"created_at":"2026-10-16T15:01:11.611496+00:00","updated_at":"2026-10-16T15:01:11.611496+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":"2994b4d3-c858-4b33-8231-c57c49ca19de","agent":"prime","category":"business_rule","title":"Supplement multipack product type choice: NUTRITIONAL_SUPPLEMENT passes validation but triggers cGMP document gate (100521/100477/100459)","problem":"Creating 22 new gummy multipack SKUs: the Kids and GrowVana packs came back INVALID in dry-run under the same HPC/VITAMIN product type their singles use, while Nitro/Vaso/Sleep validated fine. Switching type fixes validation but changes compliance gating, and the result looks like a failure at readback unless you know what to expect.","solution":"Run every create with mode=VALIDATION_PREVIEW first. If a supplement pack fails under HPC/VITAMIN, change productType to NUTRITIONAL_SUPPLEMENT and add the attributes that type requires (specific_uses, primary_supplement_type for GrowVana) — re-preview until it is VALID, then PUT for real. What to expect afterwards: all 22 creates were ACCEPTED with issues=[], about 3 minutes later each showed a 100521 review hold with no ASIN, and later ASINs were assigned but every SKU stayed DISCOVERABLE with issue codes 100521+100477+100459 (supplement cGMP/TIC certificate gate). Catalog Items GET returned 404 for all of them during this window. Treat this as a report-not-block state (Sep-14 ruling): the GMP team submits certs. Don't move the product back to a wrong category to dodge the gate. Schedule a one-shot 48h re-check (cron running a readback script) that posts to the compliance/testing group once, and only if still flagged. Also hold back any gallery images with banned badges from the create payload and renumber the remaining PT slots so they stay contiguous.","code_snippet":null,"skill":null,"platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-16","tags":null,"metadata":null,"created_at":"2026-10-16T15:01:11.062377+00:00","updated_at":"2026-10-16T15:01:11.062377+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":"b04b6fb1-14d9-41b1-b8e1-5538c5110576","agent":"prime","category":"gotcha","title":"SP-API Listings PATCH 'delete' ops must carry a value array — empty/missing value returns 400 InvalidInput","problem":"When updating existing DreamAgain multipack SKUs (-2/-3/-5) via SP-API Listings Items PATCH to reprice, add UPC, unlink from parent, and remove stale gallery slots (other_product_image_locator PT07/PT08, child_parent_sku_relationship, supplier_declared_has_product_identifier_exemption), delete ops sent with an empty value were rejected with 400 InvalidInput, failing the whole patch.","solution":"For every op:'delete' in PATCH /listings/2021-08-01/items/{sellerId}/{sku}?marketplaceIds=..., include a 'value' array that mirrors the current attribute value being removed (at minimum [{\"marketplace_id\": \"ATVPDKIKX0DPC\"}] plus the identifying sub-fields, e.g. media_location for an image slot or parent_sku for the relationship). Do not send value:[] or omit value. Build the plan first (we saved per-SKU plans to a JSON file), do a mode=VALIDATION_PREVIEW run, then submit. In one PATCH you can combine replace ops (purchasable_offer reprice, externally_assigned_product_identifier UPC, main_product_image_locator, PT01-06) with the delete ops (PT07-08, parent relationship, GTIN exemption) — after the fix all 3 SKUs returned ACCEPTED and readback showed them standalone, catalog present, only cosmetic issue 18448.","code_snippet":"{\"op\":\"delete\",\"path\":\"/attributes/other_product_image_locator_7\",\"value\":[{\"marketplace_id\":\"ATVPDKIKX0DPC\"}]}","skill":null,"platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-16","tags":null,"metadata":null,"created_at":"2026-10-16T15:01:10.93997+00:00","updated_at":"2026-10-16T15:01:10.93997+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":"07fbd442-ff23-4c18-9c02-0ac32f88fd53","agent":"nexus","category":"bug_fix","title":"New Listing Audit cron (c3f0755557f7) has been silently dying every day since 2026-10-10 — no_agent 120s ceiling vs script's real runtime","problem":"The 'New Listing Audit — 9AM PST (modernized)' cron job (id c3f0755557f7, script new_listing_audit.py) has failed with exactly 'Script timed out after 120s' on every single run from 2026-10-10 through 2026-10-15 (6 consecutive days, confirmed again in today's evidence) with zero successful completions in that window. Because the script is designed to print nothing on a clean day (silent-by-design for the dedupe/Run-Log pattern), the ongoing failure was easy to miss — it looks identical to 'ran clean' from a glance at delivery output, except the job's last_status field says 'error'.","solution":"Root cause: the job is configured with no_agent=true, which subjects it to Hermes' hard ~120s kill ceiling (vs. the ~3-minute interrupt budget agent-mode jobs get). But new_listing_audit.py's main() does, sequentially: (1) run_snapshot() for all 5 platforms (cin7/amazon/tiktok/shopify/walmart), each a subprocess.run(['python3', snapshot_<platform>.py], timeout=600) — i.e. up to 5x600s budget already dwarfs 120s; then (2) per newly-discovered SKU, check_chain() calls hm_call() against the hivemind API (CIN7 + ShipHero reads), each with up to 3 retry attempts at a 20s curl --max-time plus 1.5s sleep between attempts, plus a 0.3s sleep per item; then (3) the SAME check_chain() re-run against every currently-OPEN tracker row for auto-resolve, with another 0.2s sleep per row. Any non-trivial snapshot or backlog size blows past 120s by construction, so the no_agent kill fires before clear_checkpoint()-equivalent bookkeeping (Run Log row append, step 5) ever executes. Fix options, in order of simplicity: (a) flip the job to no_agent=false so it runs under agent-mode's longer interrupt budget instead of the harsher no_agent kill; (b) split into two chained jobs via context_from — Job A (no_agent, short) does only the 5 snapshot subprocess calls and exits; Job B (agent-mode) does the diff/chain-check/Google-Sheets-write logic against the files Job A produced; (c) parallelize the 5 run_snapshot() subprocess calls instead of running them sequentially. Until one of these lands, the 'Listing Integrity Tracker' Run Log (cols L:Q) never gets a row and the MDM KPI Scorecard's clean-run metric for this check has had no real signal since 2026-10-10 — it is not silently fine, it is silently broken.","code_snippet":null,"skill":"brandmind/platform-listing-integrity","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-16","tags":null,"metadata":null,"created_at":"2026-10-16T15:01:10.387924+00:00","updated_at":"2026-10-16T15:01:10.387924+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":"f0a27945-c926-4772-a768-5d0cc691bd24","agent":"blitz","category":"business_rule","title":"Confirmed live: video 7645033298053352734 was auto-appealed for the 25th time today with ZERO AD_STATUS_AUDIT_DENY evidence behind it — the loop is self-perpetuating because a successful appeal rewrit","problem":"Yesterday I documented the mismatched-horizon bug (5-day schedule_end_time vs 14-day rejection lookback) as a prediction. Today's run is the proof, and it is worse than a false positive — it is a ratchet. data/appeal-cache.json key `1835768155881522:7645033298053352734` now reads count: 25, lastAppeal 2026-10-16, and data/boost-history.json for the same key reads boostedAt 2026-10-16T10:09:26Z / sessionId 1879199632767137. Those two facts together are the trap: Step 5 (lines 184-188) overwrites `boostedAt: new Date().toISOString()` on every success, so the key is permanently inside `fourteenDaysAgo` and can NEVER age out of the candidate pool, no matter how many times review declines it. Meanwhile the only authoritative rejection signal in the script — Step 4's /ad/get/ with filtering {secondary_status: 'AD_STATUS_AUDIT_DENY'} — contributed nothing; the appeal came entirely from the inference path (sessionId absent from session/list). The creator field on this key is `discovery-boost-2026-05-30`, a placeholder written by the May 30 backfill, not a real handle, so nobody is even being helped by the resubmission. Same shape as the Shelf Builder BOGO keys at counts 30/31/24 (7527557533540666654, 7630139571853888798, 7539760323503459614, all lastAppeal 2026-10-14).","solution":"Treat appeal count as a hard business limit, and stop the script from laundering its own writes into fresh 'evidence'. Three changes in scripts/auto-appeal-rejected.js:\n\n1. CAP ATTEMPTS. At the top of the Step-5 loop (before reBoost), add `if ((appealCache[item.key]?.count || 0) >= 5) { console.log('  ⛔ Giving up on ' + item.itemId + ' — ' + appealCache[item.key].count + ' prior appeals'); continue; }`. Five is the right number because every one of our long-running keys that ever cleared review did so within the first few attempts; counts of 24-31 are pure treadmill. Back-fill the stop immediately for the four known runaways (7645033298053352734, 7527557533540666654, 7630139571853888798, 7539760323503459614) by adding `\"giveUp\": true` to their appeal-cache entries and honoring that flag in the same guard.\n\n2. STOP RESETTING boostedAt. Lines 184-188 must preserve the original boost date so the 14-day window can actually expire: `boostHistory[item.key] = { ...boostHistory[item.key], sessionId: result.data?.session_id || 'appeal', lastAppealAt: new Date().toISOString() };` — write lastAppealAt, never boostedAt.\n\n3. REQUIRE REAL REJECTION EVIDENCE BEFORE SPENDING AN APPEAL. The inference 'sessionId not in session/list ⇒ rejected' is false for any boost past its 5-day schedule_end_time. Before appealing an inferred key, confirm with POST /campaign/gmv_max/session/list/ {advertiser_id: '7340385658755891201', campaign_id} and skip anything whose schedule_end_time is in the past (COMPLETE, not DENIED); only Step-4 AUDIT_DENY hits or sessions that vanished while still scheduled to run deserve a re-POST. Every unjustified appeal creates a real $20 / 5-day GMV Max session object on a creative review will not clear.\n\nOperational tell for any agent auditing this: `count` in appeal-cache.json above ~5 with a `creator` value matching `discovery-boost-YYYY-MM-DD` means a backfill placeholder stuck in the ratchet, not a creator issue worth escalating.","code_snippet":"// Evidence pulled 2026-10-16 after the 10:09 run\n// data/appeal-cache.json\n//   \"1835768155881522:7645033298053352734\": { \"lastAppeal\": \"2026-10-16\", \"count\": 25 }\n//   \"1835768155881522:7527557533540666654\": { \"lastAppeal\": \"2026-10-14\", \"count\": 30 }\n//   \"1835768155881522:7630139571853888798\": { \"lastAppeal\": \"2026-10-14\", \"count\": 31 }\n//   \"1835768155881522:7539760323503459614\": { \"lastAppeal\": \"2026-10-14\", \"count\": 24 }\n// data/boost-history.json  <-- boostedAt rewritten to TODAY by the appeal itself\n//   \"1835768155881522:7645033298053352734\": {\n//     \"boostedAt\": \"2026-10-16T10:09:26.264Z\",\n//     \"sessionId\": \"1879199632767137\",\n//     \"creator\": \"discovery-boost-2026-05-30\"   // placeholder, not a real handle\n//   }\n\n// FIX 1 — cap, at the top of the Step-5 `for (const item of rejected)` loop\nconst prior = appealCache[item.key]?.count || 0;\nif (prior >= 5 || appealCache[item.key]?.giveUp) {\n  console.log(`  ⛔ Giving up on ${item.itemId} — ${prior} prior appeals`);\n  continue;\n}\n\n// FIX 2 — lines 184-188: never re-arm the detection window\nboostHistory[item.key] = {\n  ...boostHistory[item.key],                      // keeps ORIGINAL boostedAt\n  sessionId: result.data?.session_id || 'appeal',\n  creator: item.creator,\n  lastAppealAt: new Date().toISOString()\n};","skill":"tiktok-shop-ops","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-16","tags":null,"metadata":null,"created_at":"2026-10-16T15:01:09.843591+00:00","updated_at":"2026-10-16T15:01:09.843591+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":"6ed7bccd-007b-4f35-a6b9-704b5d2803db","agent":"blitz","category":"bug_fix","title":"auto-appeal-rejected.js now blows the cron foreground budget purely because getActiveSessionIds() walks 269 campaigns SEQUENTIALLY (1 call ≈ 1.26s) while buildProductIndex() right below it is already","problem":"Today's 099cb9434b34 run (2026-10-16 10:09) needed two attempts: the first died at ~200s mid-scan under the 3-minute cron window, the second completed in 339.9s and reported 39 active sessions across 269 campaigns, 1 rejected, 1 appealed (session 1879199632767137 in campaign 1835768155881522). This is the same wall logged on 2026-09-02 (325s vs a 240s kill) but it has gotten worse, not better, and the earlier note blamed it on 'the script is slow' / 'run it backgrounded'. The actual cause is a structural asymmetry inside the script: getActiveSessionIds() (lines 66-69) does `for (const campId of campIds) { await api('/campaign/gmv_max/session/list/', ...) }` — one blocking round-trip per campaign, 269 of them at ~1.26s each ≈ 340s, which is essentially the entire runtime — while buildProductIndex() 10 lines further down (lines 76-85) already does exactly the right thing with `Promise.all` over slices of 10, costing only ~27 round-trips of wall time for the same 269 campaigns. Runtime therefore scales linearly with campaign count: 269 campaigns today vs the ~250 that produced 325s six weeks ago, and it will keep breaching every fixed timeout we set as the account grows. Tom's run report also mis-stated this as '~538 sequential API calls' — the product-index half is NOT the problem, only the session sweep is.","solution":"Two things, both verified against the source at /home/wcorica/.openclaw/agents/blitz/workspace/scripts/auto-appeal-rejected.js.\n\n1. PARALLELIZE THE SESSION SWEEP using the batching pattern that already exists in the same file. Replace the sequential for-loop in getActiveSessionIds() with slices of 10 + Promise.all (code_snippet below). 269 campaigns / 10 ≈ 27 sequential rounds × ~1.3s ≈ 35s instead of 340s, bringing the whole job under a minute and making the 3-minute cron window irrelevant for the next several hundred campaigns. Keep the batch at 10 to match buildProductIndex — that width has run daily for months against /campaign/gmv_max/info/ via the hivemind-api proxy without rate-limit errors, so it is the known-safe concurrency for this advertiser (7340385658755891201). Do NOT bump API_TIMEOUT_MS (20000) — individual calls are not the problem.\n\n2. KNOW THAT A TIMED-OUT RUN IS A CLEAN NO-OP, so retrying is always safe and you must never assume a killed run half-appealed something. saveJSON(APPEAL_CACHE_FILE) and saveJSON(BOOST_HISTORY_FILE) are both at lines 201-202, i.e. AFTER the entire Step-5 appeal loop; nothing is persisted mid-scan. A SIGKILL during the sweep loses zero state. The real hazard is the opposite one: because the write is all-or-nothing at the end, a run killed DURING the appeal loop will have created live $20 sessions on TikTok that never make it into appeal-cache.json, so the next morning re-appeals them and TikTok answers 'A creative boost for this video already exists.' — the exact string sitting on 274 keys in appeal-cache.json. So: verify a suspicious '0 rejected' or a killed run by re-querying rather than trusting the log, and if you must kill, kill during the scan (before ~Step 5), not after.\n\nVerification procedure used today: confirm the appeal landed by reading data/boost-history.json for the key `<campaignId>:<itemId>` and checking sessionId + boostedAt timestamp match the run (here 1835768155881522:7645033298053352734 -> sessionId 1879199632767137, boostedAt 2026-10-16T10:09:26Z), then confirm the session is live via POST /campaign/gmv_max/session/list/ with {advertiser_id, campaign_id}.","code_snippet":"// scripts/auto-appeal-rejected.js — getActiveSessionIds(), lines 66-69\n// BEFORE: 269 blocking round-trips ≈ 340s (the whole runtime)\nfor (const campId of campIds) {\n  const r = await api('/campaign/gmv_max/session/list/', { advertiser_id: ADVERTISER_ID, campaign_id: campId });\n  for (const s of (r.data?.session_list || [])) active.add(s.session_id);\n}\n\n// AFTER: same batching buildProductIndex() already uses 10 lines below ≈ 35s\nfor (let i = 0; i < campIds.length; i += 10) {\n  const batch = campIds.slice(i, i + 10);\n  const results = await Promise.all(batch.map(id =>\n    api('/campaign/gmv_max/session/list/', { advertiser_id: ADVERTISER_ID, campaign_id: id })\n  ));\n  for (const r of results) {\n    for (const s of (r.data?.session_list || [])) active.add(s.session_id);\n  }\n}\n\n// Why a killed run is safe — persistence is all-or-nothing at the END (lines 201-202):\n// saveJSON(APPEAL_CACHE_FILE, appealCache);\n// saveJSON(BOOST_HISTORY_FILE, boostHistory);\n// => killed during Step 1-4 scan: zero state lost, just rerun.\n// => killed during Step 5 appeal loop: sessions created on TikTok but NOT cached\n//    -> next run re-POSTs and gets 'A creative boost for this video already exists.'\n//    (that failure string is already on 274 keys in data/appeal-cache.json)","skill":"tiktok-shop-ops","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-16","tags":null,"metadata":null,"created_at":"2026-10-16T15:01:09.710711+00:00","updated_at":"2026-10-16T15:01:09.710711+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":"e76c120b-49d8-478f-8974-ec98b64173fc","agent":"beacon","category":"gotcha","title":"Building a creator email recipient list from CRM sheets: dedupe, exclusions, and junk in the Name column","problem":"For an 'email all our contacts' blast, recipients were pulled from two Google Sheets (Retained Tracker 'All Retained Creators' + MASTER CRM tab tagged retained/paid-collab/on-board). The raw data is messy: the same email appears across rows and sheets, the Name column often holds notes or numbers (e.g. '$444K', '228.1K', '50% upfront payment-paid Growi 8/3', 'declined', 'follow', 'reminder'), and some rows are month headers or internal addresses. Using the name as-is would produce greetings like 'Hi $444K,' and would email people who declined or are on the standing exclusion list.","solution":"Use build_blast_recipients.py. Auth with the service account at ~/.hermes/google_token.json using scope spreadsheets.readonly, then read both sheets via sheets v4 values().get(). Extract emails with a regex (first match per cell), lowercase them and strip stray dots, and dedupe into a dict keyed by email so the first source wins. Skip rows where the handle is a month name, the name is 'declined' / 'declined us', the email ends in @cleannutra.com, the handle or email contains the standing exclusion 'shopbyjake', or the email is the requester (they get the practice send instead). Make the first name safe with a first() helper: return 'there' if the name is empty, contains a digit, '@' or ',', if the first token is not alphabetic, is shorter than 2 characters, or is in a BAD set ('made','reminder','follow','declined','contacted','april','on','doesn'). Otherwise return the token capitalized. Write recipients.json as {email:{handle,name,src,first}}. Oct 13 result: 458 tracker rows + 6052 CRM rows gave 611 unique recipients, and about half greet 'there', which is acceptable. When adding a second list (the hub list, 'recipients_hub_new.json'), tag its src, set first to 'there', and check the new list against emails already in the first blast's progress file to avoid double-emailing the same creator.","code_snippet":"def first(n):\n    n=(n or '').strip()\n    if not n or re.search(r'[\\d@,]',n): return 'there'\n    t=n.split()[0]\n    return t.capitalize() if t.isalpha() and len(t)>=2 and t.lower() not in BAD else 'there'\nif e in R or e in EXC_E or e.endswith('@cleannutra.com') or 'shopbyjake' in e: return","skill":"brandmind-creator-programs","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-16","tags":null,"metadata":null,"created_at":"2026-10-16T15:01:09.202844+00:00","updated_at":"2026-10-16T15:01:09.202844+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":"29e521a7-6c25-462b-be4c-77b68729ec75","agent":"beacon","category":"workflow","title":"Resumable inline-image (cid) HTML email blast to ~600+ creators via smtplib as collabs@cleannutra.com","problem":"Needed to email the GlutaLux 'Incentives Giveaway' creative (HTML with an embedded banner image, {first_name} personalization, plain-text fallback) to every retained/hub creator. Himalaya `template send` cannot embed cid: inline images, and a ~600-recipient loop over SMTP can be killed by the provider's rate limits (554 / 'too many') or a dropped session, which risks duplicate sends on restart.","solution":"Use send_inline_image_blast.py (workspace/<campaign>/). Folder holds email_send_version_cid.html (<img src=\"cid:CID_NAME\"> and {first_name}), email_plaintext.txt (line 1 'Subject: ...', then body), the .jpg, and recipients.json ({email:{handle,name,first}}). Build the message as MIMEMultipart('alternative') = plain part + MIMEMultipart('related') containing the HTML part and a MIMEImage with Content-ID '<CID_NAME>' and Content-Disposition inline. Connect with smtplib.SMTP('mail.privateemail.com',587), starttls(), login using the password parsed from ~/.hermes/profiles/beacon/himalaya/config.toml (key message.send.backend.auth.raw). Step 1: `python3 send_inline_image_blast.py test you@cleannutra.com FirstName` and have the requester approve the practice send, because sending is irreversible. Step 2: run `blast PROGRESS.json` with terminal(background=true, notify_on_complete=true). The loop skips emails already in progress['sent'], sends one recipient per sendmail() call, sleeps 8s between sends, saves the progress JSON every 10 sends and at the end, retries each recipient up to 3 times, reconnects on generic errors, and on 554/'too many' saves progress, sleeps 600s and reconnects. sendmail() returning {} only means the server accepted the mail, not that it was delivered. For a second list (hub creators) copy the script, change the recipients filename and use a separate progress file. Oct 2026 results: 611 sent / 0 failed for the retained list, 653 sent / 0 failed for the hub list. If the process dies, rerun the same command with the same progress file and it resumes without re-sending.","code_snippet":"rel = MIMEMultipart('related'); rel.attach(MIMEText(html,'html','utf-8'))\nimg = MIMEImage(open(IMAGE,'rb').read(),'jpeg'); img.add_header('Content-ID',f'<{CID_NAME}>'); img.add_header('Content-Disposition','inline',filename=IMAGE)\nrel.attach(img); msg.attach(MIMEText(plain,'plain','utf-8')); msg.attach(rel)\ns=smtplib.SMTP('mail.privateemail.com',587,timeout=60); s.starttls(); s.login(SENDER,pw)\nrefused=s.sendmail(SENDER,[e],msg.as_string())  # {} = accepted\ntime.sleep(8.0)  # throttle; on 554 -> dump progress, sleep(600), reconnect","skill":"brandmind-creator-programs","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-16","tags":null,"metadata":null,"created_at":"2026-10-16T15:01:09.071766+00:00","updated_at":"2026-10-16T15:01:09.071766+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":"ed1b8931-b4d6-4a34-99ca-b4033cffe54b","agent":"apex","category":"pattern","title":"Crater sweep: two more drop shapes to classify (CTR hits normalized to the fleet CTR ratio, and multi-SKU stock transitions where BuyBox is still TRUE)","problem":"The Oct-15 midday crater sweep (5,433 campaign-day rows, Oct-02..15) turned up drops that the usual cost/imps/clicks triage either misreads or misses. Melatonin Manual B0BWBY6HFD and Charcobiotic Manual B0CC8L9S24 kept flat impressions while clicks halved, which looks like a COST-ONLY artifact but isn't. Active B+ B0G77Q5GHP passed the usual stock checks (LIVE, BB TRUE $24.95), yet all of its FBA SKUs were at 0 fulfillable. Its Auto arm, which had been pinned at $300 for 5 of 5 days, fell to $44 (0.53x pvp), while its Manual arm held at 0.89x.","solution":"1) CTR-hit check. If imps pvp is about 1.0 or higher but clicks pvp is about 0.5 or lower, work out the campaign's CTR ratio (today@frac CTR / yesterday@frac CTR). Compare it with the fleet-wide CTR ratio (0.96 on Oct-15). Flag the campaign as PROVISIONAL CTR hit only if it is far below the fleet ratio. Melatonin went from 25 to 11 clicks on 7.1K->7.9K imps, and Charcobiotic from 23 to 12 clicks on 16.1K->16.8K imps. Then rule out ad status, FBA stock and Offers/BuyBox. Check whether the sibling arm is healthy: Charcobiotic Auto at 1.4x means the problem is limited to the Manual campaign's placement or relevance, not the listing. Do not clear it as a cost artifact. HOLD it for the close. 2) Stock-transition check. Do not look only at the ASIN-level BuyBox. List every FBA SKU mapped to the ASIN with fulfillable and inbound counts (Active B+: CN-CAP-METHYLATEDB-60BG 0 ful / 1,100 inbound; CN-METHLYBCOMPLEX-VEG 0/0). If all SKUs show 0 fulfillable but the offer is still buyable, Auto tends to throttle first while Manual keeps converting (10 purchases). Label it STOCK-TRANSITION, not a demotion, and watch for the inbound units to be received. Don't open a catalog case or change bids. 3) Wake after a chop. A campaign that closed chopped (Alpha Root B0DH5PL3H9 closed Oct-14 at ~0.35x) and then shows imps and purchases back above the prior day@frac the next day (Manual 17,076i vs 3,222i, 7 purchases) follows the same self-recovery as Shilajit Oct-13. Keep it on watch through one more close, and don't escalate. 4) Fade check. When a closed-day strip falls steadily over several days (Osteo Defend Auto imps 55K->48K->36K, close 0.49x of run-rate) while today's pvp is above 1 (1.27x), classify it as FADE and watch it. It is not a crater.","code_snippet":null,"skill":"amazon-ads-spend-crater-diagnosis","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-16","tags":null,"metadata":null,"created_at":"2026-10-16T15:01:08.58349+00:00","updated_at":"2026-10-16T15:01:08.58349+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":"9a64bd47-f40d-4ff2-a4cb-918af620efa5","agent":"cortex","category":"gotcha","title":"Git writer clone silently depends on GitHub CLI credential helper — fetch fails with exit 128 when gh is missing","problem":"The brain-trust-collector cron (every 30m, scripts/brain-trust/collector.py --json) failed on repeated runs on 2026-10-16 (11:56, 12:27, 12:58) with exit 1 and status:error. It died at the very first step, `git fetch origin --prune` in the dedicated writer clone ~/brandmind/.cortex-workspace-writer, before pulling any Supabase agent_learnings rows. stderr: '/usr/bin/gh auth git-credential get: 1: /usr/bin/gh: not found ... fatal: could not read Username for https://github.com: No such device or address'. The clone's git config uses `gh auth git-credential` as its credential helper (written by an earlier `gh auth setup-git`), but the GitHub CLI isn't installed on this host, and cron has no TTY to prompt for a username.","solution":"Diagnose: in the writer clone, run `git config --show-origin --get-all credential.https://github.com.helper` and `git config --global --get-all credential.helper` to find the helper that points at gh, then run `command -v gh` to confirm gh is missing. Fix options: (A) install gh (apt install gh, or the GitHub release .deb), then `gh auth login --with-token < token_file` and `gh auth setup-git`. (B) Remove the gh dependency: `git config --global --unset-all credential.https://github.com.helper`, then `git config credential.helper store` in the writer clone and write `https://x-access-token:<PAT>@github.com` to ~/.git-credentials (chmod 600). You can also switch the remote to SSH with a deploy key: `git remote set-url origin git@github.com:Allseason-Enterprises-LLC/cortex-workspace.git`. Verify with `GIT_TERMINAL_PROMPT=0 git -C ~/brandmind/.cortex-workspace-writer fetch origin --prune`, then re-run `python3 collector.py --json`. Prevention: any headless git job should set GIT_TERMINAL_PROMPT=0 and check auth first (e.g. `git ls-remote origin HEAD`). It should report a clear 'auth missing' error rather than failing every 30 minutes with the same error. Status at time of writing: unresolved. The collector failed on every run and no learnings were published.","code_snippet":"cd ~/brandmind/.cortex-workspace-writer\ngit config --show-origin --get-all credential.https://github.com.helper\ncommand -v gh || echo 'gh missing'\n# Option B (no gh):\ngit config --global --unset-all credential.https://github.com.helper\ngit config credential.helper store\nprintf 'https://x-access-token:%s@github.com\\n' \"$GH_PAT\" > ~/.git-credentials && chmod 600 ~/.git-credentials\nGIT_TERMINAL_PROMPT=0 git fetch origin --prune && cd ~/.hermes/profiles/cortex/scripts/brain-trust && python3 collector.py --json","skill":"brain-trust-collector","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-16","tags":null,"metadata":null,"created_at":"2026-10-16T15:01:08.044512+00:00","updated_at":"2026-10-16T15:01:08.044512+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":"95e2f9c8-73c0-4f26-b7ab-499ac6fa19fd","agent":"viral","category":"gotcha","title":"A 2am PT review cron gives a 48h window that holds only ~2h of the current day; kill decisions need complete PT days","problem":"The SB ABO review cron runs at 09:00Z (2am PT). Its '48h' window was yesterday plus about 2 hours of today, so each set's real two-day performance was understated, and pacing for the partial day had to be turned off. Weston asked for a rerun of the circuit breakers 'with yesterday and the day before' data, because the 2am output was not the right basis for pause decisions. The 5am PT morning engine (12:00Z) had the right timing but halted on the mass cap.","solution":"For pause/kill recommendations, build windows from complete Pacific days only. In morning mode, 48h = D-1 + D-2 full PT days and 72h = D-1..D-3, with time_range since/until set to PT dates and no partial current day. Use the current partial day only as a shield: any item with >=1 sale today is exempt from pausing. Ad sets younger than 72h (for example a newly auto-added Gifts campaign) stay in a HOLD bucket and are re-judged after the 72h mark (~2pm PT) instead of being cut on 48h data. Put the 0-sale-48h 'starved' sets in a separate Tier-2 'your call' list with their $/day total, not in the automatic pauses. Execute only after Weston's explicit Go, then read back every write (15/15 here) and log the $/day freed.","code_snippet":null,"skill":null,"platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-15","tags":null,"metadata":null,"created_at":"2026-10-15T15:00:50.595629+00:00","updated_at":"2026-10-15T15:00:50.595629+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":"0781c7d7-43ec-48ab-a5c9-55901fb1ead1","agent":"viral","category":"bug_fix","title":"Circuit-breaker reactivation rule (R1) fires on paused items because cpa(0 spend, 0 purchases) returns 0","problem":"Running the Meta ABO circuit-breaker engine in morning mode (complete-day B1/B2/B3 windows) on the 3 Shelf Builder Trybe ABOs (W1, W2, Gifts) produced 54 reactivation recommendations for ad sets/ads that were already paused. Paused items spend $0 on the pause day, the CPA helper returned cpa(0,0)=0, and 0 <= the $60 CPA reactivation threshold, so R1 treated every recently paused loser as a cheap winner to turn back on.","solution":"Before an R1/reactivation rule runs, require real delivery in the evaluation window: spend > 0 AND purchases >= 1 (or spend >= a minimum like $20). Have cpa() return None or infinity when purchases == 0, never 0, and make every threshold comparison treat None as fail. Also drop items whose effective_status was PAUSED for the whole window. Until the fix ships, run the engine with LIVE forced off, skip the protocol_log append, filter R1 output by hand, and recommend none of these reactivations. Separate gotcha from the same run: the raw output (65 sets plus 20 ads) set off the mass-cap halt at 50%. The fix was a gated second pass: pull with cbm_1014_pull.py, score with cbm_1014_score.py, export with cbm_1014_csv.py. That pass applies a 48h window of the two prior complete PT days, shields any item that sold >=1x today, and holds sets with 1-2 sales until they reach 72h. Our own executed writes appear as actor 'SystemUser', so add SystemUser to OUR_ACTORS or the engine will flag our approved pauses as outside edits.","code_snippet":"def cpa(spend, purchases):\n    return None if not purchases else spend / purchases\n\ndef r1_reactivate_ok(m, max_cpa=60):\n    c = cpa(m['spend'], m['purchases'])\n    return m['spend'] > 0 and m['purchases'] >= 1 and c is not None and c <= max_cpa","skill":"meta-circuit-breakers","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-15","tags":null,"metadata":null,"created_at":"2026-10-15T15:00:50.387949+00:00","updated_at":"2026-10-15T15:00:50.387949+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":"21fb1f1c-b66f-4c73-aba5-636b884116b7","agent":"vault","category":"business_rule","title":"Walmart COGS by ship-to state: refunded units keep their cost expensed, and the state split must tie to the already-posted JE without re-deriving a single cost","problem":"Needed September 2026 Walmart sales and COGS broken out by ship-to state for sales tax work, after JE WM-COGS-0926 (Dr 10 Cost of Goods Sold 14,652.94 / Cr 12 Inventory 14,652.94, WalMart Seller) had ALREADY been posted off Sara's COGS entry support workbook. Re-running costs from CIN7 or a different landed-cost snapshot would have produced a number that no longer agrees with the general ledger, and the natural instinct to credit COGS on the 125 refund lines would have silently reduced total cost below the posted entry.","solution":"Treat the already-posted entry as the control total and re-aggregate, never re-derive. Read the source workbook's 5_LINE_BY_LINE tab and pin it by hash (md5 7d370d0bc03342b6f78b7df507677108) in the Method sheet so the report is reproducible; state captured per line is already there, so this is a pure group-by on ship-to state. Apply the agreed refund basis confirmed by Sara on 2026-10-11: refunded goods are NOT received back into stock, so their cost stays expensed and refund lines get a blank COGS-credit column annotated 'refund - no credit' — in September that means 131 refunded units retain 696.74 of cost, which is exactly why the state COGS rows sum to the posted 14,652.94 rather than something lower. Assert three tie-outs in code before shipping: line count (2,846 rows = 2,721 sale + 125 refund), gross sales 89,570.44 against the Walmart settlement, and COGS 14,652.94 against the posted JE; then assert that the sum of all 50 state rows equals the grand total separately for gross sales, refunds AND COGS. Also assert zero blank/unknown/international ship-to codes rather than bucketing them into an 'Other' row — if any line lacks a valid 2-letter US state the split is not usable for tax and you must say so. Net Sales = Gross less refunds, with each refund attributed to the state on the refund line itself (which can differ from the original sale's period). Caveat the deliverable on its face: Walmart is a marketplace facilitator and generally remits the sales tax on these orders, so ship-to state gross sales are for NEXUS and reconciliation only and are not a substitute for the facilitator filings or a determination of taxability. Result: 50 states, gross 89,570.44, refunds -4,011.58, net 85,558.86, COGS 14,652.94, 83.6% blended margin, with TX/CA/FL at 12.7/8.4/7.7% of sales.","code_snippet":"assert len(df) == 2846 and (df.type == 'Sale').sum() == 2721\nassert round(df.gross.sum(), 2) == 89570.44      # settlement\nassert round(df.cogs_charged.sum(), 2) == 14652.94  # posted JE WM-COGS-0926\n# refunds keep cost expensed -> cogs_charged is blank on refund lines,\n# but the unit cost is NOT credited back\nby_state = df.groupby('state').agg(gross=('gross','sum'), cogs=('cogs_charged','sum'))\nfor col in ('gross', 'cogs'):\n    assert round(by_state[col].sum(), 2) == round(totals[col], 2)\nassert df.state.str.fullmatch(r'[A-Z]{2}').all()   # no blank/intl buckets","skill":"finance/marketplace-cogs-state-split","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-15","tags":null,"metadata":null,"created_at":"2026-10-15T15:00:49.832272+00:00","updated_at":"2026-10-15T15:00:49.832272+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":"33a7ed40-aaf0-4c79-b738-1d343cfee8a6","agent":"vault","category":"pattern","title":"Bridge two payroll runs by splitting new hires from continuing staff first — and diff the COLUMN SETS, because pay-code and deduction columns vanish between runs","problem":"Net pay moved 81,598.79 -> 91,125.27 (+9,526.48) between runs 3115432 (36 employees) and 3115433 (43). A whole-population delta is unreadable and unauditable: it silently mixes 7 new hires, a benefit-deduction stoppage, overtime shifts and tax changes into one number, and the two CSVs do not even share a schema — 'Hours-Holiday' (208.00 hrs) exists only in the earlier run, 'Hours-Sick' (25.00 hrs) only in the later one, and the earlier run carries 8 deduction columns against the later run's one. Neither export contains a pay-period start or end date, so which run is even later is not stated anywhere in the files.","solution":"Build a four-step bridge that ties to the cent and assert every step in code. (1) Establish sequence from evidence, not assumption: compare max(hire_date) across the two files (2026-10-03 vs 2026-09-04) and check that one population is a strict superset of the other; write the sequencing logic into the deliverable as a stated LIMIT — 'if 3115432 is in fact the later run, every sign in this analysis reverses'. (2) Split the population into new-hire and continuing sets by name key, and attribute the full net pay of each new hire as its own bridge line (here 8,047.71 = 84% of the entire movement, which immediately reframes the story). (3) Decompose ONLY the continuing set into wages / employee tax / EE deductions — 2.79 wages, -316.89 tax, +1,792.87 deductions = 1,478.77. (4) assert round(base_net + newhire_net + continuing_net, 2) == round(file_total_net, 2) before writing a single cell. For the schema drift, diff set(cols_a) ^ set(cols_b) and render every asymmetric column as 'n/a' rather than 0 — a column that does not exist is not a zero, and reporting 208 holiday hours falling to 0.00 invents a finding. Also reconcile headcount movement in both directions: here 7 joined and nobody left, but Mike Olsen carries termination date 2026-10-12 on BOTH runs and was paid 7,115.38 gross twice, which is a real open item (final pay / PTO payout) that a pure net-pay delta of 0.01 would have hidden. Keep the deliverable PII-clean: SSN, employee ID, birth date and gender are present in the source and must be read but never reproduced in the output workbook.","code_snippet":"new = set(b) - set(a); cont = set(a) & set(b)\nbridge = [\n  ('Net pay - run A',        None,                      net_a),\n  ('New hires',              sum(b[n]['net'] for n in new),  None),\n  ('EE deductions stopped',  -sum(d_ded[n] for n in cont),   None),\n  ('Wages on continuing',    sum(d_wages[n] for n in cont),  None),\n  ('EE tax on continuing',   -sum(d_tax[n] for n in cont),   None),\n]\nassert round(net_a + sum(x[1] for x in bridge[1:]), 2) == round(net_b, 2)\n\n# schema drift: a missing column is 'n/a', never 0.00\nfor c in set(cols_a) ^ set(cols_b):\n    print('ASYMMETRIC COLUMN:', c)","skill":"finance/payroll-run-variance","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-15","tags":null,"metadata":null,"created_at":"2026-10-15T15:00:49.707154+00:00","updated_at":"2026-10-15T15:00:49.707154+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":"e85bfa74-7195-436d-818b-7a4886ce8f8c","agent":"vault","category":"gotcha","title":"Payroll deduction columns are EE/ER paired — adding both overstates the paycheck impact by ~2.3x; only Total Uncollected Deductions hits net pay","problem":"Asked to explain why benefit deductions stopped between payroll runs 3115432 and 3115433 and what it did to each paycheck, I summed every deduction column per employee. The provider's Complete Summary export carries each benefit TWICE — 'Medical' and 'Medical (Employer)', 'Dental' and 'Dental (Employer)', 'Vision' and 'Vision (Employer)', 'Basic Life and AD&D (Employer)' — so the naive sum reported Sara Skef as losing 973.51 of deductions when her true employee-paid total was 355.69 against an actual net-pay movement of only 243.14. The reviewer caught it. Across 13 employees the error inflated the paycheck impact from 2,678.99 to 6,754.88 because 4,075.89 of employer-paid premiums never touch take-home pay at all.","solution":"Never aggregate deduction columns by pattern-matching the benefit name. Partition the columns first: any header containing '(Employer)' is ER and is EXCLUDED from every net-pay statement; everything else is EE. Better still, use the export's own 'Total Uncollected Deductions' field, which is already employee-only, and reconcile your column-level EE sum to it per employee before publishing anything. Prove the arithmetic row by row with an identity assert rather than trusting the totals: for each continuing employee, assert round(wages_chg + nontaxable_chg - employee_tax_chg - ee_deduction_chg, 2) == round(net_chg, 2), where net = 'Total Direct Deposit Net' (confirm 'Total Check Net' is 0.00 on both runs first, otherwise net pay is the sum of the two). Present the result as four explicit columns — 'of which: deductions', 'of which: tax', 'of which: wages', and the net change — so a reviewer can see that a 486.90 deduction stoppage only produced 342.54 of extra net pay because 144.36 was absorbed by higher tax on the restored taxable base. Second trap in the same export: in BOTH runs 'Total Collected Deductions' was 0.00 and the entire balance sat in 'Total Uncollected Deductions', meaning these premiums were being tracked as ARREARS and were never actually withheld in either period. That changes the conclusion from 'deductions stopped' to 'eligibility/arrears question' — always check Collected vs Uncollected before telling anyone a premium was withheld. Finally, a payroll summary shows the result, not the cause: a plan-year/carrier change, a deliberate premium waiver, a cleared arrears catch-up and a setup error in the later run all look identical on these reports but only the last one means premiums are under-withheld and must be recovered. Say that explicitly and route the question to the provider instead of picking one.","code_snippet":"ER = [c for c in cols if '(Employer)' in c]\nEE = [c for c in cols if c not in ER and is_deduction(c)]\n# EE only ever hits take-home pay\nee_tot = sum(row[c] for c in EE)\nassert abs(ee_tot - row['Total Uncollected Deductions']) < 0.01, row['name']\n\nnet = row['Total Direct Deposit Net'] + row['Total Check Net']\nassert round(d_wages + d_nontax - d_tax - d_ee_ded, 2) == round(d_net, 2)","skill":"finance/payroll-run-variance","platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-15","tags":null,"metadata":null,"created_at":"2026-10-15T15:00:49.592788+00:00","updated_at":"2026-10-15T15:00:49.592788+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":"2dbc2545-5783-4b07-9c52-053546079bd5","agent":"flux","category":"gotcha","title":"Resolve Klaviyo purchase metrics by integration source before building purchaser exclusions","problem":"Clean Nutra had two Klaviyo metrics named Placed Order. The existing all-time purchaser segment used an API metric that had stopped receiving activity, yielding only 1,794 profiles and making a seemingly valid purchaser exclusion severely incomplete.","solution":"Inspect the integration source and activity history of every same-named purchase metric before choosing a segment condition. In this account, XNamTZ was the Shopify Placed Order metric, while XxQhpm was the API metric with no activity since June 2026. Existing segment RkvDka referenced the latter. With approval, create the all-time purchaser segment against XNamTZ instead: Rq9mVr, named CN | Klaviyo all-time purchasers, returned 136,263 profiles. Segment creation is not proof of Meta synchronization. The remaining connection step was UI-only: Klaviyo Integrations > Meta Ads > View Settings > Custom audiences > Add connection, select Rq9mVr, and create the identically named audience. The existing integration targets act_314955010695474; verify the audience actually appears there before sharing it. The recorded work completed segment creation, not the UI connection or new-audience share-out.","code_snippet":null,"skill":null,"platform":"general","applicable_to":[],"verified":false,"session_date":"2026-10-15","tags":null,"metadata":null,"created_at":"2026-10-15T15:00:49.078973+00:00","updated_at":"2026-10-15T15:00:49.078973+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}]}