operation-report-writing
How an unattended operation report turn is written — the wording of each section and of each "Oscar recommends" line, given figures that are already calculated. Covers both the weekly report and the three rolling windows (Last 7 / 14 / 30 days). Read this ONLY when a schedule or a page warm-up has started a report-writing turn. It has no entry of its own, no merchant is waiting on it, and it must never be entered from a chat message.
name: operation-report-writing description: >- How an unattended operation report turn is written — the wording of each section and of each "Oscar recommends" line, given figures that are already calculated. Covers both the weekly report and the three rolling windows (Last 7 / 14 / 30 days). Read this ONLY when a schedule or a page warm-up has started a report-writing turn. It has no entry of its own, no merchant is waiting on it, and it must never be entered from a chat message. tags: [internal, ops, requires-yepai-tools] license: MIT-0
Writing the scheduled operation report
A schedule or a page warm-up started this turn, not a merchant — nobody is waiting on it. The figures are already worked out; your whole job is the words around them.
One call, then write
OPERATION_REPORT_SNAPSHOT with the id you were given returns everything that report needs —
the metrics, the period-over-period changes, the baselines, the top products, the
alerts that fired, and the candidate drivers the backend matched. That one call is
your entire input (one call per id, when the prompt hands you more than one — see "Two
kinds of turn" below). Do not fetch anything else, do not go looking for a number that is
missing, and do not open the store's data yourself to check one.
What is yours and what is not
| Section | Decided by | Your part |
|---|---|---|
| The three headline metrics, their change and their arrows | The snapshot | Nothing — quote them |
| The line reading those three together | Fixed logic: growth that comes from order count versus from basket size; a basket drop past a tenth is worth flagging for discounting or a shift to cheaper lines | Wording only |
| Highlights, one line each | Fixed viewpoints: sales, orders, products | The content |
| Top products, both tables | The snapshot | Nothing |
| Oscar recommends, per section | — | All of it — in three parts, see below |
| The alerts that fired this period | The alert log | Nothing |
The sections you write are the keys the turn's prompt hands you, and only those. The
Highlights row above lists three merchant-facing VIEWPOINTS, which is not the same list:
the card's Orders line is filled from the fulfilment recommendation, because "3 orders past
your usual pace · fulfilment running 27% slower" is fulfilment content. Writing an extra
key for it produces a paragraph with no section to sit above — measured on dev
(2026-08-24): a report whose stored recommends were sales / alerts / orders / fulfillment
had orders rendered nowhere at all, one whole turn spent on text no merchant ever saw.
The page now floats an unrecognised key to the top rather than dropping it, so the cost has
gone from invisible to merely misplaced — still not where you meant it.
followups is the one exception, and it is not a section. It is the four buttons under
the report card in the merchant's chat — their only way into this report from there. The
turn's prompt asks for it by name, in the same JSON object, and nothing above applies to
it: it has somewhere to go, it is never rendered as a banner, and leaving it out costs the
merchant those four buttons. Measured on dev (2026-09-14): the rule above is worded
strongly enough that two runs in a row wrote the sections and silently dropped followups,
which is why it is called out here rather than left for the prompt to win on its own.
Two kinds of turn: the report week, and the three rolling windows
Almost everything in this file is about the WEEKLY report — one frozen Monday-to-Sunday week, one snapshot, one set of sections. There is a second turn that reads this same file, and the prompt tells you which one you are in by handing you three snapshot ids at once:
7d -> "7d-<date>"
14d -> "14d-<date>"
30d -> "30d-<date>"
Call OPERATION_REPORT_SNAPSHOT once per id and write about each window from its own
snapshot. Everything under "Every Oscar recommends has three parts" and every one of the
Hard rules applies unchanged — a window is explained under exactly the rules a week is.
What differs is four things, and all four are ways of getting the window wrong:
These windows ROLL. They end YESTERDAY, in the shop's timezone. Each snapshot's
period names its own first and last day; read it. Never write "this week", never write
"the report" or "the report week" — that is a different thing with different days, and a
merchant reading a chip is not looking at it.
Never carry a figure from one window into another. They overlap: the same order is inside all three, and the seven days are inside the fourteen. A sentence about the month that quotes the week's total is wrong twice over.
dailySeries in these snapshots IS the window's own days — unlike the weekly report's,
which runs four weeks back so the "usual day" band has something to average against.
No followups here. The exception above is about the weekly report card in chat. A
rolling window has no card and no buttons, so a followups key written in this turn is
discarded — the four buttons it would have made have nowhere to appear.
Reply with one JSON object keyed by window, each window mapping section to its recommendation:
{"7d": {"sales": {...}, "fulfillment": {...}, "products": {...}},
"14d": {...}, "30d": {...}}
An answer that does not name its windows is thrown away in full. Not filed under a guess: an unlabelled paragraph could be about seven days or thirty, and putting a month's reasoning over a week's figures is the exact defect this shape exists to prevent (it was filed once already, as QA 序7). If you can only write about one window, send that one window under its own key and leave the others out.
Every "Oscar recommends" has three parts
Not one sentence. In this order, and the third is the one that gets skipped:
- Overall — what happened to this block, and which way it moved.
- Why — the driver, from
driversandusualDay, under the rules below. - Outstanding — what is still not done. Read
outstandingAlerts: things flagged BEFORE this period that are still open, so the line reads "flagged last week and still not done". Also look atalertsInPeriodfor anything raised inside the week whose status is notdone.
Leave part three out when there is nothing outstanding — do not write "nothing outstanding". An empty Outstanding line trains the merchant to stop reading the one part of the report that asks them to act.
A section with nothing to say in part one is dropped whole, as below.
Send the three parts separately, not joined into one paragraph
The turn's prompt asks for each section as an object with overall, why and
outstanding keys. Fill those keys; do not glue the three parts together and put the
result in one of them.
Because two different surfaces read this, and they need different amounts of it. The
report page shows the whole argument — that is where a merchant goes to find out why. The
summary cards (the Monday push, and the "compare to last week" card) have room for one
line, and they take overall on its own. A single glued paragraph forces those cards to
show three paragraphs under a row of KPIs, or to cut one mid-sentence — and half a sentence
on the card with the whole one on the page is one week saying two different things.
So overall has to stand alone. Write it as a sentence that is complete without the
other two: what happened to this block and which way it moved. Do not open it with a
connective that depends on text the card will not show ("Because of that, …"), and do not
end it dangling into the why.
overall is required. why and outstanding are left out entirely when you have nothing
for them — an empty string is not the same as omitting the key, and it renders as a blank
line rather than as nothing.
Hard rules
requiredClauses first, before you write anything. Each entry is a finished sentence
and the section it belongs in. Put every one of them into that section's paragraph — you
may adjust the join so it reads naturally, but the figures, the direction and the wording of
the claim are already decided and must survive intact. There is no entry you can leave out.
This list exists because asking did not work. The refund rate was in the snapshot and the rule was in this file, and across two real reports one stated it and one wrote three fulfilment sentences without mentioning refunds at all. So it now arrives written, at the top, and placing it is the whole job.
Quote every number, calculate none of them. Changes, shares, averages, days of cover are all in the snapshot; if one you want is not there, say it is not available rather than deriving it from the ones that are.
caveats is the same kind of list, and the same rule applies — it is at the top, it is
short, and everything in it belongs in the paragraph it constrains.
Explain a movement from two places, in this order. First what the store did, which is
in drivers: price and listing changes and stock-outs (drivers.productHistory), how many
orders carried a discount code and which codes (drivers.discounting), where the orders
were placed (drivers.channelMix), and new versus returning buyers
(drivers.customerMix) — then the store's own day-of-week baseline in usualDay. Then the retail calendar entries the snapshot matched for this
period, as a supplement where the store's own behaviour already explains it. When neither
place explains the movement, write no clear driver found and move on — do not reach for
a third explanation.
Price changes, listing changes and stock-outs are in drivers.productHistory — and they
are the strongest explanations you have, because they are things the merchant did. Shopify
keeps no history of any of the three, so this is our own daily record, and that is why the
next rule matters more than any other in this file.
When drivers.productHistory.coverageNote is present, its content goes into the sales
paragraph — every time, not when it seems worth mentioning. Never write that nothing changed. The note tells you how many of the
period's days were actually recorded. Where it is short, an empty priceChanges /
listingChanges / stockouts list means we were not watching, not that nothing happened,
and those two look identical on the page.
This one is not hypothetical. With one of seven days recorded, the sentence that came out was "no price, listing or stockout changes were observed in the period" — technically true, and a merchant reads it as "nothing changed". If the note is there, say the record does not reach back that far. A new merchant's first report will have almost no history, and that is normal.
Never name a price change, a listing change or a stock-out that is not in that block. They
read like the most natural explanations in the world, and that is exactly the danger: "the
price rise explains it" is a guess wearing the clothes of a finding. If the movement looks
like one of these and the block does not have it, write no clear driver found.
channelMix is where the order was PLACED — Online Store, Point of Sale, an app's own
name. It is not the referring site and cannot tell you which ad or post someone came from.
Say where the orders were placed, never where the shopper came from.
Traffic source is not missing from your input by accident, and it is not something you can
work around: Shopify keeps referrer and landing page behind a protected-customer-data
approval this app does not hold, so nobody has that data — not the report, not the page,
not another tool you could call. A merchant who asks where their customers came from should be
told plainly that the store's channel breakdown covers where orders were placed and that
traffic source is not something we can see, rather than being handed channelMix as if it
answered the question.
A key of an app means Shopify gave no name for it. Say "an app" and move on — there is
nothing more to know, and the producer no longer carries the internal id, so there is nothing
to accidentally read out.
Never search the web for a driver. No holiday lookup, no weather, no industry news; if it is not in the snapshot's own drivers or the calendar it carries, it is not a driver you can name.
The fulfilment recommendation carries the refund rate against last period. It is the one
place in the report where refunds are read, and a fulfilment section that talks only about
shipping speed leaves the merchant to guess whether the delays cost them anything. The rate is
in fulfillment.refundRate (value, previous, deltaPct, deltaPoints, direction, and a
basis saying what it divides by) and the money is in kpis.refunds — quote the rate as a
percentage, say whether the move is inside this store's normal range, and do not compute a rate
the snapshot has not given you.
The clause is already written for you: fulfillment.refundRate.sentence, which is also the
fulfillment entry in requiredClauses. Use it. Do not restate the rate in your own words
next to it, and do not recompute anything in it — the percentage, the direction and the "no
prior period" wording are all decided.
The figures behind it are there if you need to reason about them: value is a FRACTION
(0.032 is 3.2%), deltaPoints is the move in percentage POINTS, and deltaPct is absent
whenever last period was 0% because there is no base to divide by.
When value is null there is no sentence and no required clause: the store had no gross sales
in the window or could not be read. Say the rate is not available, and do NOT call it 0%.
Refund reasons: check drivers.refundReasons.worthReporting before you name one. Only a
refund raised through Shopify's returns flow carries a reason; a merchant who refunds directly
leaves none, and for small stores that is most of them. When the flag is false, do NOT list the
breakdown — say how many of the period's refunds came through returns and stop there. A list
reading "Damaged 1 · Wrong size 1" out of sixteen refunds looks like an analysis of why customers
sent things back, and is actually a count of who used the returns flow.
Recommend only what the platform can do: restock, look at the pricing, delist, chase an unshipped order. Never name a price change or a percentage. Never suggest ads, new photography, a site change or anything else nobody can act on from here.
A section that cannot be written is dropped, the report still goes out. Leave that section out silently rather than failing the whole report or filling it with a note about what went wrong.
No data means no section. Where the snapshot has nothing for a period, say there is nothing to report for it — do not display an empty shell and do not generate something to fill it.
Download
ZIP package — ready to use
Skill Info
- Creator
- Yepai
- License
- MIT-0
- Downloads
- 0
- Published
- Oct 26, 2026
- Updated
- Oct 29, 2026