Skill · Customer Success

Holiday Support Macros

Prepare your support team for BFCM: forecast holiday tickets and staffing, find policy gaps and stale macros, and generate holiday macros built only from your written policies.

Created by type.com

What it does

  • Sorts last season's holiday tickets into types (WISMO, shipping deadlines, discount codes, price adjustments, returns and more), with tickets per 100 orders and peak days
  • Forecasts this season's tickets day by day from projected orders, with agents needed on peak days, the days you're over capacity, and how long unanswered tickets pile up
  • Flags ticket types with no written policy, with a decide-by date, and catches old macros with past dates, wrong cutoffs, old codes or "guaranteed by Christmas" promises
  • Drafts holiday macros whose dates, rules and promises come only from your policies or the Shipping Cutoff Checker, with one order-by date per checkout rate
  • Maps placeholders for Gorgias, Zendesk, Help Scout and Shopify Inbox, marks any that need manual fill, and gives a dated checklist to go live by Nov 20. 24 end-to-end checks

Before you start

  • Last holiday season's tickets exported from your helpdesk to CSV: created date, subject, first message and tags
  • Your holiday policies in the DATA_CONTRACT.md format: shipping cutoffs, returns, discount rules, codes, gift options, damaged items, support hours; gaps reported, never guessed
  • Optional: your current macros, last season's daily orders plus the Budget Planner's `--json` plan, and the Shipping Cutoff Checker's `--json` result
  • Optional: how many agents you have, weekend coverage, and how many tickets each agent handles in a shift
  • Python 3.8 or newer. Nothing to install and no API keys needed

See an example

Example output from a sample run. Company names and figures are sample data.

Fictional sample store: Fernhill Goods. Offline demonstration only.

Holiday Support Macros: Fernhill Goods, as of 2026-10-07

Verdict: NOT READY. 4 critical issues and 6 warnings. Black Friday is Nov 27, 2026 (51 days away) and Cyber Monday is Nov 30; have the holiday macros live by Nov 20. Last season: 1,146 tickets (Nov 15–Dec 31, 2025). This season's forecast: 1,291 tickets (Nov 14–Dec 30, 2026), busiest on Mon Nov 30 with 88.

Issues, most severe first (all detected; nothing was changed in your helpdesk or store)

  1. CRITICAL: No written policy for “Price adjustment request” tickets (price_adjustment missing in policies.json), but they were 107 of 1,146 tickets (9.3%) last season. Decide it by Nov 13.
  2. CRITICAL: Existing macro “Holiday shipping” says “order by Dec 18, 2025”: that date has passed, and this year's last order dates are Dec 14 (Standard) and Dec 21 (Express).
  3. CRITICAL: Existing macro “Free shipping” says “over $50” but the free-shipping threshold is $75 (discounts.free_shipping_threshold).
  4. CRITICAL: Existing macro “Holiday hours” mentions dates that have passed: “Dec 24, 2025”, “Jan 1, 2026”.
  5. WARNING: Existing macro “Holiday shipping” promises “guaranteed to arrive by Christmas”.
  6. WARNING: Existing macro “Returns & exchanges” says “items within 30 days of delivery” and doesn't mention the holiday window (orders Nov 1–Dec 24 can be returned until Jan 31, 2027).
  7. WARNING: Existing macro “Black Friday code” mentions code BF20, which isn't one of this year's codes (MAYA20, VIPEARLY and WELCOME10).
  8. WARNING: Existing macro “Discount code help” mentions code SUMMER15, which isn't one of this year's codes (MAYA20, VIPEARLY and WELCOME10).
  9. WARNING: The forecast is above capacity on 10 days: Sat Nov 14 (support closed), Sun Nov 15 (support closed), Sat Nov 21 (support closed), Sun Nov 22 (support closed), Thu Nov 26 (support closed), Sat Nov 28, Sun Nov 29, Fri Dec 25 (support closed), …. Unanswered tickets peak at 63 on Sun Dec 27 and clear by Mon Dec 28.
  10. WARNING: 60 of 1,146 tickets (5.2%) matched no rule, so the mix and forecast by type are incomplete.
