docs(sales-summaries): rebuild the report on a real 90-day baseline

The previous report derived its baseline arithmetically. This one runs the job
three times over the same 90 days against identical data, writing real summaries
each pass: production's calculation, then R1, then R2. 18,900 summaries per run,
56,700 in total. Window 2026-05-10 to 2026-08-07, deliberately stopping a week
short of the backup so the partial days around the cut cannot distort it.

Reportable population, 8,350 client-days across 96 clients:

  baseline  1,024 days off, 87.74% clean, $58,531.75
  + R1        752 days off, 90.99% clean, $55,528.17
  + R1 + R2   105 days off, 98.74% clean,    $852.38

  R1  8,054 unchanged, 272 to balance, 0 to unbalance, 0 balanced-day edits
  R2  7,701 unchanged, 647 to balance, 0 to unbalance, 0 balanced-day edits

Across 90 days of trading neither fix altered a single day that already
balanced, replicating the 30-day finding on three times the sample.

The longer window surfaced two residual clusters the short one could not see:
NGMV across five days in late May and NGEB across four spanning May to July.
NGEB is the known ezCater fee question; NGMV and N-30012 are undiagnosed and
worth a look before this ships.

Also records a deployment finding: the transactor was sized for a toy database
(2 GB object cache against 27 GB of data) and the final 7,958 client-days of a
pass were crawling at ~35/min. Resized to 8 GB cache and 16 GB heap, they
finished in 90 seconds. Worth checking production's sizing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-15 10:51:07 -07:00
parent 08b7188660
commit 89baac19f3

View File

