docs(sales-summaries): report the finished migration and its one deliberate residual

Replaces the partial-completeness section with the measured end state.

  card payments  17,046,418 scoped · 0 unscoped · 0 unscopable
  refunds            51,986 scoped · 0 unscoped
  payouts           144,652 scoped · 0 unscoped · 36 with no owner
  cash shifts        69,291 scoped · 0 unscoped

Payments shared between two different clients: 0 in a 250,000-order sample
spread across the database. 104 remain shared between two orders of the same
client, left that way on purpose — Square split one tender across two of that
client's orders, both orders compute the same name so there is no second name a
copy could take, and a copy would double the client's takings. One payment
covering both is the truthful record.

That is what makes the remove-voided-orders guard a required companion rather
than belt-and-braces: the cascade still reaches those 104.

Records that a second complete pass over all 19,040,296 orders took 7.8 minutes
and changed nothing, so the migration is stoppable, resumable and repeatable —
and that with the gate now reading zero, the importer's understanding of the old
name form can be removed, which is the one remaining step.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-15 16:25:37 -07:00
parent e05f20c135
commit a16ef0bd60

View File

@@ -120,7 +120,7 @@
<div class="stat"><span class="k num">1,172</span><span class="l">client-days brought into balance</span></div> <div class="stat"><span class="k num">1,172</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 zero"><span class="k num">0</span><span class="l">days knocked out of balance</span></div>
<div class="stat"><span class="k num">97.3%</span><span class="l">of the variance removed</span></div> <div class="stat"><span class="k num">97.3%</span><span class="l">of the variance removed</span></div>
<div class="stat"><span class="k num">1.6%</span><span class="l">of card payments renamed so far</span></div> <div class="stat zero"><span class="k num">0</span><span class="l">payments shared between two clients</span></div>
</div> </div>
<div class="measure"> <div class="measure">
@@ -458,62 +458,73 @@
</section> </section>
<section> <section>
<h2>How complete is this, really</h2> <h2>How complete is this</h2>
<div class="measure"> <div class="measure">
<p>The importer deliberately understands both the old and new record names, so the change can <p>The importer understands both the old and new record names, so the change can be deployed
be deployed before the renaming finishes. That tolerance is a bridge, not a destination — before the renaming finishes. That tolerance is a bridge, not a destination — while any
while any record still carries an unscoped name, two clients can still land on it, and the record still carries an unscoped name, two clients can land on it and the guarantee rests on
guarantee rests on a convention rather than on the data.</p> a convention rather than on the data. So the renaming was run to completion and measured.</p>
<p>Measuring the whole database rather than the part that was migrated gives an honest
picture, and it is not a flattering one.</p>
</div> </div>
<div class="scroll"> <div class="scroll">
<table> <table>
<thead><tr><th>Record type</th><th class="n">Client-scoped</th><th class="n">Still unscoped</th><th class="n">No owner to scope by</th></tr></thead> <thead><tr><th>Record type</th><th class="n">Client-scoped</th><th class="n">Still unscoped</th><th class="n">No owner to scope by</th></tr></thead>
<tbody> <tbody>
<tr><td>Card payments</td><td class="n good">17,046,418</td><td class="n good">0</td><td class="n good">0</td></tr>
<tr><td>Refunds</td><td class="n good">51,986</td><td class="n good">0</td><td class="n good">0</td></tr> <tr><td>Refunds</td><td class="n good">51,986</td><td class="n good">0</td><td class="n good">0</td></tr>
<tr><td>Payouts</td><td class="n good">144,652</td><td class="n good">0</td><td class="n">36</td></tr> <tr><td>Payouts</td><td class="n good">144,652</td><td class="n good">0</td><td class="n">36</td></tr>
<tr><td>Cash-drawer shifts</td><td class="n good">69,291</td><td class="n good">0</td><td class="n good">0</td></tr> <tr><td>Cash-drawer shifts</td><td class="n good">69,291</td><td class="n good">0</td><td class="n good">0</td></tr>
<tr><td><strong>Card payments</strong></td><td class="n">259,763 <span class="dim">(1.6%)</span></td><td class="n bad">14,241,890</td><td class="n">2,122,161</td></tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="measure"> <div class="measure">
<p>Refunds, payouts and shifts are done. <strong>Card payments are not</strong> — the migration <p>Every record in the database now carries its owner's name. The 36 payouts are ones with no
described above covered the ten clients sharing a location today, within the reporting window, client or location recorded anywhere, so there is nothing to name them after.</p>
which is 1.6% of them. And sampling 2% of the orders belonging to every client that has <p>Renaming had to be driven from orders, because a payment's rightful owner is whichever
<em>ever</em> contended found <strong>12,954 payments still owned by two orders</strong>, which order refers to it — so completing it meant walking all <strong>19,040,296 orders</strong>, not
extrapolates to roughly 650,000 across that population.</p> just the clients that look shared today. Nine client pairs contended in the past without
<p>So the correct reading of the balance figures earlier in this report is: they are what the sharing a location now, and a migration scoped to the current configuration would have missed
calculation fixes are worth, measured on data where the sharing had been resolved for the every one of them.</p>
clients and period in question. They are not a claim that the whole database is clean.</p>
</div> </div>
<div class="callout warn"> <div class="scroll">
<span class="h">What finishing it requires</span> <table>
<p>Renaming has to be driven from orders, because a payment's rightful owner is whichever order <thead><tr><th>Shared payments after the migration</th><th class="n">Count</th><th>Meaning</th></tr></thead>
refers to it — so completing it means walking all 19,040,296 orders, not just the clients that <tbody>
look shared today. Nine client pairs contended in the past without sharing a location now, and <tr><td><strong>Owned by two different clients</strong></td><td class="n good">0</td><td>the problem this work exists to solve — gone</td></tr>
a migration scoped to the current configuration misses every one of them.</p> <tr><td>Owned by two orders of the same client</td><td class="n">104</td><td>left shared deliberately, see below</td></tr>
<p>On the test machine this ran at roughly 15,000 orders an hour once the database no longer <tr><td class="dim">sampled from</td><td class="n dim">250,000 orders</td><td class="dim">spread across the whole database</td></tr>
fitted in memory, which puts a full pass in days rather than hours. That is a hardware </tbody>
characteristic of a swapping 29 GB restore, not a property of the work: the same code </table>
completed 267,012 orders in 100 seconds when the cache was warm. It belongs in a maintenance </div>
window on properly sized hardware, and it is resumable and safe to re-run — a second pass over <div class="measure">
already-renamed records does nothing at all.</p> <p><strong>Why the 104 are left alone.</strong> They are cases where Square split one tender
across two of a single client's own orders. Copying the payment for each order would be wrong
twice over: both orders compute the same name, so there is no second name a copy could take —
and a copy would double that client's takings for the day. One payment covering both orders is
the truthful record.</p>
<p>The consequence is that the deletion cascade can still reach these — removing one of those
two orders would take the payment with it. That is why the guard on
<code>remove-voided-orders</code> is not optional housekeeping but a required companion to this
work: it should detach a shared payment rather than delete it, whoever the other order
belongs to.</p>
</div>
<div class="callout">
<span class="h">Re-running is safe, and that was proved at full scale</span>
<p>A second complete pass walked all 19,040,296 orders in <strong>7.8 minutes and changed
nothing</strong> — no renames, no copies. A record that already carries the right name is left
untouched, so the migration can be stopped, resumed, or repeated without consequence. Useful,
since the first full pass took hours on a machine where the 29 GB database no longer fitted in
memory; on warm cache the same code did 267,012 orders in 100 seconds.</p>
</div> </div>
<div class="measure"> <div class="measure">
<p><strong>The finish line is measurable, not a judgement call.</strong> <p><strong>What follows from the gate reading zero.</strong> <code>unscoped-report</code> counts
<code>unscoped-report</code> counts, for every record type, how many names are client-scoped, these figures on demand. Now that unscoped is zero across the board, the importer's
how many are still unscoped, and how many have no owner. Two things follow from it:</p> understanding of the old name form can be removed — at which point two clients sharing a
<ul> location becomes structurally incapable of producing a shared record, rather than prevented by
<li>While the unscoped count is above zero, keep the importer's understanding of both name a convention that a future import could quietly break. That removal is the one remaining step
forms. Removing it early would strand every record that had not yet been renamed.</li> of this piece of work.</p>
<li>Once it reaches zero and stays there across a full import cycle, delete the fallback.
At that point two clients sharing a location is structurally incapable of producing a shared
record, rather than merely unlikely to.</li>
</ul>
</div> </div>
</section> </section>