1. Last season's tickets (Nov 15–Dec 31, 2025)

1,146 tickets over 47 days and 3,055 orders (37.5 tickets per 100 orders). Classified with references/ticket_rules.json: the first matching rule wins.

Ticket typeTicketsSharePer 100 ordersPeak day
Where is my order (WISMO)36431.8%11.9Thu Dec 4 (32)
Return or exchange14212.4%4.6Fri Dec 26 (17)
Shipping deadline question1139.9%3.7Mon Dec 15 (7)
Price adjustment request1079.3%3.5Fri Nov 28 (20)
Discount code not working978.5%3.2Fri Nov 28 (23)
Cancel or change an order645.6%2.1Fri Nov 28 (7)
Damaged or defective544.7%1.8Thu Dec 4 (4)
Address change484.2%1.6Fri Nov 28 (3)
Gift options453.9%1.5Thu Nov 27 (2)
Out of stock or backorder443.8%1.4Thu Nov 27 (3)
Escalation: chargeback, legal or safety80.7%0.3Sat Nov 29 (1)
Other (unclassified)605.2%2.0Tue Dec 2 (3)

Busiest days: Mon Dec 1 (76, mostly WISMO); Fri Nov 28 (67, mostly discount code not working); Tue Dec 2 (59, mostly WISMO); Thu Dec 4 (55, mostly WISMO); Wed Dec 3 (53, mostly WISMO). Unclassified: 60 of 1,146 (5.2%). Most common: “Can't log in” (10), “Any update?” (9), “Corporate gifts” (8), “Collab request” (7), “Do you ship to Canada?” (6). Add keywords for the ones that belong to a type and re-run.

2. This season's forecast and staffing (Nov 14–Dec 30, 2026)

