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.
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)
- CRITICAL: No written policy for “Price adjustment request” tickets (
price_adjustmentmissing in policies.json), but they were 107 of 1,146 tickets (9.3%) last season. Decide it by Nov 13. - 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).
- CRITICAL: Existing macro “Free shipping” says “over $50” but the free-shipping threshold is $75 (discounts.free_shipping_threshold).
- CRITICAL: Existing macro “Holiday hours” mentions dates that have passed: “Dec 24, 2025”, “Jan 1, 2026”.
- WARNING: Existing macro “Holiday shipping” promises “guaranteed to arrive by Christmas”.
- 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).
- WARNING: Existing macro “Black Friday code” mentions code BF20, which isn't one of this year's codes (MAYA20, VIPEARLY and WELCOME10).
- WARNING: Existing macro “Discount code help” mentions code SUMMER15, which isn't one of this year's codes (MAYA20, VIPEARLY and WELCOME10).
- 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.
- 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 type | Tickets | Share | Per 100 orders | Peak day |
|---|---|---|---|---|
| Where is my order (WISMO) | 364 | 31.8% | 11.9 | Thu Dec 4 (32) |
| Return or exchange | 142 | 12.4% | 4.6 | Fri Dec 26 (17) |
| Shipping deadline question | 113 | 9.9% | 3.7 | Mon Dec 15 (7) |
| Price adjustment request | 107 | 9.3% | 3.5 | Fri Nov 28 (20) |
| Discount code not working | 97 | 8.5% | 3.2 | Fri Nov 28 (23) |
| Cancel or change an order | 64 | 5.6% | 2.1 | Fri Nov 28 (7) |
| Damaged or defective | 54 | 4.7% | 1.8 | Thu Dec 4 (4) |
| Address change | 48 | 4.2% | 1.6 | Fri Nov 28 (3) |
| Gift options | 45 | 3.9% | 1.5 | Thu Nov 27 (2) |
| Out of stock or backorder | 44 | 3.8% | 1.4 | Thu Nov 27 (3) |
| Escalation: chargeback, legal or safety | 8 | 0.7% | 0.3 | Sat Nov 29 (1) |
| Other (unclassified) | 60 | 5.2% | 2.0 | Tue 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 days | Projected orders | Forecast tickets | Main types | Agent-hours needed | Agents needed | On shift | Short by | Waiting at day end |
|---|---|---|---|---|---|---|---|---|
| Mon Nov 30 | 232 | 88 | WISMO 28, Discount code not working 18 | 25.9 | 4 | 2 | — | 56 |
| Fri Nov 27 | 353 | 80 | Discount code not working 28, Price adjustment request 24 | 23.6 | 3 | 2 | — | 43 |
| Tue Dec 1 | 119 | 67 | WISMO 31, Price adjustment request 14 | 21.8 | 3 | 2 | — | 33 |
| Thu Dec 3 | 87 | 60 | WISMO 35, Price adjustment request 7 | 10.9 | 2 | 2 | — | — |
| Wed Dec 2 | 84 | 59 | WISMO 34, Price adjustment request 9 | 16.2 | 3 | 2 | — | 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 of | Forecast tickets | Busiest day | Capacity | Days short |
|---|---|---|---|---|
| Nov 9 | 21 | Sun Nov 15 (13) | 0 | 2 |
| Nov 16 | 74 | Wed Nov 18 (11) | 450 | 2 |
| Nov 23 | 273 | Fri Nov 27 (80) | 450 | 3 |
| Nov 30 | 392 | Mon Nov 30 (88) | 540 | 0 |
| Dec 7 | 202 | Mon Dec 7 (37) | 540 | 0 |
| Dec 14 | 155 | Mon Dec 14 (28) | 540 | 0 |
| Dec 21 | 108 | Fri Dec 25 (28) | 360 | 3 |
| Dec 28 | 65 | Mon Dec 28 (26) | 270 | 0 |
3. Policy coverage (a ticket type with at least 3% of last season's tickets needs a written policy)
| Ticket type | Share | Macros generated | Status |
|---|---|---|---|
| Where is my order (WISMO) | 31.8% | 3 of 3 | OK |
| Return or exchange | 12.4% | 2 of 2 | OK |
| Shipping deadline question | 9.9% | 3 of 3 | OK |
| Price adjustment request | 9.3% | 0 of 2 | GAP: price_adjustment missing. Decide by Nov 13 |
| Discount code not working | 8.5% | 2 of 2 | OK |
| Cancel or change an order | 5.6% | 1 of 1 | OK |
| Damaged or defective | 4.7% | 1 of 1 | OK |
| Address change | 4.2% | 1 of 1 | OK |
| Gift options | 3.9% | 1 of 1 | OK |
| Out of stock or backorder | 3.8% | 1 of 1 | OK |
| Escalation: chargeback, legal or safety | 0.7% | 0 of 0 | Goes 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)
| Macro | Severity | Problem | What to do |
|---|---|---|---|
| Holiday shipping | CRITICAL | 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) | Retire it and use “Holiday 2026 - Shipping: holiday order-by dates” |
| Holiday shipping | WARNING | Promises “guaranteed to arrive by Christmas” | Remove the promise and give the last order dates instead |
| Returns & exchanges | WARNING | 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) | Add the holiday window, or use “Holiday 2026 - Returns: holiday window” |
| Black Friday code | WARNING | Mentions 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 help | WARNING | Mentions 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 shipping | CRITICAL | Says “over $50” but the free-shipping threshold is $75 (discounts.free_shipping_threshold) | Change it to $75 |
| Holiday hours | CRITICAL | Mentions 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.
| Macro | Ticket type | Use | Facts from | State |
|---|---|---|---|---|
| Holiday 2026 - WISMO: not shipped yet | WISMO | all season | shipping, bfcm_backlog | recommended |
| Holiday 2026 - WISMO: BFCM order not shipped yet | WISMO | Nov 26–Dec 3 | bfcm_backlog | recommended |
| Holiday 2026 - WISMO: shipped, in transit | WISMO | all season | none (no policy facts) | recommended |
| Holiday 2026 - Shipping: holiday order-by dates | Shipping deadline question | until Dec 14 | shipping, bfcm_backlog | recommended |
| Holiday 2026 - Shipping: only the fastest method is still on time | Shipping deadline question | Dec 15–Dec 21 | shipping, gift_options | recommended |
| Holiday 2026 - Shipping: after the last order date | Shipping deadline question | from Dec 22 | shipping, gift_options | recommended |
| Holiday 2026 - Discount: code not working | Discount code not working | all season | discounts | recommended |
| Holiday 2026 - Discount: item not in the sale | Discount code not working | Nov 26–Dec 1 | discounts | recommended |
| Holiday 2026 - Order: change or cancel | Cancel or change an order | all season | order_changes, returns | recommended |
| Holiday 2026 - Order: change the shipping address | Address change | all season | address_change | recommended |
| Holiday 2026 - Returns: holiday window | Return or exchange | until Jan 31, 2027 | returns | recommended |
| Holiday 2026 - Returns: returning a gift | Return or exchange | all season | returns | recommended |
| Holiday 2026 - Damaged or defective item | Damaged or defective | all season | damaged_defective, escalation | recommended |
| Holiday 2026 - Gift options | Gift options | all season | gift_options | recommended |
| Holiday 2026 - Out of stock: when is it back? | Out of stock or backorder | all season | out_of_stock | recommended |
| Holiday 2026 - Holiday support hours | General | all season | support_hours | recommended |
| Holiday 2026 - Shipping: international orders | General | all season | international | recommended |
- Not generated: “Holiday 2026 - Price adjustment: request (we offer them)” and “Holiday 2026 - Price adjustment: request (we don't offer them)”: need
price_adjustmentin 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)
| Situation | Route to |
|---|---|
| Any order over $250 with a problem | the support lead |
| A refund, credit or replacement worth more than $150 | the support lead, for approval |
| Chargeback, payment dispute or suspected fraud | the support lead, same day; never answer with a macro |
| Injury, burn, allergic reaction or any safety concern | the support lead, same day; never answer with a macro |
| Legal threats, press or influencer complaints | the support lead |
| “Price adjustment request” tickets, until the policy is decided | the support lead, so every customer gets the same answer |
| A customer's third message about the same order | the 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)
| # | Severity | What to do | State | By |
|---|---|---|---|---|
| 1 | CRITICAL | Existing macro “Holiday shipping”: Retire it and use “Holiday 2026 - Shipping: holiday order-by dates”. | recommended | Oct 14 |
| 2 | CRITICAL | Existing macro “Free shipping”: Change it to $75. | recommended | Oct 14 |
| 3 | CRITICAL | Existing macro “Holiday hours”: Retire it and use “Holiday 2026 - Holiday support hours”. | recommended | Oct 14 |
| 4 | CRITICAL | Decide 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). | recommended | Nov 13 |
| 5 | WARNING | Add 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. | recommended | Nov 17 |
| 6 | WARNING | Existing macro “Returns & exchanges”: Add the holiday window, or use “Holiday 2026 - Returns: holiday window”. | recommended | Nov 17 |
| 7 | WARNING | Existing macro “Black Friday code”: Remove BF20, or use “Holiday 2026 - Discount: code not working”. | recommended | Nov 17 |
| 8 | WARNING | Existing macro “Discount code help”: Remove SUMMER15, or use “Holiday 2026 - Discount: code not working”. | recommended | Nov 17 |
| 9 | WARNING | Add 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. | recommended | Nov 20 |
| 10 | PLAN | Review 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. | recommended | Nov 17 |
| 11 | PLAN | Put 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. | recommended | Nov 20 |
| 12 | PLAN | Final re-run with the final policies, cutoff dates and macros export. Brief the team on the escalation map. | recommended | Nov 24 |
| 13 | PLAN | Start using “Holiday 2026 - WISMO: BFCM order not shipped yet” through Dec 3. | recommended | Nov 26 |
| 14 | PLAN | Start using “Holiday 2026 - Discount: item not in the sale” through Dec 1. | recommended | Nov 26 |
| 15 | PLAN | Stop using “Holiday 2026 - Discount: item not in the sale” (its last day is Dec 1). | recommended | Dec 2 |
| 16 | PLAN | Stop using “Holiday 2026 - WISMO: BFCM order not shipped yet” (its last day is Dec 3). | recommended | Dec 4 |
| 17 | PLAN | Switch from “Holiday 2026 - Shipping: holiday order-by dates” to “Holiday 2026 - Shipping: only the fastest method is still on time”. | recommended | Dec 15 |
| 18 | PLAN | Switch from “Holiday 2026 - Shipping: only the fastest method is still on time” to “Holiday 2026 - Shipping: after the last order date”. | recommended | Dec 22 |
| 19 | PLAN | Stop using “Holiday 2026 - Returns: holiday window” (its last day is Jan 31, 2027). | recommended | Feb 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.
Related 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.