@@ -1,4 +1,4 @@
<title>Thirty-Day Reconciliation</title>
<title>Ninety-Day Reconciliation</title>
<style>
:root {
--paper: #F6F8F7;
@@ -16,147 +16,75 @@
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--paper: #0E1615;
--card: #151F1E;
--ink: #E8EFED;
--ink-soft: #A3B3AF;
--ink-faint: #74847F;
--rule: #26332F;
--accent: #5FBDB4;
--accent-soft: #16302E;
--good: #5FBE8C;
--bad: #E08A72;
--warn: #D6AC55;
--paper: #0E1615; --card: #151F1E; --ink: #E8EFED; --ink-soft: #A3B3AF;
--ink-faint: #74847F; --rule: #26332F; --accent: #5FBDB4; --accent-soft: #16302E;
--good: #5FBE8C; --bad: #E08A72; --warn: #D6AC55;
--shadow: 0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.3);
}
}
:root[data-theme="dark"] {
--paper: #0E1615;
--card: #151F1E;
--ink: #E8EFED;
--ink-soft: #A3B3AF;
--ink-faint: #74847F;
--rule: #26332F;
--accent: #5FBDB4;
--accent-soft: #16302E;
--good: #5FBE8C;
--bad: #E08A72;
--warn: #D6AC55;
--paper: #0E1615; --card: #151F1E; --ink: #E8EFED; --ink-soft: #A3B3AF;
--ink-faint: #74847F; --rule: #26332F; --accent: #5FBDB4; --accent-soft: #16302E;
--good: #5FBE8C; --bad: #E08A72; --warn: #D6AC55;
--shadow: 0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.3);
}
* { box-sizing: border-box; }
body {
background: var(--paper);
color: var(--ink);
background: var(--paper); color: var(--ink);
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
font-size: 16px;
line-height: 1.6;
margin: 0;
padding: 0 20px 96px;
font-size: 16px; line-height: 1.6; margin: 0; padding: 0 20px 96px;
}
.wrap { max-width: 940px; margin: 0 auto; }
.measure { max-width: 66ch; }
.num { font-variant-numeric: tabular-nums; font-feature-settings: "tnum"; }
.mono {
font-family: ui-monospace, "SF Mono", "Cascadia Code", "Roboto Mono", monospace;
font-variant-numeric: tabular-nums;
}
.mono { font-family: ui-monospace, "SF Mono", "Cascadia Code", monospace; font-variant-numeric: tabular-nums; }
header.masthead {
padding: 72px 0 40px;
border-bottom: 2px solid var(--ink);
display: flex;
flex-direction: column;
gap: 14px;
}
.eyebrow {
font-size: 12px;
letter-spacing: .14em;
text-transform: uppercase;
color: var(--accent);
font-weight: 600;
}
header.masthead { padding: 72px 0 40px; border-bottom: 2px solid var(--ink); display: flex; flex-direction: column; gap: 14px; }
.eyebrow { font-size: 12px; letter-spacing: .14em; text-transform: uppercase; color: var(--accent); font-weight: 600; }
h1 {
font-family: Georgia, "Iowan Old Style", "Times New Roman", serif;
font-size: clamp(34px, 5.4vw, 54px);
line-height: 1.08;
font-weight: 600;
letter-spacing: -.015em;
margin: 0;
text-wrap: balance;
font-size: clamp(34px, 5.4vw, 54px); line-height: 1.08; font-weight: 600;
letter-spacing: -.015em; margin: 0; text-wrap: balance;
}
.standfirst { font-size: 19px; color: var(--ink-soft); margin: 0; max-width: 60ch; }
.meta {
display: flex; flex-wrap: wrap; gap: 10px 28px;
font-size: 13px; color: var(--ink-faint);
padding-top: 6px;
}
.meta { display: flex; flex-wrap: wrap; gap: 10px 28px; font-size: 13px; color: var(--ink-faint); padding-top: 6px; }
.meta b { color: var(--ink-soft); font-weight: 600; }
section { padding-top: 56px; display: flex; flex-direction: column; gap: 20px; }
h2 {
font-family: Georgia, "Iowan Old Style", serif;
font-size: 27px; font-weight: 600; letter-spacing: -.01em;
margin: 0; text-wrap: balance;
}
h3 {
font-size: 14px; letter-spacing: .08em; text-transform: uppercase;
color: var(--ink-soft); font-weight: 700; margin: 0;
}
h2 { font-family: Georgia, "Iowan Old Style", serif; font-size: 27px; font-weight: 600; letter-spacing: -.01em; margin: 0; text-wrap: balance; }
h3 { font-size: 14px; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-soft); font-weight: 700; margin: 0; }
p { margin: 0; }
.measure p + p { margin-top: 14px; }
/* headline before/after */
.ledger {
display: grid;
grid-template-columns: 1fr auto 1fr;
align-items: stretch;
gap: 0;
border: 1px solid var(--rule);
border-radius: 4px;
background: var(--card);
box-shadow: var(--shadow);
overflow: hidden;
display: grid; grid-template-columns: 1fr auto 1fr; align-items: stretch;
border: 1px solid var(--rule); border-radius: 4px; background: var(--card);
box-shadow: var(--shadow); overflow: hidden;
}
.ledger > div { padding: 26px 28px; display: flex; flex-direction: column; gap: 6px; }
.ledger .arrow {
justify-content: center; align-items: center;
border-left: 1px solid var(--rule); border-right: 1px solid var(--rule);
color: var(--ink-faint); font-size: 22px; padding: 26px 18px;
background: var(--accent-soft);
}
.ledger .side-label {
font-size: 12px; letter-spacing: .12em; text-transform: uppercase;
color: var(--ink-faint); font-weight: 600;
color: var(--ink-faint); font-size: 22px; padding: 26px 18px; background: var(--accent-soft);
}
.ledger .side-label { font-size: 12px; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-faint); font-weight: 600; }
.figure { font-size: clamp(28px, 4.4vw, 40px); font-weight: 650; line-height: 1.05; letter-spacing: -.02em; }
.figure.after { color: var(--good); }
.subfig { font-size: 14px; color: var(--ink-soft); }
/* stat row */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 14px; }
.stat {
background: var(--card); border: 1px solid var(--rule); border-radius: 4px;
padding: 18px 20px; display: flex; flex-direction: column; gap: 4px;
}
.stat { background: var(--card); border: 1px solid var(--rule); border-radius: 4px; padding: 18px 20px; display: flex; flex-direction: column; gap: 4px; }
.stat .k { font-size: 30px; font-weight: 650; letter-spacing: -.02em; line-height: 1; }
.stat .l { font-size: 13px; color: var(--ink-soft); }
.stat.zero .k { color: var(--good); }
.stat.flag .k { color: var(--warn); }
/* steps */
.step { border-left: 2px solid var(--rule); padding-left: 26px; position: relative; }
.step::before {
content: attr(data-step);
position: absolute; left: -13px; top: 2px;
content: attr(data-step); position: absolute; left: -13px; top: 2px;
width: 24px; height: 24px; border-radius: 50%;
background: var(--accent); color: var(--paper);
font-size: 12px; font-weight: 700;
display: grid; place-items: center;
font-size: 12px; font-weight: 700; display: grid; place-items: center;
}
.step h3 { margin-bottom: 8px; }
.step-title { font-size: 20px; font-weight: 650; letter-spacing: -.01em; }
@@ -164,10 +92,7 @@
.scroll { overflow-x: auto; border: 1px solid var(--rule); border-radius: 4px; background: var(--card); }
table { border-collapse: collapse; width: 100%; font-size: 14.5px; }
th, td { padding: 11px 16px; text-align: left; border-bottom: 1px solid var(--rule); white-space: nowrap; }
thead th {
font-size: 11.5px; letter-spacing: .09em; text-transform: uppercase;
color: var(--ink-faint); font-weight: 700; background: var(--accent-soft);
}
thead th { font-size: 11.5px; letter-spacing: .09em; text-transform: uppercase; color: var(--ink-faint); font-weight: 700; background: var(--accent-soft); }
tbody tr:last-child td { border-bottom: none; }
td.n, th.n { text-align: right; font-variant-numeric: tabular-nums; }
tr.total td { font-weight: 650; background: var(--accent-soft); }
@@ -175,32 +100,13 @@
.bad { color: var(--bad); font-weight: 650; }
.dim { color: var(--ink-faint); }
.pill {
display: inline-block; font-size: 11.5px; font-weight: 700;
letter-spacing: .05em; text-transform: uppercase;
padding: 3px 9px; border-radius: 3px;
}
.pill.ok { background: var(--accent-soft); color: var(--good); }
.pill.no { background: var(--accent-soft); color: var(--bad); }
.callout {
background: var(--card); border: 1px solid var(--rule);
border-left: 3px solid var(--accent);
border-radius: 4px; padding: 20px 24px;
display: flex; flex-direction: column; gap: 10px;
}
.callout { background: var(--card); border: 1px solid var(--rule); border-left: 3px solid var(--accent); border-radius: 4px; padding: 20px 24px; display: flex; flex-direction: column; gap: 10px; }
.callout.warn { border-left-color: var(--warn); }
.callout .h { font-weight: 650; }
code {
font-family: ui-monospace, "SF Mono", monospace; font-size: .9em;
background: var(--accent-soft); padding: 1px 5px; border-radius: 3px;
}
footer {
margin-top: 72px; padding-top: 24px; border-top: 1px solid var(--rule);
font-size: 13px; color: var(--ink-faint);
display: flex; flex-direction: column; gap: 8px;
}
code { font-family: ui-monospace, "SF Mono", monospace; font-size: .9em; background: var(--accent-soft); padding: 1px 5px; border-radius: 3px; }
pre { margin: 0; padding: 20px; font-size: 13px; line-height: 1.7; white-space: pre; font-family: ui-monospace, "SF Mono", monospace; }
footer { margin-top: 72px; padding-top: 24px; border-top: 1px solid var(--rule); font-size: 13px; color: var(--ink-faint); display: flex; flex-direction: column; gap: 8px; }
ul { margin: 0; padding-left: 20px; display: flex; flex-direction: column; gap: 8px; }
</style>
@@ -208,12 +114,13 @@
<header class="masthead">
<div class="eyebrow">Sales summaries · restored production backup</div>
<h1>Thirty-Day Reconciliation</h1>
<p class="standfirst">What the deduplication work and the two calculation fixes actually did to the books, measured day by day against production's own summaries.</p>
<h1>Ninety-Day Reconciliation</h1>
<p class="standfirst">A true baseline: the job re-run three times over ninety days — production's calculation, then each fix added — so every number below is measured output, not arithmetic.</p>
<div class="meta">
<span><b>Window</b> 2026-07-15 → 2026-08-13</span>
<span><b>Client-days</b> <span class="num">3,040</span> with activity</span>
<span><b>Compared against</b> basis-t <span class="num">209608347</span></span>
<span><b>Window</b> 2026-05-10 → 2026-08-07</span>
<span><b>Client-days</b> <span class="num">8,733</span> with activity</span>
<span><b>Clients</b> <span class="num">106</span></span>
<span><b>Most recent week</b> omitted</span>
</div>
</header>
@@ -221,110 +128,50 @@
<div class="ledger">
<div>
<span class="side-label">Baseline</span>
<span class="figure num">$22,527.40</span>
<span class="subfig"><span class="num">398</span> days out of balance · <span class="num">86.00%</span> clean</span>
<span class="figure num">$58,531.75</span>
<span class="subfig"><span class="num">1,024</span> days out of balance · <span class="num">87.74%</span> clean</span>
</div>
<div class="arrow" aria-hidden="true"></div>
<div>
<span class="side-label">After fixes</span>
<span class="figure after num">$405.66</span>
<span class="subfig"><span class="num">67</span> days out of balance · <span class="num">97.64%</span> clean</span>
<span class="side-label">After both fixes</span>
<span class="figure after num">$852.38</span>
<span class="subfig"><span class="num">105</span> days out of balance · <span class="num">98.74%</span> clean</span>
</div>
</div>
<div class="stats">
<div class="stat"><span class="k num">331</span><span class="l">client-days brought into balance</span></div>
<div class="stat"><span class="k num">919</span><span class="l">client-days brought into balance</span></div>
<div class="stat zero"><span class="k num">0</span><span class="l">days knocked out of balance</span></div>
<div class="stat flag"><span class="k num">237</span><span class="l">already-balanced days whose numbers moved</span></div>
<div class="stat"><span class="k num">98.2%</span><span class="l">of the dollar variance removed</span></div>
<div class="stat zero"><span class="k num">0</span><span class="l">balanced days whose numbers moved</span></div>
<div class="stat"><span class="k num">98.5%</span><span class="l">of the dollar variance removed</span></div>
</div>
<div class="measure">
<p>Both arms are computed on the same deduplicated data, so this isolates what the <em>calculation</em> fixes are worth. Figures exclude the ten deactivated duplicate clients, which the plan says to exclude from reporting; including them the shape is identical (432 → 70 days, $25,622.98 → $1,548.64).</p>
<p>Figures exclude the ten deactivated duplicate clients, which the plan says to exclude from reporting. Across the full 8,733 client-days including them the shape is the same: 1,087 → 108 days out of balance, $63,764.92 → $1,995.36.</p>
</div>
</section>
<section>
<h2>Was anything already balanced disturbed?</h2>
<h2>Why this baseline is different</h2>
<div class="measure">
<p>This is the question that matters most for the books, and it has two halves. No day that balanced under production went out of balance — that count is zero. But <strong>237 client-days that were balanced had their line amounts change anyway</strong>, and a balance-only view would hide every one of them.</p>
<p>None of those 237 came from the calculation fixes. Both fixes are provably inert on balanced days — see the decomposition below. They came from the data being corrected underneath.</p>
</div>
<div class="scroll">
<table>
<thead>
<tr><th>Outcome, production → final</th><th class="n">Client-days</th><th>Reading</th></tr>
</thead>
<tbody>
<tr><td>Out of balance → balanced</td><td class="n good">309</td><td class="dim">the intended repair</td></tr>
<tr><td>Balanced → out of balance</td><td class="n good">0</td><td class="dim">nothing was broken</td></tr>
<tr><td>Balanced → balanced, numbers changed</td><td class="n" style="color:var(--warn);font-weight:650">237</td><td class="dim">amounts moved, balance held</td></tr>
<tr><td>Balanced → balanced, identical</td><td class="n">1,652</td><td class="dim">untouched</td></tr>
<tr><td>Still out of balance</td><td class="n">44</td><td class="dim">residual, see below</td></tr>
<tr class="total"><td>Production summaries in window</td><td class="n">2,242</td><td class="dim">of 6,300 client-day slots</td></tr>
</tbody>
</table>
</div>
<h3>What moved on those 237 days</h3>
<div class="scroll">
<table>
<thead><tr><th>Pattern</th><th class="n">Days</th><th>Cause</th></tr></thead>
<tbody>
<tr><td>Every category 0.00 → real values</td><td class="n">~100</td><td>production held an all-zero summary for a day that had orders</td></tr>
<tr><td>Card Payments ↔ Fees reallocation</td><td class="n">~135</td><td>the payout arrived, so the processing fee is now known and booked separately</td></tr>
<tr><td>Service Charges line appears</td><td class="n">13</td><td>R2, on days that were already out of balance</td></tr>
</tbody>
</table>
</div>
<div class="measure">
<p>The second pattern is worth reading carefully: <code>Card Payments</code> falls by exactly what <code>Fees</code> gains, so the day stays balanced while the split between the two lines changes. That is a data-freshness effect from re-importing, not a change in how anything is calculated. Three real rows:</p>
</div>
<div class="scroll">
<table>
<thead><tr><th>Client</th><th>Date</th><th>Line</th><th class="n">Before</th><th class="n">After</th></tr></thead>
<tbody>
<tr><td class="mono">NGAN</td><td>2026-08-12</td><td>Card Payments</td><td class="n">1,237.23</td><td class="n">1,207.64</td></tr>
<tr><td></td><td></td><td>Fees</td><td class="n">0.00</td><td class="n">29.59</td></tr>
<tr><td class="mono">NGVC</td><td>2026-08-10</td><td>Card Payments</td><td class="n">2,025.03</td><td class="n">2,074.63</td></tr>
<tr><td></td><td></td><td>Fees</td><td class="n">49.60</td><td class="n">0.00</td></tr>
<tr><td class="mono">NGGP</td><td>2026-08-13</td><td>every line</td><td class="n">0.00</td><td class="n">real figures</td></tr>
</tbody>
</table>
</div>
<div class="measure">
<p>NGAN moves $29.59 out of Card Payments and into Fees; NGVC moves $49.60 the other way. Both stay balanced to the cent. NGGP is the first pattern — production held an all-zero summary for a day with $2,478.44 of card payments, $1,148.11 of gyros and pitas, and so on.</p>
<p>The earlier thirty-day report derived its baseline arithmetically. This one does not. The job was run three separate times over the same ninety days, against the same data, writing real summaries each time:</p>
<ul>
<li><strong>Run A — baseline.</strong> The service-charge flag cleared on all 210 clients and <code>get-tip</code> restored to its tendered-only form. This is exactly what production calculates today.</li>
<li><strong>Run B — plus R1.</strong> Only <code>get-tip</code> changed.</li>
<li><strong>Run C — plus R2.</strong> The service-charge flag switched on as well.</li>
</ul>
<p>The underlying data — Phase 0, the re-key, the repaired charge references — is identical across all three, so what separates them is the calculation and nothing else. 18,900 summaries were written per run, 56,700 in total.</p>
<p>The most recent week (08-08 → 08-14) is excluded deliberately. The backup was cut mid-evening on the 14th, and the days either side of that are partial, which distorted the earlier window.</p>
</div>
</section>
<section>
<h2>Impact by underlying fix</h2>
<div class="measure">
<p>Applied in sequence, each measured against the state before it. Percentages of a 3,040 client-day population.</p>
<p>Applied in sequence, each measured against the run before it, over 8,350 reportable client-days.</p>
</div>
<div class="step" data-step="1">
<h3>Data</h3>
<div class="step-title">Deduplication — Phase 0, re-key, re-import</div>
</div>
<div class="scroll">
<table>
<thead><tr><th>Outcome</th><th class="n">Client-days</th></tr></thead>
<tbody>
<tr><td>Unchanged</td><td class="n">1,581</td></tr>
<tr><td>Numbers changed</td><td class="n">547</td></tr>
<tr><td>Out of balance → balanced</td><td class="n good">56</td></tr>
<tr><td>Balanced → out of balance</td><td class="n bad">19</td></tr>
<tr><td>Balanced → balanced, numbers changed</td><td class="n" style="color:var(--warn);font-weight:650">237</td></tr>
</tbody>
</table>
</div>
<div class="measure">
<p>The data work is the only step that unbalances anything: <strong>19 days</strong> go out of balance purely from correcting the data, mostly where a refund that had been sitting under the twin client now lands on the right one. The two calculation fixes then absorb all 19, which is why the end-to-end figure is zero.</p>
</div>
<div class="step" data-step="2">
<h3>R1</h3>
<div class="step-title">Tips on untendered orders</div>
</div>
@@ -332,17 +179,17 @@
<table>
<thead><tr><th>Outcome</th><th class="n">Client-days</th><th class="n">Share</th></tr></thead>
<tbody>
<tr><td>Unchanged</td><td class="n">2,941</td><td class="n dim">96.7%</td></tr>
<tr><td>Out of balance → balanced</td><td class="n good">86</td><td class="n dim">2.8%</td></tr>
<tr><td>Unchanged</td><td class="n">8,054</td><td class="n dim">96.5%</td></tr>
<tr><td>Out of balance → balanced</td><td class="n good">272</td><td class="n dim">3.3%</td></tr>
<tr><td>Balanced → out of balance</td><td class="n good">0</td><td class="n dim"></td></tr>
<tr><td>Balanced → balanced, numbers changed</td><td class="n good">0</td><td class="n dim"></td></tr>
<tr><td>Still out of balance, but closer</td><td class="n">13</td><td class="n dim">0.4%</td></tr>
<tr class="total"><td>Days touched · dollars moved</td><td class="n">99</td><td class="n">$973.79</td></tr>
<tr><td>Still out of balance, but closer</td><td class="n">24</td><td class="n dim">0.3%</td></tr>
<tr class="total"><td>Days touched · dollars moved</td><td class="n">296</td><td class="n">$3,623.44</td></tr>
</tbody>
</table>
</div>
<div class="step" data-step="3">
<div class="step" data-step="2">
<h3>R2</h3>
<div class="step-title">Square service charges, both signs</div>
</div>
@@ -350,26 +197,41 @@
<table>
<thead><tr><th>Outcome</th><th class="n">Client-days</th><th class="n">Share</th></tr></thead>
<tbody>
<tr><td>Unchanged</td><td class="n">2,760</td><td class="n dim">90.8%</td></tr>
<tr><td>Out of balance → balanced</td><td class="n good">276</td><td class="n dim">9.1%</td></tr>
<tr><td>Unchanged</td><td class="n">7,701</td><td class="n dim">92.2%</td></tr>
<tr><td>Out of balance → balanced</td><td class="n good">647</td><td class="n dim">7.7%</td></tr>
<tr><td>Balanced → out of balance</td><td class="n good">0</td><td class="n dim"></td></tr>
<tr><td>Balanced → balanced, numbers changed</td><td class="n good">0</td><td class="n dim"></td></tr>
<tr><td>Still out of balance, but closer</td><td class="n">4</td><td class="n dim">0.1%</td></tr>
<tr class="total"><td>Days touched · dollars moved</td><td class="n">280</td><td class="n">$23,729.45</td></tr>
<tr><td>Still out of balance, but closer</td><td class="n">2</td><td class="n dim">&lt;0.1%</td></tr>
<tr class="total"><td>Days touched · dollars moved</td><td class="n">649</td><td class="n">$54,775.79</td></tr>
</tbody>
</table>
</div>
<h3>Both together, full population</h3>
<div class="scroll">
<table>
<thead><tr><th>Outcome</th><th class="n">Client-days</th></tr></thead>
<tbody>
<tr><td>Unchanged</td><td class="n">7,749</td></tr>
<tr><td>Out of balance → balanced</td><td class="n good">979</td></tr>
<tr><td>Balanced → out of balance</td><td class="n good">0</td></tr>
<tr><td>Balanced → balanced, numbers changed</td><td class="n good">0</td></tr>
<tr><td>Still out of balance, but closer</td><td class="n">5</td></tr>
<tr class="total"><td>Days touched · dollars moved</td><td class="n">984 · $62,069.64</td></tr>
</tbody>
</table>
</div>
<div class="callout">
<span class="h">Both calculation fixes are inert on healthy days</span>
<p>Across 3,040 client-days, neither R1 nor R2 changed a single number on a day that was already balanced. Every day they touched was already out of balance. That is the strongest available evidence that they cannot quietly restate correct books — and it is why R2 carries the larger dollar figure without carrying larger risk.</p>
<span class="h">Neither fix touches a healthy day — now confirmed over three times the window</span>
<p>Across 8,733 client-days and 90 days of trading, not one day that balanced under production's calculation was altered by either fix: zero knocked out of balance, and zero whose amounts moved while staying balanced. Every day they touched was already out of balance. The thirty-day run found the same thing; this replicates it on a much larger sample.</p>
</div>
</section>
<section>
<h2>Worked examples</h2>
<div class="measure">
<p>Each fix is easiest to check on a single order. In every case below the day's imbalance equals the fix amount exactly, which is what you would expect if the fix is booking something real that was previously booked nowhere.</p>
<p>Each fix is easiest to check on a single order. In both cases the day's imbalance equals the fix amount exactly, which is what you would expect if the fix books something real that was previously booked nowhere.</p>
</div>
<h3>R1 — a tip reversal with no tender to hang it on</h3>
@@ -386,7 +248,7 @@
</table>
</div>
<div class="measure">
<p>A guest's tip was handed back. The reversal is recorded on the order, but <code>get-tip</code> reaches tips by joining through <code>:sales-order/charges</code>, and a return-only order has no charge to join through — so the $60.00 was invisible and the day credited a tip that no longer existed. The other four largest R1 repairs are the same shape: NGFF 08-01 ($40.00), NGSG 07-29 ($40.00), NGNV 08-09 ($35.00), NGJS 07-25 ($35.00).</p>
<p>A guest's tip was handed back. The reversal is recorded on the order, but <code>get-tip</code> reaches tips by joining through <code>:sales-order/charges</code>, and a return-only order has no charge to join through — so the $60.00 was invisible and the day credited a tip that no longer existed.</p>
</div>
<h3>R2 — a service charge collected but credited nowhere</h3>
@@ -402,88 +264,91 @@
</table>
</div>
<div class="measure">
<p>The $427.10 is inside the card tender the customer paid, so it lands on the debit side — but nothing credited it, leaving the day short by exactly that amount. Next largest: NGFO 07-16 ($275.23), NGRN 08-06 ($236.96), N-30008 07-24 ($229.42), N-30003 07-18 ($225.00).</p>
</div>
<h3>The 19 days the data work unbalanced — and all 19 rescued</h3>
<div class="scroll">
<table>
<thead><tr><th>Client</th><th>Date</th><th class="n">After data fix</th><th class="n">After R1 + R2</th></tr></thead>
<tbody>
<tr><td class="mono">NGSF</td><td>2026-08-13</td><td class="n bad">+137.99</td><td class="n good">0.00</td></tr>
<tr><td class="mono">NGRY</td><td>2026-08-13</td><td class="n bad">+103.26</td><td class="n good">0.00</td></tr>
<tr><td class="mono">NGDS</td><td>2026-08-13</td><td class="n bad">+100.52</td><td class="n good">0.00</td></tr>
<tr><td class="mono">NGDV</td><td>2026-08-13</td><td class="n bad">+100.52</td><td class="n good">0.00</td></tr>
<tr><td class="mono">N-30007</td><td>2026-08-13</td><td class="n bad">+86.00</td><td class="n good">0.00</td></tr>
<tr><td class="mono">N-30003</td><td>2026-08-13</td><td class="n bad">+83.53</td><td class="n good">0.00</td></tr>
<tr><td class="mono">NGSC</td><td>2026-08-13</td><td class="n bad">+75.00</td><td class="n good">0.00</td></tr>
<tr><td class="mono">NGGH</td><td>2026-08-13</td><td class="n bad">+75.00</td><td class="n good">0.00</td></tr>
<tr><td class="mono">NGDG</td><td>2026-08-13</td><td class="n bad">+70.00</td><td class="n good">0.00</td></tr>
<tr><td class="mono">NGDU</td><td>2026-08-13</td><td class="n bad">+70.00</td><td class="n good">0.00</td></tr>
<tr class="total"><td colspan="2">All 19 days</td><td class="n bad">unbalanced</td><td class="n good">balanced</td></tr>
</tbody>
</table>
</div>
<div class="measure">
<p>These are days production held an empty summary for — trivially balanced at zero. Filling them in with real figures exposes the same two calculation gaps every other day had, and both fixes then close them. Not one of the 19 survives as a regression. Their clustering on 08-13 is expected: it is the most recent day, so it is the one production had least chance to compute.</p>
</div>
</section>
<section>
<h2>Days production never had</h2>
<div class="measure">
<p>Of 6,300 client-day slots in the window, production held a summary for only 2,242. The remaining <strong>4,058</strong> had none at all — nothing to compare against, and nothing an accountant could have looked at.</p>
<p>They are not evenly spread. Eight consecutive days show <span class="num">210</span> missing summaries each, which is <em>every client in the database</em>:</p>
</div>
<div class="scroll">
<table>
<thead><tr><th>Dates</th><th class="n">Missing per day</th><th>Reading</th></tr></thead>
<tbody>
<tr><td>Jul 15 Jul 29</td><td class="n">117</td><td class="dim">clients with no summary that day</td></tr>
<tr><td class="bad">Jul 30 Aug 6</td><td class="n bad">210</td><td class="bad">every client — a total coverage hole</td></tr>
<tr><td>Aug 7 Aug 13</td><td class="n">89</td><td class="dim">clients with no summary that day</td></tr>
</tbody>
</table>
</div>
<div class="callout warn">
<span class="h">The coverage hole is real and self-confirming</span>
<p>The plan predicted a global gap at 2026-07-30 → 08-06 from reading the scheduler, which only looks back seven days and so can never backfill a hole older than that. This measurement found the same eight days independently, from the data. Every one of those 1,680 client-days now has a summary.</p>
<p>The $427.10 sits inside the card tender the customer paid, so it lands on the debit side — but nothing credited it, leaving the day short by exactly that amount. R2 is the larger of the two fixes by an order of magnitude: $54,775.79 moved against R1's $3,623.44.</p>
</div>
</section>
<section>
<h2>What is left</h2>
<div class="measure">
<p>Five client-days above the ten-cent materiality threshold, totalling $404.04. Everything else — 62 days — comes to <strong>$1.62</strong>, with the largest single day at nine cents.</p>
<p>Fifteen client-days above the ten-cent materiality threshold, out of 8,350. Everything else — 90 client-days — totals <strong>$2.26</strong>.</p>
</div>
<div class="scroll">
<table>
<thead><tr><th>Client</th><th>Date</th><th class="n">Variance</th><th>Explanation</th></tr></thead>
<thead><tr><th>Client</th><th>Date</th><th class="n">Variance</th><th>Note</th></tr></thead>
<tbody>
<tr><td class="mono">NGBK</td><td>2026-08-06</td><td class="n">+$299.42</td><td>tender exceeds order totals by exactly this much, in the source data</td></tr>
<tr><td class="mono">NGDA</td><td>2026-08-01</td><td class="n">$50.00</td><td>auto-gratuity booked as a service charge</td></tr>
<tr><td class="mono">NGEB</td><td>2026-08-10</td><td class="n">$25.00</td><td>ezCater fee semantics <span class="pill ok">predicted</span></td></tr>
<tr><td class="mono">NGEB</td><td>2026-07-29</td><td class="n">$20.00</td><td>ezCater fee semantics <span class="pill ok">predicted</span></td></tr>
<tr><td class="mono">NGPS</td><td>2026-08-12</td><td class="n">+$9.62</td><td>unexplained <span class="pill ok">predicted</span></td></tr>
<tr><td class="mono">NGBK</td><td>2026-08-06</td><td class="n">+299.42</td><td>tender exceeds order totals by exactly this much</td></tr>
<tr><td class="mono">NGMV</td><td>2026-05-24</td><td class="n">+100.87</td><td rowspan="5" class="dim">a five-day cluster in late May, new to this window and not yet diagnosed</td></tr>
<tr><td class="mono">NGMV</td><td>2026-05-20</td><td class="n">+72.05</td></tr>
<tr><td class="mono">NGMV</td><td>2026-05-21</td><td class="n">+57.64</td></tr>
<tr><td class="mono">NGMV</td><td>2026-05-22</td><td class="n">+14.41</td></tr>
<tr><td class="mono">NGMV</td><td>2026-05-26</td><td class="n">+14.41</td></tr>
<tr><td class="mono">NGEB</td><td>2026-05-19</td><td class="n">79.01</td><td rowspan="4" class="dim">ezCater fee semantics — the plan's open question §15.4</td></tr>
<tr><td class="mono">NGEB</td><td>2026-06-23</td><td class="n">50.28</td></tr>
<tr><td class="mono">NGEB</td><td>2026-05-13</td><td class="n">49.80</td></tr>
<tr><td class="mono">NGEB</td><td>2026-07-29</td><td class="n">20.00</td></tr>
<tr><td class="mono">NGDA</td><td>2026-08-01</td><td class="n">50.00</td><td>auto-gratuity booked as a service charge</td></tr>
<tr><td class="mono">N-30012</td><td>2026-05-20</td><td class="n">+17.32</td><td rowspan="2" class="dim">two consecutive days, undiagnosed</td></tr>
<tr><td class="mono">N-30012</td><td>2026-05-21</td><td class="n">+12.99</td></tr>
<tr><td class="mono">NG4S</td><td>2026-05-29</td><td class="n">11.78</td><td class="dim">undiagnosed</td></tr>
<tr><td class="mono">PNSP</td><td>2026-07-12</td><td class="n">0.14</td><td class="dim">register rounding, just over threshold</td></tr>
</tbody>
</table>
</div>
<div class="measure">
<p>The ten-cent threshold separates register rounding from real variance with nothing sitting near the boundary — the largest sub-threshold day is 9.00¢ and the smallest material one is $9.62, two orders of magnitude apart.</p>
<p><strong>NGBK 2026-08-06, traced.</strong> Summing that client-day's orders directly: tender totals <span class="num">$6,358.99</span> while the orders' own totals come to <span class="num">$6,059.57</span> — a difference of <span class="num">$299.42</span>, exactly the imbalance. Square recorded more payment than the orders account for. Three explanations were checked and ruled out: the day has no refund lines at all, no order carries two charges for one payment id, and the nine orders lacking line items carry zero tender. This is a source-data discrepancy, not a calculation defect, and not something either fix should paper over.</p>
<p>Widening from thirty days to ninety surfaced two clusters the shorter window could not see: <strong>NGMV</strong> across five days in late May, and <strong>NGEB</strong> across four days spanning May to July. NGEB is the known ezCater fee-semantics question. NGMV and N-30012 are new and worth a look before this ships — they are the kind of thing only a longer baseline exposes.</p>
<p>The ten deactivated twins, excluded above, contribute a further $1,142.98 across three days. They should not be reported on at all once one client per location is settled.</p>
</div>
</section>
<section>
<h2>How this was measured</h2>
<h2>Two performance defects found on the way</h2>
<div class="measure">
<ul>
<li>Run against a production backup restored to basis-t <span class="num">209608347</span>, newest transaction 2026-08-14 22:52. Nothing in production was touched.</li>
<li>"Before" is production's own stored summaries, read via <code>as-of</code> — not a re-simulation of them. Datomic keeps every past value, so the comparison is against exactly what was there.</li>
<li>A day counts as out of balance when debits minus credits is at least half a cent. Line amounts are compared at the cent, so floating-point noise does not read as a change.</li>
<li>R1 and R2 only ever add credits, so baseline is derived as <code>fixed + untendered tip + service charges</code>. That identity was checked against a from-scratch baseline recomputation on 20 random client-days and agreed on every one.</li>
<li>All 14,458 summaries in the database were recomputed through the real job, not a test harness.</li>
</ul>
<p>Running the job 56,700 times surfaced two problems that a normal nightly pass would hide, both now fixed or documented.</p>
</div>
<div class="scroll">
<table>
<thead><tr><th>Problem</th><th>Effect</th><th>Fix</th></tr></thead>
<tbody>
<tr><td><code>dirty-sales-summaries</code> scanned the whole index</td><td class="n">1,321 ms → 5.6 ms per client</td><td>bound the scan at the client boundary</td></tr>
<tr><td>Transactor sized for a toy database</td><td class="n">~35/min → 5,300/min</td><td>object cache 2 GB → 8 GB, heap 4 GB → 16 GB</td></tr>
</tbody>
</table>
</div>
<div class="measure">
<p>The second is a deployment setting rather than a code change, but it is dramatic: with a 2 GB object cache against a 27 GB database, the final 7,958 client-days of a pass were crawling at about 35 a minute. After resizing they completed in 90 seconds. Worth checking what production's transactor is sized at.</p>
</div>
</section>
<section>
<h2>Reproduce it</h2>
<div class="scroll">
<pre><span class="dim">;; connect to the restored backup</span>
(def conn (d/connect "datomic:dev://localhost:4337/integreat-prod-restore"))
<span class="dim">;; baseline mode: clear the flag, restore tendered-only tips</span>
(doseq [[c _] clients]
@(d/transact conn [[:db/retract c :client/feature-flags "summary-service-charges"]]))
(alter-var-root #'ss/get-tip (constantly baseline-get-tip))
<span class="dim">;; mark the window and run the real job, then capture</span>
(doseq [[c _] clients] (ss/mark-dirty c w90-start w90-end))
(pmap (fn [[c code]] (ss/refresh-client! c code)) pending)
<span class="dim">;; the single orders behind the worked examples</span>
(d/pull (d/db conn) '[*]
[:sales-order/external-id
"square/order/NGLK-SM-OxSX9gpXJV394qqT8mnBGypUwKNZY"])
(d/pull (d/db conn) '[*]
[:sales-order/external-id
"square/order/NTPT-PT-KrMZzcon1cpQEJUyetErkBIpcdEZY"])</pre>
</div>
<div class="measure">
<p>The unit tests covering both fixes and the re-key:</p>
</div>
<div class="scroll">
<pre>lein test auto-ap.jobs.sales-summaries-test auto-ap.square.core3-test
<span class="dim">;; =&gt; 15 tests, 26 assertions, 0 failures</span></pre>
</div>
<div class="callout warn">
<span class="h">Two defects still open before this ships</span>
@@ -491,52 +356,8 @@
</div>
</section>
<section>
<h2>Reproduce it</h2>
<div class="measure">
<p>Every figure here comes from the restored database and can be re-derived from it. The comparison tool is committed as <code>auto-ap.jobs.compare-sales-summaries</code>.</p>
</div>
<div class="scroll">
<pre class="mono" style="margin:0;padding:20px;font-size:13px;line-height:1.7;white-space:pre;"><span class="dim">;; connect to the restored backup (read-only is fine)</span>
(def conn (d/connect "datomic:dev://localhost:4337/integreat-prod-restore"))
<span class="dim">;; the whole comparison: production's own summaries vs now</span>
(require '[auto-ap.jobs.compare-sales-summaries :as cs])
(def r (cs/compare-against 209608347
(clj-time.core/date-time 2026 7 15)
(clj-time.core/date-time 2026 8 14)))
(:tally r) <span class="dim">;; identical / numbers-changed / added</span>
(:balance-transitions r) <span class="dim">;; the four-way counts</span>
(count (:previously-balanced-and-changed r)) <span class="dim">;; =&gt; 237</span>
<span class="dim">;; what actually moved on one of those days</span>
(cs/line-diff (first (:previously-balanced-and-changed r)))
<span class="dim">;; the single order behind the R1 example</span>
(d/pull (d/db conn) '[*]
[:sales-order/external-id
"square/order/NGLK-SM-OxSX9gpXJV394qqT8mnBGypUwKNZY"])
<span class="dim">;; the single order behind the R2 example</span>
(d/pull (d/db conn) '[*]
[:sales-order/external-id
"square/order/NTPT-PT-KrMZzcon1cpQEJUyetErkBIpcdEZY"])</pre>
</div>
<div class="measure">
<p>The unit tests covering both fixes and the re-key run with:</p>
</div>
<div class="scroll">
<pre class="mono" style="margin:0;padding:20px;font-size:13px;line-height:1.7;white-space:pre;">lein test auto-ap.jobs.sales-summaries-test auto-ap.square.core3-test
<span class="dim">;; =&gt; 15 tests, 26 assertions, 0 failures</span></pre>
</div>
<div class="measure">
<p>Note that the restore currently has Phase 0 applied and the twins deactivated. Re-running the comparison after re-activating them will move the contended clients' figures; everything outside those ten client pairs is unaffected.</p>
</div>
</section>
<footer>
<span>Measured 2026-08-15 on <span class="mono">integreat-prod-restore</span>. Branch <span class="mono">worktree-sales-summary-balance</span>.</span>
<span>Measured 2026-08-15 on <span class="mono">integreat-prod-restore</span>, restored from backup point 209608347. Nothing in production was touched. Branch <span class="mono">worktree-sales-summary-balance</span>.</span>
<span>Service charges map to account 49000 Service Income, chosen to make measurement possible and still pending accounting sign-off. The account affects only whether a day can reach accepted — never whether it balances — so no figure here depends on it.</span>
</footer>