Each day = last season's tickets on the same weekday (364 days earlier, so Black Friday lines up with Black Friday) × this season's projected orders ÷ last season's orders, over the 7 days up to that day. Projected orders 3,430 vs 3,055 last season (+12.3%). Forecast 1,291 tickets (+12.7%). Dec 7–Dec 30 have no projection: used last season's orders × 1.12 (this season's projection ÷ last season over Nov 14–Dec 6). Capacity: 2 agents on weekdays and 1 on weekends from Sat Nov 28 to Sun Dec 20 only (support_hours.weekend_coverage), 45 tickets per agent per 8-hour shift; support closed Thu Nov 26 and Fri Dec 25. Agents needed and agent-hours cover the day's forecast plus the tickets still waiting from the day before.

Busiest daysProjected ordersForecast ticketsMain typesAgent-hours neededAgents neededOn shiftShort byWaiting at day end
Mon Nov 3023288WISMO 28, Discount code not working 1825.942—56
Fri Nov 2735380Discount code not working 28, Price adjustment request 2423.632—43
Tue Dec 111967WISMO 31, Price adjustment request 1421.832—33
Thu Dec 38760WISMO 35, Price adjustment request 710.922——
Wed Dec 28459WISMO 34, Price adjustment request 916.232—1

Over capacity on 10 days of 47: Sat Nov 14 (short 8), Sun Nov 15 (short 13), Sat Nov 21 (short 10), Sun Nov 22 (short 11), Thu Nov 26 (short 53), Sat Nov 28 (short 8), Sun Nov 29 (short 7), Fri Dec 25 (short 28), Sat Dec 26 (short 19), Sun Dec 27 (short 16). With carry-over, unanswered tickets peak at 63 on Sun Dec 27 and are cleared by Mon Dec 28.

Week ofForecast ticketsBusiest dayCapacityDays short
Nov 921Sun Nov 15 (13)02
Nov 1674Wed Nov 18 (11)4502
Nov 23273Fri Nov 27 (80)4503
Nov 30392Mon Nov 30 (88)5400
Dec 7202Mon Dec 7 (37)5400
Dec 14155Mon Dec 14 (28)5400
Dec 21108Fri Dec 25 (28)3603
Dec 2865Mon Dec 28 (26)2700
3. Policy coverage (a ticket type with at least 3% of last season's tickets needs a written policy)
Ticket typeShareMacros generatedStatus
Where is my order (WISMO)31.8%3 of 3OK
Return or exchange12.4%2 of 2OK
Shipping deadline question9.9%3 of 3OK
Price adjustment request9.3%0 of 2GAP: price_adjustment missing. Decide by Nov 13
Discount code not working8.5%2 of 2OK
Cancel or change an order5.6%1 of 1OK
Damaged or defective4.7%1 of 1OK
Address change4.2%1 of 1OK
Gift options3.9%1 of 1OK
Out of stock or backorder3.8%1 of 1OK
Escalation: chargeback, legal or safety0.7%0 of 0Goes to a person (escalation map), not a macro
  • Price adjustment request: customers will ask, and without a written rule each agent will improvise. Decide the rule, publish it where customers can see it, add it to policies.json and re-run. Options and trade-offs: references/playbook.md §3.
4. Existing macros (10 macros checked)
MacroSeverityProblemWhat to do
Holiday shippingCRITICALSays “order by Dec 18, 2025”: that date has passed, and this year's last order dates are Dec 14 (Standard) and Dec 21 (Express)Retire it and use “Holiday 2026 - Shipping: holiday order-by dates”
Holiday shippingWARNINGPromises “guaranteed to arrive by Christmas”Remove the promise and give the last order dates instead
Returns & exchangesWARNINGSays “items within 30 days of delivery” and doesn't mention the holiday window (orders Nov 1–Dec 24 can be returned until Jan 31, 2027)Add the holiday window, or use “Holiday 2026 - Returns: holiday window”
Black Friday codeWARNINGMentions code BF20, which isn't one of this year's codes (MAYA20, VIPEARLY and WELCOME10)Remove BF20, or use “Holiday 2026 - Discount: code not working”
Discount code helpWARNINGMentions code SUMMER15, which isn't one of this year's codes (MAYA20, VIPEARLY and WELCOME10)Remove SUMMER15, or use “Holiday 2026 - Discount: code not working”
Free shippingCRITICALSays “over $50” but the free-shipping threshold is $75 (discounts.free_shipping_threshold)Change it to $75
Holiday hoursCRITICALMentions dates that have passed: “Dec 24, 2025”, “Jan 1, 2026”Retire it and use “Holiday 2026 - Holiday support hours”

No problems found in 4: Where is my order, Cancel order, Damaged item, Gift message. Checks: dates that have passed or don't match this year's policy, return / damage / change windows, free-shipping threshold and reply time against policies.json, codes that aren't this year's, and delivery promises.

5. Holiday macros generated from your policies (drafts for review)

17 of 20 templates generated; 2 not generated because a policy is missing; 1 not applicable to your policies. Every date, number and rule in them comes from policies.json.

MacroTicket typeUseFacts fromState
Holiday 2026 - WISMO: not shipped yetWISMOall seasonshipping, bfcm_backlogrecommended
Holiday 2026 - WISMO: BFCM order not shipped yetWISMONov 26–Dec 3bfcm_backlogrecommended
Holiday 2026 - WISMO: shipped, in transitWISMOall seasonnone (no policy facts)recommended
Holiday 2026 - Shipping: holiday order-by datesShipping deadline questionuntil Dec 14shipping, bfcm_backlogrecommended
Holiday 2026 - Shipping: only the fastest method is still on timeShipping deadline questionDec 15–Dec 21shipping, gift_optionsrecommended
Holiday 2026 - Shipping: after the last order dateShipping deadline questionfrom Dec 22shipping, gift_optionsrecommended
Holiday 2026 - Discount: code not workingDiscount code not workingall seasondiscountsrecommended
Holiday 2026 - Discount: item not in the saleDiscount code not workingNov 26–Dec 1discountsrecommended
Holiday 2026 - Order: change or cancelCancel or change an orderall seasonorder_changes, returnsrecommended
Holiday 2026 - Order: change the shipping addressAddress changeall seasonaddress_changerecommended
Holiday 2026 - Returns: holiday windowReturn or exchangeuntil Jan 31, 2027returnsrecommended
Holiday 2026 - Returns: returning a giftReturn or exchangeall seasonreturnsrecommended
Holiday 2026 - Damaged or defective itemDamaged or defectiveall seasondamaged_defective, escalationrecommended
Holiday 2026 - Gift optionsGift optionsall seasongift_optionsrecommended
Holiday 2026 - Out of stock: when is it back?Out of stock or backorderall seasonout_of_stockrecommended
Holiday 2026 - Holiday support hoursGeneralall seasonsupport_hoursrecommended
Holiday 2026 - Shipping: international ordersGeneralall seasoninternationalrecommended
  • Not generated: “Holiday 2026 - Price adjustment: request (we offer them)” and “Holiday 2026 - Price adjustment: request (we don't offer them)”: need price_adjustment in policies.json.
  • Not needed with your policies: “Holiday 2026 - Returns: standard window”.

Placeholders {{customer_first_name}}, {{order_number}}, {{tracking_url}} and {{agent_first_name}} are left for your helpdesk to fill. Map them to your helpdesk's syntax before saving (table in macros_preview.md and references/playbook.md §4): Gorgias leaves a variable it can't fill blank, and Help Scout and Shopify Inbox have no order-number or tracking-link variable, so agents type those in. A live macro that still needs a placeholder filled by hand is marked “needs manual fill”, never verified.

6. Escalation map (these go to a person, not a macro)
SituationRoute to
Any order over $250 with a problemthe support lead
A refund, credit or replacement worth more than $150the support lead, for approval
Chargeback, payment dispute or suspected fraudthe support lead, same day; never answer with a macro
Injury, burn, allergic reaction or any safety concernthe support lead, same day; never answer with a macro
Legal threats, press or influencer complaintsthe support lead
“Price adjustment request” tickets, until the policy is decidedthe support lead, so every customer gets the same answer
A customer's third message about the same orderthe support lead

Last season, 8 of 1,146 tickets (0.7%) contained an escalation word from policies.json: chargeback 1, dispute 3, cut myself 4.

7. Checklist (nothing has been changed)
#SeverityWhat to doStateBy
1CRITICALExisting macro “Holiday shipping”: Retire it and use “Holiday 2026 - Shipping: holiday order-by dates”.recommendedOct 14
2CRITICALExisting macro “Free shipping”: Change it to $75.recommendedOct 14
3CRITICALExisting macro “Holiday hours”: Retire it and use “Holiday 2026 - Holiday support hours”.recommendedOct 14
4CRITICALDecide the policy for “Price adjustment request” tickets and publish it, add price_adjustment to policies.json and re-run so the macros can be generated (options: references/playbook.md §3).recommendedNov 13
5WARNINGAdd keywords for the most common unclassified subjects to references/ticket_rules.json and re-run. The rules are in English: if many tickets are in another language, add keywords in that language too.recommendedNov 17
6WARNINGExisting macro “Returns & exchanges”: Add the holiday window, or use “Holiday 2026 - Returns: holiday window”.recommendedNov 17
7WARNINGExisting macro “Black Friday code”: Remove BF20, or use “Holiday 2026 - Discount: code not working”.recommendedNov 17
8WARNINGExisting macro “Discount code help”: Remove SUMMER15, or use “Holiday 2026 - Discount: code not working”.recommendedNov 17
9WARNINGAdd cover on Sat Nov 28 (53 tickets forecast, 45 capacity) and Sun Nov 29 (52 tickets forecast, 45 capacity); plan who clears the tickets that arrive while support is closed on Sat Nov 14 (8), Sun Nov 15 (13), Sat Nov 21 (10), Sun Nov 22 (11), Thu Nov 26 (53), Fri Dec 25 (28), Sat Dec 26 (19) and Sun Dec 27 (16). Options: a second agent on those days, longer shifts, outside help or a trained teammate from another role.recommendedNov 20
10PLANReview the 17 draft macros in macros_preview.md: check each fact against the policy, adapt the tone to your brand voice without changing any fact, and map the placeholders to your helpdesk.recommendedNov 17
11PLANPut the macros live in your helpdesk with their tags and test each one on a test ticket. Then export your macros and re-run this skill with --macros to mark them verified.recommendedNov 20
12PLANFinal re-run with the final policies, cutoff dates and macros export. Brief the team on the escalation map.recommendedNov 24
13PLANStart using “Holiday 2026 - WISMO: BFCM order not shipped yet” through Dec 3.recommendedNov 26
14PLANStart using “Holiday 2026 - Discount: item not in the sale” through Dec 1.recommendedNov 26
15PLANStop using “Holiday 2026 - Discount: item not in the sale” (its last day is Dec 1).recommendedDec 2
16PLANStop using “Holiday 2026 - WISMO: BFCM order not shipped yet” (its last day is Dec 3).recommendedDec 4
17PLANSwitch from “Holiday 2026 - Shipping: holiday order-by dates” to “Holiday 2026 - Shipping: only the fastest method is still on time”.recommendedDec 15
18PLANSwitch from “Holiday 2026 - Shipping: only the fastest method is still on time” to “Holiday 2026 - Shipping: after the last order date”.recommendedDec 22
19PLANStop using “Holiday 2026 - Returns: holiday window” (its last day is Jan 31, 2027).recommendedFeb 1, 2027

Gap threshold: ticket types with ≥ 3% of last season's tickets. Forecast: weekday-aligned (364 days), scaled by projected orders over 7 days. States: detected → recommended → applied → verified. Read-only: no replies sent and no helpdesk, store or macro changed.

AI for customer success teams: a practical guide

Browse the technical files
---
name: holiday-support-macros
description: Get a DTC support team ready for BFCM and the holidays. Learns last season's holiday ticket mix from a helpdesk export (Gorgias, Zendesk, Help Scout and others), forecasts this season's tickets and the agents needed on peak days, finds ticket types customers will ask about that have no written policy, catches stale or contradictory macros (past dates, wrong return windows or cutoff dates, old codes, delivery promises), and generates a full set of holiday macros whose facts come only from the store's written policies, with a dated checklist to have them live by Nov 20. Read-only; macros are drafts for review.
---

# Holiday Support Macros

Black Friday 2026 is **Nov 27** and Cyber Monday is **Nov 30**. Holiday support goes wrong in predictable ways. "Where is my order?" swamps the team the week after Black Friday while the backlog ships. Customers who bought a week early ask for the sale price, and with no written rule each agent answers differently. A code that doesn't stack with the sale produces a wave of "my code isn't working". And last year's macros are still live: "Order by Dec 18, 2025", a 30-day returns answer that ignores the holiday extension, a code that expired.

This skill does the preparation from local exports and puts a date on every step: **learn last season's mix → forecast tickets and staffing → close the policy gaps → fix or retire stale macros → generate the holiday macros from the written policies → have them live by Nov 20.**

It sits next to other skills and does not repeat them:
- **Holiday Shipping Cutoff Checker** works out the last safe order dates and the BFCM backlog. Pass its `--json` result unchanged with `--cutoffs`: its dates, checkout rates, same-day cutoff time and backlog replace policies.json's where they disagree, and the report warns with both values. Run it with the Budget Planner's `--csv` plan as demand and `--aov` set to the planner's AOV (88.66 in the sample), or the backlog will differ.
- **BFCM Offer & Discount Config QA** checks how codes and the sale are set up in Shopify. Use its fixed configuration for the codes listed in `policies.json`.
- **BFCM Holiday Budget Planner** projects BFCM orders. Its `--json` plan is this skill's order projection (its `--csv` has no orders column and is refused). Pass it together with last season's daily orders: `--orders last_season.csv --orders plan.json`. If the orders file also holds this season's projection rows, remove them or add `--prefer-plan`.
- **BFCM Live Pulse** watches the sale as it runs; **Review Insight Miner** clusters reviews and tickets into product themes after the season.

## Before you run

This skill ships scripts and sample data alongside this SKILL.md. Before running any command:

1. **Get the files.** Make sure the skill's other files (`scripts/`, `examples/`, `tests/`, `references/` and `DATA_CONTRACT.md`) are in your working folder at the same relative paths. Some environments load only SKILL.md. If yours did, fetch each file from this skill's published files and write it to the matching path. In type.com, read them with the skill-file tools. Anywhere else, the type.com Skills Library API lists every file with its path, content and `sha256`: GET `https://api.type.com/api/public/library/skills` and take the entry with slug `holiday-support-macros`.
2. **Check the copies are exact.** Compare each file's size in bytes, not characters (and its hash, where your tools report one), with the published version before running. A copy written out from the published file is fine once its byte size and hash match. Never run a script you summarised or reconstructed from memory.
3. **Run from the skill's folder**, calling interpreters explicitly: `python3 scripts/…` and `bash examples/run.sh`. Python 3.8+ and the standard library only; there is nothing to install.
4. **Try the sample first.** Run `bash examples/run.sh`. Its output must match `examples/expected_output.txt` exactly. If it doesn't, stop and report the first differing line rather than running on real data. `bash tests/run_tests.sh` runs the full check suite.

## When to use this

- October to mid-November, for a DTC brand's support team, or for each brand an agency supports.
- Someone asks "are our holiday macros ready?", "how many tickets should we expect over BFCM?", "do we need more people on Black Friday weekend?" or "what do we tell customers who want the sale price?"
- After a policy changes (new cutoff dates, a new or retired code, a different return window): re-run to catch macros that now contradict it.
- After the new macros are live: re-run with a fresh macros export to mark them verified, and again on the final-run date.
- On a schedule: weekly from October to Black Friday. Stay silent when the verdict is READY and nothing changed.

## Operating rules

1. **Read-only.** The script reads local files and writes a report and drafts. Never send a reply, create, edit or delete a macro, change helpdesk rules or tags, or change store settings unless the user approves that specific change. When they do, make it, read it back, export the macros and re-run to confirm.
2. **Never invent a policy.** Every date, number, price, code, rule and promise in a macro comes from `policies.json` (or the Shipping Cutoff Checker's `--cutoffs` result). If a policy is missing, report the gap and ask the owner to decide; don't fill it from "what most stores do" or a competitor's page. The script enforces this: no macro without its policy, and templates can't carry facts or promise words (guarantee, refund, free, always, never, Christmas, holiday, arrive by, in full) of their own.
3. **Policies are the business's decision.** For a gap, lay out the options in `references/playbook.md` §3 and let the owner choose. Write the decision into `policies.json` and publish it where customers can see it.
4. **Macros are drafts.** A person reviews every macro before it goes live. Tone can change; facts can't (see "Adapting the tone" below).
5. **Quote exact numbers**: X of Y tickets, the percentage, the date. Forecasts are estimates scaled from last season; say what they rest on.
6. **Keep exports local.** Tickets carry names, emails and order numbers. The report masks emails, phone numbers, street addresses, names after "my name is" / "this is" / a sign-off, and long numbers in its examples; `tickets_classified.csv` holds the raw ticket text (formula-safe for spreadsheets). Don't paste exports into chat, and delete them when done.
7. **Escalations go to a person.** Never answer anything on the escalation map with a macro.
8. Say which state each item is in: **detected**, **recommended**, **applied** or **verified**.

## Gathering the inputs

Exact columns and fields are in `DATA_CONTRACT.md`.

1. **Holiday policies, required** (`policies.json`). Write them from the store's published pages (shipping, returns, FAQ) and its discount setup: shipping services with last safe order dates and the checkout rate each one sits under, the share of customers the dates hold for, the BFCM backlog note, returns (standard and holiday window), price adjustments, discount rules and this season's codes, change/cancel window, address changes, gift options, backorders, damaged items, international shipping, holiday support hours (and the dates weekends are covered) and escalation thresholds. Leave out what isn't decided. The report will name it.
2. **Last season's tickets, required** (`tickets.csv`). Export last November to December from your helpdesk as CSV with created date, subject, first message (a preview is enough) and tags.
3. **Existing macros, optional** (`macros_existing.csv`): name and body of every macro or saved reply in use. Without it, stale macros are not checked.
4. **Orders, optional** (`orders.csv`): last season's orders by day from Shopify Analytics, and this season's projection by day (your own, or the BFCM Holiday Budget Planner's `--json` plan). A date may appear only once: with the planner, pass last season's orders plus `plan.json`, and remove this season's rows from the orders file or add `--prefer-plan`. Without it, the forecast is last season's tickets × `--growth`.
5. **Capacity, optional**: agents on shift on weekdays and weekends, and tickets one agent handles in a shift (your helpdesk's productivity report, or last December's average).

If the user has connected tools that can export these (a helpdesk integration, Shopify), you may pull the same data that way, but write it into these files and run the script on them. Never ask the user to paste API keys or tokens into chat.

## Running it

```bash
python3 scripts/holiday_support.py --policies policies.json --tickets tickets.csv \
  [--macros macros_existing.csv] [--helpdesk gorgias|zendesk|help-scout|shopify-inbox] \
  [--orders orders.csv ...] [--prefer-plan] [--cutoffs cutoffs.json] [--date-format mdy|dmy] \
  [--agents 2 --weekend-agents 1 --tickets-per-agent 45] \
  [--as-of YYYY-MM-DD] [--out-dir out/] [--json analysis.json]
```

`bash examples/run.sh [OUT_DIR]` runs it on the bundled sample store (Fernhill Goods, fictional) and writes the macros to `OUT_DIR`.

What it does:

1. **Learns last season's mix.** Sorts every ticket with the keyword rules in `references/ticket_rules.json` (whole words; escalations first, then "first" phrases such as "hasn't arrived", then the first matching type; a keyword with "not", "no", "isn't" or "wasn't" up to 3 words before it doesn't count; edit freely) into escalation (chargeback, legal, safety: flagged for a person), WISMO, shipping deadline, discount code, price adjustment, cancel/change, address change, return/exchange, damaged, gift options, out of stock, or other. Reports the mix, tickets per 100 orders by type, peak days, and the most common unclassified subjects so you can add keywords.
2. **Forecasts this season.** Each day = last season's tickets on the same weekday (364 days earlier, so Black Friday lines up) × this season's projected orders ÷ last season's, over the 7 days up to that day. Then agent-hours and agents needed on the busiest days (the day's tickets plus those still waiting), days over capacity (weekends outside `support_hours.weekend_coverage` count as closed), and how long unanswered tickets carry over.
3. **Finds policy gaps.** Any ticket type with at least 3% of last season's tickets and no policy to answer it is a **GAP** with a decide-by date (a week before the macros go live).
4. **Audits existing macros.** Past dates and years, cutoff dates and return windows that contradict the policy, other numbers (free-shipping threshold, damage-report and change windows, reply time), codes that aren't this season's, and delivery promises such as "guaranteed by Christmas".
5. **Generates the holiday macros** from `references/macro_templates.json`, one or more per ticket type (for example WISMO before shipping during the BFCM backlog, WISMO in transit, and three stages of shipping deadlines, with one date per checkout rate, never a carrier date customers can't choose), filled only with policy values, with `{{customer_first_name}}`, `{{order_number}}`, `{{tracking_url}}` and `{{agent_first_name}}` left for the helpdesk. A macro whose policy is missing is not generated and the gap is reported.
6. **Gives a verdict and a dated checklist**: `NOT READY` (any critical), `FIX BEFORE NOV 20` (warnings only) or `READY`, with when to start and stop using each time-bound macro.

Exit codes: 0 means the report was written, 2 means invalid input (with the file, line or field and the reason).

## Acting on the report

Present the verdict and the issues, then work through them in this order. Each change needs the user's approval. Details and templates are in `references/playbook.md`.

1. **Stale macros first** (CRITICAL within a week). They may already be in use. Retire or correct each flagged macro; the report names the replacement.
2. **Close the policy gaps** by the decide-by date. Price adjustments are the usual one: the playbook lists the options and what each costs. Add the decision to `policies.json` and re-run, and the macro appears.
3. **Review the drafts** in `macros_preview.md`: check each fact against the policy, adapt the tone (below), and map the placeholders to your helpdesk with the table in `macros_preview.md`. That table shows which variables were confirmed in each helpdesk's documentation and which say "verify in your helpdesk".
4. **Staffing.** Cover the days over capacity, especially weekends and days support is closed, or agree the response time customers will see.
5. **Go live by Nov 20**, tagged, tested on a test ticket. Export the macros and re-run with `--macros --helpdesk <yours>`: unchanged drafts with their placeholders mapped show as **verified**, an edited fact is caught (**applied**), and a macro with a placeholder still in generic form or one your helpdesk can't fill (order number and tracking link on Help Scout and Shopify Inbox) shows as **needs manual fill**, never verified.
6. **Switch on the dates in the checklist** (BFCM backlog macro on, then off; the shipping-deadline macros in three stages), and re-run on the final-run date. Once the backlog has cleared (the catch-up date), the same-day sentence in “WISMO: not shipped yet” and “Shipping: holiday order-by dates” still describes the Black Friday rush; with the owner's approval, cut it back to the plain same-day rule so customers don't read a date that has passed.

### Adapting the tone to your brand voice without changing facts

You may change: greetings and sign-offs, word choice, sentence order, warmth, emoji (if your brand uses them), length, and formatting such as bullets. You may not change: any date, weekday, number, price, percentage, code, shipping method name, URL, condition ("unused and in the original packaging"), what is or isn't offered, or who pays for what. Don't add promises ("guaranteed", "for sure") or facts that aren't in `policies.json`, and keep every placeholder. Easiest: put your voice in the templates (they hold wording only) and re-run, so next season starts in your voice. After editing, export the live macros and re-run with `--macros`: any changed or dropped date or number shows as **applied** instead of **verified**.

### Escalation map: what must go to a person

The report builds this from `policies.json` → `escalation` and counts last season's tickets that would have matched. Never answer these with a macro:

- **Money above the threshold**: an order over `order_value_over`, or a refund, credit or replacement worth more than `refund_over`, needs the owner's approval.
- **Chargebacks, payment disputes, fraud**: same day, to the owner.
- **Safety**: injury, burns, allergic reactions, broken glass. Same day, to the owner; keep the photos.
- **Legal threats, press, influencers.**
- **Policy gaps**: until a gap is decided, every such ticket goes to the owner so each customer gets the same answer.
- **Third contact about the same order**: a person, not another macro.

## Files

- `scripts/holiday_support.py`: the analyzer and macro generator. Local files only, deterministic output.
- `DATA_CONTRACT.md`: every input field, the facts templates can use, outputs, thresholds and dates.
- `references/ticket_rules.json`: editable keyword rules for the ticket types.
- `references/macro_templates.json`: the macro templates (wording only; facts come from the policies).
- `references/playbook.md`: tuning the rules, forecasting and staffing, options for common policy gaps, helpdesk placeholder syntax with sources, brand-voice examples, the escalation map, the 2026 timeline and a status-update template.
- `examples/run.sh`, `examples/expected_output.txt`, `examples/data/`, `examples/expected/`: sample run and its expected report, macros, preview and forecast. `examples/make_fixtures.py` regenerates the sample data.
- `tests/run_tests.sh`, `tests/make_cases.py`: 24 end-to-end checks on hand-checkable inputs, including a Budget Planner → Shipping Cutoff Checker → this skill run when those packages sit next to this one (skipped, and said so, otherwise). No network needed.