<?xml version="1.0" encoding="utf-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Han Ngo</title><link>https://truonghan.com/</link><description>Notes on building firms, running operations, and designing systems.</description><language>en</language><lastBuildDate>Fri, 09 Oct 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://truonghan.com/feed.xml" rel="self" type="application/rss+xml"/><item><title>Designing Out SNAP Fraud</title><link>https://truonghan.com/designing-out-snap-fraud/</link><guid isPermaLink="true">https://truonghan.com/designing-out-snap-fraud/</guid><pubDate>Fri, 09 Oct 2026 00:00:00 +0000</pubDate><description>A taxonomy of SNAP trafficking, theft, and error, and a layered program and software architecture that designs fraud out. 8,800 words, 26 figures.</description><content:encoded><![CDATA[<p class="lede">The Supplemental Nutrition Assistance Program (SNAP) cost about $100 billion in federal funds in fiscal year 2024 and served an average of 41.7 million people each month. Public debate often treats the program's payment error rate, near 11 percent in recent years, as a fraud rate. That reading is wrong. The error rate counts overpayments and underpayments from all causes, and most of it reflects mistakes by households and state agencies. The most studied fraud, trafficking (selling benefits for cash), diverted an estimated 1.6 to 2.0 percent of benefits from 2015 to 2017, almost all of it through small stores. Card skimming, a newer threat, steals from recipients. This report builds a taxonomy of SNAP fraud and explains why trafficking persists: the payment record carries no item list, and the shopper and the cashier both profit from the lie. It proposes a layered architecture covering eligibility, credentials, transactions, merchant payouts, and analytics. Its central problem is finding price anomalies across millions of daily transactions without human basket review. The answer is store-level scoring, automated graduated responses, and an investigation queue capped by budget. The report closes with harms, a policy alternative, and a phased roadmap.</p>
<h2 id="1-introduction">1. Introduction<a class="anchor" href="#1-introduction" aria-label="Link to this section"></a></h2>
<p>SNAP is the largest food assistance program in the United States. The US Department of Agriculture (USDA) funds the benefits and sets the rules. States determine eligibility, issue benefits, and run their own integrity units. USDA's Food and Nutrition Service (FNS) authorizes and polices the retailers that accept benefits. In 2026 USDA renamed FNS the Food and Nutrition Administration (FNA). This report keeps the name FNS because the studies and regulations it cites use that name.</p>
<p>Households receive benefits on an Electronic Benefit Transfer (EBT) card. The card works like a debit card restricted to a closed network of authorized retailers. A state loads the monthly benefit onto the household's account. The household buys eligible food at an authorized store, and the store's EBT processor debits the account. Within a few banking days the store receives the same amount in its bank account. Most EBT cards issued until recently used a magnetic stripe and a PIN. They carried no chip.</p>
<p>The scale is large on both sides of the transaction. USDA's Economic Research Service reports that SNAP served a monthly average of 41.7 million people in fiscal year 2024, about 12 percent of the US population, at a federal cost of $99.8 billion. Federal spending rose to about $102 billion in fiscal year 2025. Retailers number roughly 260,000. Superstores and supermarkets make up a small share of those stores but redeem most of the benefits. Convenience stores, small groceries, and specialty shops make up most of the authorized locations but redeem a small share of the dollars.</p>
<p>Fraud matters for two reasons. The first is budgetary. A benefit dollar that becomes 50 cents of cash for a recipient and 50 cents of profit for a store buys no food. The second is legitimacy. SNAP depends on public support, and that support erodes when voters believe the program leaks. Inflated claims about fraud do their own damage. They lead to controls that cost more than they save and that push eligible families out of the program. A sound design starts from accurate numbers (<a href="#fig-01-program-actors">Figure 1</a>).</p>
<figure class="fig" id="fig-01-program-actors">
  <div class="fig-head"><span class="eyebrow">Figure 1</span><h4>Who does what in SNAP</h4><p class="dek">Federal funds, state eligibility, and private payment rails all meet at one place: the store's card terminal.</p></div>
  <div class="fig-body">
    <div class="pa-key" aria-hidden="true">
      <span><svg width="34" height="10" viewBox="0 0 34 10"><line x1="1" y1="5" x2="33" y2="5" class="s-defense" stroke-width="2.5"/></svg>money or benefits</span>
      <span><svg width="34" height="10" viewBox="0 0 34 10"><line x1="1" y1="5" x2="33" y2="5" class="s-muted" stroke-width="2" stroke-dasharray="5 4"/></svg>data</span>
      <span><svg width="34" height="10" viewBox="0 0 34 10"><line x1="1" y1="5" x2="33" y2="5" class="s-caution" stroke-width="2" stroke-dasharray="5 4"/></svg>data feed the report proposes</span>
    </div>
    <div class="pa-canvas">
      <svg viewBox="0 0 806 545" role="img" aria-label="SNAP actors and flows. FNS funds the states. States check income against outside records and load monthly benefits onto recipients' EBT cards. Recipients swipe at authorized retailers. The retailer's terminal sends an authorization message with no item list to the EBT processor, which reimburses the store and sends daily records to FNS. Wholesale purchase records would feed FNS under the proposal.">
        <defs>
          <marker id="fig-01-program-actors-m-money" viewBox="0 0 10 10" refX="8.5" refY="5" markerUnits="userSpaceOnUse" markerWidth="10" markerHeight="10" orient="auto"><path d="M0,1 L9,5 L0,9 Z" class="f-defense"/></marker>
          <marker id="fig-01-program-actors-m-data" viewBox="0 0 10 10" refX="8.5" refY="5" markerUnits="userSpaceOnUse" markerWidth="10" markerHeight="10" orient="auto"><path d="M0,1 L9,5 L0,9 Z" class="f-muted"/></marker>
          <marker id="fig-01-program-actors-m-prop" viewBox="0 0 10 10" refX="8.5" refY="5" markerUnits="userSpaceOnUse" markerWidth="10" markerHeight="10" orient="auto"><path d="M0,1 L9,5 L0,9 Z" class="f-caution"/></marker>
        </defs>

        <!-- flows -->
        <g fill="none" stroke-linejoin="round">
          <path data-group="fns state" d="M594,70 H493" class="s-defense" stroke-width="2.5" marker-end="url(#fig-01-program-actors-m-money)"/>
          <path data-group="state recipient" d="M340,144 V200 H101 V251" class="s-defense" stroke-width="2.5" marker-end="url(#fig-01-program-actors-m-money)"/>
          <path data-group="recipient retailer" d="M196,330 H297" class="s-defense" stroke-width="2.5" marker-end="url(#fig-01-program-actors-m-money)"/>
          <path data-group="processor retailer" d="M594,346 H493" class="s-defense" stroke-width="2.5" marker-end="url(#fig-01-program-actors-m-money)"/>
          <path data-group="income state" d="M196,84 H297" class="s-muted" stroke-width="2" stroke-dasharray="5 4" marker-end="url(#fig-01-program-actors-m-data)"/>
          <path data-group="fns retailer" d="M630,144 V200 H440 V243" class="s-muted" stroke-width="2" stroke-dasharray="5 4" marker-end="url(#fig-01-program-actors-m-data)"/>
          <path data-group="retailer processor" d="M490,316 H591" class="s-muted" stroke-width="2" stroke-dasharray="5 4" marker-end="url(#fig-01-program-actors-m-data)"/>
          <path data-group="processor fns" d="M690,254 V147" class="s-muted" stroke-width="2" stroke-dasharray="5 4" marker-end="url(#fig-01-program-actors-m-data)"/>
          <path data-group="pos retailer" d="M395,446 V379" class="s-muted" stroke-width="2" stroke-dasharray="5 4" marker-end="url(#fig-01-program-actors-m-data)"/>
          <path data-group="wholesale fns" d="M101,510 V532 H796 V84 H787" class="s-caution" stroke-width="2" stroke-dasharray="5 4" marker-end="url(#fig-01-program-actors-m-prop)"/>
        </g>

        <!-- flow labels -->
        <g font-size="12" text-anchor="middle">
          <text data-group="fns state" x="543" y="62" class="f-ink" font-weight="600">federal funds</text>
          <text data-group="fns state" x="543" y="89" class="f-muted mono" font-size="11">$99.8B, FY2024</text>
          <text data-group="income state" x="248" y="76" class="f-ink" font-weight="600">income checks</text>
          <text data-group="state recipient" x="220" y="192" class="f-ink" font-weight="600">monthly benefit load</text>
          <text data-group="fns retailer" x="535" y="192" class="f-ink" font-weight="600">authorizes stores</text>
          <text data-group="recipient retailer" x="247" y="322" class="f-ink" font-weight="600">benefits</text>
          <text data-group="recipient retailer" x="247" y="347" class="f-muted mono" font-size="11">card swipe</text>
          <text data-group="retailer processor" x="542" y="308" class="f-ink" font-weight="600">auth message</text>
          <text data-group="retailer processor" x="542" y="333" class="f-fraud mono" font-size="11" font-weight="600">no item list</text>
          <text data-group="processor retailer" x="543" y="364" class="f-ink" font-weight="600">$ reimbursement</text>
          <text data-group="processor retailer" x="543" y="379" class="f-muted" font-size="11">few banking days</text>
          <text data-group="wholesale fns" x="560" y="524" class="f-caution" font-weight="600">purchase records (proposed)</text>
        </g>
        <g font-size="12">
          <text data-group="processor fns" x="698" y="192" class="f-ink" font-weight="600">daily records</text>
          <text data-group="processor fns" x="698" y="207" class="f-muted">to ALERT</text>
          <text data-group="processor fns" x="698" y="222" class="f-muted mono" font-size="11">250M a month</text>
          <text data-group="pos retailer" x="404" y="416" class="f-ink" font-weight="600">builds sale message</text>
        </g>

        <!-- nodes -->
        <g font-size="12">
          <rect data-tip="Payroll, wage, tax, and bank records let states check reported income." data-group="income" x="6" y="34" width="190" height="100" rx="10" class="f-neutral-tint s-rule"/>
          <text data-group="income" x="20" y="55" class="f-muted mono" font-size="11" letter-spacing="0.06em">OUTSIDE DATA</text>
          <text data-group="income" x="20" y="76" class="f-ink" font-size="13.5" font-weight="700">Income records</text>
          <text data-group="income" x="20" y="96" class="f-muted">Payroll, wage records</text>
          <text data-group="income" x="20" y="114" class="f-muted">Tax data, bank checks</text>

          <rect data-tip="States decide eligibility, issue benefits on EBT, run integrity units, and contract EBT processors." data-group="state" x="300" y="24" width="190" height="120" rx="10" class="f-defense-tint s-defense" stroke-opacity="0.45"/>
          <text data-group="state" x="314" y="48" class="f-ink" font-size="13.5" font-weight="700">State agencies</text>
          <text data-group="state" x="314" y="70" class="f-muted">Decide eligibility</text>
          <text data-group="state" x="314" y="88" class="f-muted">Issue benefits on EBT</text>
          <text data-group="state" x="314" y="106" class="f-muted">Run integrity units</text>
          <text data-group="state" x="314" y="124" class="f-muted">Contract EBT processors</text>

          <rect data-tip="USDA FNS funds benefits, sets rules, authorizes retailers, and scores every store in ALERT." data-group="fns" x="594" y="24" width="190" height="120" rx="10" class="f-defense-tint s-defense" stroke-opacity="0.45"/>
          <text data-group="fns" x="608" y="48" class="f-ink" font-size="13.5" font-weight="700">USDA FNS</text>
          <text data-group="fns" x="608" y="66" class="f-defense mono" font-size="11">renamed FNA in 2026</text>
          <text data-group="fns" x="608" y="86" class="f-muted">Funds benefits, sets rules</text>
          <text data-group="fns" x="608" y="104" class="f-muted">Authorizes retailers</text>
          <text data-group="fns" x="608" y="122" class="f-muted">ALERT scores every store</text>

          <rect data-tip="41.7M people a month in FY2024 hold an EBT card and PIN." data-group="recipient" x="6" y="254" width="190" height="108" rx="10" class="f-surface s-rule"/>
          <text data-group="recipient" x="20" y="278" class="f-ink" font-size="13.5" font-weight="700">Recipient household</text>
          <text data-group="recipient" x="20" y="298" class="f-muted">EBT card and PIN</text>
          <text data-group="recipient" x="20" y="322" class="f-ink mono" font-size="11.5" font-weight="600">41.7M people a month</text>
          <text data-group="recipient" x="20" y="339" class="f-muted" font-size="11">FY2024 average</text>

          <rect data-tip="About 260,000 stores. The card terminal is where every flow meets." data-group="retailer" x="300" y="246" width="190" height="130" rx="10" class="f-surface s-rule"/>
          <text data-group="retailer" x="314" y="270" class="f-ink" font-size="13.5" font-weight="700">Authorized retailer</text>
          <text data-group="retailer" x="314" y="288" class="f-muted mono" font-size="11">about 260,000 stores</text>
          <rect data-group="retailer" x="314" y="300" width="162" height="62" rx="6" class="f-neutral-tint s-fraud" stroke-opacity="0.55"/>
          <text data-group="retailer" x="326" y="324" class="f-ink" font-weight="700">Card terminal</text>
          <text data-group="retailer" x="326" y="343" class="f-muted" font-size="11">every flow meets here</text>

          <rect data-tip="Approves the sale, debits the EBT account, and reports each sale to FNS." data-group="processor" x="594" y="254" width="190" height="108" rx="10" class="f-defense-tint s-defense" stroke-opacity="0.45"/>
          <text data-group="processor" x="608" y="278" class="f-ink" font-size="13.5" font-weight="700">EBT processor</text>
          <text data-group="processor" x="608" y="298" class="f-muted">Approves the sale</text>
          <text data-group="processor" x="608" y="316" class="f-muted">Debits the EBT account</text>
          <text data-group="processor" x="608" y="334" class="f-muted">Reports each sale to FNS</text>

          <rect data-tip="POS vendors supply the terminals and registers that build the sale message." data-group="pos" x="300" y="446" width="190" height="64" rx="10" class="f-surface s-rule"/>
          <text data-group="pos" x="314" y="471" class="f-ink" font-size="13.5" font-weight="700">POS vendors</text>
          <text data-group="pos" x="314" y="491" class="f-muted">Terminals and registers</text>

          <rect data-tip="Supplier invoices and shipments are the outside data the report proposes feeding to FNS." data-group="wholesale" x="6" y="446" width="190" height="64" rx="10" class="f-neutral-tint s-rule"/>
          <text data-group="wholesale" x="20" y="464" class="f-muted mono" font-size="11" letter-spacing="0.06em">OUTSIDE DATA</text>
          <text data-group="wholesale" x="20" y="482" class="f-ink" font-size="13.5" font-weight="700">Wholesale suppliers</text>
          <text data-group="wholesale" x="20" y="500" class="f-muted">Invoices, shipments</text>
        </g>
      </svg>
    </div>
    <p class="pa-note"><span class="chip chip--fraud">weak point</span> The authorization message carries store, card, amount, date, and time. No item list reaches FNS, so every check infers fraud from amounts and timing.</p>
  </div>
  <figcaption><strong>Read it as:</strong> many actors touch each benefit dollar, yet the one record they all share holds an amount and no basket. The amber feed is the report's proposal: purchase records the store does not write.</figcaption>
</figure>

<h2 id="2-a-taxonomy-of-snap-fraud">2. A taxonomy of SNAP fraud<a class="anchor" href="#2-a-taxonomy-of-snap-fraud" aria-label="Link to this section"></a></h2>
<p>The Congressional Research Service groups SNAP integrity problems into trafficking, retailer application fraud, household errors and fraud, state agency errors and fraud, and external scams that steal benefits. This report uses a slightly finer grouping because each type calls for a different control (<a href="#fig-02-fraud-taxonomy">Figure 2</a>).</p>
<div class="table-wrap"><table><thead><tr><th>Type</th><th>Who commits it</th><th>Who loses</th><th>Mechanism</th><th>Main control today</th></tr></thead><tbody><tr><td>Trafficking</td><td>Recipient and store, together</td><td>Taxpayers</td><td>Card swiped for a sale that does not happen; store pays recipient cash at a discount</td><td>ALERT transaction analytics, undercover buys, retailer disqualification</td></tr><tr><td>Recipient eligibility fraud</td><td>Applicant</td><td>Taxpayers</td><td>Underreported income, invented household members, hidden assets, enrollment in two states</td><td>State verification, data matching, quality control reviews, intentional program violation hearings</td></tr><tr><td>Retailer ineligible sales and overcharging</td><td>Store, sometimes with shopper</td><td>Taxpayers or recipient</td><td>Benefits used for alcohol, tobacco, or household goods; prices inflated for EBT shoppers</td><td>Undercover buys, complaints, term disqualification</td></tr><tr><td>Card skimming and cloning</td><td>Outside criminals</td><td>Recipient</td><td>Device copies magstripe data and PIN; clone drains the account soon after the monthly load</td><td>Card replacement policies, chip cards (new), recipient card locks</td></tr><tr><td>Insider fraud</td><td>Caseworker or state employee</td><td>Taxpayers</td><td>Fictitious cases, benefits issued to accomplices, data sold</td><td>Audits, separation of duties, access logs</td></tr><tr><td>Organized rings</td><td>Groups spanning stores, recruiters, and card holders</td><td>Taxpayers or recipients</td><td>Many cards trafficked through a few stores; mass skimming operations</td><td>Joint federal and state investigations, network analysis</td></tr></tbody>
</table></div>
<figure class="fig" id="fig-02-fraud-taxonomy">
  <div class="fig-head"><span class="eyebrow">Figure 2</span><h4>Six kinds of SNAP fraud: who commits it, who pays</h4><p class="dek">Most fraud drains taxpayer money; skimming steals the family's food money.</p></div>
  <div class="fig-body">
    <div class="tx" role="group" aria-label="Matrix of six SNAP fraud types by who commits each and who loses. Five types cost taxpayers. Card skimming, by outside criminals, takes the recipient family's benefits.">
      <div class="tx-lane tx-lane--tax" aria-hidden="true"></div>
      <div class="tx-lane tx-lane--rec" aria-hidden="true"></div>
      <div class="tx-corner eyebrow" aria-hidden="true">Who loses / who commits</div>
      <div class="tx-colhead eyebrow" data-group="col-recipient" style="--c: 2" aria-hidden="true">Recipient</div>
      <div class="tx-colhead eyebrow" data-group="col-store" style="--c: 3" aria-hidden="true">Store</div>
      <div class="tx-colhead eyebrow" data-group="col-outside" style="--c: 4" aria-hidden="true">Outside criminal</div>
      <div class="tx-colhead eyebrow" data-group="col-caseworker" style="--c: 5" aria-hidden="true">Caseworker</div>
      <div class="tx-colhead eyebrow" data-group="col-ring" style="--c: 6" aria-hidden="true">Organized ring</div>
      <div class="tx-rowhead tx-rowhead--tax" data-group="row-tax" aria-hidden="true"><strong>Taxpayers</strong><span>program money is lost</span></div>
      <div class="tx-rowhead tx-rowhead--rec" data-group="row-rec" aria-hidden="true"><strong class="t-fraud">The family</strong><span>its food money is gone</span></div>

      <div class="node tx-card tx-traf" data-tip="Card swiped for a sale that never happens; the store pays the recipient cash at a discount." data-group="traffic col-recipient col-store row-tax">
        <h5>Trafficking</h5>
        <p>Card swiped for a sale that never happens; the store pays the recipient cash at a discount.</p>
        <p class="tx-tag"><span class="chip">recipient and store, together</span></p>
        <p class="tx-meta"><span class="chip">by: recipient + store</span><span class="chip">loses: taxpayers</span></p>
      </div>
      <div class="node tx-card tx-elig" data-tip="Underreported income, invented members, or two-state enrollment. Taxpayers lose." data-group="elig col-recipient row-tax">
        <h5>Eligibility fraud</h5>
        <p>Underreported income, invented members, two-state enrollment.</p>
        <p class="tx-meta"><span class="chip">by: applicant</span><span class="chip">loses: taxpayers</span></p>
      </div>
      <div class="node tx-card tx-inel" data-tip="Collusive: alcohol, tobacco, or paper goods paid with EBT." data-group="inel col-store row-tax">
        <h5>Ineligible sales, overcharging</h5>
        <p><strong>Collusive:</strong> alcohol, tobacco, or paper goods paid with EBT.</p>
        <p class="tx-meta"><span class="chip">by: store</span><span class="chip">loses: taxpayers</span></p>
      </div>
      <div class="node tx-card tx-cont tx-inel2" data-tip="EBT shoppers pay inflated prices and do not notice. The family loses." data-group="inel2 col-store row-rec">
        <h5>Overcharging, predatory form</h5>
        <p>EBT shoppers pay inflated prices and do not notice.</p>
        <p class="tx-meta"><span class="chip">by: store</span><span class="chip chip--fraud">loses: family</span></p>
      </div>
      <div class="node tx-card tx-skim" data-tip="A device copies stripe data and PIN; a clone drains the account soon after the monthly load." data-group="skim col-outside row-rec">
        <h5>Card skimming and cloning</h5>
        <p>A device copies stripe data and PIN; a clone drains the account soon after the monthly load.</p>
        <p class="tx-victim t-fraud">Victim: the family finds a zero balance</p>
        <p class="tx-meta"><span class="chip">by: outside criminal</span><span class="chip chip--fraud">loses: family</span></p>
      </div>
      <div class="tx-repl" data-group="skim row-rec">
        <p>Federal replacement covered some skimming losses, until it ended in December 2024.</p>
      </div>
      <div class="node tx-card tx-ins" data-tip="Fictitious cases, benefits to accomplices, data sold. Rare, and each case can be large." data-group="ins col-caseworker row-tax">
        <h5>Insider fraud</h5>
        <p>Fictitious cases, benefits to accomplices, data sold. Rare, and each case can be large.</p>
        <p class="tx-meta"><span class="chip">by: caseworker</span><span class="chip">loses: taxpayers</span></p>
      </div>
      <div class="node tx-card tx-ring" data-tip="Many cards run through a few stores under nominee owners." data-group="ring col-ring row-tax">
        <h5>Organized rings</h5>
        <p>Trafficking networks: many cards through a few stores under nominee owners.</p>
        <p class="tx-meta"><span class="chip">by: ring</span><span class="chip">loses: taxpayers</span></p>
      </div>
      <div class="node tx-card tx-cont tx-ring2" data-tip="Mass card theft across states." data-group="ring2 col-ring row-rec">
        <h5>Rings, skimming crews</h5>
        <p>Mass card theft across states.</p>
        <p class="tx-meta"><span class="chip">by: ring</span><span class="chip chip--fraud">loses: family</span></p>
      </div>
    </div>
  </div>
  <figcaption><strong>Read it as:</strong> five types sit in the taxpayer lane. Skimming sits only in the family's lane. Overcharging and rings reach the family in some variants (dashed cards).</figcaption>
</figure>

<p><strong>Trafficking.</strong> Trafficking is the exchange of benefits for cash or anything other than eligible food. The typical case involves a recipient who lets a store clerk swipe the card for an amount, say $100, and receives a smaller amount of cash. Prosecutors in federal cases have described rates around 50 cents on the dollar, and some cases report higher or lower rates. Trafficking requires a retailer in almost every case, because only an authorized retailer can turn an EBT debit into a deposit. A recipient can also traffic by selling the card and PIN to a third party, who then shops with it.</p>
<p><strong>Recipient eligibility fraud.</strong> An applicant may underreport earnings, omit a working household member, add a person who does not live in the home, or enroll in a second state. Honest mistakes produce the same outcomes, so eligibility problems show up in the error rate and only some of them are fraud.</p>
<p><strong>Retailer ineligible sales and overcharging.</strong> A store may let EBT pay for items the law excludes, such as alcohol, tobacco, or paper goods. A store may also charge EBT shoppers more than cash shoppers, or inflate the price of real food items. The second practice can be collusive (the shopper accepts inflated prices in exchange for credit or cash) or predatory (the shopper does not notice).</p>
<p><strong>Card skimming and cloning.</strong> Skimming is theft from recipients. A criminal attaches a device to a store's card reader or PIN pad. The device records the magnetic stripe data and the PIN. The criminal writes the data onto a blank card and drains the account, often within hours of the monthly benefit load. The recipient arrives at the store to find a zero balance. Magnetic stripe cards are easy to clone because the stripe carries static data that works on every swipe. A chip card generates a one-time code for each purchase, so copied data does not work for a second purchase.</p>
<p><strong>Insider fraud.</strong> State employees with system access can create fictitious cases or issue benefits to accomplices. Cases are rare, and each can be large because the insider controls the record.</p>
<p><strong>Organized rings.</strong> Rings combine the other types, for example by running several trafficking stores under nominee owners or skimming crews across states. They leave patterns across many cards and stores that no single transaction shows.</p>
<h3 id="21-fraud-versus-improper-payments">2.1 Fraud versus improper payments<a class="anchor" href="#21-fraud-versus-improper-payments" aria-label="Link to this section"></a></h3>
<p>The claim that &quot;11 percent of SNAP is fraud&quot; comes from the national payment error rate. USDA computes that rate each year from a quality control sample of cases. Reviewers check whether each sampled household received the correct benefit. Any difference above a small tolerance counts as an error, in either direction.</p>
<div class="table-wrap"><table><thead><tr><th>Fiscal year</th><th class="num">National payment error rate</th><th class="num">Overpayment rate</th><th class="num">Underpayment rate</th></tr></thead><tbody><tr><td>2023</td><td class="num">11.68%</td><td class="num">10.03%</td><td class="num">1.64%<span class="tnote" style="width:7ch"></span></td></tr><tr><td>2024</td><td class="num">10.93%</td><td class="num">9.26%</td><td class="num">1.67%<span class="tnote" style="width:7ch"></span></td></tr><tr><td>2025</td><td class="num">10.62%</td><td class="num">9.28%</td><td class="num">1.34%<span class="tnote" style="width:7ch">derived</span></td></tr></tbody>
</table></div>
<p>Four facts separate this rate from fraud. First, it includes underpayments, which are money the program failed to pay to eligible families. Second, it counts errors regardless of cause, and the largest causes are mistakes in income and household data made by households or caseworkers. USDA's own release for fiscal year 2024 stated that the rate is not a measure of fraud. Third, it measures eligibility and benefit amounts only. It does not measure trafficking at all, because a trafficked benefit was correctly issued to an eligible household before it was sold. Fourth, error rates move with administrative load. They fell to about 3 percent around 2013, then rose after the pandemic disrupted state operations and staffing (<a href="#fig-02-fraud-vs-errors">Figure 3</a>).</p>
<figure class="fig" id="fig-02-fraud-vs-errors">
  <div class="fig-head"><span class="eyebrow">Figure 3</span><h4>The 11 percent is mostly error, not fraud</h4><p class="dek">The payment error rate counts mistakes in both directions; trafficking, the best fraud measure, is about 1.6 to 2.0 percent.</p></div>
  <div class="fig-body">
    <div class="fe">
      <div class="fe-context">
        <p class="fe-ctx-label">All SNAP benefits, about $100 billion a year (FY2024) = 100%</p>
        <div class="fe-strip" role="img" aria-label="On a bar of all SNAP benefits, the 10.93 percent payment error rate is a thin slice at the left; that 0 to 12 percent slice is enlarged below.">
          <span class="fe-strip-err" data-tip="Payment error rate, FY2024: 10.93% of all SNAP benefits." data-group="row-err" data-animate="grow" data-step="0" style="width: 10.93%"></span>
          <span class="fe-strip-win" style="width: 12%"></span>
        </div>
        <p class="fe-ctx-note">Outlined: the 0 to 12% slice, enlarged below on one scale.</p>
      </div>

      <div class="fe-chart" role="img" aria-label="On one scale from 0 to 12 percent of benefits: the FY2024 payment error rate is 10.93 percent, 9.26 points overpayments and 1.67 underpayments. Trafficking, the fraud estimate for 2015 to 2017, is 1.6 to 2.0 percent.">
        <div class="fe-axis" aria-hidden="true">
          <span style="left: 0%">0%</span><span style="left: 16.6667%">2%</span><span style="left: 33.3333%">4%</span><span style="left: 50%">6%</span><span style="left: 66.6667%">8%</span><span style="left: 83.3333%">10%</span><span style="left: 100%">12%</span>
        </div>
        <div class="fe-rowlabel" data-group="row-err">
          <strong>Payment error rate</strong>
          <span>FY2024, all causes, both directions</span>
          <span class="num fe-total">10.93% <span class="t-muted">about $10B</span></span>
        </div>
        <div class="fe-track">
          <span class="fe-grid" aria-hidden="true"></span>
          <span class="fe-seg fe-seg--over" data-tip="Overpayments, FY2024: 9.26% of benefits." data-group="over row-err" data-animate="grow" data-step="1" style="left: 0; width: 77.1667%"></span>
          <span class="fe-seg fe-seg--under" data-tip="Underpayments, FY2024: 1.67% of benefits." data-group="under row-err" data-animate="grow" data-step="1.5" style="left: 77.1667%; width: 13.9167%"></span>
        </div>
        <ul class="fe-legend">
          <li data-group="over"><span class="fe-sw fe-sw--over"></span>Overpayments <span class="num">9.26%</span>: mostly income and household data mistakes</li>
          <li data-group="under"><span class="fe-sw fe-sw--under"></span>Underpayments <span class="num">1.67%</span>: money families were owed and not paid</li>
        </ul>

        <div class="fe-rowlabel" data-group="row-traf">
          <strong class="t-fraud">Trafficking (fraud)</strong>
          <span>2015 to 2017 estimate</span>
          <span class="num fe-total t-fraud">1.6% to 2.0%</span>
        </div>
        <div class="fe-track">
          <span class="fe-grid" aria-hidden="true"></span>
          <span class="fe-seg fe-seg--traf" data-tip="Trafficking, 2015 to 2017: 1.6% of benefits, updated definition." data-group="traf row-traf" data-animate="grow" data-step="2.5" style="left: 0; width: 13.3333%"></span>
          <span class="fe-seg fe-seg--traf-old" data-tip="Trafficking, older definition: up to 2.0% of benefits." data-group="traf-old row-traf" data-animate="grow" data-step="3" style="left: 13.3333%; width: 3.3333%"></span>
        </div>
        <ul class="fe-legend">
          <li data-group="traf"><span class="fe-sw fe-sw--traf"></span><span class="num">1.6%</span> updated definition, about $1.0B a year</li>
          <li data-group="traf-old"><span class="fe-sw fe-sw--traf-old"></span>to <span class="num">2.0%</span> older definition, about $1.3B a year</li>
        </ul>

      </div>

      <div class="fe-notes">
        <div class="node node--muted">
          <h5>Errors, either direction</h5>
          <p>Any gap above a small tolerance counts, whatever the cause. USDA, FY2024: the rate is not a measure of fraud.</p>
        </div>
        <div class="node node--fraud">
          <h5>Trafficking is not in the 11%</h5>
          <p>A trafficked benefit was correctly issued to an eligible household, then sold for cash. The error rate never sees it.</p>
        </div>
        <div class="node node--caution">
          <h5>Read sizes, not sums</h5>
          <p>Different years, methods, and measures. GAO: true trafficking is uncertain, about $960 million to $4.7 billion a year.</p>
        </div>
      </div>
    </div>
  </div>
  <figcaption><strong>Read it as:</strong> compare bar lengths. The grey bar is error, mostly honest mistakes; the red bar is the fraud estimate, under a fifth of its length. Controls aimed at one do little for the other.</figcaption>
</figure>

<p>The error rate is still a serious problem. Improper payments of about $10 billion a year are real money, and Congress has tied state cost sharing to error rates starting with fiscal year 2028. The point is narrower: the error rate and the fraud rate measure different things, and controls aimed at one do little for the other. The best available national fraud measure is the trafficking estimate. FNS's eighth study in that series, covering 2015 to 2017 and published in 2021, estimated that 1.6 percent of benefits were trafficked under its updated definition (about $1.0 billion a year) and 2.0 percent under the older definition (about $1.3 billion a year). The earlier 2012 to 2014 study put the rate near 1.5 percent. GAO has warned that the true figure is uncertain and could range from about $960 million to $4.7 billion a year, because the estimate rests on untested assumptions about how much of a trafficking store's volume is trafficked.</p>
<h2 id="3-anatomy-of-trafficking">3. Anatomy of trafficking<a class="anchor" href="#3-anatomy-of-trafficking" aria-label="Link to this section"></a></h2>
<p>The money flow of a single trafficking transaction is simple (<a href="#fig-03-trafficking-flow">Figure 4</a>).</p>
<figure class="fig" id="fig-03-trafficking-flow">
  <div class="fig-head"><span class="eyebrow">Figure 4</span><h4>How one trafficking swipe moves the money</h4><p class="dek">The recipient and the store split $100 of food aid as cash; the program pays in full and no food changes hands.</p></div>
  <div class="fig-body">
    <svg class="tf-defs" width="0" height="0" aria-hidden="true" focusable="false">
      <defs>
        <marker id="fig-03-trafficking-flow-ah-fraud" viewBox="0 0 10 10" refX="8" refY="5" markerUnits="userSpaceOnUse" markerWidth="9" markerHeight="9" orient="auto"><path d="M0 0 L10 5 L0 10 z" class="f-fraud"/></marker>
        <marker id="fig-03-trafficking-flow-ah-defense" viewBox="0 0 10 10" refX="8" refY="5" markerUnits="userSpaceOnUse" markerWidth="9" markerHeight="9" orient="auto"><path d="M0 0 L10 5 L0 10 z" class="f-defense"/></marker>
      </defs>
    </svg>
    <div class="tf">
      <div class="tf-row">
        <div class="node tf-party" data-group="recipient" data-tip="Restricted benefits become cash, which can pay rent or utilities.">
          <svg class="tf-icon t-muted" viewBox="0 0 24 24" role="img" aria-label="EBT card"><rect x="2.5" y="5.5" width="19" height="13" rx="2" fill="none" stroke="currentColor" stroke-width="1.5"/><line x1="2.5" y1="9.5" x2="21.5" y2="9.5" stroke="currentColor" stroke-width="2.5"/><line x1="5.5" y1="14.5" x2="10.5" y2="14.5" stroke="currentColor" stroke-width="1.5"/></svg>
          <h5>Recipient</h5>
          <p>EBT card holds <span class="num">$100</span> of benefits</p>
        </div>

        <div class="tf-legs">
          <span class="tf-zone eyebrow t-fraud">At the counter: the lie</span>
          <div data-animate="sequence" data-step="0" data-group="recipient store" data-tip="The store keys in an amount; the card is debited and no food changes hands." class="tf-leg t-fraud">
            <span class="tf-lbl"><span class="tf-step">1</span><strong class="num">$100 swipe</strong></span>
            <svg class="tf-arrow" viewBox="0 0 96 14" role="img" aria-label="Recipient to store: card swiped for 100 dollars"><line x1="2" y1="7" x2="92" y2="7" class="s-fraud" stroke-width="2" data-animate="travel" marker-end="url(#fig-03-trafficking-flow-ah-fraud)"/></svg>
            <span class="tf-sub">card debited, no food handed over</span>
          </div>
          <div data-animate="sequence" data-step="1" data-group="recipient store" data-tip="Cash at 50 to 70 cents per benefit dollar; the store keeps the spread." class="tf-leg t-fraud">
            <span class="tf-lbl"><span class="tf-step">2</span><strong class="num">$50 cash</strong></span>
            <svg class="tf-arrow" viewBox="0 0 96 14" role="img" aria-label="Store to recipient: 50 dollars cash"><line x1="94" y1="7" x2="4" y2="7" class="s-fraud" stroke-width="2" data-animate="travel" marker-end="url(#fig-03-trafficking-flow-ah-fraud)"/></svg>
            <span class="tf-sub">paid back over the counter</span>
          </div>
        </div>

        <div class="node node--fraud tf-party" data-group="store" data-tip="Small stores handled about 15 percent of redemptions but over 95 percent of trafficked dollars.">
          <svg class="tf-icon t-fraud" viewBox="0 0 24 24" role="img" aria-label="Storefront"><path d="M3 9.5 L4.5 4.5 H19.5 L21 9.5 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><path d="M4.5 9.5 V19.5 H19.5 V9.5" fill="none" stroke="currentColor" stroke-width="1.5"/><rect x="10" y="13" width="4" height="6.5" fill="none" stroke="currentColor" stroke-width="1.5"/></svg>
          <h5>Corrupt store</h5>
          <p>Small, owner-run shop</p>
        </div>

        <div class="tf-legs">
          <span class="tf-zone eyebrow">In the network: looks normal</span>
          <div data-animate="sequence" data-step="2" data-group="store usda" data-tip="The record carries store, card, amount, date, and time. It carries no list of items." class="tf-leg t-fraud">
            <span class="tf-lbl"><span class="tf-step">3</span><strong class="num">$100 claim</strong></span>
            <svg class="tf-arrow" viewBox="0 0 96 14" role="img" aria-label="Store to USDA: a recorded 100 dollar sale"><line x1="2" y1="7" x2="92" y2="7" class="s-fraud" stroke-width="2" data-animate="travel" marker-end="url(#fig-03-trafficking-flow-ah-fraud)"/></svg>
            <span class="tf-sub">store, card, amount, time: approved</span>
          </div>
          <div data-animate="sequence" data-step="3" data-group="store usda" data-tip="Within a few banking days the store receives the same amount in its bank account." class="tf-leg t-defense">
            <span class="tf-lbl"><span class="tf-step">4</span><strong class="num">$100 deposit</strong></span>
            <svg class="tf-arrow" viewBox="0 0 96 14" role="img" aria-label="USDA to store: 100 dollar deposit"><line x1="94" y1="7" x2="4" y2="7" class="s-defense" stroke-width="2" data-animate="travel" marker-end="url(#fig-03-trafficking-flow-ah-defense)"/></svg>
            <span class="tf-sub">paid in full, in a few banking days</span>
          </div>
        </div>

        <div class="node node--defense tf-party" data-group="usda" data-tip="USDA funds the benefits; FNS authorizes and polices the retailers that accept them.">
          <svg class="tf-icon t-defense" viewBox="0 0 24 24" role="img" aria-label="Government building"><path d="M3 9 L12 4 L21 9 Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 11 V17 M10 11 V17 M14 11 V17 M18 11 V17 M3.5 19.5 H20.5" fill="none" stroke="currentColor" stroke-width="1.5"/></svg>
          <h5>USDA (taxpayers)</h5>
          <p>Reimburses stores for EBT sales</p>
        </div>
      </div>

      <div class="tf-end">
        <p class="eyebrow">Where each party ends up</p>
        <div class="tf-end-grid">
          <div class="tf-out" data-animate="sequence" data-step="4" data-group="recipient">
            <span class="num tf-amt t-fraud" data-animate="count" data-step="4">+$50</span>
            <h5>Recipient: cash</h5>
            <p>Gave up $100 of food benefits for half as much spendable cash.</p>
          </div>
          <div class="tf-out" data-animate="sequence" data-step="5" data-group="store">
            <span class="num tf-amt t-fraud" data-animate="count" data-step="5">+$50</span>
            <h5>Store: profit</h5>
            <p>Received $100, paid out $50, sold no food.</p>
          </div>
          <div class="tf-out tf-out--loss" data-animate="sequence" data-step="6" data-group="usda">
            <span class="num tf-amt t-fraud" data-animate="count" data-step="6">&minus;$100</span>
            <h5>Taxpayers: food aid</h5>
            <p>Paid in full for a sale that bought no food.</p>
          </div>
        </div>
      </div>
      <p class="tf-note"><span class="chip chip--caution">Illustrative</span> Example amounts. Federal cases describe rates around 50 cents on the dollar; some report higher or lower.</p>
    </div>
  </div>
  <figcaption><strong>Read it as:</strong> both people at the counter gain, so neither complains. The full loss lands on the program, which sees only a normal approved sale.</figcaption>
</figure>

<p><strong>The core weakness.</strong> The EBT authorization message carries the store, the card, the amount, the date, and the time. It carries no list of items. FNS's 2016 feasibility study on capturing purchases at the point of sale confirmed that only total transaction amounts reach FNS. A trafficking store therefore never has to invent products. It keys in an amount, the network approves it, and the deposit follows. Every downstream check has to infer the fraud from the shape of the amounts and timings, because the record contains nothing else (<a href="#fig-03-blind-record">Figure 5</a>).</p>
<figure class="fig" id="fig-03-blind-record">
  <div class="fig-head"><span class="eyebrow">Figure 5</span><h4>The blind record: an amount with no basket</h4><p class="dek">The EBT record says where, which card, how much, and when, but never what was bought.</p></div>
  <div class="fig-body">
    <div class="br">
      <div class="br-pair">
        <div class="br-panel" aria-label="What the EBT record carries">
          <p class="eyebrow t-defense">What the record carries</p>
          <div class="br-slip">
            <div class="br-slip-head" data-group="approve"><span>EBT AUTHORIZATION</span><span class="chip chip--defense">APPROVED</span></div>
            <dl class="br-fields">
              <div data-tip="Retailer number only; the record never names what the store sold." data-group="store"><dt>STORE</dt><dd>retailer 0418823</dd></div>
              <div data-tip="Last four digits of the EBT card." data-group="card"><dt>CARD</dt><dd>&bull;&bull;&bull;&bull; 5207</dd></div>
              <div data-tip="The total is $87.43. Nothing says what was bought." data-group="amount" class="br-amount"><dt>AMOUNT</dt><dd>$87.43</dd></div>
              <div data-group="date"><dt>DATE</dt><dd>day of sale</dd></div>
              <div data-tip="Time of the swipe, 14:02 in this example." data-group="time"><dt>TIME</dt><dd>14:02</dd></div>
            </dl>
          </div>
          <p class="br-note">Sent to FNS daily, for every SNAP sale. <span class="chip chip--caution">Illustrative values</span></p>
        </div>

        <div class="br-panel" aria-label="What the EBT record lacks: the item list">
          <p class="eyebrow t-fraud">What it lacks: the item list</p>
          <div class="br-missing" data-tip="Only total amounts reach FNS (FNS 2016 feasibility study)." data-group="items">
            <div class="br-row br-row--head"><span>ITEM</span><span>QTY</span><span>PRICE</span></div>
            <div class="br-row"><span>?</span><span>?</span><span>?</span></div>
            <div class="br-row"><span>?</span><span>?</span><span>?</span></div>
            <div class="br-row"><span>?</span><span>?</span><span>?</span></div>
            <div class="br-row"><span>&hellip;</span><span></span><span></span></div>
            <span class="br-stamp">NOT SENT</span>
          </div>
          <p class="br-note">Only total amounts reach FNS (FNS 2016 feasibility study).</p>
        </div>
      </div>

      <ol class="br-chain" aria-label="So a trafficking store never has to invent products">
        <li data-group="amount"><strong>Store keys in an amount</strong><span>no basket behind it</span></li>
        <li data-group="approve"><strong>Network approves</strong><span>valid card, enough balance</span></li>
        <li data-group="deposit"><strong>Deposit follows</strong><span>store paid in full</span></li>
      </ol>

      <div class="br-variant node node--muted" aria-label="Overcharging variant">
        <h5>Variant: overcharging (partial trafficking)</h5>
        <p>Real groceries go home, but the register charges more than they cost.</p>
        <div class="br-bar" role="img" aria-label="Register charges 70 dollars: 40 dollars of real groceries, 15 dollars back to the shopper as cash or credit, 15 dollars kept by the store above its margin.">
          <span class="br-bar-food" data-tip="$40 of the $70 charge is real groceries." data-group="food" style="width: 57.1429%"><span class="num">$40</span> real groceries</span>
          <span class="br-bar-cut" data-tip="$15 goes back to the shopper as cash or credit." data-group="shopper" style="width: 21.4286%"><span class="num">$15</span> shopper</span>
          <span class="br-bar-cut br-bar-cut--store" data-tip="$15 stays with the store above its margin." data-group="store-cut" style="width: 21.4286%"><span class="num">$15</span> store</span>
        </div>
        <div class="br-bar-total"><span>Register charges <span class="num">$70</span></span><span class="chip chip--caution">Illustrative</span></div>
        <p>The basket looks normal in size and timing, so amount-based analytics struggle to catch it.</p>
      </div>
    </div>
  </div>
  <figcaption><strong>Read it as:</strong> a real $87.43 basket and a trafficked $87.43 swipe produce the same record. Every later check must guess from amounts and timing.</figcaption>
</figure>

<p><strong>Both parties lie together.</strong> In most fraud, a victim has an incentive to report. In trafficking, the shopper and the cashier both gain. The recipient converts restricted benefits into cash, which can pay rent or utilities, or in worse cases buy drugs. The store keeps the spread. Neither party will complain, and any data either party enters can be shaped to look normal. This collusion property drives most of the design choices later in this report.</p>
<p><strong>Why small stores dominate.</strong> The 2015 to 2017 study found that small stores (small and medium groceries and convenience stores) handled about 15 percent of redemptions but accounted for over 95 percent of trafficked dollars, and 99 percent under the updated definition. Several factors explain the concentration. A small store is often owner-operated, so one person controls the register, the cash drawer, and the books. Large chains run centralized point-of-sale systems with item scanning, cashier logins, and loss-prevention audits, and a cashier who trafficked would be stealing from the employer. Small stores also have few transactions per day, so a trafficking swipe does not need to hide in a long queue of scanned baskets. FNS itself excludes large stores from its watch list on the ground that their own controls deter trafficking.</p>
<p><strong>The overcharging variant.</strong> A store can also traffic partially. The shopper buys real groceries worth $40, the register charges $70, and the shopper receives $15 in cash or store credit. The store earns $15 above its margin. This version leaves a basket that looks normal in size and timing, so it is harder for amount-based analytics to catch. It also has a predatory form, where the store inflates prices for EBT shoppers who do not check their receipts.</p>
<p><strong>The economics.</strong> Any restricted currency trades at a discount to cash. A dollar of benefits that can only buy groceries is worth less than a dollar to a household that needs to pay rent. A household that already spends more than its benefit on food gains little from trafficking, because benefits replace cash food spending dollar for dollar. A household with an urgent cash need, or one whose benefit exceeds its food budget, may value a benefit dollar at well under a dollar. Trafficking monetizes that gap. A store that can offer cash at 50 to 70 cents per dollar captures the remainder. Enforcement leaves the gap in place and raises the price of crossing it, by adding the risk of disqualification and prosecution to the store's side of the trade.</p>
<h2 id="4-current-detection-and-enforcement">4. Current detection and enforcement<a class="anchor" href="#4-current-detection-and-enforcement" aria-label="Link to this section"></a></h2>
<h3 id="41-transaction-analytics-alert">4.1 Transaction analytics: ALERT<a class="anchor" href="#41-transaction-analytics-alert" aria-label="Link to this section"></a></h3>
<p>FNS's main detection tool for retailer trafficking is the Anti-Fraud Locator using EBT Retailer Transactions (ALERT). State EBT processors send FNS daily records of every SNAP transaction. GAO reported in 2018 that ALERT scanned about 250 million transactions per month. The system assigns each store a numeric score for the likelihood of trafficking. Stores above a threshold join a watch list, and analysts prioritize them using factors such as average transaction size relative to store type. Analysts also compare a suspect store with similar stores in the same ZIP code. CRS reported that over 80 percent of the retailer trafficking FNS detects is found mainly through EBT transaction analysis (<a href="#fig-04-detection-pipeline">Figure 6</a>).</p>
<figure class="fig" id="fig-04-detection-pipeline">
  <div class="fig-head"><span class="eyebrow">Figure 6</span><h4>How SNAP catches trafficking today</h4><p class="dek">Transaction analytics find most cases, people confirm them, and sanctions fall mostly on stores, after the money is paid.</p></div>
  <div class="fig-body">
    <svg class="dp-defs" width="0" height="0" aria-hidden="true" focusable="false">
      <defs>
        <marker id="fig-04-detection-pipeline-ah" viewBox="0 0 10 10" refX="8" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M0 0 L10 5 L0 10 z" class="f-defense"/></marker>
      </defs>
    </svg>
    <div class="dp">
      <div class="dp-flow">
        <div class="node dp-stage">
          <span class="dp-n">1</span>
          <h5>EBT records</h5>
          <p>State processors send FNS every transaction, daily.</p>
          <span class="chip"><span class="num">~250M</span>/month (GAO 2018)</span>
        </div>
        <div class="dp-arrow"><span class="dp-arrow-lbl">daily records</span><svg viewBox="0 0 48 14" role="img" aria-label="EBT records flow into ALERT"><line x1="2" y1="7" x2="44" y2="7" class="s-defense" stroke-width="2" marker-end="url(#fig-04-detection-pipeline-ah)"/></svg></div>
        <div class="node node--defense dp-stage dp-alert">
          <span class="dp-n">2</span>
          <h5>ALERT flags</h5>
          <p>Each store gets a trafficking score; stores above a threshold join a watch list.</p>
          <p class="dp-big"><span class="num t-defense">80%+</span> of detected retailer trafficking is found mainly through EBT data (CRS)</p>
        </div>
        <div class="dp-arrow"><span class="dp-arrow-lbl">watch list</span><svg viewBox="0 0 48 14" role="img" aria-label="ALERT watch list goes to analysts"><line x1="2" y1="7" x2="44" y2="7" class="s-defense" stroke-width="2" marker-end="url(#fig-04-detection-pipeline-ah)"/></svg></div>
        <div class="node dp-stage">
          <span class="dp-n">3</span>
          <h5>Analyst review</h5>
          <p>Rank by sale size against store type; compare with similar stores in the same ZIP code.</p>
        </div>
        <div class="dp-arrow"><span class="dp-arrow-lbl">priority cases</span><svg viewBox="0 0 48 14" role="img" aria-label="Analysts pass priority cases to investigators"><line x1="2" y1="7" x2="44" y2="7" class="s-defense" stroke-width="2" marker-end="url(#fig-04-detection-pipeline-ah)"/></svg></div>
        <div class="node dp-stage">
          <span class="dp-n">4</span>
          <h5>Undercover buy</h5>
          <p>FNS or USDA OIG agents offer to sell benefits; state police in 28 states by agreement (GAO 2018).</p>
        </div>
        <div class="dp-arrow"><span class="dp-arrow-lbl">evidence</span><svg viewBox="0 0 48 14" role="img" aria-label="Evidence leads to sanctions"><line x1="2" y1="7" x2="44" y2="7" class="s-defense" stroke-width="2" marker-end="url(#fig-04-detection-pipeline-ah)"/></svg></div>
        <div class="dp-sanctions">
          <div class="node node--defense dp-stage">
            <span class="dp-n">5a</span>
            <h5>Retailer disqualified</h5>
            <p>Trafficking: permanent (7 CFR 278.6). Other violations: 6 months to 5 years; repeats double.</p>
            <span class="chip chip--defense">EBT data alone can support it</span>
          </div>
          <div class="node node--caution dp-stage">
            <span class="dp-n">5b</span>
            <h5>Recipient penalties</h5>
            <p>12 months, 24 months, then permanent (7 CFR 273.16). Trafficking conviction of $500+: permanent.</p>
            <span class="chip chip--caution">often stalls: EBT data rarely enough</span>
          </div>
        </div>
      </div>

      <p class="dp-late"><strong>Every stage runs after the sale:</strong> the store has already been paid.</p>

      <div class="dp-lower">
        <div class="node node--muted">
          <h5>Patterns that drive cases</h5>
          <p>Each is a proxy for "no real basket behind this amount."</p>
          <ul class="dp-list">
            <li>Many sales in round or repeated amounts, such as totals ending in .00</li>
            <li>Several swipes from one household account in a short window</li>
            <li>Sales too large for the store's size and type</li>
            <li>Many households making similar sales in a short period</li>
            <li>Accounts drained in one or two swipes, sometimes by out-of-state cards</li>
          </ul>
        </div>
        <div class="node node--caution">
          <h5 class="t-caution">Five structural limits</h5>
          <ol class="dp-list dp-limits">
            <li><strong>Detects after the fact.</strong> The store is already paid; owners can reopen under a new name.</li>
            <li><strong>Sees only amounts.</strong> A real $87.43 basket and a trafficked $87.43 swipe look the same.</li>
            <li><strong>Targets stores, largely misses recipients.</strong> EBT data alone is generally not enough to disqualify a recipient.</li>
            <li><strong>Does not scale with investigators.</strong> GAO: all stores reauthorized on a five-year cycle, regardless of risk.</li>
            <li><strong>The card was weak.</strong> Magnetic stripes made skimming cheap.</li>
          </ol>
        </div>
      </div>
    </div>
  </div>
  <figcaption><strong>Read it as:</strong> the pipeline is a strong detector of crude store fraud. It acts late, sees only amounts, and rarely reaches the recipient side of a case.</figcaption>
</figure>

<p>FNS charge letters and agency decisions describe the patterns that drive these cases:</p>
<ul>
<li>A large number of transactions in repeated or round dollar values, such as many sales ending in .00.</li>
<li>Multiple transactions from one household account within a short window, a pattern unusual for a small store with limited stock.</li>
<li>Transactions that are excessively large for the store's size and type.</li>
<li>Multiple households making similar transactions within a short period.</li>
<li>Accounts drained in one or two swipes, sometimes by out-of-state cards.</li>
</ul>
<p>Each pattern is a proxy for &quot;no real basket behind this amount.&quot; Round totals are rare when real prices are summed. Repeat swipes from one card suggest the store is splitting a large trafficking amount to avoid a size flag. Large totals at a store with a few shelves of stock suggest the store sells more food than it holds.</p>
<h3 id="42-undercover-investigations-and-penalties">4.2 Undercover investigations and penalties<a class="anchor" href="#42-undercover-investigations-and-penalties" aria-label="Link to this section"></a></h3>
<p>FNS investigators and USDA's Office of Inspector General conduct undercover buys, in which an investigator with a test card offers to sell benefits or to buy ineligible items. Under state law enforcement bureau agreements, which GAO counted in 28 states in 2018, state and local police also run their own undercover cases with SNAP cards.</p>
<p>Penalties are set by regulation. For retailers, 7 CFR 278.6 provides:</p>
<div class="table-wrap"><table><thead><tr><th>Violation</th><th>Retailer sanction</th></tr></thead><tbody><tr><td>Trafficking</td><td>Permanent disqualification (278.6(e)(1))</td></tr><tr><td>Trafficking, with an effective compliance program in place before the violation and owners not involved</td><td>Trafficking civil money penalty in place of permanent disqualification (278.6(i))</td></tr><tr><td>Selling ineligible items after a warning, as a practice (expensive or conspicuous items, alcohol, tobacco)</td><td>5 years for a first sanction (278.6(e)(2))</td></tr><tr><td>Common nonfood items after a warning, or (e)(2) conduct without warning</td><td>3 years (278.6(e)(3))</td></tr><tr><td>Ineligible sales by owners or managers without a warning, or credit sales</td><td>1 year (278.6(e)(4))</td></tr><tr><td>Violations from carelessness or poor supervision</td><td>6 months (278.6(e)(5))</td></tr><tr><td>Repeat sanctions</td><td>The above periods double (278.6(e)(6))</td></tr></tbody>
</table></div>
<p>The regulation lets FNS rely on &quot;evidence obtained through a transaction report under an electronic benefit transfer system.&quot; That clause is what lets ALERT data alone support a permanent disqualification.</p>
<p>For recipients, 7 CFR 273.16 sets disqualification periods for an intentional program violation: 12 months for a first violation, 24 months for a second, and permanent for a third. A court conviction for trafficking $500 or more brings permanent disqualification on the first occasion. A fraudulent statement about identity or residence to receive benefits in more than one place brings a 10-year bar. Transactions involving firearms, ammunition, or explosives bring permanent disqualification. Transactions involving controlled substances bring 24 months, then permanent.</p>
<h3 id="43-eligibility-and-retailer-standards">4.3 Eligibility and retailer standards<a class="anchor" href="#43-eligibility-and-retailer-standards" aria-label="Link to this section"></a></h3>
<p>On the eligibility side, states verify income and identity using federal and state data sources. The 2018 Farm Bill required a National Accuracy Clearinghouse (NAC) to stop the same person from receiving benefits in two states at once. FNS issued an interim final rule in October 2022 requiring all state agencies to join the NAC and to act on its match results, with full compliance due by 2027. Before the NAC, states relied on the slower Public Assistance Reporting Information System (PARIS) for interstate matching.</p>
<p>On the retailer side, FNS published a final rule in December 2016 to raise stocking standards. It required stores qualifying on inventory to carry at least seven varieties in each of four staple food categories, with perishable items in at least three categories and at least three units of each variety. The aim was partly integrity: a &quot;store&quot; with a few cans on a shelf and a busy EBT terminal is a classic trafficking front. Appropriations riders blocked enforcement of the variety and breadth provisions for years. USDA proposed updated standards in 2025, and its current guidance says retailers must comply starting in 2026.</p>
<h3 id="44-skimming-response">4.4 Skimming response<a class="anchor" href="#44-skimming-response" aria-label="Link to this section"></a></h3>
<p>The Consolidated Appropriations Act, 2023 (P.L. 117-328, Division HH, section 501) gave states temporary authority to replace stolen benefits with federal funds, limited to the lesser of the amount stolen or two months of benefits, and to two replacements per household per fiscal year. A later continuing resolution extended the window to cover thefts through December 20, 2024. Congress did not extend it further. According to USDA figures cited by the Center on Budget and Policy Priorities, states replaced more than $150 million in stolen benefits for over 315,000 households between January 2023 and September 2024. The underlying fix is the card itself. California became the first state to issue chip-and-tap EBT cards in 2025, and state officials told legislators that all clients had received new cards by spring 2025. Other states have started similar migrations (<a href="#fig-04-skimming-timeline">Figure 7</a>).</p>
<figure class="fig" id="fig-04-skimming-timeline">
  <div class="fig-head"><span class="eyebrow">Figure 7</span><h4>Skimming: how a family's benefits vanish</h4><p class="dek">A copied magstripe card is drained soon after the monthly load, and since federal replacement ended in December 2024 the family bears the loss.</p></div>
  <div class="fig-body">
    <div class="sk">
      <p class="eyebrow sk-title">The attack, one benefit month (not to scale)</p>
      <ol class="sk-steps">
        <li class="node" data-tip="Step 1, Skimmer installed: a device fitted on a store's card reader or PIN pad." data-group="step-1" data-animate="sequence" data-step="0">
          <span class="sk-n">1</span>
          <h5>Skimmer installed</h5>
          <p>Device fitted on a store's card reader or PIN pad.</p>
          <span class="chip chip--fraud">criminal</span>
        </li>
        <li class="node" data-tip="Step 2, Card data copied: at a normal purchase, the device records stripe data and PIN." data-group="step-2" data-animate="sequence" data-step="1">
          <span class="sk-n">2</span>
          <h5>Card data copied</h5>
          <p>At a normal purchase, the device records stripe data and PIN.</p>
          <span class="chip chip--fraud">magstripe, no chip</span>
        </li>
        <li class="node" data-tip="Step 3, Clone made: data written onto a blank card; static stripe data works on every swipe." data-group="step-3" data-animate="sequence" data-step="2">
          <span class="sk-n">3</span>
          <h5>Clone made</h5>
          <p>Data written onto a blank card. Static stripe data works on every swipe.</p>
          <span class="chip chip--fraud">criminal</span>
        </li>
        <li class="node node--defense" data-tip="Step 4, Benefits load: the state loads the monthly benefit onto the family's account." data-group="step-4" data-animate="sequence" data-step="3">
          <span class="sk-n">4</span>
          <h5>Benefits load</h5>
          <p>The state loads the monthly benefit onto the family's account.</p>
          <span class="chip chip--defense">state</span>
        </li>
        <li class="node node--fraud" data-tip="Step 5, Card drained: the clone spends the whole balance, often within hours." data-group="step-5" data-animate="sequence" data-step="4">
          <span class="sk-n">5</span>
          <h5>Card drained</h5>
          <p>The clone spends the whole balance.</p>
          <span class="chip chip--fraud">often within hours</span>
        </li>
        <li class="node sk-hit" data-tip="Step 6, Family finds $0: they arrive at the store to a zero balance." data-group="step-6" data-animate="sequence" data-step="5">
          <span class="sk-n">6</span>
          <h5>Family finds <span class="num">$0</span></h5>
          <p>They arrive at the store to a zero balance. The month's food money is gone.</p>
          <span class="chip chip--fraud">victim: recipient</span>
        </li>
      </ol>
      <p class="sk-why"><strong class="t-defense">Why a chip fixes it:</strong> a magnetic stripe carries static data that works on every swipe. A chip card makes a one-time code per purchase, so copied data fails a second time.</p>

      <div class="sk-cover">
        <p class="eyebrow sk-title">Who covers the loss</p>
        <div class="sk-axis" aria-hidden="true">
          <span style="left: 0%">2023</span><span style="left: 25%">2024</span><span style="left: 50%">2025</span><span style="left: 75%">2026</span>
        </div>
        <div class="sk-track" role="img" aria-label="From January 2023 to December 20, 2024, federal funds replaced stolen benefits. After that window ended, families bear the loss.">
          <span class="sk-fed" data-tip="Federal funds replaced stolen benefits, January 2023 to December 20, 2024." data-group="fed" data-animate="grow" data-step="6" style="width: 49.26%">Federal funds replace stolen benefits</span>
          <span class="sk-fam" data-tip="Families bear the loss after December 20, 2024." data-group="fam" data-animate="grow" data-step="7" style="left: 49.26%">Families bear the loss</span>
          <span class="sk-end" data-tip="Dec 20, 2024: the window ends; Congress did not extend it." data-group="end" style="left: 49.26%"></span>
        </div>
        <p class="sk-endlabel" data-group="end" style="padding-left: 49.26%"><strong class="num t-fraud">Dec 20, 2024</strong> <span>window ends; Congress did not extend it</span></p>
        <div class="sk-notes">
          <div class="node node--defense">
            <h5>P.L. 117-328 replacement rules</h5>
            <p>Capped at the lesser of the amount stolen or two months of benefits; two replacements per household per fiscal year.</p>
          </div>
          <div class="node">
            <h5><span class="num">$150M+</span> replaced</h5>
            <p>Jan 2023 to Sep 2024: more than $150 million for over 315,000 households (USDA, via CBPP).</p>
          </div>
          <div class="node node--defense">
            <h5>The lasting fix: chip cards</h5>
            <p>California issued the first chip-and-tap EBT cards in 2025; all clients had them by spring 2025. Other states have started.</p>
          </div>
        </div>
      </div>
    </div>
  </div>
  <figcaption><strong>Read it as:</strong> the theft hits the family, not the store or the program. Since December 2024 no federal backstop covers it, so the card itself has to change.</figcaption>
</figure>

<h3 id="45-limits-of-the-current-approach">4.5 Limits of the current approach<a class="anchor" href="#45-limits-of-the-current-approach" aria-label="Link to this section"></a></h3>
<p>The current model has five structural limits.</p>
<p>First, it detects after the fact. ALERT reads completed transactions, and the store has already been paid. By the time a store is disqualified, it may have trafficked for months, and the owners can reopen under a new name.</p>
<p>Second, it sees only amounts. Without items, ALERT cannot tell a real $87.43 basket from a trafficked $87.43 swipe. Smart traffickers learn the flags and stop using round amounts.</p>
<p>Third, it targets stores and largely misses recipients. EBT data alone is generally not enough to disqualify a recipient, so states struggle to act on the cardholder side of ALERT cases.</p>
<p>Fourth, it does not scale with investigator capacity. GAO found that FNS reauthorized all stores on a five-year cycle regardless of risk, and that its trafficking estimate rested on assumptions it had not tested.</p>
<p>Fifth, the card was weak. Magnetic stripe cards made skimming cheap. The program bore part of the loss through replacement, and recipients bore the rest after the replacement authority ended. Figures <a href="#uml-ops-use-cases">A1</a> to <a href="#uml-ops-skimming-response">A4</a> in Appendix A model this response as UML operations diagrams.</p>
<h2 id="5-design-principles">5. Design principles<a class="anchor" href="#5-design-principles" aria-label="Link to this section"></a></h2>
<p>Four principles shape the architecture in the next section.</p>
<div class="callout callout-important" role="note"><p class="callout-label">important</p>
<p><strong>Collusion resistance.</strong> When the shopper and the cashier both cheat, any data they enter can be faked. A store can key fake item lines, and a shopper will confirm any receipt. The system therefore needs evidence that neither party controls. Examples include wholesale invoices from suppliers, distributor shipment records, the stock a field inspector sees on the shelf, and the behavior of the same cards at other stores. Third-party evidence is the only class of data that a colluding pair cannot shape (<a href="#fig-05-collusion-resistance">Figure 8</a>).</p></div>
<figure class="fig" id="fig-05-collusion-resistance">
  <div class="fig-head"><span class="eyebrow">Figure 8</span><h4>Collusion resistance: who writes the evidence?</h4><p class="dek">Build detection on evidence the colluders cannot write.</p></div>
  <div class="fig-body">
    <div class="cr-pair">
      <svg viewBox="0 0 380 122" role="img" aria-label="In trafficking the shopper and the cashier trade a benefit swipe for cash at a discount. Both gain, so neither reports.">
        <defs>
          <marker id="fig-05-collusion-resistance-m" viewBox="0 0 10 10" refX="8.5" refY="5" markerUnits="userSpaceOnUse" markerWidth="10" markerHeight="10" orient="auto"><path d="M0,1 L9,5 L0,9 Z" class="f-fraud"/></marker>
        </defs>
        <g data-group="fake" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="s-ink">
          <circle cx="46" cy="30" r="12"/>
          <path d="M20,76 C20,56 32,48 46,48 C60,48 72,56 72,76"/>
          <path d="M308,40 H368 L362,26 H314 Z"/>
          <path d="M314,40 V76 H362 V40"/>
          <path d="M330,76 V58 H346 V76"/>
        </g>
        <g data-group="fake" fill="none" stroke-width="2.5" class="s-fraud">
          <path d="M90,42 H292" marker-end="url(#fig-05-collusion-resistance-m)"/>
          <path d="M292,66 H90" marker-end="url(#fig-05-collusion-resistance-m)"/>
        </g>
        <g data-group="fake" font-size="14" text-anchor="middle">
          <text x="191" y="34" class="f-ink" font-weight="600">benefit swipe</text>
          <text x="191" y="84" class="f-ink" font-weight="600">cash at a discount</text>
          <text x="46" y="96" class="f-ink" font-size="15" font-weight="700">Shopper</text>
          <text x="338" y="96" class="f-ink" font-size="15" font-weight="700">Cashier</text>
          <text x="338" y="115" class="f-muted">or owner</text>
          <text x="191" y="114" class="f-fraud" font-weight="600">both gain, so neither reports</text>
        </g>
      </svg>
    </div>

    <div class="cr-cols">
      <div data-group="fake" class="cr-col cr-col--fake" aria-label="Evidence the colluders control">
        <div class="cr-hd"><span class="chip chip--fraud">fakeable</span><h5>The pair writes it</h5></div>
        <ul>
          <li data-tip="The store keys any total. The network approves it and the deposit follows." data-group="fake"><div class="cr-row"><b>Amount keyed at the terminal</b><span class="cr-writer">store</span></div><p>The store keys any total. The network approves it and the deposit follows.</p></li>
          <li data-tip="Typed item lines can cover a $50 trafficking swipe." data-group="fake"><div class="cr-row"><b>Typed item lines</b><span class="cr-writer">store</span></div><p>"4 gallons of milk, 3 loaves of bread" can cover a $50 trafficking swipe.</p></li>
          <li data-tip="The shopper will confirm any receipt the store prints." data-group="fake"><div class="cr-row"><b>The receipt</b><span class="cr-writer">store, shopper</span></div><p>The shopper will confirm any receipt the store prints.</p></li>
          <li data-tip="A trafficking shopper never presses price looks wrong. Reports catch only predatory overcharging." data-group="fake"><div class="cr-row"><b>Cardholder app reports</b><span class="cr-writer">shopper</span></div><p>A trafficking shopper never presses "price looks wrong." Reports catch only predatory overcharging.</p></li>
        </ul>
      </div>

      <div class="cr-wall" aria-hidden="true"><span>the pair's reach ends here</span></div>

      <div data-group="real" class="cr-col cr-col--real" aria-label="Evidence neither party controls">
        <div class="cr-hd"><span class="chip chip--defense">independent</span><h5>Neither party writes it</h5></div>
        <ul>
          <li data-tip="A store cannot sell 400 cartons of eggs if suppliers delivered 50." data-group="real"><div class="cr-row"><b>Supplier invoices, distributor feeds</b><span class="cr-writer">supplier</span></div><p>A store cannot sell 400 cartons of eggs if suppliers delivered 50.</p></li>
          <li data-tip="200 cards that spend nearly all benefits at three small related stores." data-group="real"><div class="cr-row"><b>Patterns across many cards and stores</b><span class="cr-writer">network</span></div><p>200 cards that spend nearly all benefits at three small related stores.</p></li>
          <li data-tip="The stock an inspector sees on the shelf." data-group="real"><div class="cr-row"><b>Field inspector's shelf check</b><span class="cr-writer">inspector</span></div><p>The stock an inspector sees on the shelf.</p></li>
          <li data-tip="Payroll, wage, and tax records replace self-reported income." data-group="real"><div class="cr-row"><b>Outside income data</b><span class="cr-writer">employer, tax data</span></div><p>Payroll, wage, and tax records replace self-reported income.</p></li>
        </ul>
      </div>
    </div>

    <p class="cr-limit node node--caution"><span><strong class="t-caution">Limit:</strong> a store can buy stock for cash at a wholesale club, which leaves no record in a distributor feed. <strong class="t-caution">Fix:</strong> make purchase records a condition of authorization, backed by random and targeted audits.</span></p>
  </div>
  <figcaption><strong>Read it as:</strong> every item on the left comes from the shopper or the store, so a colluding pair can make it look normal. Every item on the right comes from someone outside the deal.</figcaption>
</figure>

<div class="callout callout-important" role="note"><p class="callout-label">important</p>
<p><strong>Defense in depth.</strong> No single control stops all fraud types. Chip cards stop cloning and do nothing about trafficking. Item data constrains overcharging and only raises the cost of trafficking. Each layer should assume the layers before it have failed, and each should produce data the analytics layer can use to catch what got through.</p></div>
<div class="callout callout-important" role="note"><p class="callout-label">important</p>
<p><strong>Economic framing.</strong> Integrity spending is worth doing only up to the point where the next dollar of control saves at least a dollar of loss, counting the costs that controls impose on honest participants. Trafficking of about $1 billion a year is a large number in absolute terms and a small share of a $100 billion program. A control that costs $600 million a year to prevent $300 million in trafficking is a bad trade.</p></div>
<div class="callout callout-important" role="note"><p class="callout-label">important</p>
<p><strong>Protect honest participants.</strong> False positives cut food to families. A wrongly disqualified store can leave a neighborhood with no place to use EBT. A wrongly flagged recipient can lose benefits during an appeal. Error costs fall on people with little cushion. Every automated action needs a reversible design, a fast appeal, and human review before any benefit cutoff.</p></div>
<h2 id="6-a-layered-program-and-software-architecture">6. A layered program and software architecture<a class="anchor" href="#6-a-layered-program-and-software-architecture" aria-label="Link to this section"></a></h2>
<p>The architecture has four transaction-path layers and one analytics layer that reads from all of them. Confirmed cases flow back into the analytics layer as labels (<a href="#fig-06-layered-architecture">Figure 9</a>).</p>
<figure class="fig" id="fig-06-layered-architecture">
  <div class="fig-head"><span class="eyebrow">Figure 9</span><h4>A layered program and software architecture</h4><p class="dek">Four layers act on each enrollment or sale; one analytics layer reads them all, because fraud is weak in one event and strong in a pattern.</p></div>
  <div class="fig-body">
    <div class="la" role="group" aria-label="Eligibility, credential, transaction, and merchant payout layers act in sequence. Each sends events to an analytics layer, which also reads third-party wholesale data. Analytics sends the top stores by expected loss to a budget-capped investigation queue and the rest above normal to automated actions. Closed cases return to analytics as labels.">
      <p class="la-band eyebrow">Transaction path: acts on each enrollment or sale</p>
      <div class="la-path">
        <div class="la-layer" data-group="l1" data-tip="Income match, a national duplicate check before first issuance, and household cross-checks stop eligibility fraud.">
          <div class="la-hd"><span class="la-n">1</span><h5>Eligibility</h5><svg class="la-ico" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="4" width="16" height="12" rx="2"/><circle cx="7" cy="9" r="2"/><path d="M4.5 13.5c.6-1.3 1.4-2 2.5-2s1.9.7 2.5 2M12 8h4M12 11h3"/></svg></div>
          <p class="la-role">Who gets benefits, and how much</p>
          <ul class="la-ctl">
            <li>Income match: payroll, wage, tax, bank</li>
            <li>National duplicate check (NAC) before first issuance, rechecked monthly</li>
            <li>Household composition cross-checks</li>
          </ul>
          <p class="la-stops"><span class="la-k">stops</span>Eligibility fraud, two-state enrollment</p>
          <p class="la-emit">sends analytics: match results</p>
        </div>
        <div class="la-step" data-group="l1 l2" aria-hidden="true"><svg viewBox="0 0 40 12" fill="currentColor"><path d="M0 5h32v2H0z"/><path d="M31 1l9 5-9 5z"/></svg><span>approved case</span></div>
        <div class="la-layer" data-group="l2" data-tip="Chip, tap, app lock, and per-swipe alerts stop card skimming and cloning.">
          <div class="la-hd"><span class="la-n">2</span><h5>Credential and card</h5><svg class="la-ico" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="4" width="16" height="12" rx="2"/><rect x="5" y="7" width="4" height="3" rx="0.5"/><path d="M5 13h6"/></svg></div>
          <p class="la-role">Who can spend a benefit</p>
          <ul class="la-ctl">
            <li>EMV chip and tap, phone wallet</li>
            <li>App lock, out-of-state and online blocks</li>
            <li>Per-swipe alerts, load-day step-up</li>
          </ul>
          <p class="la-stops"><span class="la-k">stops</span>Card skimming and cloning</p>
          <p class="la-emit">sends analytics: card events</p>
        </div>
        <div class="la-step" data-group="l2 l3" aria-hidden="true"><svg viewBox="0 0 40 12" fill="currentColor"><path d="M0 5h32v2H0z"/><path d="M31 1l9 5-9 5z"/></svg><span>verified card</span></div>
        <div class="la-layer" data-group="l3" data-tip="Item-level baskets and real-time rejects of ineligible items make trafficking costlier.">
          <div class="la-hd"><span class="la-n">3</span><h5>Transaction</h5><svg class="la-ico" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 2h10v16l-2-1.5-1.5 1.5-1.5-1.5L8.5 18 7 16.5 5 18z"/><path d="M8 6h4M8 9h4M8 12h2"/></svg></div>
          <p class="la-role">What each sale contains</p>
          <ul class="la-ctl">
            <li>Item-level basket: barcode, quantity, unit price</li>
            <li>Real-time reject of ineligible items</li>
            <li>Per-store product catalog</li>
          </ul>
          <p class="la-stops"><span class="la-k">stops</span>Ineligible sales; makes trafficking costlier</p>
          <p class="la-emit">sends analytics: baskets</p>
        </div>
        <div class="la-step" data-group="l3 l4" aria-hidden="true"><svg viewBox="0 0 40 12" fill="currentColor"><path d="M0 5h32v2H0z"/><path d="M31 1l9 5-9 5z"/></svg><span>approved sale</span></div>
        <div class="la-layer" data-group="l4" data-tip="Probation, risk-tiered delays, caps, and time-limited holds keep money recoverable.">
          <div class="la-hd"><span class="la-n">4</span><h5>Merchant payout</h5><svg class="la-ico" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2 7l8-4 8 4z"/><path d="M4.5 9v6M8.5 9v6M11.5 9v6M15.5 9v6M2 17.5h16"/></svg></div>
          <p class="la-role">When and how the store gets paid</p>
          <ul class="la-ctl">
            <li>Probation for new stores</li>
            <li>Risk-tiered payout delays and volume caps</li>
            <li>Time-limited holds, rolling reserve</li>
          </ul>
          <p class="la-stops"><span class="la-k">stops</span>Hit-and-run trafficking; keeps money recoverable</p>
          <p class="la-emit">sends analytics: payouts</p>
        </div>
      </div>

      <div class="la-feeds" aria-hidden="true">
        <span class="la-feed" data-group="l1"><svg viewBox="0 0 12 30" fill="currentColor"><path d="M5 0h2v22H5z"/><path d="M1 21l5 9 5-9z"/></svg>match results</span>
        <span class="la-feed" data-group="l2"><svg viewBox="0 0 12 30" fill="currentColor"><path d="M5 0h2v22H5z"/><path d="M1 21l5 9 5-9z"/></svg>card events</span>
        <span class="la-feed" data-group="l3"><svg viewBox="0 0 12 30" fill="currentColor"><path d="M5 0h2v22H5z"/><path d="M1 21l5 9 5-9z"/></svg>baskets</span>
        <span class="la-feed" data-group="l4"><svg viewBox="0 0 12 30" fill="currentColor"><path d="M5 0h2v22H5z"/><path d="M1 21l5 9 5-9z"/></svg>payouts</span>
      </div>
      <div class="la-down la-tofive" data-group="l1 l2 l3 l4 an" aria-hidden="true"><svg viewBox="0 0 12 30" fill="currentColor"><path d="M5 0h2v22H5z"/><path d="M1 21l5 9 5-9z"/></svg><span>all four layers send events</span></div>

      <div class="la-cycle">
        <div class="la-loop" data-group="closed an" aria-hidden="true"><span>labels: fraud or not fraud</span></div>

        <div class="la-analytics" data-group="an" data-tip="Acts on aggregates over time; scores stores, cards, and cases, and reads third-party wholesale data.">
          <div class="la-hd la-an-head"><span class="la-n">5</span><h5>Analytics layer</h5><svg class="la-ico" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 3v14h14"/><path d="M6 13l3-4 3 2 4-6"/></svg><p>Acts on aggregates over time. Scores stores, cards, and cases.</p></div>
          <div class="la-an-grid">
            <div class="la-method" data-group="an">
              <h6>Price anomaly score</h6>
              <p>Store-month prices against regional reference prices per barcode</p>
              <p class="la-stops"><span class="la-k">catches</span>Overcharging</p>
            </div>
            <div class="la-method" data-group="an">
              <h6>Inventory reconciliation</h6>
              <p>EBT sales per category against wholesale purchases</p>
              <p class="la-stops"><span class="la-k">catches</span>Fake baskets, trafficking</p>
            </div>
            <div class="la-method" data-group="an">
              <h6>Card-store graph</h6>
              <p>Clusters of cards at a few stores, shared owners, shared bank accounts</p>
              <p class="la-stops"><span class="la-k">catches</span>Organized rings</p>
            </div>
            <div class="la-in" data-group="an" aria-hidden="true"><svg viewBox="0 0 30 12" fill="currentColor"><path d="M8 5h22v2H8z"/><path d="M9 1L0 6l9 5z"/></svg></div>
            <div class="la-third" data-group="an">
              <span class="eyebrow">Third-party data</span>
              <b>Wholesale invoices</b>
              <b>Distributor feeds</b>
              <p>The stores do not write these.</p>
            </div>
          </div>
        </div>

        <div class="la-out">
          <div class="la-out-col">
            <div class="la-down" data-group="queue" aria-hidden="true"><svg viewBox="0 0 12 30" fill="currentColor"><path d="M5 0h2v22H5z"/><path d="M1 21l5 9 5-9z"/></svg><span>top N by expected loss</span></div>
            <div class="node node--defense la-queue" data-group="queue" data-tip="Capped by budget: investigator hours set N. A case gets review only if expected benefit beats cost.">
              <h5>Investigation queue <span class="chip chip--defense">people</span></h5>
              <p>Capped by budget: investigator hours set N.</p>
              <p>A case gets a review only if its expected benefit beats the review's cost.</p>
            </div>
            <div class="la-down" data-group="queue closed" aria-hidden="true"><svg viewBox="0 0 12 30" fill="currentColor"><path d="M5 0h2v22H5z"/><path d="M1 21l5 9 5-9z"/></svg><span>a person decides</span></div>
            <div class="node node--muted la-closed" data-group="closed" data-tip="Confirmed or cleared, both become labels; cleared cases teach the models which patterns are benign.">
              <h5>Closed cases</h5>
              <p>Confirmed or cleared: both become labels. Cleared cases teach the models which patterns are benign.</p>
              <p class="la-back"><svg viewBox="0 0 12 14" fill="currentColor" aria-hidden="true"><path d="M5 5h2v9H5z"/><path d="M1 6l5-6 5 6z"/></svg>labels retrain the scores in layer 5</p>
            </div>
          </div>
          <div class="la-out-col">
            <div class="la-down" data-group="auto" aria-hidden="true"><svg viewBox="0 0 12 30" fill="currentColor"><path d="M5 0h2v22H5z"/><path d="M1 21l5 9 5-9z"/></svg><span><span class="la-m">from layer 5: </span>above normal, below the cutoff</span></div>
            <div class="node la-auto" data-group="auto" data-tip="Warning letter, payout delay, volume cap. Reversible, with no investigator time.">
              <h5>Automated actions <span class="chip">software</span></h5>
              <p>Warning letter, payout delay, volume cap.</p>
              <p>Reversible. No investigator time.</p>
            </div>
          </div>
        </div>
      </div>

      <div class="node node--caution la-limits">
        <h5 class="t-caution">Limits built into the design</h5>
        <p>Item data raises the effort of trafficking, but a store can still key fake lines. Supplier data is what exposes them.</p>
        <p>Scores open cases and never cut benefits. A person decides, with notice and a hearing, and every automated action has a fast appeal.</p>
      </div>
    </div>
  </div>
  <figcaption><strong>Read it as:</strong> each layer assumes the one before it failed, and each feeds the analytics that catch what got through. People see only the top of a ranked list, and every closed case makes the next ranking better.</figcaption>
</figure>

<p>The transaction-path layers act on each enrollment or sale. The analytics layer acts on aggregates over time, because most fraud signals are weak in a single event and strong in a pattern. Figures <a href="#uml-sw-components">A5</a> to <a href="#uml-sw-domain-model">A7</a> in Appendix A model this architecture as UML software diagrams.</p>
<h3 id="61-eligibility-layer">6.1 Eligibility layer<a class="anchor" href="#61-eligibility-layer" aria-label="Link to this section"></a></h3>
<p>The eligibility layer decides who receives benefits and how much. Its job is to stop overpayments before the first payment, which is cheaper than recovering them later.</p>
<p><strong>Automated income matching.</strong> At application and at recertification, the system should query payroll data (such as commercial employment verification services and state new-hire registries), state wage records, federal tax data where the law allows, and bank account verification. A discrepancy above a tolerance triggers a request for documents instead of a denial. The goal is to replace self-reported income with verified income where a source exists, and to save caseworker time for cases where no source exists, such as informal or gig work.</p>
<p><strong>National duplicate-enrollment check.</strong> Every new application should check the NAC before the first issuance, and every active case should be rechecked on a schedule, such as monthly. A match pauses the second state's issuance and opens a resolution task with notice to the household. Many matches are benign, such as a family that moved and has not closed the old case. The software should treat a match as a question to resolve quickly, and it should close the old case when the household confirms the move.</p>
<p>The eligibility check runs at signup, at recertification, and on a schedule, and it settles every question before the first payment.</p>
<figure class="code"><figcaption>Pseudocode</figcaption><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">check_eligibility</span><span class="p">(</span><span class="n">application</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Verified income replaces self-reported income where a source exists.</span>
</span></span><span class="line"><span class="cl">    <span class="n">sources</span> <span class="o">=</span> <span class="p">[</span><span class="n">payroll_data</span><span class="p">,</span> <span class="n">new_hire_registry</span><span class="p">,</span> <span class="n">state_wage_records</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="n">federal_tax_data</span><span class="p">,</span>  <span class="c1"># only where the law allows</span>
</span></span><span class="line"><span class="cl">               <span class="n">bank_account_verification</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">verified</span> <span class="o">=</span> <span class="p">[</span><span class="n">s</span><span class="o">.</span><span class="n">income_for</span><span class="p">(</span><span class="n">application</span><span class="o">.</span><span class="n">household</span><span class="p">)</span> <span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">sources</span> <span class="k">if</span> <span class="n">s</span><span class="o">.</span><span class="n">covers</span><span class="p">(</span><span class="n">application</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="ow">not</span> <span class="n">verified</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">assign_caseworker</span><span class="p">(</span><span class="n">application</span><span class="p">)</span>  <span class="c1"># informal or gig work has no source</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">income</span> <span class="ow">in</span> <span class="n">verified</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nb">abs</span><span class="p">(</span><span class="n">income</span> <span class="o">-</span> <span class="n">application</span><span class="o">.</span><span class="n">reported_income</span><span class="p">)</span> <span class="o">&gt;</span> <span class="n">INCOME_TOLERANCE</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">request_documents</span><span class="p">(</span><span class="n">application</span><span class="p">)</span>  <span class="c1"># a mismatch asks, never denies</span>
</span></span><span class="line"><span class="cl">    <span class="k">match</span> <span class="o">=</span> <span class="n">NAC</span><span class="o">.</span><span class="n">find_active_case</span><span class="p">(</span><span class="n">application</span><span class="o">.</span><span class="n">household</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="k">match</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">pause_issuance</span><span class="p">(</span><span class="n">application</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">open_resolution_task</span><span class="p">(</span><span class="n">application</span><span class="p">,</span> <span class="k">match</span><span class="p">,</span> <span class="n">notify_household</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Most matches are moves; close the old case on confirmation.</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s2">&#34;pending_resolution&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">approve_first_payment</span><span class="p">(</span><span class="n">application</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">recheck_active_cases</span><span class="p">(</span><span class="n">active_cases</span><span class="p">):</span>  <span class="c1"># on a schedule, such as monthly</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="k">case</span> <span class="ow">in</span> <span class="n">active_cases</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">NAC</span><span class="o">.</span><span class="n">find_active_case</span><span class="p">(</span><span class="k">case</span><span class="o">.</span><span class="n">household</span><span class="p">,</span> <span class="n">excluding</span><span class="o">=</span><span class="k">case</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="n">pause_issuance</span><span class="p">(</span><span class="k">case</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">open_resolution_task</span><span class="p">(</span><span class="k">case</span><span class="p">,</span> <span class="n">notify_household</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span></span></span></code></pre></div></figure>
<p><strong>Household composition checks.</strong> Cross-checks against other benefit programs and state records can flag a member listed in two households. These checks catch fraud and honest error alike, so the response should be a request for clarification.</p>
<h3 id="62-credential-layer">6.2 Credential layer<a class="anchor" href="#62-credential-layer" aria-label="Link to this section"></a></h3>
<p>The credential layer controls who can spend a benefit. Its main target is theft from recipients.</p>
<p><strong>Chip and contactless cards.</strong> EMV chip cards defeat magnetic stripe skimming, because a cloned stripe or a captured one-time code cannot authorize a second purchase. Tap-to-pay reduces exposure to tampered card slots. California's rollout shows the main obstacle: retailers must upgrade terminals, and for a time most transactions still fell back to the stripe. The program should set a date after which stripe fallback is declined except at stores with an approved exception.</p>
<p><strong>Phone wallets.</strong> Mobile wallet credentials add device binding and biometric unlock. The 2018 Farm Bill authorized mobile payment pilots for SNAP. A wallet also gives the program a direct channel to the recipient.</p>
<p><strong>Recipient app controls.</strong> A recipient app should let the household lock the card between uses, block out-of-state and online transactions by default, and receive an alert for every swipe. Locks and geography blocks stop most cloned-card drains. Per-swipe alerts let a household report theft within minutes.</p>
<p><strong>Countering load-day drains.</strong> Skimming crews strike when the monthly benefit lands, because that is when the balance is highest. Two controls help. The first is to vary the load time within the day or to spread loads across days by case number, so a crew cannot predict the exact moment. The second is a step-up check for large spending soon after a load: if more than a set share of the balance is spent within a short window, at a store far from the recipient's usual stores, the system asks for confirmation in the app before approving. The check must fail open for recipients without phones, or it will cut off the households least able to absorb a delay (<a href="#fig-06-card-hardening">Figure 10</a>).</p>
<p>The credential check runs on every swipe and asks the household to confirm only a large spend soon after benefits load.</p>
<figure class="code"><figcaption>Pseudocode</figcaption><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">authorize_swipe</span><span class="p">(</span><span class="n">card</span><span class="p">,</span> <span class="n">txn</span><span class="p">,</span> <span class="n">settings</span><span class="p">,</span> <span class="n">now</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">send_swipe_alert</span><span class="p">(</span><span class="n">card</span><span class="o">.</span><span class="n">household</span><span class="p">,</span> <span class="n">txn</span><span class="p">)</span>  <span class="c1"># households report theft within minutes</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">txn</span><span class="o">.</span><span class="n">entry_mode</span> <span class="o">==</span> <span class="s2">&#34;stripe&#34;</span> <span class="ow">and</span> <span class="n">now</span> <span class="o">&gt;=</span> <span class="n">STRIPE_FALLBACK_DEADLINE</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="n">txn</span><span class="o">.</span><span class="n">store</span><span class="o">.</span><span class="n">has_approved_exception</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">decline</span><span class="p">(</span><span class="s2">&#34;STRIPE_FALLBACK_ENDED&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">settings</span><span class="o">.</span><span class="n">card_locked</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">decline</span><span class="p">(</span><span class="s2">&#34;CARD_LOCKED&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">txn</span><span class="o">.</span><span class="n">is_out_of_state</span> <span class="ow">and</span> <span class="n">settings</span><span class="o">.</span><span class="n">block_out_of_state</span><span class="p">:</span>  <span class="c1"># on by default</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">decline</span><span class="p">(</span><span class="s2">&#34;OUT_OF_STATE_BLOCKED&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">txn</span><span class="o">.</span><span class="n">is_online</span> <span class="ow">and</span> <span class="n">settings</span><span class="o">.</span><span class="n">block_online</span><span class="p">:</span>  <span class="c1"># on by default</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">decline</span><span class="p">(</span><span class="s2">&#34;ONLINE_BLOCKED&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">needs_step_up</span><span class="p">(</span><span class="n">card</span><span class="p">,</span> <span class="n">txn</span><span class="p">,</span> <span class="n">now</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">card</span><span class="o">.</span><span class="n">household</span><span class="o">.</span><span class="n">has_app</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="ow">not</span> <span class="n">confirm_in_app</span><span class="p">(</span><span class="n">card</span><span class="o">.</span><span class="n">household</span><span class="p">,</span> <span class="n">txn</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">                <span class="k">return</span> <span class="n">decline</span><span class="p">(</span><span class="s2">&#34;NOT_CONFIRMED&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># No phone means fail open, never a cutoff.</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">approve</span><span class="p">(</span><span class="n">txn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">needs_step_up</span><span class="p">(</span><span class="n">card</span><span class="p">,</span> <span class="n">txn</span><span class="p">,</span> <span class="n">now</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">soon_after_load</span> <span class="o">=</span> <span class="n">now</span> <span class="o">-</span> <span class="n">card</span><span class="o">.</span><span class="n">last_load_time</span> <span class="o">&lt;=</span> <span class="n">STEP_UP_WINDOW</span>
</span></span><span class="line"><span class="cl">    <span class="n">spent</span> <span class="o">=</span> <span class="n">card</span><span class="o">.</span><span class="n">spent_since_last_load</span> <span class="o">+</span> <span class="n">txn</span><span class="o">.</span><span class="n">amount</span>
</span></span><span class="line"><span class="cl">    <span class="n">large_share</span> <span class="o">=</span> <span class="n">spent</span> <span class="o">&gt;</span> <span class="n">STEP_UP_SHARE</span> <span class="o">*</span> <span class="n">card</span><span class="o">.</span><span class="n">balance_at_last_load</span>
</span></span><span class="line"><span class="cl">    <span class="n">far_away</span> <span class="o">=</span> <span class="n">distance</span><span class="p">(</span><span class="n">txn</span><span class="o">.</span><span class="n">store</span><span class="p">,</span> <span class="n">card</span><span class="o">.</span><span class="n">usual_stores</span><span class="p">)</span> <span class="o">&gt;</span> <span class="n">STEP_UP_DISTANCE</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">soon_after_load</span> <span class="ow">and</span> <span class="n">large_share</span> <span class="ow">and</span> <span class="n">far_away</span></span></span></code></pre></div></figure>
<figure class="fig" id="fig-06-card-hardening">
  <div class="fig-head"><span class="eyebrow">Figure 10</span><h4>Hardening the card: breaking the skimming path</h4><p class="dek">A skimming theft needs three steps to succeed, and each control breaks the chain at a different link.</p></div>
  <div class="fig-body">
    <div class="ch-grid" role="group" aria-label="Skimming attack path: skimmer copies the stripe and PIN, a clone card is made, the crew drains the account on load day, and the family finds a zero balance. Tap to pay and phone wallets break the copy. EMV chips and ending stripe fallback break the clone. Card locks, location and online blocks, varied load timing, and step-up checks break the drain. Per-swipe alerts shrink the loss.">
      <p class="ch-lane ch-lane--a eyebrow">Attack path</p>
      <p class="ch-lane ch-lane--c eyebrow">Controls, placed at the link they break</p>

      <div class="ch-atk ch-a1" data-group="s1" data-tip="A device on the card reader or PIN pad records the magnetic stripe and the PIN.">
        <div class="ch-hd"><span class="ch-n">1</span><h5>Skimmer</h5><svg class="ch-ico" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5" y="2" width="10" height="16" rx="2"/><path d="M5 7h10M8 11h1M11 11h1M8 14h1M11 14h1"/></svg></div>
        <p>A device on the card reader or PIN pad records the magnetic stripe and the PIN.</p>
      </div>
      <div class="ch-ctl ch-c1" data-group="s1" data-tip="Tap to pay and phone wallets break the copy: there is no stripe to copy.">
        <p class="ch-cut"><svg viewBox="0 0 26 12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M10 2H6a4 4 0 0 0 0 8h4M16 2h4a4 4 0 0 1 0 8h-4"/></svg>breaks the copy</p>
        <div class="ch-card"><h6>Tap to pay</h6><p>Fewer cards pass through a tampered slot.</p></div>
        <div class="ch-card"><h6>Phone wallet</h6><p>Device binding and biometric unlock. No stripe to copy. The 2018 Farm Bill authorized mobile pilots.</p></div>
      </div>
      <div class="ch-arrow ch-x1" data-group="s1" aria-hidden="true"><svg viewBox="0 0 40 12" fill="currentColor"><path d="M0 5h32v2H0z"/><path d="M31 1l9 5-9 5z"/></svg><span>stripe + PIN</span></div>

      <div class="ch-atk ch-a2" data-group="s2" data-tip="A stripe holds static data, so the copy works on every swipe.">
        <div class="ch-hd"><span class="ch-n">2</span><h5>Clone</h5><svg class="ch-ico" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="3" width="12" height="9" rx="1.5"/><rect x="6" y="8" width="12" height="9" rx="1.5"/><path d="M6 11.5h12"/></svg></div>
        <p>The data goes onto a blank card. A stripe holds static data, so the copy works on every swipe.</p>
      </div>
      <div class="ch-ctl ch-c2" data-group="s2" data-tip="The EMV chip makes a one-time code for each purchase, so copied data fails the second time.">
        <p class="ch-cut"><svg viewBox="0 0 26 12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M10 2H6a4 4 0 0 0 0 8h4M16 2h4a4 4 0 0 1 0 8h-4"/></svg>breaks the clone</p>
        <div class="ch-card"><h6>EMV chip</h6><p>A one-time code for each purchase: copied data fails the second time.</p></div>
        <div class="ch-card"><h6>End stripe fallback</h6><p>Decline stripe sales after a set date, except at stores with an approved exception.</p></div>
      </div>
      <div class="ch-arrow ch-x2" data-group="s2" aria-hidden="true"><svg viewBox="0 0 40 12" fill="currentColor"><path d="M0 5h32v2H0z"/><path d="M31 1l9 5-9 5z"/></svg><span>working copy</span></div>

      <div class="ch-atk ch-a3" data-group="s3" data-tip="The crew spends the balance within hours of the monthly load, when it is highest.">
        <div class="ch-hd"><span class="ch-n">3</span><h5>Drain on load day</h5><svg class="ch-ico" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="4" width="16" height="13" rx="2"/><path d="M2 8h16M6 2v4M14 2v4M7 12l6 0"/></svg></div>
        <p>The crew spends the balance within hours of the monthly load, when it is highest.</p>
      </div>
      <div class="ch-ctl ch-c3" data-group="s3" data-tip="Locks, out-of-state and online blocks, varied load timing, and step-up checks break the drain.">
        <p class="ch-cut"><svg viewBox="0 0 26 12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M10 2H6a4 4 0 0 0 0 8h4M16 2h4a4 4 0 0 1 0 8h-4"/></svg>breaks the drain</p>
        <div class="ch-card"><h6>App card lock, location and online blocks</h6><p>Lock between uses. Out-of-state and online sales blocked by default. Stops most cloned-card drains.</p></div>
        <div class="ch-card"><h6>Varied load timing, step-up check</h6><p>Spread loads by case number. A big spend soon after a load, far from usual stores, needs confirmation in the app.</p></div>
      </div>
      <div class="ch-arrow ch-x3" data-group="s3" aria-hidden="true"><svg viewBox="0 0 40 12" fill="currentColor"><path d="M0 5h32v2H0z"/><path d="M31 1l9 5-9 5z"/></svg><span>benefits</span></div>

      <div class="ch-atk ch-atk--end ch-a4" data-group="s4" data-tip="Federal replacement covered thefts only through Dec 20, 2024. Since then the family bears the loss.">
        <div class="ch-hd"><h5>Family finds a zero balance</h5></div>
        <p>Federal replacement covered thefts only through Dec 20, 2024. Since then the family bears the loss.</p>
      </div>
      <div class="ch-ctl ch-c4" data-group="s4" data-tip="Per-swipe alerts let the household report theft within minutes instead of at the checkout.">
        <p class="ch-cut"><svg viewBox="0 0 26 12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M10 2H6a4 4 0 0 0 0 8h4M16 2h4a4 4 0 0 1 0 8h-4"/></svg>shrinks the loss</p>
        <div class="ch-card"><h6>Per-swipe alerts</h6><p>An alert for every swipe. The household reports theft within minutes instead of at the checkout.</p></div>
      </div>
    </div>

    <p class="node node--caution ch-catch"><span><strong class="t-caution">Catch:</strong> in California's chip rollout, retailers had to upgrade terminals, and for a time most sales still fell back to the stripe. The step-up check must fail open for recipients without phones, or it cuts off the households least able to wait.</span></p>
  </div>
  <figcaption><strong>Read it as:</strong> the chip kills the clone at link 2, so it does the most work. Locks, blocks, and alerts catch whatever still gets through at link 3.</figcaption>
</figure>

<h3 id="63-transaction-layer">6.3 Transaction layer<a class="anchor" href="#63-transaction-layer" aria-label="Link to this section"></a></h3>
<p>The transaction layer is where the program can close the core weakness: the payment record with no item list.</p>
<p><strong>Mandatory item-level basket data.</strong> Each EBT sale should carry a line for each item: the barcode (UPC or PLU), quantity, unit price, and SNAP-eligible flag. FNS's 2016 feasibility study showed that item-level data can be captured from integrated electronic cash registers and matched to EBT transaction records at above 99 percent accuracy using card number, date, store, and amount. The new standard would carry the basket in the authorization message itself or in a linked message sent within seconds.</p>
<p><strong>Real-time rules.</strong> With item data in the message, the processor can reject a sale that includes ineligible items before it completes. It can also reject or flag lines for products the store is not known to stock. The program would maintain a per-store product catalog, seeded from the store's own price list at authorization and from wholesale records over time. A convenience store that suddenly sells 30 pounds of beef in one basket, a product it has never bought from a supplier, would see the line flagged.</p>
<p>The processor validates the itemized basket inside the authorization and declines a bad one at the register with a reason code.</p>
<figure class="code"><figcaption>Pseudocode</figcaption><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">validate_basket</span><span class="p">(</span><span class="n">txn</span><span class="p">,</span> <span class="n">products</span><span class="p">,</span> <span class="n">store_catalog</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">total</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">txn</span><span class="o">.</span><span class="n">basket_lines</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">product</span> <span class="o">=</span> <span class="n">products</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">line</span><span class="o">.</span><span class="n">upc</span><span class="p">)</span>  <span class="c1"># UPC or PLU</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">product</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">reject</span><span class="p">(</span><span class="n">txn</span><span class="p">,</span> <span class="s2">&#34;UNKNOWN_BARCODE&#34;</span><span class="p">,</span> <span class="n">line</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="n">product</span><span class="o">.</span><span class="n">snap_eligible</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">reject</span><span class="p">(</span><span class="n">txn</span><span class="p">,</span> <span class="s2">&#34;INELIGIBLE_ITEM&#34;</span><span class="p">,</span> <span class="n">line</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Catalog seeded from price list, then supplier records.</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">line</span><span class="o">.</span><span class="n">upc</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">store_catalog</span><span class="p">[</span><span class="n">txn</span><span class="o">.</span><span class="n">store</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">reject</span><span class="p">(</span><span class="n">txn</span><span class="p">,</span> <span class="s2">&#34;NOT_STOCKED&#34;</span><span class="p">,</span> <span class="n">line</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">total</span> <span class="o">+=</span> <span class="n">line</span><span class="o">.</span><span class="n">quantity</span> <span class="o">*</span> <span class="n">line</span><span class="o">.</span><span class="n">unit_price</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">total</span> <span class="o">!=</span> <span class="n">txn</span><span class="o">.</span><span class="n">amount</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">reject</span><span class="p">(</span><span class="n">txn</span><span class="p">,</span> <span class="s2">&#34;TOTAL_MISMATCH&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">accept</span><span class="p">(</span><span class="n">txn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">reject</span><span class="p">(</span><span class="n">txn</span><span class="p">,</span> <span class="n">reason_code</span><span class="p">,</span> <span class="n">line</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Repeated unstocked lines are a signal for the monthly score.</span>
</span></span><span class="line"><span class="cl">    <span class="n">report_to_analytics</span><span class="p">(</span><span class="n">txn</span><span class="o">.</span><span class="n">store</span><span class="p">,</span> <span class="n">reason_code</span><span class="p">,</span> <span class="n">line</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">Decline</span><span class="p">(</span><span class="n">txn</span><span class="p">,</span> <span class="n">reason_code</span><span class="p">)</span></span></span></code></pre></div></figure>
<div class="callout callout-caution" role="note"><p class="callout-label">caution</p>
<p><strong>The limit.</strong> A corrupt store can type fake item lines. It can key &quot;4 gallons of milk, 3 loaves of bread, 2 pounds of chicken&quot; to cover a $50 trafficking swipe. Item data therefore raises the effort of trafficking and gives analytics more to work with. It does not end the scheme on its own. Its value is that fake baskets must be consistent with the store's purchases from its suppliers, which the store does not control. That link is what the analytics layer exploits.</p></div>
<p><strong>Small-store capacity.</strong> Many small stores do not run integrated scanning registers. The standard needs a low-cost path, such as a subsidized, certified tablet point-of-sale app, or the rollout will drive stores out of the program.</p>
<h3 id="64-merchant-payout-layer">6.4 Merchant payout layer<a class="anchor" href="#64-merchant-payout-layer" aria-label="Link to this section"></a></h3>
<p>Card networks and payment processors face the same problem SNAP faces: some merchants are fronts for fraud. Processors such as Stripe and PayPal manage that risk at payout time. The SNAP payout layer should adopt the same tools.</p>
<p><strong>Probation for new stores.</strong> A newly authorized store, or a store under new ownership, starts in a probation tier. It receives payouts on a longer delay, faces a monthly volume cap set from its stated size and stock, and gets an early field visit. Trafficking stores often show high volume soon after authorization, so probation targets the riskiest period.</p>
<p><strong>Risk-tiered delays and caps.</strong> Every store sits in a tier set by its analytics score. Low-risk stores get the standard settlement time. Higher tiers get longer settlement delays and volume caps. A delay of a week or two does not stop an honest store from operating. It does let the program withhold funds when a case opens, which makes recovery possible (<a href="#fig-06-merchant-risk-tiers">Figure 11</a>).</p>
<figure class="fig" id="fig-06-merchant-risk-tiers">
  <div class="fig-head"><span class="eyebrow">Figure 11</span><h4>The merchant risk ladder</h4><p class="dek">Like a card processor, the program pays clean stores fast and slows, caps, or holds money as a store's risk score climbs.</p></div>
  <div class="fig-body">
    <div class="mr" role="group" aria-label="New stores start in probation and move to normal with a clean history or to elevated if the score spikes. Normal stores get standard settlement. Elevated stores get longer delays and volume caps. High-risk stores have part of their payouts held pending review. Disqualified stores, confirmed by investigation, are paid nothing and the reserve funds recovery. Stores move back down as scores fall or reviews clear.">
      <div class="mr-entry">
        <span class="eyebrow">Entry: new store or new owner</span>
        <div class="mr-rung mr-rung--prob" data-group="prob" data-tip="New stores get a longer payout delay and a monthly volume cap set from stated size and stock.">
          <div class="mr-tier"><h5>Probation</h5><span class="chip chip--caution">delayed and capped</span></div>
          <ul class="mr-what">
            <li>Longer payout delay</li>
            <li>Monthly volume cap set from stated size and stock</li>
            <li>Early field visit</li>
          </ul>
          <p class="mr-why">Trafficking stores often show high volume soon after authorization, so probation covers the riskiest period.</p>
        </div>
        <p class="mr-exit mr-exit--good" data-group="prob norm"><svg viewBox="0 0 12 12" aria-hidden="true"><path d="M1 3h10L6 10z" class="f-defense"/></svg>clean history: to Normal</p>
        <p class="mr-exit mr-exit--bad" data-group="prob elev high"><svg viewBox="0 0 12 12" aria-hidden="true"><path d="M1 3h10L6 10z" class="f-fraud"/></svg>score spikes: to Elevated or High risk</p>
      </div>

      <div class="mr-ladder">
        <div class="mr-scale" aria-hidden="true"><span>low risk</span><i></i><span>high risk</span></div>
        <ol>
          <li class="mr-rung mr-rung--1" data-group="norm" data-tip="Standard settlement time; software updates the score every month.">
            <div class="mr-tier"><h5>Normal</h5><span class="chip chip--defense">standard settlement</span></div>
            <ul class="mr-what"><li>Standard settlement time</li><li>Score keeps updating every month</li></ul>
            <p class="mr-who"><span>who decides</span>Software: the monthly score</p>
          </li>
          <li class="mr-move" data-group="norm elev"><span class="mr-dn"><svg viewBox="0 0 12 12" aria-hidden="true"><path d="M1 3h10L6 10z" class="f-fraud"/></svg>score crosses tier threshold</span><span class="mr-up"><svg viewBox="0 0 12 12" aria-hidden="true"><path d="M1 9h10L6 2z" class="f-defense"/></svg>score falls, history clean</span></li>
          <li class="mr-rung mr-rung--2" data-group="elev" data-tip="Longer settlement delay and a volume cap; software acts with no investigator time.">
            <div class="mr-tier"><h5>Elevated</h5><span class="chip chip--caution">delayed and capped</span></div>
            <ul class="mr-what"><li>Longer settlement delay and a volume cap</li><li>Warning letter: prices sit well above peers</li></ul>
            <p class="mr-who"><span>who decides</span>Software, no investigator time</p>
          </li>
          <li class="mr-move" data-group="elev high"><span class="mr-dn"><svg viewBox="0 0 12 12" aria-hidden="true"><path d="M1 3h10L6 10z" class="f-fraud"/></svg>score crosses high threshold</span><span class="mr-up"><svg viewBox="0 0 12 12" aria-hidden="true"><path d="M1 9h10L6 2z" class="f-defense"/></svg>review clears, hold released</span></li>
          <li class="mr-rung mr-rung--3" data-group="high" data-tip="Part of payouts held pending review; the hold has a time limit and a rolling reserve is kept.">
            <div class="mr-tier"><h5>High risk</h5><span class="chip chip--fraud">partly held</span></div>
            <ul class="mr-what"><li>Part of payouts held pending review</li><li>Hold has a time limit; rolling reserve kept</li></ul>
            <p class="mr-who"><span>who decides</span>Software holds; a person reviews</p>
          </li>
          <li class="mr-move" data-group="high dq"><span class="mr-dn"><svg viewBox="0 0 12 12" aria-hidden="true"><path d="M1 3h10L6 10z" class="f-fraud"/></svg>investigation confirms trafficking</span></li>
          <li class="mr-rung mr-rung--4" data-group="dq" data-tip="Permanent for trafficking under 7 CFR 278.6(e)(1); the reserve funds recovery.">
            <div class="mr-tier"><h5>Disqualified</h5><span class="chip chip--fraud">stopped</span></div>
            <ul class="mr-what"><li>Permanent for trafficking, 7 CFR 278.6(e)(1)</li><li>Reserve funds recovery; a civil money penalty option exists</li></ul>
            <p class="mr-who"><span>who decides</span>Investigator, with notice and appeal</p>
          </li>
        </ol>
      </div>
    </div>

    <p class="node node--caution mr-guard"><span><strong class="t-caution">Guardrails:</strong> a hold without a time limit and a release path is a penalty without process. Publish appeal timelines, track reversals, and pay interest on wrongful holds. Before disqualifying the last authorized store in an area, run an access test, with a civil money penalty as the alternative.</span></p>
  </div>
  <figcaption><strong>Read it as:</strong> software moves a store up and down the first three rungs with no investigator time. Only a person can disqualify, and a week or two of delay leaves money to recover when a case opens.</figcaption>
</figure>

<p><strong>Holds pending review.</strong> When a store's score crosses a high threshold, the program can hold part of its payouts until review. A hold of this kind must have a time limit and a clear path to release, or it becomes a penalty without process.</p>
<p><strong>Recovery reserve.</strong> For high-tier stores, a rolling reserve (a share of payouts held for a period) gives the program a fund to recover from after a disqualification.</p>
<p>The payout layer pays clean stores on the standard schedule and slows, caps, or holds money as a store's tier climbs.</p>
<figure class="code"><figcaption>Pseudocode</figcaption><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">payout_treatment</span><span class="p">(</span><span class="n">store</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">store</span><span class="o">.</span><span class="n">tier</span> <span class="o">==</span> <span class="s2">&#34;disqualified&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">Action</span><span class="p">(</span><span class="n">pay</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">recover_from</span><span class="o">=</span><span class="n">store</span><span class="o">.</span><span class="n">reserve</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">store</span><span class="o">.</span><span class="n">tier</span> <span class="o">==</span> <span class="s2">&#34;probation&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">Action</span><span class="p">(</span><span class="n">delay</span><span class="o">=</span><span class="n">PROBATION_DELAY</span><span class="p">,</span> <span class="n">field_visit</span><span class="o">=</span><span class="s2">&#34;early&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                      <span class="n">monthly_cap</span><span class="o">=</span><span class="n">cap_from</span><span class="p">(</span><span class="n">store</span><span class="o">.</span><span class="n">stated_size</span><span class="p">,</span> <span class="n">store</span><span class="o">.</span><span class="n">stated_stock</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">store</span><span class="o">.</span><span class="n">tier</span> <span class="o">==</span> <span class="s2">&#34;normal&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">Action</span><span class="p">(</span><span class="n">delay</span><span class="o">=</span><span class="n">STANDARD_SETTLEMENT</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">store</span><span class="o">.</span><span class="n">tier</span> <span class="o">==</span> <span class="s2">&#34;elevated&#34;</span><span class="p">:</span>  <span class="c1"># a delay of a week or two</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">Action</span><span class="p">(</span><span class="n">delay</span><span class="o">=</span><span class="n">ELEVATED_DELAY</span><span class="p">,</span> <span class="n">monthly_cap</span><span class="o">=</span><span class="n">ELEVATED_CAP</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">store</span><span class="o">.</span><span class="n">tier</span> <span class="o">==</span> <span class="s2">&#34;high_risk&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">Action</span><span class="p">(</span><span class="n">delay</span><span class="o">=</span><span class="n">HIGH_RISK_DELAY</span><span class="p">,</span> <span class="n">monthly_cap</span><span class="o">=</span><span class="n">HIGH_RISK_CAP</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                      <span class="n">hold_share</span><span class="o">=</span><span class="n">HOLD_SHARE</span><span class="p">,</span>  <span class="c1"># hold until review</span>
</span></span><span class="line"><span class="cl">                      <span class="n">hold_expires</span><span class="o">=</span><span class="n">HOLD_TIME_LIMIT</span><span class="p">,</span>  <span class="c1"># needs a release path</span>
</span></span><span class="line"><span class="cl">                      <span class="n">reserve_share</span><span class="o">=</span><span class="n">RESERVE_SHARE</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">next_tier</span><span class="p">(</span><span class="n">store</span><span class="p">,</span> <span class="n">score</span><span class="p">,</span> <span class="n">today</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">store</span><span class="o">.</span><span class="k">case</span> <span class="ow">and</span> <span class="n">store</span><span class="o">.</span><span class="k">case</span><span class="o">.</span><span class="n">decision</span> <span class="o">==</span> <span class="s2">&#34;disqualify&#34;</span><span class="p">:</span>  <span class="c1"># a person decides</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s2">&#34;disqualified&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">today</span> <span class="o">-</span> <span class="n">store</span><span class="o">.</span><span class="n">authorized_or_new_owner_on</span> <span class="o">&lt;</span> <span class="n">PROBATION_PERIOD</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s2">&#34;probation&#34;</span>  <span class="c1"># trafficking stores spike soon after authorization</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">score</span> <span class="o">&gt;=</span> <span class="n">HIGH_RISK_THRESHOLD</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s2">&#34;high_risk&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="s2">&#34;elevated&#34;</span> <span class="k">if</span> <span class="n">score</span> <span class="o">&gt;=</span> <span class="n">ELEVATED_THRESHOLD</span> <span class="k">else</span> <span class="s2">&#34;normal&#34;</span></span></span></code></pre></div></figure>
<h3 id="65-analytics-layer">6.5 Analytics layer<a class="anchor" href="#65-analytics-layer" aria-label="Link to this section"></a></h3>
<p>The analytics layer reads from all four transaction-path layers and from third-party sources. It produces store, card, and case scores, and it feeds the investigation queue. Two methods matter most beyond price anomaly detection, which Section 7 covers.</p>
<p><strong>Inventory reconciliation.</strong> Compare EBT sales per product category against the store's wholesale purchases of that category. A store cannot sell 400 cartons of eggs in a month if its suppliers delivered 50. Purchase data can come from supplier invoices that the store submits and from data feeds provided by large distributors under agreement. Without item data, reconciliation works at the level of total food purchases against total EBT redemptions. With item data, it works per product. The limit is that a store can buy stock for cash at a wholesale club, which leaves no supplier record in a distributor feed. The program should make purchase records a condition of authorization: a store must keep invoices or receipts for food it sells and submit them on request. Random audits of a sample of stores each year, plus targeted audits of high scorers, keep the requirement credible. A store that cannot show purchases to support its sales has a problem regardless of what its baskets say (<a href="#fig-06-inventory-reconciliation">Figure 12</a>).</p>
<p>Reconciliation compares what a store sold through EBT with what its suppliers delivered, evidence the store does not write.</p>
<figure class="code"><figcaption>Pseudocode</figcaption><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">reconcile_inventory</span><span class="p">(</span><span class="n">store</span><span class="p">,</span> <span class="n">month</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Wholesale-club cash buys leave no distributor feed record.</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="ow">not</span> <span class="n">store</span><span class="o">.</span><span class="n">purchase_records_submitted</span><span class="p">(</span><span class="n">month</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">request_purchase_records</span><span class="p">(</span><span class="n">store</span><span class="p">,</span> <span class="n">month</span><span class="p">)</span>  <span class="c1"># a condition of authorization</span>
</span></span><span class="line"><span class="cl">    <span class="n">invoices</span> <span class="o">=</span> <span class="n">distributor_feeds</span><span class="o">.</span><span class="n">invoices</span><span class="p">(</span><span class="n">store</span><span class="p">,</span> <span class="n">month</span><span class="p">)</span> <span class="o">+</span> <span class="n">store</span><span class="o">.</span><span class="n">submitted_invoices</span><span class="p">(</span><span class="n">month</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="ow">not</span> <span class="n">store</span><span class="o">.</span><span class="n">has_item_data</span><span class="p">(</span><span class="n">month</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">sold</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">t</span><span class="o">.</span><span class="n">amount</span> <span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">store</span><span class="o">.</span><span class="n">ebt_transactions</span><span class="p">(</span><span class="n">month</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="n">bought</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">i</span><span class="o">.</span><span class="n">food_total</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">invoices</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">[</span><span class="n">Flag</span><span class="p">(</span><span class="n">store</span><span class="p">,</span> <span class="n">month</span><span class="p">,</span> <span class="s2">&#34;ALL_FOOD&#34;</span><span class="p">)]</span> <span class="k">if</span> <span class="n">sold</span> <span class="o">-</span> <span class="n">bought</span> <span class="o">&gt;</span> <span class="n">TOTAL_TOLERANCE</span> <span class="k">else</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">    <span class="n">delivered</span> <span class="o">=</span> <span class="n">units_by_upc</span><span class="p">(</span><span class="n">line</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">invoices</span> <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">i</span><span class="o">.</span><span class="n">invoice_lines</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">sold</span> <span class="o">=</span> <span class="n">units_by_upc</span><span class="p">(</span><span class="n">line</span> <span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">store</span><span class="o">.</span><span class="n">ebt_transactions</span><span class="p">(</span><span class="n">month</span><span class="p">)</span> <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">t</span><span class="o">.</span><span class="n">basket_lines</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">flags</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">upc</span><span class="p">,</span> <span class="n">units_sold</span> <span class="ow">in</span> <span class="n">sold</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="n">gap</span> <span class="o">=</span> <span class="n">units_sold</span> <span class="o">-</span> <span class="n">delivered</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">upc</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">gap</span> <span class="o">&gt;</span> <span class="n">UNIT_TOLERANCE</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">flags</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">Flag</span><span class="p">(</span><span class="n">store</span><span class="p">,</span> <span class="n">month</span><span class="p">,</span> <span class="n">upc</span><span class="p">,</span> <span class="n">gap</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">flags</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Report example: 400 cartons of eggs sold, 50 delivered.</span></span></span></code></pre></div></figure>
<figure class="fig" id="fig-06-inventory-reconciliation">
  <div class="fig-head"><span class="eyebrow">Figure 12</span><h4>Sales need supply</h4><p class="dek">EBT sales of a product cannot outrun what suppliers delivered, so a growing gap points to fake or inflated baskets.</p></div>
  <div class="fig-body">

    <div style="display:flex;flex-wrap:wrap;gap:1.25rem;align-items:flex-start">
      <div style="flex:2 1 420px;min-width:0">
        <svg viewBox="0 0 700 310" role="img" aria-label="Illustrative store, eggs: EBT sales climb from 60 to 400 cartons a month while supplier deliveries stay at 50, so the unexplained gap grows to 350 cartons."><text x="30" y="22" class="f-muted" font-size="11">cartons of eggs per month</text><line x1="70" x2="580" y1="270" y2="270" class="s-rule" stroke-width="1"/><text x="60" y="274" class="f-muted mono" font-size="11" text-anchor="end">0</text><line x1="70" x2="580" y1="212.5" y2="212.5" class="s-rule" stroke-width="1"/><text x="60" y="216.5" class="f-muted mono" font-size="11" text-anchor="end">100</text><line x1="70" x2="580" y1="155" y2="155" class="s-rule" stroke-width="1"/><text x="60" y="159" class="f-muted mono" font-size="11" text-anchor="end">200</text><line x1="70" x2="580" y1="97.5" y2="97.5" class="s-rule" stroke-width="1"/><text x="60" y="101.5" class="f-muted mono" font-size="11" text-anchor="end">300</text><line x1="70" x2="580" y1="40.00000000000003" y2="40.00000000000003" class="s-rule" stroke-width="1"/><text x="60" y="44" class="f-muted mono" font-size="11" text-anchor="end">400</text><polygon data-tip="Unexplained gap: by month 6, 350 cartons sold with no supply behind them." data-group="gap" points="80,235.5 176,206.8 272,166.5 368,126.3 464,86 560,40 560,241.3 464,241.3 368,241.3 272,241.3 176,241.3 80,241.3" class="f-fraud-tint"/><polyline data-tip="Supplier deliveries stay flat at 50 cartons a month." data-group="supplier-deliveries" data-animate="draw" data-step="0" points="80,241.3 176,241.3 272,241.3 368,241.3 464,241.3 560,241.3" fill="none" class="s-defense" stroke-width="2.5"/><polyline data-tip="EBT units sold climb from 60 to 400 cartons a month." data-group="ebt-sales" data-animate="draw" data-step="0" points="80,235.5 176,206.8 272,166.5 368,126.3 464,86 560,40" fill="none" class="s-fraud" stroke-width="2.5"/><circle data-tip="Month 1: 50 cartons delivered by suppliers." data-group="supplier-deliveries" data-animate="pop" data-step="1" cx="80" cy="241.3" r="3.5" class="f-defense"/><circle data-tip="Month 1: 60 cartons of EBT sales against 50 delivered." data-group="ebt-sales" data-animate="pop" data-step="1" cx="80" cy="235.5" r="3.5" class="f-fraud"/><text x="80" y="292" class="f-muted mono" font-size="11" text-anchor="middle">Month 1</text><circle data-tip="Month 2: 50 cartons delivered by suppliers." data-group="supplier-deliveries" data-animate="pop" data-step="2" cx="176" cy="241.3" r="3.5" class="f-defense"/><circle data-tip="Month 2: EBT sales keep climbing while deliveries stay at 50 cartons." data-group="ebt-sales" data-animate="pop" data-step="2" cx="176" cy="206.8" r="3.5" class="f-fraud"/><text x="176" y="292" class="f-muted mono" font-size="11" text-anchor="middle">Month 2</text><circle data-tip="Month 3: 50 cartons delivered by suppliers." data-group="supplier-deliveries" data-animate="pop" data-step="3" cx="272" cy="241.3" r="3.5" class="f-defense"/><circle data-tip="Month 3: EBT sales keep climbing while deliveries stay at 50 cartons." data-group="ebt-sales" data-animate="pop" data-step="3" cx="272" cy="166.5" r="3.5" class="f-fraud"/><text x="272" y="292" class="f-muted mono" font-size="11" text-anchor="middle">Month 3</text><circle data-tip="Month 4: 50 cartons delivered by suppliers." data-group="supplier-deliveries" data-animate="pop" data-step="4" cx="368" cy="241.3" r="3.5" class="f-defense"/><circle data-tip="Month 4: EBT sales keep climbing while deliveries stay at 50 cartons." data-group="ebt-sales" data-animate="pop" data-step="4" cx="368" cy="126.3" r="3.5" class="f-fraud"/><text x="368" y="292" class="f-muted mono" font-size="11" text-anchor="middle">Month 4</text><circle data-tip="Month 5: 50 cartons delivered by suppliers." data-group="supplier-deliveries" data-animate="pop" data-step="5" cx="464" cy="241.3" r="3.5" class="f-defense"/><circle data-tip="Month 5: EBT sales keep climbing while deliveries stay at 50 cartons." data-group="ebt-sales" data-animate="pop" data-step="5" cx="464" cy="86" r="3.5" class="f-fraud"/><text x="464" y="292" class="f-muted mono" font-size="11" text-anchor="middle">Month 5</text><circle data-tip="Month 6: 50 cartons delivered by suppliers." data-group="supplier-deliveries" data-animate="pop" data-step="6" cx="560" cy="241.3" r="5.5" class="f-defense"/><circle data-group="ebt-sales" data-animate="pop" data-step="6" cx="560" cy="40" r="11" class="f-fraud" fill-opacity="0.18"/><circle data-tip="Month 6: 400 cartons of EBT sales against 50 delivered, a gap of 350." data-group="ebt-sales" data-animate="pop" data-step="6" cx="560" cy="40" r="5.5" class="f-fraud"/><text x="560" y="292" class="f-muted mono" font-size="11" text-anchor="middle">Month 6</text><text data-group="ebt-sales" x="578" y="45" class="f-fraud mono" font-size="15" font-weight="600">400</text><text data-group="ebt-sales" x="578" y="61" class="f-ink" font-size="11">EBT units sold</text><text data-group="supplier-deliveries" x="578" y="238.3" class="f-defense mono" font-size="15" font-weight="600">50</text><text data-group="supplier-deliveries" x="578" y="254.3" class="f-ink" font-size="11">delivered by</text><text data-group="supplier-deliveries" x="578" y="268.3" class="f-ink" font-size="11">suppliers</text><line data-group="gap" x1="580" x2="580" y1="70" y2="219.3" class="s-fraud" stroke-width="1.5" stroke-dasharray="3 3"/><text data-group="gap" x="468" y="190" class="f-fraud" font-size="13" font-weight="700" text-anchor="middle">Gap: 350 cartons</text><text data-group="gap" x="468" y="206" class="f-fraud" font-size="11" text-anchor="middle">sold with no supply behind them</text></svg>
        <p style="margin:0.5rem 0 0;font-size:0.85rem;color:var(--muted)"><span class="chip chip--caution">Illustrative</span> Month 6 matches the report's example: 400 sold, 50 delivered. Months 1 to 5 are invented to show the trend.</p>
      </div>
      <div style="flex:1 1 240px;display:flex;flex-direction:column;gap:0.75rem">
        <div class="node node--defense"><h5 class="t-defense">The check</h5><p>EBT sales per product category against the store's wholesale purchases of that category, from supplier invoices and distributor feeds.</p></div>
        <div class="node node--caution"><h5 class="t-caution">Caveat: cash buys</h5><p>A store can buy stock for cash at a wholesale club. That leaves no record in a distributor feed. So authorization requires:</p>
          <p><span class="num t-caution">1</span> purchase records, kept and submitted on request<br><span class="num t-caution">2</span> random audits of a sample of stores each year<br><span class="num t-caution">3</span> targeted audits of high scorers</p></div>
      </div>
    </div>
  </div>
  <figcaption><strong>Read it as:</strong> the red band is EBT sales with no recorded supply behind them. A store that cannot show purchases to support its sales has a problem, whatever its baskets look like.</figcaption>
</figure>

<p><strong>Network and graph analysis.</strong> Build a graph with cards and stores as nodes and transactions as edges. Trafficking rings leave clusters: groups of cards that all visit the same few suspicious stores, often traveling past closer and larger stores to do so. A single-transaction rule sees a normal-looking $60 sale. The graph sees 200 cards that spend nearly all their benefits at three small stores owned by related parties. Community detection, shared-owner links from authorization records, and shared bank accounts on payout records together reveal rings that no store-level score would. The same graph helps on the recipient side: a card that appears in many confirmed trafficking cases is a candidate for a state investigation with evidence beyond a single transaction pattern (<a href="#fig-06-ring-detection">Figure 13</a>).</p>
<p>The ring finder builds a card to store graph and flags groups of cards whose spending piles into the same few stores.</p>
<figure class="code"><figcaption>Pseudocode</figcaption><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">find_rings</span><span class="p">(</span><span class="n">transactions</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">graph</span> <span class="o">=</span> <span class="n">Graph</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">txn</span> <span class="ow">in</span> <span class="n">transactions</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">graph</span><span class="o">.</span><span class="n">add_edge</span><span class="p">(</span><span class="n">txn</span><span class="o">.</span><span class="n">card</span><span class="p">,</span> <span class="n">txn</span><span class="o">.</span><span class="n">store</span><span class="p">,</span> <span class="n">weight</span><span class="o">=</span><span class="n">txn</span><span class="o">.</span><span class="n">amount</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">rings</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">cluster</span> <span class="ow">in</span> <span class="n">community_detection</span><span class="p">(</span><span class="n">graph</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">stores</span> <span class="o">=</span> <span class="n">cluster</span><span class="o">.</span><span class="n">stores_by_spend</span><span class="p">()[:</span><span class="n">FEW_STORES</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="n">cards</span> <span class="o">=</span> <span class="p">[</span><span class="n">c</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">cluster</span><span class="o">.</span><span class="n">cards</span>
</span></span><span class="line"><span class="cl">                 <span class="k">if</span> <span class="n">graph</span><span class="o">.</span><span class="n">spend_share</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">stores</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="n">CONCENTRATION_SHARE</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">cards</span><span class="p">)</span> <span class="o">&lt;</span> <span class="n">MIN_RING_CARDS</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">continue</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Rings travel past closer and larger stores.</span>
</span></span><span class="line"><span class="cl">        <span class="n">skipped</span> <span class="o">=</span> <span class="n">count_cards_passing_closer_larger_stores</span><span class="p">(</span><span class="n">cards</span><span class="p">,</span> <span class="n">stores</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">linked</span> <span class="o">=</span> <span class="n">shared_owners</span><span class="p">(</span><span class="n">stores</span><span class="p">)</span> <span class="ow">or</span> <span class="n">shared_payout_accounts</span><span class="p">(</span><span class="n">stores</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">rings</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">Ring</span><span class="p">(</span><span class="n">stores</span><span class="p">,</span> <span class="n">cards</span><span class="p">,</span> <span class="n">skipped</span><span class="p">,</span> <span class="n">linked</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">rings</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">recipient_leads</span><span class="p">(</span><span class="n">graph</span><span class="p">,</span> <span class="n">confirmed_cases</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Evidence beyond one transaction pattern, for state investigators.</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">[</span><span class="n">c</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">graph</span><span class="o">.</span><span class="n">cards</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">confirmed_cases</span><span class="o">.</span><span class="n">count_with</span><span class="p">(</span><span class="n">c</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="n">RECIPIENT_CASE_COUNT</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Report example: 200 cards, nearly all benefits at three stores.</span></span></span></code></pre></div></figure>
<figure class="fig" id="fig-06-ring-detection">
  <div class="fig-head"><span class="eyebrow">Figure 13</span><h4>The graph sees what one sale hides</h4><p class="dek">With cards and stores as nodes and sales as edges, a ring shows up as a closed cluster of cards around a few stores.</p></div>
  <div class="fig-body">

    <svg data-fk="graph" viewBox="0 0 690 420" role="img" aria-label="Network of EBT cards and stores. Most cards spread their spending across many stores. One closed cluster of 13 cards spends only at the same three small stores, which share owners, while skipping a larger store nearby: a trafficking ring."><text x="20" y="22" class="f-ink" font-size="12" font-weight="700">Normal shopping: each card spreads across many stores</text><ellipse data-group="ring" cx="563" cy="212" rx="112" ry="104" class="f-fraud-tint s-fraud" stroke-width="1.5" stroke-dasharray="5 4"/><text data-group="ring" x="563" y="338" class="f-fraud" font-size="12" font-weight="700" text-anchor="middle">Ring cluster: 13 cards, 3 stores</text><text data-group="ring" x="563" y="354" class="f-fraud" font-size="11" text-anchor="middle">no edge leaves the cluster</text><line x1="45" y1="60" x2="90" y2="95" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c1 s1"/><line x1="45" y1="60" x2="120" y2="255" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c1 s4"/><line x1="45" y1="60" x2="225" y2="70" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c1 s2"/><line x1="150" y1="45" x2="90" y2="95" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c2 s1"/><line x1="150" y1="45" x2="225" y2="70" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c2 s2"/><line x1="150" y1="45" x2="120" y2="255" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c2 s4"/><line x1="300" y1="55" x2="225" y2="70" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c3 s2"/><line x1="300" y1="55" x2="345" y2="120" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c3 s3"/><line x1="300" y1="55" x2="360" y2="295" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c3 s6"/><line x1="395" y1="60" x2="345" y2="120" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c4 s3"/><line x1="395" y1="60" x2="250" y2="215" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c4 s5"/><line x1="395" y1="60" x2="225" y2="70" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c4 s2"/><line x1="60" y1="160" x2="120" y2="255" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c5 s4"/><line x1="60" y1="160" x2="90" y2="95" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c5 s1"/><line x1="60" y1="160" x2="225" y2="70" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c5 s2"/><line x1="170" y1="140" x2="250" y2="215" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c6 s5"/><line x1="170" y1="140" x2="225" y2="70" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c6 s2"/><line x1="170" y1="140" x2="90" y2="95" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c6 s1"/><line x1="290" y1="165" x2="225" y2="70" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c7 s2"/><line x1="290" y1="165" x2="250" y2="215" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c7 s5"/><line x1="290" y1="165" x2="345" y2="120" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c7 s3"/><line x1="400" y1="200" x2="360" y2="295" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c8 s6"/><line x1="400" y1="200" x2="345" y2="120" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c8 s3"/><line x1="400" y1="200" x2="250" y2="215" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c8 s5"/><line x1="190" y1="300" x2="120" y2="255" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c9 s4"/><line x1="190" y1="300" x2="250" y2="215" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c9 s5"/><line x1="190" y1="300" x2="225" y2="70" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c9 s2"/><line x1="50" y1="330" x2="120" y2="255" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c10 s4"/><line x1="50" y1="330" x2="250" y2="215" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c10 s5"/><line x1="50" y1="330" x2="90" y2="95" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c10 s1"/><line x1="100" y1="200" x2="120" y2="255" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c11 s4"/><line x1="100" y1="200" x2="90" y2="95" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c11 s1"/><line x1="100" y1="200" x2="225" y2="70" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c11 s2"/><line x1="320" y1="350" x2="360" y2="295" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c12 s6"/><line x1="320" y1="350" x2="250" y2="215" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c12 s5"/><line x1="320" y1="350" x2="120" y2="255" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c12 s4"/><line x1="230" y1="355" x2="120" y2="255" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c13 s4"/><line x1="230" y1="355" x2="360" y2="295" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c13 s6"/><line x1="230" y1="355" x2="250" y2="215" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c13 s5"/><line x1="300" y1="245" x2="250" y2="215" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c14 s5"/><line x1="300" y1="245" x2="360" y2="295" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c14 s6"/><line x1="300" y1="245" x2="345" y2="120" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c14 s3"/><line x1="400" y1="360" x2="360" y2="295" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c15 s6"/><line x1="400" y1="360" x2="345" y2="120" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c15 s3"/><line x1="400" y1="360" x2="120" y2="255" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c15 s4"/><line x1="160" y1="230" x2="250" y2="215" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c16 s5"/><line x1="160" y1="230" x2="120" y2="255" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c16 s4"/><line x1="160" y1="230" x2="225" y2="70" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c16 s2"/><line x1="50" y1="260" x2="120" y2="255" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c17 s4"/><line x1="50" y1="260" x2="90" y2="95" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c17 s1"/><line x1="50" y1="260" x2="250" y2="215" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c17 s5"/><line x1="215" y1="115" x2="225" y2="70" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c18 s2"/><line x1="215" y1="115" x2="90" y2="95" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c18 s1"/><line x1="215" y1="115" x2="345" y2="120" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c18 s3"/><line x1="395" y1="60" x2="440" y2="62" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c4 s7"/><line x1="400" y1="200" x2="440" y2="62" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c8 s7"/><line x1="300" y1="55" x2="440" y2="62" class="s-muted" stroke-opacity="0.35" stroke-width="1" data-fk-edge="c3 s7"/><line x1="637.5" y1="233.2" x2="525" y2="195" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c19 s8" data-group="ring"/><line x1="637.5" y1="233.2" x2="605" y2="178" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c19 s9" data-group="ring"/><line x1="625.4" y1="269.1" x2="525" y2="195" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c20 s8" data-group="ring"/><line x1="625.4" y1="269.1" x2="605" y2="178" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c20 s9" data-group="ring"/><line x1="625.4" y1="269.1" x2="570" y2="262" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c20 s10" data-group="ring"/><line x1="592.3" y1="298" x2="525" y2="195" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c21 s8" data-group="ring"/><line x1="592.3" y1="298" x2="605" y2="178" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c21 s9" data-group="ring"/><line x1="592.3" y1="298" x2="570" y2="262" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c21 s10" data-group="ring"/><line x1="549.1" y1="282.6" x2="525" y2="195" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c22 s8" data-group="ring"/><line x1="549.1" y1="282.6" x2="605" y2="178" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c22 s9" data-group="ring"/><line x1="549.1" y1="282.6" x2="570" y2="262" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c22 s10" data-group="ring"/><line x1="508.9" y1="275.8" x2="525" y2="195" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c23 s8" data-group="ring"/><line x1="508.9" y1="275.8" x2="605" y2="178" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c23 s9" data-group="ring"/><line x1="473.7" y1="249.2" x2="525" y2="195" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c24 s8" data-group="ring"/><line x1="473.7" y1="249.2" x2="605" y2="178" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c24 s9" data-group="ring"/><line x1="473.7" y1="249.2" x2="570" y2="262" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c24 s10" data-group="ring"/><line x1="485.1" y1="207.8" x2="525" y2="195" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c25 s8" data-group="ring"/><line x1="485.1" y1="207.8" x2="605" y2="178" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c25 s9" data-group="ring"/><line x1="485.1" y1="207.8" x2="570" y2="262" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c25 s10" data-group="ring"/><line x1="487.6" y1="170.3" x2="525" y2="195" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c26 s8" data-group="ring"/><line x1="487.6" y1="170.3" x2="605" y2="178" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c26 s9" data-group="ring"/><line x1="487.6" y1="170.3" x2="570" y2="262" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c26 s10" data-group="ring"/><line x1="512.1" y1="134.9" x2="525" y2="195" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c27 s8" data-group="ring"/><line x1="512.1" y1="134.9" x2="605" y2="178" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c27 s9" data-group="ring"/><line x1="558.1" y1="140.4" x2="525" y2="195" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c28 s8" data-group="ring"/><line x1="558.1" y1="140.4" x2="605" y2="178" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c28 s9" data-group="ring"/><line x1="558.1" y1="140.4" x2="570" y2="262" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c28 s10" data-group="ring"/><line x1="598.9" y1="138.1" x2="525" y2="195" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c29 s8" data-group="ring"/><line x1="598.9" y1="138.1" x2="605" y2="178" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c29 s9" data-group="ring"/><line x1="598.9" y1="138.1" x2="570" y2="262" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c29 s10" data-group="ring"/><line x1="640" y1="156.2" x2="525" y2="195" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c30 s8" data-group="ring"/><line x1="640" y1="156.2" x2="605" y2="178" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c30 s9" data-group="ring"/><line x1="640" y1="156.2" x2="570" y2="262" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c30 s10" data-group="ring"/><line x1="639.7" y1="198.9" x2="525" y2="195" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c31 s8" data-group="ring"/><line x1="639.7" y1="198.9" x2="605" y2="178" class="s-fraud" stroke-opacity="0.45" stroke-width="1" data-fk-edge="c31 s9" data-group="ring"/><path data-group="ring" d="M525,195 L605,178 L570,262 Z" fill="none" class="s-fraud" stroke-width="2" stroke-dasharray="4 3"/><circle cx="45" cy="60" r="5" class="f-defense" data-fk-node="c1"/><circle cx="150" cy="45" r="5" class="f-defense" data-fk-node="c2"/><circle cx="300" cy="55" r="5" class="f-defense" data-fk-node="c3"/><circle cx="395" cy="60" r="5" class="f-defense" data-fk-node="c4"/><circle cx="60" cy="160" r="5" class="f-defense" data-fk-node="c5"/><circle cx="170" cy="140" r="5" class="f-defense" data-fk-node="c6"/><circle cx="290" cy="165" r="5" class="f-defense" data-fk-node="c7"/><circle cx="400" cy="200" r="5" class="f-defense" data-fk-node="c8"/><circle cx="190" cy="300" r="5" class="f-defense" data-fk-node="c9"/><circle cx="50" cy="330" r="5" class="f-defense" data-fk-node="c10"/><circle cx="100" cy="200" r="5" class="f-defense" data-fk-node="c11"/><circle cx="320" cy="350" r="5" class="f-defense" data-fk-node="c12"/><circle cx="230" cy="355" r="5" class="f-defense" data-fk-node="c13"/><circle cx="300" cy="245" r="5" class="f-defense" data-fk-node="c14"/><circle cx="400" cy="360" r="5" class="f-defense" data-fk-node="c15"/><circle cx="160" cy="230" r="5" class="f-defense" data-fk-node="c16"/><circle cx="50" cy="260" r="5" class="f-defense" data-fk-node="c17"/><circle cx="215" cy="115" r="5" class="f-defense" data-fk-node="c18"/><circle cx="637.5" cy="233.2" r="5" class="f-fraud" data-fk-node="c19" data-group="ring"/><circle cx="625.4" cy="269.1" r="5" class="f-fraud" data-fk-node="c20" data-group="ring"/><circle cx="592.3" cy="298" r="5" class="f-fraud" data-fk-node="c21" data-group="ring"/><circle cx="549.1" cy="282.6" r="5" class="f-fraud" data-fk-node="c22" data-group="ring"/><circle cx="508.9" cy="275.8" r="5" class="f-fraud" data-fk-node="c23" data-group="ring"/><circle cx="473.7" cy="249.2" r="5" class="f-fraud" data-fk-node="c24" data-group="ring"/><circle cx="485.1" cy="207.8" r="5" class="f-fraud" data-fk-node="c25" data-group="ring"/><circle cx="487.6" cy="170.3" r="5" class="f-fraud" data-fk-node="c26" data-group="ring"/><circle cx="512.1" cy="134.9" r="5" class="f-fraud" data-fk-node="c27" data-group="ring"/><circle cx="558.1" cy="140.4" r="5" class="f-fraud" data-fk-node="c28" data-group="ring"/><circle cx="598.9" cy="138.1" r="5" class="f-fraud" data-fk-node="c29" data-group="ring"/><circle cx="640" cy="156.2" r="5" class="f-fraud" data-fk-node="c30" data-group="ring"/><circle cx="639.7" cy="198.9" r="5" class="f-fraud" data-fk-node="c31" data-group="ring"/><rect x="81" y="86" width="18" height="18" rx="2" class="f-neutral-tint s-muted" stroke-width="1.5" data-fk-node="s1"/><rect x="216" y="61" width="18" height="18" rx="2" class="f-neutral-tint s-muted" stroke-width="1.5" data-fk-node="s2"/><rect x="336" y="111" width="18" height="18" rx="2" class="f-neutral-tint s-muted" stroke-width="1.5" data-fk-node="s3"/><rect x="111" y="246" width="18" height="18" rx="2" class="f-neutral-tint s-muted" stroke-width="1.5" data-fk-node="s4"/><rect x="241" y="206" width="18" height="18" rx="2" class="f-neutral-tint s-muted" stroke-width="1.5" data-fk-node="s5"/><rect x="351" y="286" width="18" height="18" rx="2" class="f-neutral-tint s-muted" stroke-width="1.5" data-fk-node="s6"/><rect x="426" y="48" width="28" height="28" rx="3" class="f-neutral-tint s-muted" stroke-width="1.5" data-tip="Larger store nearby: ring cards skip it, often passing closer and larger stores." data-fk-node="s7"/><text x="462" y="60" class="f-muted" font-size="11">larger store nearby,</text><text x="462" y="74" class="f-muted" font-size="11">ring cards skip it</text><rect x="517" y="187" width="16" height="16" rx="2" class="f-fraud" data-tip="Ring store: one of three small stores with related owners; the ring&#39;s cards shop only here." data-fk-node="s8" data-group="ring"/><rect x="597" y="170" width="16" height="16" rx="2" class="f-fraud" data-tip="Ring store: one of three small stores with related owners; the ring&#39;s cards shop only here." data-fk-node="s9" data-group="ring"/><rect x="562" y="254" width="16" height="16" rx="2" class="f-fraud" data-tip="Ring store: one of three small stores with related owners; the ring&#39;s cards shop only here." data-fk-node="s10" data-group="ring"/><line x1="20" x2="676" y1="378" y2="378" class="s-rule" stroke-width="1"/><circle cx="28" cy="396" r="5" class="f-defense"/><text x="40" y="400" class="f-muted" font-size="11">EBT card</text><rect x="108" y="388" width="14" height="14" rx="2" class="f-neutral-tint s-muted" stroke-width="1.5"/><text x="130" y="400" class="f-muted" font-size="11">store</text><line x1="176" x2="202" y1="396" y2="396" class="s-muted" stroke-width="1.5"/><text x="208" y="400" class="f-muted" font-size="11">card shopped at store</text><circle cx="346" cy="396" r="5" class="f-fraud"/><rect x="356" y="389" width="13" height="13" rx="2" class="f-fraud"/><text x="376" y="400" class="f-muted" font-size="11">ring card, ring store</text><line x1="502" x2="528" y1="396" y2="396" class="s-fraud" stroke-width="2" stroke-dasharray="4 3"/><text x="534" y="400" class="f-muted" font-size="11">shared owner or bank</text></svg>
    <div class="fk-controls" hidden data-fk-controls><span data-fk-toggle="ring" data-fk-label="Highlight the ring" data-fk-note="13 cards spend only at 3 linked stores, skip the larger store nearby, and no edge leaves the cluster."></span><span data-fk-hint="Hover or tap a card or store to trace its sales."></span></div>
    <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,220px),1fr));gap:0.75rem;margin-top:1rem">
      <div class="node node--muted"><h5>Single-transaction rule</h5><p>Sees one <span class="num">$60</span> sale at a licensed store. Normal amount, normal store. It passes.</p></div>
      <div class="node node--fraud"><h5 class="t-fraud">Graph view</h5><p>Report example: <span class="num">200</span> cards spend nearly all their benefits at three small stores owned by related parties, often passing closer and larger stores.</p></div>
      <div class="node node--defense"><h5 class="t-defense">Signals that expose a ring</h5><p style="display:flex;flex-wrap:wrap;gap:0.35rem"><span class="chip chip--defense">community detection</span><span class="chip chip--defense">shared owners</span><span class="chip chip--defense">shared bank accounts</span></p><p>Owners come from authorization records, bank accounts from payout records.</p></div>
    </div>
  </div>
  <figcaption><strong>Read it as:</strong> each ring sale looks normal alone. The evidence is the shape: many cards that shop only at the same three linked stores. The graph shows a sample; the report's example ring has 200 cards.</figcaption>
</figure>

<p><strong>Feedback.</strong> Every closed case, whether confirmed or cleared, becomes a labeled example. The scoring models retrain on these labels. Cleared cases matter as much as confirmed ones, because they teach the models which patterns are benign, such as round prices at a store that prices in whole dollars.</p>
<h2 id="7-price-anomaly-detection-without-human-basket-review">7. Price anomaly detection without human basket review<a class="anchor" href="#7-price-anomaly-detection-without-human-basket-review" aria-label="Link to this section"></a></h2>
<p>Item-level data creates a new problem: volume. Retailers process millions of SNAP transactions per day. No program can afford a person to look at baskets. The design question is how to turn that volume into a short list of stores worth a human's time, so that operating cost grows with investigator headcount while transaction volume affects only computing cost. <a href="#uml-sw-scoring-sequence">Figure A8</a> in Appendix A shows the monthly scoring run as a sequence.</p>
<h3 id="71-reference-prices">7.1 Reference prices<a class="anchor" href="#71-reference-prices" aria-label="Link to this section"></a></h3>
<p>The system builds a reference price for each barcode in each region from all stores' basket data. For a given barcode and region in a given month, the reference is a robust central value of observed prices, such as the median, with a spread measure such as the interquartile range. Separate references for store classes (supermarket, convenience store, small grocery) reflect the fact that convenience stores charge more for the same item. Products with few observations borrow strength from related products in the same category and from neighboring regions (<a href="#fig-07-reference-price">Figure 14</a>).</p>
<figure class="fig" id="fig-07-reference-price">
  <div class="fig-head"><span class="eyebrow">Figure 14</span><h4>Score the store, not the basket</h4><p class="dek">Each barcode gets a regional reference price, and a store whose lines sit far above it month after month is the signal.</p></div>
  <div class="fig-body">

    <div style="display:flex;flex-wrap:wrap;align-items:baseline;gap:0.5rem 0.75rem;margin-bottom:0.4rem"><span class="eyebrow">A. One barcode, one region</span><span style="font-size:0.85rem;color:var(--muted)">A gallon of milk, one month, one store class</span><span class="chip chip--caution">Illustrative</span></div>
    <svg viewBox="0 0 680 206" role="img" aria-label="Illustrative dot strip: 22 stores price a gallon of milk between $3.62 and $4.39 around a $4.00 regional reference. Store X charges $5.60, 40% above reference."><line x1="84.2" x2="84.2" y1="40" y2="160" class="s-rule" stroke-width="1"/><text x="84.2" y="182" class="f-muted mono" font-size="11" text-anchor="middle">$3.50</text><line x1="205" x2="205" y1="40" y2="160" class="s-rule" stroke-width="1"/><text x="205" y="182" class="f-muted mono" font-size="11" text-anchor="middle">$4.00</text><line x1="325.8" x2="325.8" y1="40" y2="160" class="s-rule" stroke-width="1"/><text x="325.8" y="182" class="f-muted mono" font-size="11" text-anchor="middle">$4.50</text><line x1="446.7" x2="446.7" y1="40" y2="160" class="s-rule" stroke-width="1"/><text x="446.7" y="182" class="f-muted mono" font-size="11" text-anchor="middle">$5.00</text><line x1="567.5" x2="567.5" y1="40" y2="160" class="s-rule" stroke-width="1"/><text x="567.5" y="182" class="f-muted mono" font-size="11" text-anchor="middle">$5.50</text><rect data-tip="Middle half of stores price a gallon of milk from $3.80 to $4.20." data-group="reference" x="156.7" y="44" width="96.7" height="112" class="f-defense-tint"/><line data-tip="Regional reference: $4.00, the median price for a gallon of milk." data-group="reference" data-animate="draw" data-step="0" x1="205" x2="205" y1="34" y2="160" class="s-defense" stroke-width="2"/><text data-group="reference" x="205" y="26" class="f-defense" font-size="12" font-weight="700" text-anchor="middle">reference $4.00 (median)</text><line x1="60" x2="640" y1="160" y2="160" class="s-muted" stroke-width="1"/><text data-group="reference" x="150.7" y="152" class="f-defense" font-size="11" text-anchor="end">middle half</text><text data-group="reference" x="150.7" y="140" class="f-defense mono" font-size="11" text-anchor="end">$3.80 to $4.20</text><circle data-tip="One of 22 stores: price sits inside the $3.62 to $4.39 range around the $4.00 reference." data-group="stores" data-animate="pop" data-step="0" cx="113.2" cy="96" r="5.5" class="f-defense" fill-opacity="0.7"/><circle data-tip="One of 22 stores: price sits inside the $3.62 to $4.39 range around the $4.00 reference." data-group="stores" data-animate="pop" data-step="0.1" cx="134.9" cy="70" r="5.5" class="f-defense" fill-opacity="0.7"/><circle data-tip="One of 22 stores: price sits inside the $3.62 to $4.39 range around the $4.00 reference." data-group="stores" data-animate="pop" data-step="0.2" cx="151.8" cy="112" r="5.5" class="f-defense" fill-opacity="0.7"/><circle data-tip="One of 22 stores: price sits inside the $3.62 to $4.39 range around the $4.00 reference." data-group="stores" data-animate="pop" data-step="0.3" cx="161.5" cy="84" r="5.5" class="f-defense" fill-opacity="0.7"/><circle data-tip="One of 22 stores: price sits inside the $3.62 to $4.39 range around the $4.00 reference." data-group="stores" data-animate="pop" data-step="0.4" cx="168.8" cy="128" r="5.5" class="f-defense" fill-opacity="0.7"/><circle data-tip="One of 22 stores: price sits inside the $3.62 to $4.39 range around the $4.00 reference." data-group="stores" data-animate="pop" data-step="0.5" cx="176" cy="104" r="5.5" class="f-defense" fill-opacity="0.7"/><circle data-tip="One of 22 stores: price sits inside the $3.62 to $4.39 range around the $4.00 reference." data-group="stores" data-animate="pop" data-step="0.6" cx="180.8" cy="66" r="5.5" class="f-defense" fill-opacity="0.7"/><circle data-tip="One of 22 stores: price sits inside the $3.62 to $4.39 range around the $4.00 reference." data-group="stores" data-animate="pop" data-step="0.7" cx="188.1" cy="120" r="5.5" class="f-defense" fill-opacity="0.7"/><circle data-tip="One of 22 stores: price sits inside the $3.62 to $4.39 range around the $4.00 reference." data-group="stores" data-animate="pop" data-step="0.8" cx="192.9" cy="90" r="5.5" class="f-defense" fill-opacity="0.7"/><circle data-tip="One of 22 stores: price sits inside the $3.62 to $4.39 range around the $4.00 reference." data-group="stores" data-animate="pop" data-step="0.9" cx="197.8" cy="136" r="5.5" class="f-defense" fill-opacity="0.7"/><circle data-tip="One of 22 stores: price sits inside the $3.62 to $4.39 range around the $4.00 reference." data-group="stores" data-animate="pop" data-step="1" cx="202.6" cy="76" r="5.5" class="f-defense" fill-opacity="0.7"/><circle data-tip="One of 22 stores: price sits inside the $3.62 to $4.39 range around the $4.00 reference." data-group="stores" data-animate="pop" data-step="1.1" cx="205" cy="108" r="5.5" class="f-defense" fill-opacity="0.7"/><circle data-tip="One of 22 stores: price sits inside the $3.62 to $4.39 range around the $4.00 reference." data-group="stores" data-animate="pop" data-step="1.2" cx="209.8" cy="128" r="5.5" class="f-defense" fill-opacity="0.7"/><circle data-tip="One of 22 stores: price sits inside the $3.62 to $4.39 range around the $4.00 reference." data-group="stores" data-animate="pop" data-step="1.3" cx="214.7" cy="94" r="5.5" class="f-defense" fill-opacity="0.7"/><circle data-tip="One of 22 stores: price sits inside the $3.62 to $4.39 range around the $4.00 reference." data-group="stores" data-animate="pop" data-step="1.4" cx="219.5" cy="70" r="5.5" class="f-defense" fill-opacity="0.7"/><circle data-tip="One of 22 stores: price sits inside the $3.62 to $4.39 range around the $4.00 reference." data-group="stores" data-animate="pop" data-step="1.5" cx="226.8" cy="116" r="5.5" class="f-defense" fill-opacity="0.7"/><circle data-tip="One of 22 stores: price sits inside the $3.62 to $4.39 range around the $4.00 reference." data-group="stores" data-animate="pop" data-step="1.6" cx="234" cy="84" r="5.5" class="f-defense" fill-opacity="0.7"/><circle data-tip="One of 22 stores: price sits inside the $3.62 to $4.39 range around the $4.00 reference." data-group="stores" data-animate="pop" data-step="1.7" cx="241.3" cy="134" r="5.5" class="f-defense" fill-opacity="0.7"/><circle data-tip="One of 22 stores: price sits inside the $3.62 to $4.39 range around the $4.00 reference." data-group="stores" data-animate="pop" data-step="1.8" cx="250.9" cy="100" r="5.5" class="f-defense" fill-opacity="0.7"/><circle data-tip="One of 22 stores: price sits inside the $3.62 to $4.39 range around the $4.00 reference." data-group="stores" data-animate="pop" data-step="1.9" cx="263" cy="116" r="5.5" class="f-defense" fill-opacity="0.7"/><circle data-tip="One of 22 stores: price sits inside the $3.62 to $4.39 range around the $4.00 reference." data-group="stores" data-animate="pop" data-step="2" cx="279.9" cy="128" r="5.5" class="f-defense" fill-opacity="0.7"/><circle data-tip="One of 22 stores: price sits inside the $3.62 to $4.39 range around the $4.00 reference." data-group="stores" data-animate="pop" data-step="2.1" cx="299.3" cy="106" r="5.5" class="f-defense" fill-opacity="0.7"/><line data-group="store-x" x1="253.3" x2="579.7" y1="80" y2="80" class="s-fraud" stroke-width="1.5" stroke-dasharray="4 3"/><text data-group="store-x" x="442.5" y="72" class="f-fraud" font-size="12" text-anchor="middle">excess over range: $1.40 per gallon</text><circle data-group="store-x" data-animate="pop" data-step="3" cx="591.7" cy="80" r="12" class="f-fraud" fill-opacity="0.18"/><circle data-tip="Store X is the outlier: $5.60 a gallon, 40% above the $4.00 reference and $1.40 over the range." data-group="store-x" data-animate="pop" data-step="3" cx="591.7" cy="80" r="6.5" class="f-fraud"/><text data-group="store-x" x="591.7" y="110" class="f-fraud" font-size="12" font-weight="700" text-anchor="middle">Store X: $5.60</text><text data-group="store-x" x="591.7" y="126" class="f-fraud" font-size="11" text-anchor="middle">40% above reference</text><text x="350" y="198" class="f-muted" font-size="11" text-anchor="middle">price paid per gallon, one dot per store</text></svg>
    <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,280px),1fr));gap:1.25rem;margin-top:1.5rem;padding-top:1.25rem;border-top:1px solid var(--rule)">
      <div class="node node--muted"><span class="eyebrow">B1. Noise</span><h5>One odd basket means little</h5><svg viewBox="0 0 320 200" role="img" aria-label="Illustrative basket: five items sit near the reference price and one line sits 40% above it. One odd line is noise."><line x1="44" x2="308" y1="150" y2="150" class="s-rule" stroke-width="1"/><text x="38" y="154" class="f-muted mono" font-size="11" text-anchor="end">-10%</text><line x1="44" x2="308" y1="130" y2="130" class="s-muted" stroke-width="1"/><text x="38" y="134" class="f-muted mono" font-size="11" text-anchor="end">0%</text><line x1="44" x2="308" y1="90" y2="90" class="s-rule" stroke-width="1"/><text x="38" y="94" class="f-muted mono" font-size="11" text-anchor="end">+20%</text><line x1="44" x2="308" y1="50" y2="50" class="s-rule" stroke-width="1"/><text x="38" y="54" class="f-muted mono" font-size="11" text-anchor="end">+40%</text><rect data-tip="Milk: sits near the reference price." data-group="noise" data-animate="grow" data-step="4" data-axis="y" x="56.7" y="124" width="26" height="6" rx="1.5" class="f-muted"/><text x="69.7" y="166" class="f-muted" font-size="11" text-anchor="middle">milk</text><rect data-tip="Bread: sits near the reference price." data-group="noise" data-animate="grow" data-step="4.2" data-axis="y" x="100" y="130" width="26" height="8" rx="1.5" class="f-muted"/><text x="113" y="166" class="f-muted" font-size="11" text-anchor="middle">bread</text><rect data-tip="Rice: sits near the reference price." data-group="noise" data-animate="grow" data-step="4.4" data-axis="y" x="143.3" y="126" width="26" height="4" rx="1.5" class="f-muted"/><text x="156.3" y="166" class="f-muted" font-size="11" text-anchor="middle">rice</text><rect data-tip="Cereal: 40% above reference. One odd line in one basket is noise." data-group="noise" data-animate="grow" data-step="4.6" data-axis="y" x="186.7" y="50" width="26" height="80" rx="1.5" class="f-caution"/><text x="199.7" y="166" class="f-muted" font-size="11" text-anchor="middle">cereal</text><rect data-tip="Beans: sit near the reference price." data-group="noise" data-animate="grow" data-step="4.8" data-axis="y" x="230" y="130" width="26" height="4" rx="1.5" class="f-muted"/><text x="243" y="166" class="f-muted" font-size="11" text-anchor="middle">beans</text><rect data-tip="Eggs: sit near the reference price." data-group="noise" data-animate="grow" data-step="5" data-axis="y" x="273.3" y="120" width="26" height="10" rx="1.5" class="f-muted"/><text x="286.3" y="166" class="f-muted" font-size="11" text-anchor="middle">eggs</text><text data-group="noise" x="199.6" y="42" class="f-caution mono" font-size="12" font-weight="600" text-anchor="middle">+40%</text><text data-group="noise" x="176" y="190" class="f-muted" font-size="11" text-anchor="middle">one odd line in one basket</text></svg><p>A promotion, a slow item priced high, or a mis-keyed price. Scoring each basket floods the system with noise.</p></div>
      <div class="node node--fraud"><span class="eyebrow t-fraud">B2. Signal</span><h5 class="t-fraud">A store-month pattern</h5><svg viewBox="0 0 320 200" role="img" aria-label="Illustrative store-month: 14 of 16 item lines sit about 40% above reference. A consistent pattern is the signal."><line x1="44" x2="308" y1="150" y2="150" class="s-rule" stroke-width="1"/><text x="38" y="154" class="f-muted mono" font-size="11" text-anchor="end">-10%</text><line x1="44" x2="308" y1="130" y2="130" class="s-muted" stroke-width="1"/><text x="38" y="134" class="f-muted mono" font-size="11" text-anchor="end">0%</text><line x1="44" x2="308" y1="90" y2="90" class="s-rule" stroke-width="1"/><text x="38" y="94" class="f-muted mono" font-size="11" text-anchor="end">+20%</text><line x1="44" x2="308" y1="50" y2="50" class="s-rule" stroke-width="1"/><text x="38" y="54" class="f-muted mono" font-size="11" text-anchor="end">+40%</text><line data-group="signal" x1="44" x2="308" y1="50" y2="50" class="s-fraud" stroke-width="1.5" stroke-dasharray="4 3"/><rect data-tip="Item line 1 of 16: about 40% above reference." data-group="signal" data-animate="grow" data-step="5" data-axis="y" x="51.1" y="46" width="10.1" height="84" rx="1.5" class="f-fraud"/><rect data-tip="Item line 2 of 16: about 40% above reference." data-group="signal" data-animate="grow" data-step="5.1" data-axis="y" x="67.3" y="54" width="10.1" height="76" rx="1.5" class="f-fraud"/><rect data-tip="Item line 3 of 16: about 40% above reference." data-group="signal" data-animate="grow" data-step="5.2" data-axis="y" x="83.6" y="40" width="10.1" height="90" rx="1.5" class="f-fraud"/><rect data-tip="Item line 4 of 16: about 40% above reference." data-group="signal" data-animate="grow" data-step="5.3" data-axis="y" x="99.8" y="50" width="10.1" height="80" rx="1.5" class="f-fraud"/><rect data-tip="Item line 5 of 16: near the reference price, one of the 2 lines not high." data-group="signal" data-animate="grow" data-step="5.4" data-axis="y" x="116.1" y="124" width="10.1" height="6" rx="1.5" class="f-fraud"/><rect data-tip="Item line 6 of 16: about 40% above reference." data-group="signal" data-animate="grow" data-step="5.5" data-axis="y" x="132.3" y="48" width="10.1" height="82" rx="1.5" class="f-fraud"/><rect data-tip="Item line 7 of 16: about 40% above reference." data-group="signal" data-animate="grow" data-step="5.6" data-axis="y" x="148.6" y="56" width="10.1" height="74" rx="1.5" class="f-fraud"/><rect data-tip="Item line 8 of 16: about 40% above reference." data-group="signal" data-animate="grow" data-step="5.7" data-axis="y" x="164.8" y="42" width="10.1" height="88" rx="1.5" class="f-fraud"/><rect data-tip="Item line 9 of 16: about 40% above reference." data-group="signal" data-animate="grow" data-step="5.8" data-axis="y" x="181.1" y="52" width="10.1" height="78" rx="1.5" class="f-fraud"/><rect data-tip="Item line 10 of 16: about 40% above reference." data-group="signal" data-animate="grow" data-step="5.9" data-axis="y" x="197.3" y="44" width="10.1" height="86" rx="1.5" class="f-fraud"/><rect data-tip="Item line 11 of 16: near the reference price, one of the 2 lines not high." data-group="signal" data-animate="grow" data-step="6" data-axis="y" x="213.6" y="130" width="10.1" height="4" rx="1.5" class="f-fraud"/><rect data-tip="Item line 12 of 16: about 40% above reference." data-group="signal" data-animate="grow" data-step="6.1" data-axis="y" x="229.8" y="50" width="10.1" height="80" rx="1.5" class="f-fraud"/><rect data-tip="Item line 13 of 16: about 40% above reference." data-group="signal" data-animate="grow" data-step="6.2" data-axis="y" x="246.1" y="58" width="10.1" height="72" rx="1.5" class="f-fraud"/><rect data-tip="Item line 14 of 16: about 40% above reference." data-group="signal" data-animate="grow" data-step="6.3" data-axis="y" x="262.3" y="38" width="10.1" height="92" rx="1.5" class="f-fraud"/><rect data-tip="Item line 15 of 16: about 40% above reference." data-group="signal" data-animate="grow" data-step="6.4" data-axis="y" x="278.6" y="48" width="10.1" height="82" rx="1.5" class="f-fraud"/><rect data-tip="Item line 16 of 16: about 40% above reference." data-group="signal" data-animate="grow" data-step="6.5" data-axis="y" x="294.8" y="54" width="10.1" height="76" rx="1.5" class="f-fraud"/><text data-group="signal" x="308" y="24" class="f-fraud mono" font-size="12" font-weight="600" text-anchor="end">+40%</text><text x="176" y="166" class="f-muted" font-size="11" text-anchor="middle">16 item lines across the month</text><text data-group="signal" x="176" y="190" class="f-fraud" font-size="11" text-anchor="middle">14 of 16 sit about 40% above</text></svg><p>Most of the store's lines sit about 40% above reference for a whole month. Shrinkage keeps small stores from extreme scores.</p></div>
    </div>
    <p style="margin:0.9rem 0 0;font-size:0.85rem;color:var(--muted)">Bars show price vs reference, where 0 is the reference price. The reference uses the median and the middle half of all stores' prices, set per store class; rare products borrow from related products and neighboring regions.</p>
  </div>
  <figcaption><strong>Read it as:</strong> Store X's single $5.60 price is one dot. What matters is panel B2: the same store high on most lines all month.</figcaption>
</figure>

<h3 id="72-score-stores-not-baskets">7.2 Score stores, not baskets<a class="anchor" href="#72-score-stores-not-baskets" aria-label="Link to this section"></a></h3>
<p>One odd price means little. A store may run a promotion, price a slow item high, or key a price wrong. Independent stores set their own prices, and small stores pay more at wholesale than chains. Scoring each basket would flood the system with noise.</p>
<p>The unit of scoring is the store-month. For each store and month, the system computes:</p>
<ul>
<li>The excess price share: the sum over lines of (price paid minus the upper bound of the reference range, when positive), divided by total EBT sales.</li>
<li>The share of lines priced above the reference range.</li>
<li>Basket plausibility: how far the mix of items departs from what EBT shoppers buy at similar stores (for example, baskets made of a few high-priced items that recur in round-total combinations).</li>
<li>Supply consistency: EBT sales per category divided by known purchases per category, from the inventory reconciliation.</li>
</ul>
<p>These features feed a model trained on labels from past cases. Before scoring, the system applies statistical shrinkage so that a store with few transactions does not get an extreme score from a handful of odd lines.</p>
<p>The monthly run builds a reference price per barcode and region, then scores each store-month from all of its lines together.</p>
<figure class="code"><figcaption>Pseudocode</figcaption><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">reference_prices</span><span class="p">(</span><span class="n">lines</span><span class="p">,</span> <span class="n">month</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">refs</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="n">upc</span><span class="p">,</span> <span class="n">region</span><span class="p">,</span> <span class="n">store_class</span><span class="p">),</span> <span class="n">prices</span> <span class="ow">in</span> <span class="n">group</span><span class="p">(</span><span class="n">lines</span><span class="p">,</span> <span class="n">by</span><span class="o">=</span><span class="p">(</span><span class="n">upc</span><span class="p">,</span> <span class="n">region</span><span class="p">,</span> <span class="n">store_class</span><span class="p">)):</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">prices</span><span class="p">)</span> <span class="o">&lt;</span> <span class="n">MIN_OBSERVATIONS</span><span class="p">:</span>  <span class="c1"># borrow from category and neighbors</span>
</span></span><span class="line"><span class="cl">            <span class="n">prices</span> <span class="o">+=</span> <span class="n">related_prices</span><span class="p">(</span><span class="n">category_of</span><span class="p">(</span><span class="n">upc</span><span class="p">),</span> <span class="n">neighboring_regions</span><span class="p">(</span><span class="n">region</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="n">mid</span><span class="p">,</span> <span class="n">spread</span> <span class="o">=</span> <span class="n">median</span><span class="p">(</span><span class="n">prices</span><span class="p">),</span> <span class="n">interquartile_range</span><span class="p">(</span><span class="n">prices</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">refs</span><span class="p">[(</span><span class="n">upc</span><span class="p">,</span> <span class="n">region</span><span class="p">,</span> <span class="n">store_class</span><span class="p">)]</span> <span class="o">=</span> <span class="n">Reference</span><span class="p">(</span><span class="n">mid</span><span class="p">,</span> <span class="n">upper</span><span class="o">=</span><span class="n">mid</span> <span class="o">+</span> <span class="n">REF_RANGE_WIDTH</span> <span class="o">*</span> <span class="n">spread</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">refs</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">score_store_month</span><span class="p">(</span><span class="n">store</span><span class="p">,</span> <span class="n">month</span><span class="p">,</span> <span class="n">refs</span><span class="p">,</span> <span class="n">reconciliation</span><span class="p">,</span> <span class="n">model</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">lines</span> <span class="o">=</span> <span class="n">store</span><span class="o">.</span><span class="n">ebt_basket_lines</span><span class="p">(</span><span class="n">month</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">upper</span> <span class="o">=</span> <span class="p">{</span><span class="n">l</span><span class="p">:</span> <span class="n">refs</span><span class="p">[(</span><span class="n">l</span><span class="o">.</span><span class="n">upc</span><span class="p">,</span> <span class="n">store</span><span class="o">.</span><span class="n">region</span><span class="p">,</span> <span class="n">store</span><span class="o">.</span><span class="n">store_class</span><span class="p">)]</span><span class="o">.</span><span class="n">upper</span> <span class="k">for</span> <span class="n">l</span> <span class="ow">in</span> <span class="n">lines</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">sales</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">l</span><span class="o">.</span><span class="n">quantity</span> <span class="o">*</span> <span class="n">l</span><span class="o">.</span><span class="n">unit_price</span> <span class="k">for</span> <span class="n">l</span> <span class="ow">in</span> <span class="n">lines</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">excess</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="nb">max</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">l</span><span class="o">.</span><span class="n">unit_price</span> <span class="o">-</span> <span class="n">upper</span><span class="p">[</span><span class="n">l</span><span class="p">])</span> <span class="o">*</span> <span class="n">l</span><span class="o">.</span><span class="n">quantity</span> <span class="k">for</span> <span class="n">l</span> <span class="ow">in</span> <span class="n">lines</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">features</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;excess_price_share&#34;</span><span class="p">:</span> <span class="n">excess</span> <span class="o">/</span> <span class="n">sales</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;share_lines_above&#34;</span><span class="p">:</span> <span class="n">count</span><span class="p">(</span><span class="n">l</span> <span class="k">for</span> <span class="n">l</span> <span class="ow">in</span> <span class="n">lines</span> <span class="k">if</span> <span class="n">l</span><span class="o">.</span><span class="n">unit_price</span> <span class="o">&gt;</span> <span class="n">upper</span><span class="p">[</span><span class="n">l</span><span class="p">])</span> <span class="o">/</span> <span class="nb">len</span><span class="p">(</span><span class="n">lines</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;basket_plausibility&#34;</span><span class="p">:</span> <span class="n">mix_distance</span><span class="p">(</span><span class="n">store</span><span class="o">.</span><span class="n">baskets</span><span class="p">(</span><span class="n">month</span><span class="p">),</span> <span class="n">peers</span><span class="p">(</span><span class="n">store</span><span class="o">.</span><span class="n">store_class</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;supply_consistency&#34;</span><span class="p">:</span> <span class="n">reconciliation</span><span class="o">.</span><span class="n">sales_vs_purchases_by_category</span><span class="p">(</span><span class="n">store</span><span class="p">,</span> <span class="n">month</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">raw</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">predict</span><span class="p">(</span><span class="n">features</span><span class="p">)</span>  <span class="c1"># trained on labels from past cases</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Shrinkage: one odd basket cannot swing a small store.</span>
</span></span><span class="line"><span class="cl">    <span class="n">score</span> <span class="o">=</span> <span class="n">shrink</span><span class="p">(</span><span class="n">raw</span><span class="p">,</span> <span class="n">toward</span><span class="o">=</span><span class="n">peer_average</span><span class="p">,</span> <span class="n">n</span><span class="o">=</span><span class="nb">len</span><span class="p">(</span><span class="n">lines</span><span class="p">),</span> <span class="n">strength</span><span class="o">=</span><span class="n">SHRINKAGE_STRENGTH</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">StoreMonthScore</span><span class="p">(</span><span class="n">store</span><span class="p">,</span> <span class="n">month</span><span class="p">,</span> <span class="n">score</span><span class="p">,</span> <span class="n">features</span><span class="p">)</span></span></span></code></pre></div></figure>
<h3 id="73-rank-by-expected-loss">7.3 Rank by expected loss<a class="anchor" href="#73-rank-by-expected-loss" aria-label="Link to this section"></a></h3>
<p>The output of scoring is a ranked list of stores by expected loss:</p>
<p>expected loss = probability of fraud x estimated excess dollars per year</p>
<p>The probability comes from the model. The excess dollars come from the excess price share and the supply gap, multiplied by the store's EBT volume. The program then acts on the list through a funnel (<a href="#fig-07-review-funnel">Figure 15</a>).</p>
<figure class="fig" id="fig-07-review-funnel">
  <div class="fig-head"><span class="eyebrow">Figure 15</span><h4>The review funnel</h4><p class="dek">Software shrinks millions of daily baskets to a short list, and people see only the top N stores the budget can cover.</p></div>
  <div class="fig-body">

    <div class="fn">
      <div class="fn-zone"><span class="chip chip--defense">software, no person</span></div>
      <ol class="fn-stages">
      <li class="fn-stage" style="--w:100%; --w-from:100%" data-animate="narrow" data-step="0" data-group="baskets" data-tip="Retailers process millions of SNAP transactions per day. No program can afford a person to look at baskets.">
        <span class="fn-n num">01</span>
        <div class="fn-txt"><h5>Every basket</h5><p>Item-level EBT data from every store. Volume drives computing cost only.</p></div>
        <span class="chip chip--defense fn-vol">millions / day</span>
        <span class="fn-bar" style="--v:100%; --v-from:100%" aria-hidden="true"></span>
      </li>
      <li class="fn-stage" style="--w:90%; --w-from:100%" data-animate="narrow" data-step="1" data-group="prices" data-tip="Separate references per store class, since convenience stores charge more for the same item.">
        <span class="fn-n num">02</span>
        <div class="fn-txt"><h5>Item price vs reference price</h5><p>Each line checked against the regional reference for its barcode and store class.</p></div>
        <span class="chip chip--defense fn-vol">every line</span>
        <span class="fn-bar" style="--v:100%; --v-from:100%" aria-hidden="true"></span>
      </li>
      <li class="fn-stage" style="--w:78%; --w-from:90%" data-animate="narrow" data-step="2" data-group="scores" data-tip="One odd price means little, so the store-month is the unit of scoring, with shrinkage for small stores.">
        <span class="fn-n num">03</span>
        <div class="fn-txt"><h5>Store-month scores</h5><p>Excess price share, lines above range, basket plausibility, supply consistency.</p></div>
        <span class="chip chip--defense fn-vol">1 per store / month</span>
        <span class="fn-bar" style="--v:22%; --v-from:100%" aria-hidden="true"></span>
      </li>
      <li class="fn-stage" style="--w:66%; --w-from:78%" data-animate="narrow" data-step="3" data-group="rank auto people" data-tip="expected loss = probability of fraud x estimated excess dollars per year">
        <span class="fn-n num">04</span>
        <div class="fn-txt"><h5>Ranked by expected loss</h5><p>Highest expected loss first, not highest sales.</p></div>
        <span class="chip chip--defense fn-vol">probability x excess $</span>
        <span class="fn-bar" style="--v:22%; --v-from:22%" aria-hidden="true"></span>
      </li>
      </ol>
      <div class="fn-fork" aria-hidden="true">
        <svg viewBox="0 0 400 34" preserveAspectRatio="none"><path d="M200 0 V12 M200 12 H100 V34 M200 12 H300 V34" fill="none" class="s-muted" stroke-width="1.5" vector-effect="non-scaling-stroke"/></svg>
      </div>
      <div class="fn-split">
        <div class="node node--defense fn-out" data-group="auto" data-tip="Warning letters, payout delays, and volume caps are reversible and need no investigator time.">
          <span class="fn-n num">05a</span>
          <h5 class="t-defense">Automatic actions</h5>
          <p><span class="chip chip--defense">most stores above normal</span></p>
          <p class="fn-chips"><span class="chip">warning letter</span><span class="chip">payout delay</span><span class="chip">volume cap</span></p>
          <p>Reversible, no investigator time. Scores keep updating; a store that keeps rising enters the queue.</p>
        </div>
        <div class="node node--fraud fn-out" data-group="people" data-tip="The program sets the investigator budget first; that number fixes N.">
          <span class="fn-n num">05b</span>
          <h5 class="t-fraud">Top N stores to investigators</h5>
          <p><span class="chip chip--fraud">N set by budget</span></p>
          <p class="fn-person">A person reviews a case only when its expected benefit tops the cost of one review.</p>
        </div>
      </div>
      <div class="fn-loop" data-animate="pulse" data-step="7" data-group="people scores" data-tip="Cleared cases matter as much as confirmed ones: they teach the models which patterns are benign.">
        <svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true"><path d="M5 17 V9 a4 4 0 0 1 4 -4 H19" fill="none" class="s-defense" stroke-width="2"/><path d="M15 1.5 L19.5 5 L15 8.5" fill="none" class="s-defense" stroke-width="2"/></svg>
        <p><strong>Feedback to step 03.</strong> Every closed case, confirmed or cleared, becomes a label. The scoring model retrains on both, so it learns which patterns are benign.</p>
      </div>
      <div class="fn-costs">
        <div><span class="eyebrow">Computing cost</span><p>grows with transaction volume, and stays small</p></div>
        <div><span class="eyebrow">Human cost</span><p>grows with investigator headcount, which the budget fixes first</p></div>
      </div>
    </div>
  </div>
  <figcaption><strong>Read it as:</strong> every step above the fork runs in software, so more baskets cost only computing. People enter at one point, the top N, and the budget sets N.</figcaption>
</figure>

<p>The automated tier handles most stores that score above normal. A warning letter tells a store its prices sit well above its peers and invites an explanation. A payout delay or volume cap limits exposure without accusing anyone. These actions are reversible and need no investigator time. They also deter: a store that knows the system watches its prices has less reason to inflate them.</p>
<h3 id="74-cost-model">7.4 Cost model<a class="anchor" href="#74-cost-model" aria-label="Link to this section"></a></h3>
<p>Human cost in this design scales with the number of investigators. Transaction volume drives only computing cost, which is small. That property follows from three rules.</p>
<p>First, set the investigator budget first. The program decides how many investigator hours it can fund in a period. That number fixes N, the count of cases the queue can absorb.</p>
<p>Second, review only what the budget covers. The queue takes the top N stores by expected loss. Stores below the cutoff stay in the automated tier, where their scores keep updating. A store that keeps rising will eventually enter the queue.</p>
<p>Third, review a case only when the expected benefit exceeds the review cost. Even within budget, a case with low expected loss should not consume a review. The test is:</p>
<p>expected loss x share prevented or recovered by action &gt; cost of one review</p>
<p>The share prevented or recovered reflects that a review does not recover every dollar. It may end future losses through disqualification, recover part of past losses, and deter others.</p>
<div class="callout callout-caution" role="note"><p class="callout-label">caution</p>
<p><strong>Worked example (illustrative numbers only).</strong> These figures are invented to show the method. They are not estimates of real store behavior or real investigation costs. Assume a review costs $6,000 in investigator time, and a confirmed case prevents or recovers half of the expected annual loss.</p></div>
<div class="table-wrap"><table><thead><tr><th>Store</th><th class="num">Monthly EBT sales</th><th class="num">Estimated excess share</th><th class="num">Excess dollars per year</th><th class="num">Probability of fraud</th><th class="num">Expected loss per year</th><th class="num">Expected benefit of review (50%)</th><th>Review?</th></tr></thead><tbody><tr><td>A</td><td class="num">$60,000</td><td class="num">30%</td><td class="num">$216,000</td><td class="num">0.8</td><td class="num">$172,800</td><td class="num">$86,400</td><td>Yes, rank 1</td></tr><tr><td>B</td><td class="num">$25,000</td><td class="num">40%</td><td class="num">$120,000</td><td class="num">0.6</td><td class="num">$72,000</td><td class="num">$36,000</td><td>Yes, rank 2</td></tr><tr><td>C</td><td class="num">$120,000</td><td class="num">5%</td><td class="num">$72,000</td><td class="num">0.3</td><td class="num">$21,600</td><td class="num">$10,800</td><td>Passes threshold; budget decides</td></tr><tr><td>D</td><td class="num">$15,000</td><td class="num">20%</td><td class="num">$36,000</td><td class="num">0.4</td><td class="num">$14,400</td><td class="num">$7,200</td><td>Passes threshold; budget decides</td></tr><tr><td>E</td><td class="num">$8,000</td><td class="num">10%</td><td class="num">$9,600</td><td class="num">0.2</td><td class="num">$1,920</td><td class="num">$960</td><td>No; automated tier only</td></tr></tbody>
</table></div>
<p>Excess dollars per year equal monthly sales times excess share times 12. For Store A: $60,000 x 0.30 x 12 = $216,000, and $216,000 x 0.8 = $172,800.</p>
<p>Store E fails the threshold: a $6,000 review to protect $960 is a loss. It receives a warning letter and stays under watch. Stores A through D all pass. If the budget covers two reviews this cycle, A and B go to investigators. C and D receive payout delays and volume caps, and they remain at the top of next month's list. Note that Store C has the largest sales but a low excess share and a low probability. Ranking by sales volume alone, a common heuristic, would have sent the wrong store first (<a href="#fig-07-expected-loss-ranking">Figure 16</a>).</p>
<p>The planner ranks stores by expected loss, sends the budget's top N to people, and puts the rest on automatic actions.</p>
<figure class="code"><figcaption>Pseudocode</figcaption><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">plan_reviews</span><span class="p">(</span><span class="n">scores</span><span class="p">,</span> <span class="n">investigator_hours</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">n</span> <span class="o">=</span> <span class="n">investigator_hours</span> <span class="o">//</span> <span class="n">HOURS_PER_REVIEW</span>  <span class="c1"># budget fixes N before the run</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">scores</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Excess share comes from excess prices and supply gap.</span>
</span></span><span class="line"><span class="cl">        <span class="n">s</span><span class="o">.</span><span class="n">expected_loss</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">probability_of_fraud</span> <span class="o">*</span> <span class="n">s</span><span class="o">.</span><span class="n">monthly_ebt_sales</span> <span class="o">*</span> <span class="n">s</span><span class="o">.</span><span class="n">excess_share</span> <span class="o">*</span> <span class="mi">12</span>
</span></span><span class="line"><span class="cl">    <span class="n">ranked</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">scores</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="n">expected_loss</span><span class="p">,</span> <span class="n">descending</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">worth</span> <span class="o">=</span> <span class="p">[</span><span class="n">s</span> <span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">ranked</span> <span class="k">if</span> <span class="n">s</span><span class="o">.</span><span class="n">expected_loss</span> <span class="o">*</span> <span class="n">SHARE_PREVENTED</span> <span class="o">&gt;</span> <span class="n">REVIEW_COST</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">worth</span><span class="p">[</span><span class="n">n</span><span class="p">:]:</span>
</span></span><span class="line"><span class="cl">        <span class="n">apply</span><span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">store</span><span class="p">,</span> <span class="p">[</span><span class="n">Action</span><span class="p">(</span><span class="s2">&#34;payout_delay&#34;</span><span class="p">),</span> <span class="n">Action</span><span class="p">(</span><span class="s2">&#34;volume_cap&#34;</span><span class="p">)])</span>  <span class="c1"># back on next list</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">ranked</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">s</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">worth</span> <span class="ow">and</span> <span class="n">s</span><span class="o">.</span><span class="n">score</span> <span class="o">&gt;</span> <span class="n">NORMAL_SCORE</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">apply</span><span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">store</span><span class="p">,</span> <span class="p">[</span><span class="n">Action</span><span class="p">(</span><span class="s2">&#34;warning_letter&#34;</span><span class="p">)])</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">[</span><span class="n">Case</span><span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">store</span><span class="p">,</span> <span class="n">s</span><span class="o">.</span><span class="n">expected_loss</span><span class="p">)</span> <span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">worth</span><span class="p">[:</span><span class="n">n</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">close_case</span><span class="p">(</span><span class="k">case</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Cleared cases teach the model which patterns are benign.</span>
</span></span><span class="line"><span class="cl">    <span class="n">training_labels</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="k">case</span><span class="o">.</span><span class="n">store_month_score</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="k">case</span><span class="o">.</span><span class="n">outcome</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Worked example: REVIEW_COST 6,000, SHARE_PREVENTED 0.5, N = 2</span>
</span></span><span class="line"><span class="cl"><span class="c1"># store: expected_loss = p x sales x share x 12; x 0.5 vs 6,000; action</span>
</span></span><span class="line"><span class="cl"><span class="c1"># A: 0.8 x 60,000 x 0.30 x 12 = 172,800; 86,400 &gt; 6,000; review, rank 1</span>
</span></span><span class="line"><span class="cl"><span class="c1"># B: 0.6 x 25,000 x 0.40 x 12 = 72,000; 36,000 &gt; 6,000; review, rank 2</span>
</span></span><span class="line"><span class="cl"><span class="c1"># C: 0.3 x 120,000 x 0.05 x 12 = 21,600; 10,800 &gt; 6,000; delay and cap, past N</span>
</span></span><span class="line"><span class="cl"><span class="c1"># D: 0.4 x 15,000 x 0.20 x 12 = 14,400; 7,200 &gt; 6,000; delay and cap, past N</span>
</span></span><span class="line"><span class="cl"><span class="c1"># E: 0.2 x 8,000 x 0.10 x 12 = 1,920; 960 &lt; 6,000; warning letter</span></span></span></code></pre></div></figure>
<figure class="fig" id="fig-07-expected-loss-ranking" data-fk="threshold-rank" data-fk-share="0.5" data-fk-format="usd" data-fk-scale="170 0.002444" data-fk-layout="38 44 14 11 300" data-fk-zone-class="f-fraud f-defense f-muted" data-fk-zone-name="human review|automatic actions|no human review" data-fk-unit="review reviews" data-fk-item-unit="store stores">
  <div class="fig-head"><span class="eyebrow">Figure 16</span><h4>Rank stores by expected loss</h4><p class="dek">Review cost sets the break-even line, and the budget sets how many stores above it get a person.</p></div>
  <div class="fig-body">

    <div style="display:flex;flex-wrap:wrap;align-items:center;gap:0.5rem 0.75rem;margin-bottom:0.75rem;font-size:0.88rem"><span class="chip chip--caution">Illustrative, Section 7.3</span><span>Review if <span class="num" data-fk-tpl="expected loss x 50% recovered &gt; {cost}">expected loss x 50% recovered &gt; $6,000</span> per review, so expected loss must top <span class="num" data-fk-tpl="{threshold}">$12,000</span>.</span></div>
    <svg viewBox="0 0 680 320" role="img" aria-label="Illustrative stores ranked by expected loss: A $172,800 and B $72,000 go to human review; C $21,600 and D $14,400 pass the $12,000 break-even but fall outside a budget of two reviews; E $1,920 is below break-even." data-fk-aria="Illustrative stores ranked by expected loss at a review cost of {cost} and a budget of {n}: human review {list0}; automatic actions {list1}; below the {threshold} break-even {list2}."><rect data-fk-band="0" x="0" y="38" width="680" height="88" rx="6" class="f-fraud-tint"/><text data-fk-band-label="0" x="670" y="110" class="f-fraud" font-size="12" font-weight="700" text-anchor="end">human review</text><rect data-fk-band="1" x="0" y="140" width="680" height="88" rx="6" class="f-defense-tint"/><text data-fk-band-label="1" x="670" y="212" class="f-defense" font-size="12" font-weight="700" text-anchor="end">automatic actions</text><rect data-fk-band="2" x="0" y="228" width="680" height="44" rx="6" class="f-neutral-tint"/><text data-fk-band-label="2" x="670" y="256" class="f-muted" font-size="12" font-weight="700" text-anchor="end">no human review</text><line x1="170" x2="170" y1="34" y2="274" class="s-rule" stroke-width="1"/><text x="170" y="292" class="f-muted mono" font-size="11" text-anchor="middle">$0</text><line x1="292.2" x2="292.2" y1="34" y2="274" class="s-rule" stroke-width="1"/><text x="292.2" y="292" class="f-muted mono" font-size="11" text-anchor="middle">$50k</text><line x1="414.4" x2="414.4" y1="34" y2="274" class="s-rule" stroke-width="1"/><text x="414.4" y="292" class="f-muted mono" font-size="11" text-anchor="middle">$100k</text><line x1="536.6" x2="536.6" y1="34" y2="274" class="s-rule" stroke-width="1"/><text x="536.6" y="292" class="f-muted mono" font-size="11" text-anchor="middle">$150k</text><g data-fk-item="A" data-fk-value="172800" data-fk-y="49" data-group="a" data-tip="Store A: 0.8 x $60,000 x 30% x 12 = $172,800 a year; a review protects $86,400. Zone: human review." data-fk-tip="Store A: 0.8 x $60,000 x 30% x 12 = $172,800 a year; a review protects {benefit}. Zone: {zone}."><rect x="4" y="43" width="672" height="34" fill="transparent"/><text x="12" y="66" class="f-ink" font-size="16" font-weight="800">A</text><text x="34" y="59" class="f-muted mono" font-size="11">$60k x 30%</text><text x="34" y="73" class="f-muted mono" font-size="11">p = 0.8</text><rect data-fk-bar x="170" y="49" width="422.3" height="22" rx="2" class="f-fraud" data-animate="grow" data-step="0"/><text x="598.3" y="65" class="f-ink mono" font-size="12" font-weight="600">$172,800</text></g><g data-fk-item="B" data-fk-value="72000" data-fk-y="93" data-group="b" data-tip="Store B: 0.6 x $25,000 x 40% x 12 = $72,000 a year; a review protects $36,000. Zone: human review." data-fk-tip="Store B: 0.6 x $25,000 x 40% x 12 = $72,000 a year; a review protects {benefit}. Zone: {zone}."><rect x="4" y="87" width="672" height="34" fill="transparent"/><text x="12" y="110" class="f-ink" font-size="16" font-weight="800">B</text><text x="34" y="103" class="f-muted mono" font-size="11">$25k x 40%</text><text x="34" y="117" class="f-muted mono" font-size="11">p = 0.6</text><rect data-fk-bar x="170" y="93" width="176.0" height="22" rx="2" class="f-fraud" data-animate="grow" data-step="0.5"/><text x="352.0" y="109" class="f-ink mono" font-size="12" font-weight="600">$72,000</text></g><g data-fk-item="C" data-fk-value="21600" data-fk-y="151" data-group="c" data-tip="Store C: 0.3 x $120,000 x 5% x 12 = $21,600 a year; a review protects $10,800. Zone: automatic actions." data-fk-tip="Store C: 0.3 x $120,000 x 5% x 12 = $21,600 a year; a review protects {benefit}. Zone: {zone}."><rect x="4" y="145" width="672" height="34" fill="transparent"/><text x="12" y="168" class="f-ink" font-size="16" font-weight="800">C</text><text x="34" y="161" class="f-muted mono" font-size="11">$120k x 5%</text><text x="34" y="175" class="f-muted mono" font-size="11">p = 0.3</text><rect data-fk-bar x="170" y="151" width="52.8" height="22" rx="2" class="f-defense" data-animate="grow" data-step="1"/><text x="228.8" y="167" class="f-ink mono" font-size="12" font-weight="600">$21,600</text></g><g data-fk-item="D" data-fk-value="14400" data-fk-y="195" data-group="d" data-tip="Store D: 0.4 x $15,000 x 20% x 12 = $14,400 a year; a review protects $7,200. Zone: automatic actions." data-fk-tip="Store D: 0.4 x $15,000 x 20% x 12 = $14,400 a year; a review protects {benefit}. Zone: {zone}."><rect x="4" y="189" width="672" height="34" fill="transparent"/><text x="12" y="212" class="f-ink" font-size="16" font-weight="800">D</text><text x="34" y="205" class="f-muted mono" font-size="11">$15k x 20%</text><text x="34" y="219" class="f-muted mono" font-size="11">p = 0.4</text><rect data-fk-bar x="170" y="195" width="35.2" height="22" rx="2" class="f-defense" data-animate="grow" data-step="1.5"/><text x="211.2" y="211" class="f-ink mono" font-size="12" font-weight="600">$14,400</text></g><g data-fk-item="E" data-fk-value="1920" data-fk-y="239" data-group="e" data-tip="Store E: 0.2 x $8,000 x 10% x 12 = $1,920 a year; a review protects $960. Zone: no human review." data-fk-tip="Store E: 0.2 x $8,000 x 10% x 12 = $1,920 a year; a review protects {benefit}. Zone: {zone}."><rect x="4" y="233" width="672" height="34" fill="transparent"/><text x="12" y="256" class="f-ink" font-size="16" font-weight="800">E</text><text x="34" y="249" class="f-muted mono" font-size="11">$8k x 10%</text><text x="34" y="263" class="f-muted mono" font-size="11">p = 0.2</text><rect data-fk-bar x="170" y="239" width="4.7" height="22" rx="2" class="f-muted" data-animate="grow" data-step="2"/><text x="180.7" y="255" class="f-ink mono" font-size="12" font-weight="600">$1,920</text></g><line data-fk-threshold x1="199.3" x2="199.3" y1="20" y2="274" class="s-ink" stroke-width="1.5" stroke-dasharray="5 3"/><text data-fk-threshold-label data-fk-tpl="break-even {threshold}" x="204.3" y="18" class="f-ink" font-size="11" font-weight="700">break-even $12,000</text><line data-fk-cutoff x1="0" x2="680" y1="134" y2="134" class="s-fraud" stroke-width="1.5"/><text data-fk-cutoff-label data-fk-tpl="budget cutoff: N = {n} {unit}" data-fk-tpl-under="budget cutoff: N = {n}, {count0} used" x="670" y="129" class="f-fraud" font-size="11" font-weight="700" text-anchor="end">budget cutoff: N = 2 reviews</text><text x="390" y="310" class="f-muted" font-size="11" text-anchor="middle">expected loss per year = probability of fraud x excess dollars per year</text></svg>
    <div class="fk-controls" hidden data-fk-controls><span data-fk-input="cost" data-fk-label="Review cost" data-fk-range="500 50000 500 6000" data-fk-format="usd"></span><span data-fk-input="budget" data-fk-label="Investigator budget N" data-fk-range="1 5 1 2"></span><span data-fk-reset="Reset to the example"></span><span data-fk-summary="{count0items} to investigators, {count1} to automatic actions, {count2} below cost"></span></div>
    <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,210px),1fr));gap:0.75rem;margin-top:1rem">
      <div class="node node--fraud" data-fk-zone-group="0" data-group="a b"><h5 class="t-fraud" data-fk-tpl="Human review: {list0}">Human review: A, B</h5><p data-fk-tpl="Above break-even and inside a budget of {nword} {unit}.">Above break-even and inside a budget of two reviews.</p></div>
      <div class="node node--defense" data-fk-zone-group="1" data-group="c d"><h5 class="t-defense" data-fk-tpl="Automatic actions: {list1}">Automatic actions: C, D</h5><p>Pass the threshold, fall outside budget. Payout delays and volume caps; they top next month's list.</p></div>
      <div class="node node--muted" data-fk-zone-group="2" data-group="e"><h5 data-fk-tpl="No human review: {list2}">No human review: E</h5><p data-fk-num data-fk-zone="2" data-fk-tpl="A {cost} review to protect {lowbenefit} is a loss. Warning letter, stays under watch." data-fk-tpl-none="Every store clears the review cost at this price.">A <span class="num">$6,000</span> review to protect <span class="num">$960</span> is a loss. Warning letter, stays under watch.</p></div>
      <div class="node node--caution" data-group="c"><h5 class="t-caution">Sales volume misleads</h5><p>Store C has the largest sales, <span class="num">$120k</span> a month, but ranks third. Ranking by sales would send the wrong store first.</p></div>
    </div>
  </div>
  <figcaption><strong>Read it as:</strong> bars left of the dashed line cost more to review than they could recover. Of the stores to its right, only the top N fit the budget; the rest get automatic, reversible actions.</figcaption>
</figure>

<h3 id="75-recipient-side-reporting">7.5 Recipient-side reporting<a class="anchor" href="#75-recipient-side-reporting" aria-label="Link to this section"></a></h3>
<p>Recipients can supply a free signal against overcharging. The recipient app should show an itemized receipt for each EBT purchase, with a &quot;price looks wrong&quot; button on each line. Reports feed the store's score as one more feature, weighted by the reporter's history so that one angry customer cannot sink a store. Reports also help the household directly, by showing what each item cost.</p>
<p>This channel does nothing against trafficking. The trafficking shopper colludes with the store and will never press the button. Recipient reporting is a tool for the predatory form of overcharging, where the store cheats its customer. That distinction should shape how the program weights these reports: they are strong evidence of predatory pricing and no evidence about trafficking.</p>
<h2 id="8-trade-offs-and-harms">8. Trade-offs and harms<a class="anchor" href="#8-trade-offs-and-harms" aria-label="Link to this section"></a></h2>
<p>Every control in this report imposes costs on honest participants. The table lists the main ones (<a href="#fig-08-tradeoff-map">Figure 17</a>).</p>
<div class="table-wrap"><table><thead><tr><th>Control</th><th>Fraud it targets</th><th>Cost or harm</th><th>Mitigation</th></tr></thead><tbody><tr><td>Item-level basket tracking</td><td>Overcharging, ineligible sales, fake baskets</td><td>Privacy: the state records what poor people eat. Data could be used for purposes beyond integrity, such as food-choice policing</td><td>Data minimization, retention limits, statutory purpose limits, aggregation for research</td></tr><tr><td>Strict store rules (stocking, scanning)</td><td>Front stores, trafficking</td><td>Food-desert access loss when small stores leave the program</td><td>Subsidized point-of-sale equipment, exceptions for underserved areas, phased deadlines</td></tr><tr><td>Payout holds and delays</td><td>Hit-and-run trafficking, unrecoverable losses</td><td>Honest small stores face cash flow strain</td><td>Time limits on holds, partial holds, standard tier for clean history</td></tr><tr><td>Aggressive scoring</td><td>All store-side fraud</td><td>False positives; slow appeals can close an honest store</td><td>Appeal deadlines with service levels, independent review, publication of error rates</td></tr><tr><td>Heavier eligibility checks</td><td>Eligibility fraud, duplicates</td><td>Eligible people drop out because of paperwork and delays</td><td>Use data matches to reduce paperwork, request documents only on mismatch</td></tr><tr><td>Step-up confirmation for large spends</td><td>Skimming drains</td><td>Delays for recipients without phones</td><td>Fail-open path, opt-in, store-based fallback</td></tr></tbody>
</table></div>
<figure class="fig" id="fig-08-tradeoff-map">
  <div class="fig-head"><span class="eyebrow">Figure 17</span><h4>Every control has a cost</h4><p class="dek">Each control stops some fraud and harms some honest participants, and the mitigation decides how much harm remains.</p></div>
  <div class="fig-body">

    <div style="display:flex;justify-content:center;margin-bottom:0.25rem"><span class="chip chip--caution">Qualitative placement: judgment, not measurement</span></div>
    <div style="max-width:620px;margin-inline:auto"><svg viewBox="0 0 520 380" role="img" aria-label="Qualitative map of six controls by fraud stopped and harm to honest participants. Item-level tracking, strict store rules, and aggressive scoring stop the most fraud but carry high harm; step-up confirmation carries the least harm."><rect x="70" y="30" width="215" height="155" class="f-fraud-tint" fill-opacity="0.6"/><rect x="285" y="185" width="215" height="155" class="f-defense-tint" fill-opacity="0.6"/><line x1="285" x2="285" y1="30" y2="340" class="s-rule" stroke-width="1"/><line x1="70" x2="500" y1="185" y2="185" class="s-rule" stroke-width="1"/><path d="M70 30 V340 H500" fill="none" class="s-muted" stroke-width="1.2"/><text x="78" y="46" class="f-fraud" font-size="11" font-weight="700">much harm, little fraud stopped</text><text x="492" y="330" class="f-defense" font-size="11" font-weight="700" text-anchor="end">little harm, much fraud stopped</text><text x="70" y="356" class="f-muted" font-size="11">less</text><text x="500" y="356" class="f-muted" font-size="11" text-anchor="end">more</text><text x="285" y="374" class="f-ink" font-size="12" font-weight="700" text-anchor="middle">fraud stopped</text><text x="62" y="38" class="f-muted" font-size="11" text-anchor="end">more</text><text x="62" y="340" class="f-muted" font-size="11" text-anchor="end">less</text><text x="26" y="185" class="f-ink" font-size="12" font-weight="700" text-anchor="middle" transform="rotate(-90 26 185)">harm to honest participants</text><text data-group="item-level" x="353.6" y="83.8" class="f-ink" font-size="12" font-weight="700" text-anchor="start">Item-level tracking</text><text data-group="item-level" x="353.6" y="97.8" class="f-fraud" font-size="11" text-anchor="start">privacy</text><circle data-tip="Item-level tracking. Harm: privacy. Mitigation: data minimization, retention limits, statutory purpose limits, aggregation." data-group="item-level" data-animate="pop" data-step="0" cx="336.6" cy="85.8" r="12" class="f-defense"/><text data-group="item-level" data-animate="pop" data-step="0" x="336.6" y="89.8" class="f-surface mono" font-size="12" font-weight="600" text-anchor="middle">1</text><text data-group="strict-store" x="397" y="133.4" class="f-ink" font-size="12" font-weight="700" text-anchor="end">Strict store rules</text><text data-group="strict-store" x="397" y="147.4" class="f-fraud" font-size="11" text-anchor="end">food-desert access loss</text><circle data-tip="Strict store rules. Harm: food-desert access loss. Mitigation: subsidized point-of-sale equipment, exceptions, phased deadlines." data-group="strict-store" data-animate="pop" data-step="0.5" cx="414" cy="135.4" r="12" class="f-defense"/><text data-group="strict-store" data-animate="pop" data-step="0.5" x="414" y="139.4" class="f-surface mono" font-size="12" font-weight="600" text-anchor="middle">2</text><text data-group="payout-holds" x="293.4" y="214" class="f-ink" font-size="12" font-weight="700" text-anchor="start">Payout holds</text><text data-group="payout-holds" x="293.4" y="228" class="f-fraud" font-size="11" text-anchor="start">cash flow strain</text><circle data-tip="Payout holds. Harm: cash flow strain for honest small stores. Mitigation: time limits, partial holds, standard tier for clean history." data-group="payout-holds" data-animate="pop" data-step="1" cx="276.4" cy="216" r="12" class="f-defense"/><text data-group="payout-holds" data-animate="pop" data-step="1" x="276.4" y="220" class="f-surface mono" font-size="12" font-weight="600" text-anchor="middle">3</text><text data-group="scoring" x="440" y="183" class="f-ink" font-size="12" font-weight="700" text-anchor="end">Aggressive scoring</text><text data-group="scoring" x="440" y="197" class="f-fraud" font-size="11" text-anchor="end">false positives</text><circle data-tip="Aggressive scoring. Harm: false positives. Mitigation: appeal deadlines with service levels, independent review, published error rates." data-group="scoring" data-animate="pop" data-step="1.5" cx="457" cy="185" r="12" class="f-defense"/><text data-group="scoring" data-animate="pop" data-step="1.5" x="457" y="189" class="f-surface mono" font-size="12" font-weight="600" text-anchor="middle">4</text><text data-group="eligibility" x="182" y="108.6" class="f-ink" font-size="12" font-weight="700" text-anchor="end">Eligibility checks</text><text data-group="eligibility" x="182" y="122.6" class="f-fraud" font-size="11" text-anchor="end">eligible people drop out</text><circle data-tip="Eligibility checks. Harm: eligible people drop out. Mitigation: data matches to cut paperwork, documents only on mismatch." data-group="eligibility" data-animate="pop" data-step="2" cx="199" cy="110.6" r="12" class="f-defense"/><text data-group="eligibility" data-animate="pop" data-step="2" x="199" y="114.6" class="f-surface mono" font-size="12" font-weight="600" text-anchor="middle">5</text><text data-group="step-up" x="259" y="276" class="f-ink" font-size="12" font-weight="700" text-anchor="start">Step-up confirmation</text><text data-group="step-up" x="259" y="290" class="f-fraud" font-size="11" text-anchor="start">delays without a phone</text><circle data-tip="Step-up confirmation. Harm: delays without a phone. Mitigation: fail-open path, opt-in, store-based fallback." data-group="step-up" data-animate="pop" data-step="2.5" cx="242" cy="278" r="12" class="f-defense"/><text data-group="step-up" data-animate="pop" data-step="2.5" x="242" y="282" class="f-surface mono" font-size="12" font-weight="600" text-anchor="middle">6</text></svg></div>
    <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,260px),1fr));gap:0.75rem;margin-top:1.25rem">
      <div class="node" data-group="item-level">
        <h5><span class="num" style="display:inline-grid;place-items:center;width:1.5em;height:1.5em;margin-right:0.4em;border-radius:50%;background:var(--defense);color:var(--surface);font-size:0.8rem">1</span>Item-level basket tracking</h5>
        <p><strong style="color:var(--ink)">Targets:</strong> overcharging, ineligible sales, fake baskets</p>
        <p><strong class="t-fraud">Harm:</strong> privacy: the state records what poor people eat, and could use it for food-choice policing</p>
        <p><strong class="t-defense">Mitigation:</strong> data minimization, retention limits, statutory purpose limits, aggregation for research</p>
      </div>
      <div class="node" data-group="strict-store">
        <h5><span class="num" style="display:inline-grid;place-items:center;width:1.5em;height:1.5em;margin-right:0.4em;border-radius:50%;background:var(--defense);color:var(--surface);font-size:0.8rem">2</span>Strict store rules</h5>
        <p><strong style="color:var(--ink)">Targets:</strong> front stores, trafficking</p>
        <p><strong class="t-fraud">Harm:</strong> food-desert access loss when small stores leave the program</p>
        <p><strong class="t-defense">Mitigation:</strong> subsidized point-of-sale equipment, exceptions for underserved areas, phased deadlines</p>
      </div>
      <div class="node" data-group="payout-holds">
        <h5><span class="num" style="display:inline-grid;place-items:center;width:1.5em;height:1.5em;margin-right:0.4em;border-radius:50%;background:var(--defense);color:var(--surface);font-size:0.8rem">3</span>Payout holds and delays</h5>
        <p><strong style="color:var(--ink)">Targets:</strong> hit-and-run trafficking, unrecoverable losses</p>
        <p><strong class="t-fraud">Harm:</strong> honest small stores face cash flow strain</p>
        <p><strong class="t-defense">Mitigation:</strong> time limits on holds, partial holds, standard tier for clean history</p>
      </div>
      <div class="node" data-group="scoring">
        <h5><span class="num" style="display:inline-grid;place-items:center;width:1.5em;height:1.5em;margin-right:0.4em;border-radius:50%;background:var(--defense);color:var(--surface);font-size:0.8rem">4</span>Aggressive scoring</h5>
        <p><strong style="color:var(--ink)">Targets:</strong> all store-side fraud</p>
        <p><strong class="t-fraud">Harm:</strong> false positives; slow appeals can close an honest store</p>
        <p><strong class="t-defense">Mitigation:</strong> appeal deadlines with service levels, independent review, published error rates</p>
      </div>
      <div class="node" data-group="eligibility">
        <h5><span class="num" style="display:inline-grid;place-items:center;width:1.5em;height:1.5em;margin-right:0.4em;border-radius:50%;background:var(--defense);color:var(--surface);font-size:0.8rem">5</span>Heavier eligibility checks</h5>
        <p><strong style="color:var(--ink)">Targets:</strong> eligibility fraud, duplicates</p>
        <p><strong class="t-fraud">Harm:</strong> eligible people drop out because of paperwork and delays</p>
        <p><strong class="t-defense">Mitigation:</strong> data matches to cut paperwork, documents only on mismatch</p>
      </div>
      <div class="node" data-group="step-up">
        <h5><span class="num" style="display:inline-grid;place-items:center;width:1.5em;height:1.5em;margin-right:0.4em;border-radius:50%;background:var(--defense);color:var(--surface);font-size:0.8rem">6</span>Step-up confirmation for large spends</h5>
        <p><strong style="color:var(--ink)">Targets:</strong> skimming drains</p>
        <p><strong class="t-fraud">Harm:</strong> delays for recipients without phones</p>
        <p><strong class="t-defense">Mitigation:</strong> fail-open path, opt-in, store-based fallback</p>
      </div>
    </div>
  </div>
  <figcaption><strong>Read it as:</strong> no control sits in the safe corner on its own. The strongest controls carry the most harm, so each one ships with its mitigation or honest households and stores pay for the fraud.</figcaption>
</figure>

<p><strong>Privacy.</strong> Item-level data is the most sensitive control. Once the state holds a record of every item every SNAP household buys, pressure will follow to use it for diet policing, immigration enforcement, or commercial sale. The data design should limit use by statute to program integrity and approved research. It should keep raw baskets for a short period, such as 13 months, then retain only store-level aggregates. It should separate identity from basket data so that analysts work with tokenized card numbers, and only a case opened by an investigator can resolve a token to a household.</p>
<p><strong>Access.</strong> Small stores are often the only food retailers in poor urban and rural areas. They also carry most trafficking. A rule that removes trafficking stores also removes some honest ones, and each loss can strand households far from a supermarket. Mitigation requires subsidy for equipment and an explicit access test before disqualifying the last authorized store in an area, with a path to a civil money penalty instead.</p>
<p><strong>Due process.</strong> ALERT cases already rest mostly on transaction data, and stores have ten days to respond to a charge letter. Adding automated holds and caps raises the stakes of a wrong score. The program should publish appeal timelines, track how often automated actions reverse on appeal, and pay interest on wrongful holds.</p>
<p><strong>Human review before cutoff.</strong> No household should lose benefits because of an automated score. Scores can open a case. A person decides it, with notice and a hearing, as 7 CFR 273.16 already requires for intentional program violations.</p>
<h2 id="9-the-policy-alternative">9. The policy alternative<a class="anchor" href="#9-the-policy-alternative" aria-label="Link to this section"></a></h2>
<p>Trafficking exists because SNAP benefits are restricted to food at authorized stores while cash is unrestricted. If the program paid benefits as unrestricted cash, trafficking would end by definition, because there would be no discount between a benefit dollar and a cash dollar for anyone to capture. The retailer authorization system, ALERT, undercover buys, and most of the merchant-side architecture in this report would become unnecessary.</p>
<p>That is a policy choice, and it carries its own debates. Supporters point to lower administrative cost, more dignity and flexibility for households, and economic research finding that, for most households, SNAP affects food spending much as an equal amount of cash would. Opponents argue that restricting benefits to food is the program's purpose and the basis of its political support, that cash could shift spending away from food for some households, and that the change would remove an effect that supports grocery retailers. A middle path, such as a partial cash-out, would shrink the trafficking discount without ending it.</p>
<p>This report takes no position on that question. The point for designers is that the software architecture addresses a problem the policy creates. If policy changes, the integrity problem changes with it: eligibility fraud and payment theft remain, and trafficking disappears.</p>
<h2 id="10-implementation-roadmap">10. Implementation roadmap<a class="anchor" href="#10-implementation-roadmap" aria-label="Link to this section"></a></h2>
<p>The architecture is too large to deploy at once, and some parts depend on others. A three-phase rollout orders the work by cost, dependency, and the size of the loss each step addresses (<a href="#fig-10-roadmap">Figure 18</a>).</p>
<figure class="fig" id="fig-10-roadmap">
  <div class="fig-head"><span class="eyebrow">Figure 18</span><h4>Implementation roadmap: three phases, four parties</h4><p class="dek">Ship the cheap controls that need no new standard first, then item data, then analytics, and measure each phase.</p></div>
  <div class="fig-body">
    <div class="rm" role="group" aria-label="Three-phase roadmap. Phase 1, credentials and payout risk. Phase 2, an item-level data standard. Phase 3, analytics and the investigation queue. Each phase lists what retailers, point-of-sale vendors, states, and FNS must do, and what the phase measures.">
      <div class="rm-lanes" aria-hidden="true">
        <span class="rm-corner">Order set by cost, dependency, and loss size; no dates</span>
        <span>Retailers</span>
        <span>POS vendors</span>
        <span>States</span>
        <span>FNS<small>federal agency</small></span>
        <span class="rm-lane-m">Measure</span>
      </div>

      <div class="rm-phase">
        <div class="rm-head rm-head--1"><span class="rm-ph">Phase 1</span><h5>Credentials and payout risk</h5><p>No new data standard needed. Attacks skimming and hit-and-run trafficking.</p></div>
        <div class="rm-cell"><b class="rm-who">Retailers</b><ul><li>Upgrade terminals for chip and tap</li><li>Accept payout tier rules</li></ul></div>
        <div class="rm-cell"><b class="rm-who">POS vendors</b><ul><li>Support EMV for EBT</li><li>End stripe fallback by the deadline</li></ul></div>
        <div class="rm-cell"><b class="rm-who">States</b><ul><li>Issue chip cards</li><li>Run the recipient app or approve third-party apps</li><li>Vary load timing</li></ul></div>
        <div class="rm-cell"><b class="rm-who">FNS</b><ul><li>Set chip standards and the fallback deadline</li><li>Implement payout tiers in settlement, using the existing ALERT score</li></ul></div>
        <div class="rm-cell rm-measure"><b class="rm-who">Measure</b><ul><li>Skimming claims per thousand cards, before and after the switch</li></ul></div>
      </div>

      <div class="rm-phase">
        <div class="rm-head rm-head--2"><span class="rm-ph">Phase 2</span><h5>Item-level data standard</h5><p>Basket standard, certified POS, ineligible items rejected, reference prices begun. Privacy rules come first.</p></div>
        <div class="rm-cell"><b class="rm-who">Retailers</b><ul><li>Install certified registers or the subsidized app</li><li>Keep product price lists current</li></ul></div>
        <div class="rm-cell"><b class="rm-who">POS vendors</b><ul><li>Implement the basket message standard</li><li>Pass certification</li></ul></div>
        <div class="rm-cell"><b class="rm-who">States</b><ul><li>Fund outreach to small stores</li><li>Update EBT processor contracts</li></ul></div>
        <div class="rm-cell"><b class="rm-who">FNS</b><ul><li>Publish the standard; certify vendors</li><li>Adopt privacy and retention rules</li></ul></div>
        <div class="rm-cell rm-measure"><b class="rm-who">Measure</b><ul><li>Share of EBT volume carrying item data</li><li>Count of small stores that left the program</li></ul></div>
      </div>

      <div class="rm-phase">
        <div class="rm-head rm-head--3"><span class="rm-ph">Phase 3</span><h5>Analytics and the investigation queue</h5><p>Reconciliation, card-store graph, store-month scoring, a budget-sized queue, case feedback.</p></div>
        <div class="rm-cell"><b class="rm-who">Retailers</b><ul><li>Keep purchase invoices</li><li>Submit them on request</li><li>Accept audits</li></ul></div>
        <div class="rm-cell"><b class="rm-who">POS vendors</b><ul><li>Provide data hooks for catalog updates</li></ul></div>
        <div class="rm-cell"><b class="rm-who">States</b><ul><li>Join graph-based recipient investigations</li><li>Resolve NAC and income matches quickly</li></ul></div>
        <div class="rm-cell"><b class="rm-who">FNS</b><ul><li>Contract distributor data feeds</li><li>Build scoring and the queue</li><li>Publish appeal metrics</li></ul></div>
        <div class="rm-cell rm-measure"><b class="rm-who">Measure</b><ul><li>Confirmation rate of reviewed cases</li><li>Reversal rates on appeal</li><li>Trafficking estimate from the next FNS study</li></ul></div>
      </div>
    </div>
  </div>
  <figcaption><strong>Read it as:</strong> read across a row to see one party's work grow phase by phase, and down a column to see what one phase asks of everyone. Without the measure row, the program cannot tell whether controls pay for themselves.</figcaption>
</figure>

<p><strong>Phase 1: credentials and payout risk.</strong> Complete the national move to chip and contactless EBT cards. Launch a recipient app with card lock, geography and online blocks, and per-swipe alerts. Introduce merchant risk tiers using the existing ALERT score: probation for new stores, settlement delays and caps for high tiers. These steps need no new data standard and attack skimming and hit-and-run trafficking directly.</p>
<p><strong>Phase 2: item-level data standard.</strong> Define and publish an item-level basket standard for EBT messages. Certify point-of-sale vendors against it. Fund a low-cost certified option for small stores. Turn on real-time rejection of ineligible items. Begin building reference prices and per-store product catalogs. Set privacy rules in regulation before data collection starts.</p>
<p><strong>Phase 3: analytics and the investigation queue.</strong> Add inventory reconciliation with distributor feeds and purchase-record requirements. Build the card-store graph. Deploy store-month price scoring and expected-loss ranking. Size the investigation queue to the budget and run the feedback loop from closed cases.</p>
<div class="table-wrap"><table><thead><tr><th>Phase</th><th>Retailers</th><th>Point-of-sale vendors</th><th>States</th><th>FNS</th></tr></thead><tbody><tr><td>1</td><td>Upgrade terminals for chip and tap; accept payout tier rules</td><td>Support EMV for EBT; end stripe fallback by deadline</td><td>Issue chip cards; run the recipient app or approve third-party apps; vary load timing</td><td>Set chip standards and fallback deadline; implement payout tiers in settlement</td></tr><tr><td>2</td><td>Install certified registers or the subsidized app; keep product price lists current</td><td>Implement the basket message standard; pass certification</td><td>Fund outreach to small stores; update EBT processor contracts</td><td>Publish the standard; certify vendors; adopt privacy and retention rules</td></tr><tr><td>3</td><td>Keep purchase invoices; submit them on request; accept audits</td><td>Provide data hooks for catalog updates</td><td>Join graph-based recipient investigations; resolve NAC and income matches quickly</td><td>Contract distributor data feeds; build scoring and the queue; publish appeal metrics</td></tr></tbody>
</table></div>
<p>Each phase should ship with measurement. Phase 1 should report skimming claims per thousand cards before and after the switch. Phase 2 should report the share of EBT volume carrying item data and the count of small stores that left the program. Phase 3 should report the confirmation rate of reviewed cases, reversal rates on appeal, and estimated trafficking from the next FNS study. Without measurement, the program cannot tell whether controls pay for themselves.</p>
<h2 id="11-conclusion">11. Conclusion<a class="anchor" href="#11-conclusion" aria-label="Link to this section"></a></h2>
<p>SNAP fraud is smaller and more specific than public debate suggests. The headline error rate near 11 percent measures payment accuracy, and it counts mistakes in both directions. The best estimate of trafficking, the program's signature fraud, is about 1.6 to 2.0 percent of benefits, concentrated in small stores. Skimming is a separate problem whose victims are recipients.</p>
<p>Trafficking persists because the EBT record carries amounts without items and because both parties to the lie gain from it. Controls that rely on data the shopper or the store enters cannot fully solve it. The architecture in this report responds in layers: verify eligibility from outside sources before the first payment, harden the card, carry items in the transaction, manage payouts the way processors manage risky merchants, and reconcile what stores sell against what their suppliers delivered. Price anomaly detection runs at the store-month level, ranks stores by expected loss, and sends only what the budget covers to people, so that oversight cost tracks investigator headcount.</p>
<p>Each control harms some honest participants, through lost privacy, lost store access, delayed payouts, or wrongful flags. Those costs belong in the same calculation as the fraud the controls stop. A program that counts them, publishes its error and reversal rates, and keeps a person between any score and a family's food will protect both the budget and the trust that the program depends on.</p>
<h2 id="appendix-a-uml-models">Appendix A. UML models<a class="anchor" href="#appendix-a-uml-models" aria-label="Link to this section"></a></h2>
<p>These models turn the design into operations and software specifications. The operations models come first.</p>
<h3 id="a1-operations">A.1 Operations<a class="anchor" href="#a1-operations" aria-label="Link to this section"></a></h3>
<p>The figures so far follow the money and the fraud. The next four show the operations side in UML, the notation software teams use to specify systems. The first is a use case diagram. It names each party that touches the integrity program and what each one does there, from the household and the store to the federal retailer analyst at FNS (renamed FNA in 2026).</p>
<p>Two points stand out. Recipients and retailers run controls themselves: a household locks its own card, and a store submits its own purchase records. And every sanction rests with a person, as section 8 requires.</p>
<figure class="fig" id="uml-ops-use-cases">
  <div class="fig-head"><span class="eyebrow">Figure A1</span><h4>Who does what in the integrity program</h4><p class="dek">Recipients and retailers use the program; caseworkers, analysts, and investigators police it. A person decides every sanction.</p></div>
  <div class="fig-body">
    <svg data-fk="graph" viewBox="0 0 1000 910" role="img" aria-label="UML use case diagram of the SNAP integrity program. Recipients apply, buy groceries, lock cards, and report wrong prices. Retailers sell, receive payouts, submit purchase records, and appeal. Caseworkers verify eligibility and replace stolen benefits. Federal analysts review flagged stores and impose sanctions; investigators run undercover buys. A person decides every sanction." xmlns="http://www.w3.org/2000/svg">
      <defs><marker id="uml-ops-use-cases-a" viewBox="0 0 12 12" refX="11" refY="6" markerWidth="12" markerHeight="12" markerUnits="userSpaceOnUse" orient="auto"><path d="M1,1.5 L11,6 L1,10.5" fill="none" class="s-ink" stroke-width="1.5"/></marker><marker id="uml-ops-use-cases-am" viewBox="0 0 12 12" refX="11" refY="6" markerWidth="12" markerHeight="12" markerUnits="userSpaceOnUse" orient="auto"><path d="M1,1.5 L11,6 L1,10.5" fill="none" class="s-muted" stroke-width="1.5"/></marker></defs>
      <rect data-fk-area data-tip="System boundary: use cases inside the SNAP integrity program, actors outside." x="196" y="40" width="608" height="740" rx="6" fill="none" class="s-ink" stroke-width="1.4"/>
      <text x="212" y="63" font-size="13" text-anchor="start" class="f-ink" font-weight="700">SNAP integrity program</text>
      <ellipse data-fk-node="apply" data-tip="Apply for benefits: a recipient applies to the state program." data-n="1" cx="300" cy="110" rx="92" ry="27" class="f-surface s-ink" stroke-width="1.4"/>
      <text data-fk-node="apply" x="300" y="114.2" font-size="12" text-anchor="middle" class="f-ink">Apply for benefits</text>
      <ellipse data-fk-node="verify" data-tip="Verify eligibility (data match): a caseworker checks the application against data sources." data-n="1" cx="700" cy="110" rx="92" ry="27" class="f-defense-tint s-defense" stroke-width="1.4"/>
      <text data-fk-node="verify" x="700" y="107.2" font-size="12" text-anchor="middle" class="f-ink">Verify eligibility</text>
      <text data-fk-node="verify" x="700" y="121.2" font-size="12" text-anchor="middle" class="f-ink">(data match)</text>
      <ellipse data-fk-node="lock" data-tip="Lock card / report skimming: a recipient locks a card in the app and reports theft." data-n="1" cx="300" cy="195" rx="92" ry="27" class="f-defense-tint s-defense" stroke-width="1.4"/>
      <text data-fk-node="lock" x="300" y="192.2" font-size="12" text-anchor="middle" class="f-ink">Lock card /</text>
      <text data-fk-node="lock" x="300" y="206.2" font-size="12" text-anchor="middle" class="f-ink">report skimming</text>
      <ellipse data-fk-node="replace" data-tip="Replace stolen benefits: a caseworker restores stolen benefits when replacement is available." data-n="1" cx="700" cy="195" rx="92" ry="27" class="f-surface s-ink" stroke-width="1.4"/>
      <text data-fk-node="replace" x="700" y="192.2" font-size="12" text-anchor="middle" class="f-ink">Replace stolen</text>
      <text data-fk-node="replace" x="700" y="206.2" font-size="12" text-anchor="middle" class="f-ink">benefits</text>
      <ellipse data-fk-node="wrongprice" data-tip="Report a wrong price: a recipient flags a price charged above the shelf price." data-n="1" cx="300" cy="280" rx="92" ry="27" class="f-surface s-ink" stroke-width="1.4"/>
      <text data-fk-node="wrongprice" x="300" y="277.2" font-size="12" text-anchor="middle" class="f-ink">Report a</text>
      <text data-fk-node="wrongprice" x="300" y="291.2" font-size="12" text-anchor="middle" class="f-ink">wrong price</text>
      <ellipse data-fk-node="certify" data-tip="Certify POS system: a POS vendor certifies its checkout system for the program." data-n="1" cx="700" cy="280" rx="92" ry="27" class="f-surface s-ink" stroke-width="1.4"/>
      <text data-fk-node="certify" x="700" y="277.2" font-size="12" text-anchor="middle" class="f-ink">Certify POS</text>
      <text data-fk-node="certify" x="700" y="291.2" font-size="12" text-anchor="middle" class="f-ink">system</text>
      <ellipse data-fk-node="buy" data-tip="Buy groceries: a recipient pays with a benefit card, or a retailer completes the sale." data-n="1" cx="700" cy="365" rx="92" ry="27" class="f-surface s-ink" stroke-width="1.4"/>
      <text data-fk-node="buy" x="700" y="369.2" font-size="12" text-anchor="middle" class="f-ink">Buy groceries</text>
      <ellipse data-fk-node="validate" data-tip="Validate basket: the program checks the purchase against its rules." data-n="1" cx="500" cy="465" rx="92" ry="27" class="f-defense-tint s-defense" stroke-width="1.4"/>
      <text data-fk-node="validate" x="500" y="469.2" font-size="12" text-anchor="middle" class="f-ink">Validate basket</text>
      <ellipse data-fk-node="payout" data-tip="Receive payout: a retailer is paid for redeemed benefits." data-n="1" cx="700" cy="465" rx="92" ry="27" class="f-surface s-ink" stroke-width="1.4"/>
      <text data-fk-node="payout" x="700" y="469.2" font-size="12" text-anchor="middle" class="f-ink">Receive payout</text>
      <ellipse data-fk-node="review" data-tip="Review flagged store: a federal analyst examines a store the score flagged." data-n="1" cx="500" cy="555" rx="92" ry="27" class="f-caution-tint s-caution" stroke-width="1.4"/>
      <text data-fk-node="review" x="500" y="552.2" font-size="12" text-anchor="middle" class="f-ink">Review</text>
      <text data-fk-node="review" x="500" y="566.2" font-size="12" text-anchor="middle" class="f-ink">flagged store</text>
      <ellipse data-fk-node="hold" data-tip="Hold payout: the program holds a store's payout when the risk score is high." data-n="1" cx="700" cy="555" rx="92" ry="27" class="f-defense-tint s-defense" stroke-width="1.4"/>
      <text data-fk-node="hold" x="700" y="559.2" font-size="12" text-anchor="middle" class="f-ink">Hold payout</text>
      <ellipse data-fk-node="sanction" data-tip="Impose sanction: a person decides the penalty, with notice and a hearing." data-n="1" cx="300" cy="645" rx="92" ry="27" class="f-fraud-tint s-fraud" stroke-width="1.4"/>
      <text data-fk-node="sanction" x="300" y="649.2" font-size="12" text-anchor="middle" class="f-ink">Impose sanction</text>
      <ellipse data-fk-node="appeal" data-tip="Appeal a decision: a retailer contests a sanction." data-n="1" cx="700" cy="645" rx="92" ry="27" class="f-caution-tint s-caution" stroke-width="1.4"/>
      <text data-fk-node="appeal" x="700" y="649.2" font-size="12" text-anchor="middle" class="f-ink">Appeal a decision</text>
      <ellipse data-fk-node="undercover" data-tip="Run undercover buy: an investigator tests a store with a covert purchase." data-n="1" cx="500" cy="735" rx="92" ry="27" class="f-surface s-ink" stroke-width="1.4"/>
      <text data-fk-node="undercover" x="500" y="732.2" font-size="12" text-anchor="middle" class="f-ink">Run undercover</text>
      <text data-fk-node="undercover" x="500" y="746.2" font-size="12" text-anchor="middle" class="f-ink">buy</text>
      <ellipse data-fk-node="submit" data-tip="Submit purchase records: a retailer or supplier provides purchase records." data-n="1" cx="700" cy="735" rx="92" ry="27" class="f-surface s-ink" stroke-width="1.4"/>
      <text data-fk-node="submit" x="700" y="732.2" font-size="12" text-anchor="middle" class="f-ink">Submit purchase</text>
      <text data-fk-node="submit" x="700" y="746.2" font-size="12" text-anchor="middle" class="f-ink">records</text>
      <circle data-fk-node="recipient" data-tip="Recipient: a household member who uses benefits." data-n="1" cx="100" cy="270" r="8" class="f-surface s-ink" stroke-width="1.5"/>
      <path data-fk-node="recipient" d="M100,278 V302 M86,288 H114 M100,302 L89,320 M100,302 L111,320" fill="none" class="s-ink" stroke-width="1.5"/>
      <text data-fk-node="recipient" x="100" y="337" font-size="12" text-anchor="middle" class="f-ink" font-weight="600">Recipient</text>
      <path data-fk-edge="recipient apply" data-fk-part="as0" data-tip="Association: recipient takes part in apply." d="M118,290 L235.6,128.9" fill="none" class="s-ink" stroke-width="1.2"/>
      <path data-fk-edge="recipient lock" data-fk-part="as1" data-tip="Association: recipient takes part in lock." d="M118,290 L227.1,211.2" fill="none" class="s-ink" stroke-width="1.2"/>
      <path data-fk-edge="recipient wrongprice" data-fk-part="as2" data-tip="Association: recipient takes part in wrongprice." d="M118,290 L208.7,283.1" fill="none" class="s-ink" stroke-width="1.2"/>
      <path data-fk-edge="recipient buy" data-fk-part="as3" data-tip="Association: recipient takes part in buy." d="M118,290 L610.9,359.6" fill="none" class="s-ink" stroke-width="1.2"/>
      <circle data-fk-node="analyst" data-tip="Federal retailer analyst: reviews flagged stores and imposes sanctions." data-n="1" cx="100" cy="570" r="8" class="f-surface s-ink" stroke-width="1.5"/>
      <path data-fk-node="analyst" d="M100,578 V602 M86,588 H114 M100,602 L89,620 M100,602 L111,620" fill="none" class="s-ink" stroke-width="1.5"/>
      <text data-fk-node="analyst" x="100" y="637" font-size="12" text-anchor="middle" class="f-ink" font-weight="600">Federal retailer</text>
      <text data-fk-node="analyst" x="100" y="651" font-size="12" text-anchor="middle" class="f-ink" font-weight="600">analyst</text>
      <path data-fk-edge="analyst review" data-fk-part="as4" data-tip="Association: analyst takes part in review." d="M118,590 L409.6,559.2" fill="none" class="s-ink" stroke-width="1.2"/>
      <path data-fk-edge="analyst sanction" data-fk-part="as5" data-tip="Association: analyst takes part in sanction." d="M118,590 L219.2,632.4" fill="none" class="s-ink" stroke-width="1.2"/>
      <circle data-fk-node="caseworker" data-tip="State caseworker: verifies eligibility and replaces stolen benefits." data-n="1" cx="900" cy="115" r="8" class="f-surface s-ink" stroke-width="1.5"/>
      <path data-fk-node="caseworker" d="M900,123 V147 M886,133 H914 M900,147 L889,165 M900,147 L911,165" fill="none" class="s-ink" stroke-width="1.5"/>
      <text data-fk-node="caseworker" x="900" y="182" font-size="12" text-anchor="middle" class="f-ink" font-weight="600">State</text>
      <text data-fk-node="caseworker" x="900" y="196" font-size="12" text-anchor="middle" class="f-ink" font-weight="600">caseworker</text>
      <path data-fk-edge="caseworker verify" data-fk-part="as6" data-tip="Association: caseworker takes part in verify." d="M882,135 L788.4,117.2" fill="none" class="s-ink" stroke-width="1.2"/>
      <path data-fk-edge="caseworker replace" data-fk-part="as7" data-tip="Association: caseworker takes part in replace." d="M882,135 L779.8,181.7" fill="none" class="s-ink" stroke-width="1.2"/>
      <circle data-fk-node="posvendor" data-tip="POS vendor: supplies and certifies checkout systems." data-n="1" cx="900" cy="260" r="8" class="f-surface s-ink" stroke-width="1.5"/>
      <path data-fk-node="posvendor" d="M900,268 V292 M886,278 H914 M900,292 L889,310 M900,292 L911,310" fill="none" class="s-ink" stroke-width="1.5"/>
      <text data-fk-node="posvendor" x="900" y="327" font-size="12" text-anchor="middle" class="f-ink" font-weight="600">POS vendor</text>
      <path data-fk-edge="posvendor certify" data-fk-part="as8" data-tip="Association: posvendor takes part in certify." d="M882,280 L792,280" fill="none" class="s-ink" stroke-width="1.2"/>
      <circle data-fk-node="retailer" data-tip="Retailer: sells to recipients, gets paid, submits records, and can appeal." data-n="1" cx="900" cy="570" r="8" class="f-surface s-ink" stroke-width="1.5"/>
      <path data-fk-node="retailer" d="M900,578 V602 M886,588 H914 M900,602 L889,620 M900,602 L911,620" fill="none" class="s-ink" stroke-width="1.5"/>
      <text data-fk-node="retailer" x="900" y="637" font-size="12" text-anchor="middle" class="f-ink" font-weight="600">Retailer</text>
      <path data-fk-edge="retailer buy" data-fk-part="as9" data-tip="Association: retailer takes part in buy." d="M882,590 L762.1,384.7" fill="none" class="s-ink" stroke-width="1.2"/>
      <path data-fk-edge="retailer payout" data-fk-part="as10" data-tip="Association: retailer takes part in payout." d="M882,590 L769,482.5" fill="none" class="s-ink" stroke-width="1.2"/>
      <path data-fk-edge="retailer appeal" data-fk-part="as11" data-tip="Association: retailer takes part in appeal." d="M882,590 L780.8,632.4" fill="none" class="s-ink" stroke-width="1.2"/>
      <path data-fk-edge="retailer submit" data-fk-part="as12" data-tip="Association: retailer takes part in submit." d="M882,590 L767,716.9" fill="none" class="s-ink" stroke-width="1.2"/>
      <circle data-fk-node="supplier" data-tip="Supplier (secondary actor): supplies purchase records." data-n="1" cx="900" cy="760" r="8" class="f-surface s-ink" stroke-width="1.5"/>
      <path data-fk-node="supplier" d="M900,768 V792 M886,778 H914 M900,792 L889,810 M900,792 L911,810" fill="none" class="s-ink" stroke-width="1.5"/>
      <text data-fk-node="supplier" x="900" y="827" font-size="12" text-anchor="middle" class="f-ink" font-weight="600">Supplier</text>
      <text data-fk-node="supplier" x="900" y="841" font-size="12" text-anchor="middle" class="f-muted">(secondary)</text>
      <path data-fk-edge="supplier submit" data-fk-part="as13" data-tip="Association: supplier takes part in submit." d="M882,780 L783.5,746.2" fill="none" class="s-ink" stroke-width="1.2"/>
      <circle data-fk-node="investigator" data-tip="Investigator: runs undercover buys." data-n="1" cx="500" cy="815" r="8" class="f-surface s-ink" stroke-width="1.5"/>
      <path data-fk-node="investigator" d="M500,823 V847 M486,833 H514 M500,847 L489,865 M500,847 L511,865" fill="none" class="s-ink" stroke-width="1.5"/>
      <text data-fk-node="investigator" x="500" y="882" font-size="12" text-anchor="middle" class="f-ink" font-weight="600">Investigator</text>
      <path data-fk-edge="investigator undercover" data-fk-part="as14" data-tip="Association: investigator takes part in undercover." d="M500,807 L500,762" fill="none" class="s-ink" stroke-width="1.2"/>
      <path data-fk-edge="apply verify" data-fk-part="inc1" data-tip="include: Apply for benefits always runs Verify eligibility." d="M392,110 L608,110" fill="none" class="s-muted" stroke-width="1.4" stroke-dasharray="6 4" marker-end="url(#uml-ops-use-cases-am)"/>
      <text data-fk-edge="apply verify" data-fk-part="inc1" data-tip="«include»" x="500" y="102" font-size="11" text-anchor="middle" class="f-muted mono">«include»</text>
      <path data-fk-edge="replace lock" data-fk-part="ext1" data-tip="extend: Replace stolen benefits adds to Lock card / report skimming [replacement available]." d="M608,195 L392,195" fill="none" class="s-muted" stroke-width="1.4" stroke-dasharray="6 4" marker-end="url(#uml-ops-use-cases-am)"/>
      <text data-fk-edge="replace lock" data-fk-part="ext1" data-tip="«extend»" x="500" y="187" font-size="11" text-anchor="middle" class="f-muted mono">«extend»</text>
      <text data-fk-edge="replace lock" data-fk-part="ext1" data-tip="[replacement available]" x="500" y="211" font-size="11" text-anchor="middle" class="f-caution mono">[replacement available]</text>
      <path data-fk-edge="buy validate" data-fk-part="inc2" data-tip="include: Buy groceries always runs Validate basket." d="M653.4,388.3 L546.6,441.7" fill="none" class="s-muted" stroke-width="1.4" stroke-dasharray="6 4" marker-end="url(#uml-ops-use-cases-am)"/>
      <text data-fk-edge="buy validate" data-fk-part="inc2" data-tip="«include»" x="560" y="401" font-size="11" text-anchor="middle" class="f-muted mono">«include»</text>
      <path data-fk-edge="hold payout" data-fk-part="ext2" data-tip="extend: Hold payout adds to Receive payout [high risk score]." d="M700,528 L700,492" fill="none" class="s-muted" stroke-width="1.4" stroke-dasharray="6 4" marker-end="url(#uml-ops-use-cases-am)"/>
      <text data-fk-edge="hold payout" data-fk-part="ext2" data-tip="«extend»" x="692" y="506" font-size="11" text-anchor="end" class="f-muted mono">«extend»</text>
      <text data-fk-edge="hold payout" data-fk-part="ext2" data-tip="[high risk score]" x="692" y="520" font-size="11" text-anchor="end" class="f-fraud mono">[high risk score]</text>
      <path data-fk-edge="undercover review" data-fk-part="ext3" data-tip="extend: Run undercover buy adds to Review flagged store [case opened]." d="M500,708 L500,582" fill="none" class="s-muted" stroke-width="1.4" stroke-dasharray="6 4" marker-end="url(#uml-ops-use-cases-am)"/>
      <text data-fk-edge="undercover review" data-fk-part="ext3" data-tip="«extend»" x="508" y="641" font-size="11" text-anchor="start" class="f-muted mono">«extend»</text>
      <text data-fk-edge="undercover review" data-fk-part="ext3" data-tip="[case opened]" x="508" y="655" font-size="11" text-anchor="start" class="f-caution mono">[case opened]</text>
      <path data-fk-node="note" data-tip="Note: a score can open a case, and a person decides it, with notice and a hearing." data-n="1" d="M212,700 H380 L392,712 V764 H212 Z" class="f-neutral-tint s-muted" stroke-width="1.2"/>
      <path data-fk-node="note" d="M380,700 V712 H392" fill="none" class="s-muted" stroke-width="1.2"/>
      <text data-fk-node="note" x="222" y="721" font-size="12" text-anchor="start" class="f-ink">A score can open a case.</text>
      <text data-fk-node="note" x="222" y="736" font-size="12" text-anchor="start" class="f-ink">A person decides it, with</text>
      <text data-fk-node="note" x="222" y="751" font-size="12" text-anchor="start" class="f-ink">notice and a hearing.</text>
      <path data-fk-edge="note sanction" data-fk-part="nt" data-tip="Note attached to Impose sanction." d="M300,700 L300,672" fill="none" class="s-muted" stroke-width="1.2" stroke-dasharray="6 4"/>
    </svg>
  </div>
  <figcaption><strong>Read it as:</strong> stick figures are actors outside the system, and ovals are use cases inside it. A solid line means the actor takes part. A dashed «include» arrow points to a step its base case always runs. A dashed «extend» arrow points back to the base case it sometimes adds to, under the condition in brackets.</figcaption>
</figure>

<p>An activity diagram shows how one case moves between parties. It starts in software. The analytics system scores every store each month, and only stores in the top N reach a federal analyst; the rest get an automated warning letter, payout delay, or volume cap. Once a case opens, three lines of evidence run at the same time: supplier records, an undercover buy, and the store's own purchase records.</p>
<p>The case file joins them before anyone decides. A sanction can go to independent review, and every outcome, closed, upheld, or reversed, becomes a label for next month's scores.</p>
<figure class="fig" id="uml-ops-investigation-activity">
  <div class="fig-head"><span class="eyebrow">Figure A2</span><h4>From flagged store to final decision</h4><p class="dek">Software picks the store, three lines of evidence run in parallel, and every closed case returns to scoring as a label.</p></div>
  <div class="fig-body">
    <svg data-fk="graph" viewBox="0 0 1000 1120" role="img" aria-label="UML activity diagram of a store investigation across five swimlanes. Analytics scores and ranks stores; stores in the top N go to a federal analyst, the rest get an automated warning, delay, or cap. Supplier records, an undercover buy, and the store&#x27;s purchase records run in parallel into a case file. The analyst closes the case or imposes a sanction, the retailer may appeal to independent review, and every outcome feeds back to scoring." xmlns="http://www.w3.org/2000/svg">
      <defs><marker id="uml-ops-investigation-activity-a" viewBox="0 0 12 12" refX="11" refY="6" markerWidth="12" markerHeight="12" markerUnits="userSpaceOnUse" orient="auto"><path d="M1,1.5 L11,6 L1,10.5" fill="none" class="s-ink" stroke-width="1.5"/></marker><marker id="uml-ops-investigation-activity-am" viewBox="0 0 12 12" refX="11" refY="6" markerWidth="12" markerHeight="12" markerUnits="userSpaceOnUse" orient="auto"><path d="M1,1.5 L11,6 L1,10.5" fill="none" class="s-muted" stroke-width="1.5"/></marker></defs>
      <rect x="20" y="20" width="960" height="1090" class="f-surface s-ink" stroke-width="1.4"/>
      <rect x="20" y="20" width="960" height="36" class="f-neutral-tint s-ink" stroke-width="1.4"/>
      <text data-group="analytics" x="110" y="43" font-size="12.5" text-anchor="middle" class="f-ink" font-weight="700">Analytics system</text>
      <path d="M200,20 V1110" fill="none" class="s-ink" stroke-width="1.4"/>
      <text data-group="investigator" x="280" y="43" font-size="12.5" text-anchor="middle" class="f-ink" font-weight="700">Investigator</text>
      <path d="M360,20 V1110" fill="none" class="s-ink" stroke-width="1.4"/>
      <text data-group="analyst" x="510" y="43" font-size="12.5" text-anchor="middle" class="f-ink" font-weight="700">Federal analyst</text>
      <path d="M660,20 V1110" fill="none" class="s-ink" stroke-width="1.4"/>
      <text data-group="retailer" x="740" y="43" font-size="12.5" text-anchor="middle" class="f-ink" font-weight="700">Retailer</text>
      <path d="M820,20 V1110" fill="none" class="s-ink" stroke-width="1.4"/>
      <text data-group="appeals" x="900" y="43" font-size="12.5" text-anchor="middle" class="f-ink" font-weight="700">Appeals</text>
      <circle data-fk-node="start" data-tip="Initial node: the monthly scoring run starts." data-group="analytics" data-n="1" cx="110" cy="86" r="8" class="f-ink"/>
      <path data-fk-edge="start score" data-fk-part="f1" data-tip="Flow: start to Score every store-month." data-animate="draw" data-step="0" d="M110,94 L110,116" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-investigation-activity-a)"/>
      <rect data-fk-node="score" data-tip="Score every store-month." data-group="analytics" data-n="1" x="35" y="116" width="150" height="38" rx="12" class="f-defense-tint s-defense" stroke-width="1.4"/>
      <text data-fk-node="score" data-group="analytics" x="110" y="132.2" font-size="12" text-anchor="middle" class="f-ink">Score every</text>
      <text data-fk-node="score" data-group="analytics" x="110" y="146.2" font-size="12" text-anchor="middle" class="f-ink">store-month</text>
      <path data-fk-edge="score rank" data-fk-part="f2" data-tip="Flow: Score every store-month to Rank stores by expected loss." data-animate="draw" data-step="1" d="M110,154 L110,181" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-investigation-activity-a)"/>
      <rect data-fk-node="rank" data-tip="Rank stores by expected loss." data-group="analytics" data-n="1" x="35" y="181" width="150" height="38" rx="12" class="f-defense-tint s-defense" stroke-width="1.4"/>
      <text data-fk-node="rank" data-group="analytics" x="110" y="197.2" font-size="12" text-anchor="middle" class="f-ink">Rank stores by</text>
      <text data-fk-node="rank" data-group="analytics" x="110" y="211.2" font-size="12" text-anchor="middle" class="f-ink">expected loss</text>
      <path data-fk-edge="rank d-rank" data-fk-part="f3" data-tip="Flow: Rank stores by expected loss to the top N decision." data-animate="draw" data-step="2" d="M110,219 L110,249" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-investigation-activity-a)"/>
      <path data-fk-node="d-rank" data-tip="Decision: [in top N] goes to triage; [else] gets an automated action." data-group="d-rank" data-n="1" d="M110,249 L126,265 L110,281 L94,265 Z" class="f-surface s-ink" stroke-width="1.4"/>
      <path data-fk-edge="d-rank triage" data-fk-part="f4" data-tip="[in top N]" data-animate="draw" data-step="3" d="M126,265 L425,265" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-investigation-activity-a)"/>
      <text data-fk-edge="d-rank triage" data-fk-part="f4" x="210" y="257" font-size="11" text-anchor="start" class="f-ink mono">[in top N]</text>
      <rect data-fk-node="triage" data-tip="Triage the flagged store." data-group="analyst" data-n="1" x="425" y="246" width="170" height="38" rx="12" class="f-caution-tint s-caution" stroke-width="1.4"/>
      <text data-fk-node="triage" data-group="analyst" x="510" y="262.2" font-size="12" text-anchor="middle" class="f-ink">Triage the</text>
      <text data-fk-node="triage" data-group="analyst" x="510" y="276.2" font-size="12" text-anchor="middle" class="f-ink">flagged store</text>
      <path data-fk-edge="d-rank warn" data-fk-part="f5" data-tip="[else]" d="M110,281 L110,316" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-investigation-activity-a)"/>
      <text data-fk-edge="d-rank warn" data-fk-part="f5" x="118" y="303" font-size="11" text-anchor="start" class="f-ink mono">[else]</text>
      <rect data-fk-node="warn" data-tip="Warning letter, payout delay, or cap." data-group="analytics" data-n="1" x="35" y="316" width="150" height="38" rx="12" class="f-defense-tint s-defense" stroke-width="1.4"/>
      <text data-fk-node="warn" data-group="analytics" x="110" y="332.2" font-size="12" text-anchor="middle" class="f-ink">Warning letter,</text>
      <text data-fk-node="warn" data-group="analytics" x="110" y="346.2" font-size="12" text-anchor="middle" class="f-ink">payout delay, or cap</text>
      <path data-fk-edge="warn stop" data-fk-part="f6" data-tip="Flow: automated action to the flow final." d="M110,354 L110,380" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-investigation-activity-a)"/>
      <circle data-fk-node="stop" data-tip="Flow final: the store stays in the automated tier." data-group="analytics" data-n="1" cx="110" cy="391" r="9" class="f-surface s-ink" stroke-width="1.4"/>
      <path data-fk-node="stop" data-group="analytics" d="M103.6,384.6 L116.4,397.4 M116.4,384.6 L103.6,397.4" fill="none" class="s-ink" stroke-width="1.4"/>
      <text data-fk-node="stop" data-group="analytics" x="110" y="418" font-size="11" text-anchor="middle" class="f-muted">stays in the automated tier</text>
      <path data-fk-edge="triage fork" data-fk-part="f7" data-tip="Flow: Triage the flagged store to the parallel split." data-animate="draw" data-step="4" d="M510,284 L510,436" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-investigation-activity-a)"/>
      <rect data-fk-node="fork" data-tip="Fork: three lines of evidence start in parallel." data-n="1" x="40" y="437" width="560" height="6" rx="1" class="f-ink"/>
      <path data-fk-edge="fork supplier" data-fk-part="f8" data-tip="Flow: fork to Pull supplier records." data-animate="draw" data-step="5" d="M110,443 L110,480" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-investigation-activity-a)"/>
      <path data-fk-edge="fork buy" data-fk-part="f9" data-tip="Flow: fork to Run undercover buy." data-animate="draw" data-step="6" d="M280,443 L280,480" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-investigation-activity-a)"/>
      <path data-fk-edge="fork request" data-fk-part="f10" data-tip="Flow: fork to Request purchase records." data-animate="draw" data-step="7" d="M510,443 L510,480" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-investigation-activity-a)"/>
      <rect data-fk-node="supplier" data-tip="Pull supplier records." data-group="analytics" data-n="1" x="35" y="481" width="150" height="38" rx="12" class="f-defense-tint s-defense" stroke-width="1.4"/>
      <text data-fk-node="supplier" data-group="analytics" x="110" y="497.2" font-size="12" text-anchor="middle" class="f-ink">Pull supplier</text>
      <text data-fk-node="supplier" data-group="analytics" x="110" y="511.2" font-size="12" text-anchor="middle" class="f-ink">records</text>
      <rect data-fk-node="buy" data-tip="Run undercover buy." data-group="investigator" data-n="1" x="210" y="481" width="140" height="38" rx="12" class="f-caution-tint s-caution" stroke-width="1.4"/>
      <text data-fk-node="buy" data-group="investigator" x="280" y="497.2" font-size="12" text-anchor="middle" class="f-ink">Run undercover</text>
      <text data-fk-node="buy" data-group="investigator" x="280" y="511.2" font-size="12" text-anchor="middle" class="f-ink">buy</text>
      <rect data-fk-node="request" data-tip="Request purchase records." data-group="analyst" data-n="1" x="425" y="481" width="170" height="38" rx="12" class="f-caution-tint s-caution" stroke-width="1.4"/>
      <text data-fk-node="request" data-group="analyst" x="510" y="497.2" font-size="12" text-anchor="middle" class="f-ink">Request purchase</text>
      <text data-fk-node="request" data-group="analyst" x="510" y="511.2" font-size="12" text-anchor="middle" class="f-ink">records</text>
      <path data-fk-edge="request submit" data-fk-part="f11" data-tip="Flow: Request purchase records to Submit invoices and receipts." data-animate="draw" data-step="8" d="M595,500 L670,500" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-investigation-activity-a)"/>
      <rect data-fk-node="submit" data-tip="Submit invoices and receipts." data-group="retailer" data-n="1" x="670" y="481" width="140" height="38" rx="12" class="f-surface s-ink" stroke-width="1.4"/>
      <text data-fk-node="submit" data-group="retailer" x="740" y="497.2" font-size="12" text-anchor="middle" class="f-ink">Submit invoices</text>
      <text data-fk-node="submit" data-group="retailer" x="740" y="511.2" font-size="12" text-anchor="middle" class="f-ink">and receipts</text>
      <path data-fk-edge="submit reconcile" data-fk-part="f12" data-tip="Flow: Submit invoices and receipts to Reconcile sales vs purchases." data-animate="draw" data-step="9" d="M740,519 L740,575 L595,575" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-investigation-activity-a)"/>
      <rect data-fk-node="reconcile" data-tip="Reconcile sales vs purchases." data-group="analyst" data-n="1" x="425" y="556" width="170" height="38" rx="12" class="f-caution-tint s-caution" stroke-width="1.4"/>
      <text data-fk-node="reconcile" data-group="analyst" x="510" y="572.2" font-size="12" text-anchor="middle" class="f-ink">Reconcile sales</text>
      <text data-fk-node="reconcile" data-group="analyst" x="510" y="586.2" font-size="12" text-anchor="middle" class="f-ink">vs purchases</text>
      <path data-fk-edge="supplier join" data-fk-part="f13" data-tip="Flow: Pull supplier records to the join." data-animate="draw" data-step="10" d="M110,519 L110,646" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-investigation-activity-a)"/>
      <path data-fk-edge="buy join" data-fk-part="f14" data-tip="Flow: Run undercover buy to the join." data-animate="draw" data-step="11" d="M280,519 L280,646" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-investigation-activity-a)"/>
      <path data-fk-edge="reconcile join" data-fk-part="f15" data-tip="Flow: Reconcile sales vs purchases to the join." data-animate="draw" data-step="12" d="M510,594 L510,646" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-investigation-activity-a)"/>
      <rect data-fk-node="join" data-tip="Join: waits for all three lines of evidence." data-n="1" x="40" y="647" width="560" height="6" rx="1" class="f-ink"/>
      <path data-fk-edge="join casefile" data-fk-part="f16" data-tip="Flow: join to Case file." data-animate="draw" data-step="13" d="M510,653 L510,682" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-investigation-activity-a)"/>
      <rect data-fk-node="casefile" data-tip="Case file: the combined evidence." data-group="analyst" data-n="1" x="450" y="683" width="120" height="34" class="f-surface s-ink" stroke-width="1.4"/>
      <text data-fk-node="casefile" data-group="analyst" x="510" y="704.2" font-size="12" text-anchor="middle" class="f-ink" font-weight="600">Case file</text>
      <path data-fk-edge="casefile d-outcome" data-fk-part="f17" data-tip="Flow: Case file to the outcome decision." data-animate="draw" data-step="14" d="M510,717 L510,753" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-investigation-activity-a)"/>
      <path data-fk-node="d-outcome" data-tip="Decision: [no finding] closes the case; [substantiated] leads to a sanction." data-group="d-outcome" data-n="1" d="M510,754 L526,770 L510,786 L494,770 Z" class="f-surface s-ink" stroke-width="1.4"/>
      <path data-fk-edge="d-outcome close" data-fk-part="f18" data-tip="[no finding]" d="M494,770 L435,770 L435,822" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-investigation-activity-a)"/>
      <text data-fk-edge="d-outcome close" data-fk-part="f18" x="488" y="761" font-size="11" text-anchor="end" class="f-ink mono">[no finding]</text>
      <path data-fk-edge="d-outcome sanction" data-fk-part="f19" data-tip="[substantiated]" data-animate="draw" data-step="15" d="M526,770 L585,770 L585,822" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-investigation-activity-a)"/>
      <text data-fk-edge="d-outcome sanction" data-fk-part="f19" x="532" y="761" font-size="11" text-anchor="start" class="f-ink mono">[substantiated]</text>
      <rect data-fk-node="close" data-tip="Close case, no finding." data-group="analyst" data-n="1" x="370" y="823" width="130" height="38" rx="12" class="f-surface s-ink" stroke-width="1.4"/>
      <text data-fk-node="close" data-group="analyst" x="435" y="839.2" font-size="12" text-anchor="middle" class="f-ink">Close case,</text>
      <text data-fk-node="close" data-group="analyst" x="435" y="853.2" font-size="12" text-anchor="middle" class="f-ink">no finding</text>
      <rect data-fk-node="sanction" data-tip="Impose sanction (disqualify or fine)." data-group="analyst" data-n="1" x="520" y="823" width="130" height="38" rx="12" class="f-fraud-tint s-fraud" stroke-width="1.4"/>
      <text data-fk-node="sanction" data-group="analyst" x="585" y="839.2" font-size="12" text-anchor="middle" class="f-ink">Impose sanction</text>
      <text data-fk-node="sanction" data-group="analyst" x="585" y="853.2" font-size="12" text-anchor="middle" class="f-ink">(disqualify or fine)</text>
      <path data-fk-edge="sanction d-appeal" data-fk-part="f20" data-tip="Flow: Impose sanction to the appeal decision." data-animate="draw" data-step="16" d="M650,842 L723,842" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-investigation-activity-a)"/>
      <path data-fk-node="d-appeal" data-tip="Decision: [appeal] goes to independent review; [no appeal] ends the case." data-group="d-appeal" data-n="1" d="M740,826 L756,842 L740,858 L724,842 Z" class="f-surface s-ink" stroke-width="1.4"/>
      <path data-fk-edge="d-appeal review" data-fk-part="f21" data-tip="[appeal]" d="M756,842 L829,842" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-investigation-activity-a)"/>
      <text data-fk-edge="d-appeal review" data-fk-part="f21" x="761" y="834" font-size="11" text-anchor="start" class="f-ink mono">[appeal]</text>
      <rect data-fk-node="review" data-tip="Independent review of the sanction." data-group="appeals" data-n="1" x="830" y="823" width="140" height="38" rx="12" class="f-caution-tint s-caution" stroke-width="1.4"/>
      <text data-fk-node="review" data-group="appeals" x="900" y="839.2" font-size="12" text-anchor="middle" class="f-ink">Independent</text>
      <text data-fk-node="review" data-group="appeals" x="900" y="853.2" font-size="12" text-anchor="middle" class="f-ink">review</text>
      <path data-fk-edge="review decision" data-fk-part="f22" data-tip="Flow: Independent review to Final decision." d="M900,861 L900,891" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-investigation-activity-a)"/>
      <rect data-fk-node="decision" data-tip="Final decision: uphold or reverse." data-group="appeals" data-n="1" x="830" y="891" width="140" height="38" rx="12" class="f-caution-tint s-caution" stroke-width="1.4"/>
      <text data-fk-node="decision" data-group="appeals" x="900" y="907.2" font-size="12" text-anchor="middle" class="f-ink">Final decision:</text>
      <text data-fk-node="decision" data-group="appeals" x="900" y="921.2" font-size="12" text-anchor="middle" class="f-ink">uphold or reverse</text>
      <path data-fk-edge="d-appeal merge" data-fk-part="f23" data-tip="[no appeal]" data-animate="draw" data-step="17" d="M740,858 L740,943" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-investigation-activity-a)"/>
      <text data-fk-edge="d-appeal merge" data-fk-part="f23" x="746" y="905" font-size="11" text-anchor="start" class="f-ink mono">[no appeal]</text>
      <path data-fk-edge="decision merge" data-fk-part="f24" data-tip="Flow: Final decision to the merge." d="M900,929 L900,960 L757,960" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-investigation-activity-a)"/>
      <path data-fk-edge="close merge" data-fk-part="f25" data-tip="Flow: Close case to the merge." d="M435,861 L435,960 L723,960" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-investigation-activity-a)"/>
      <path data-fk-node="merge" data-tip="Merge: every outcome rejoins before it returns to scoring." data-group="merge" data-n="1" d="M740,944 L756,960 L740,976 L724,960 Z" class="f-surface s-ink" stroke-width="1.4"/>
      <path data-fk-edge="merge label" data-fk-part="f26" data-tip="Flow: merge to Label the outcome, retrain scoring." data-animate="draw" data-step="18" d="M740,976 L740,1015 L186,1015" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-investigation-activity-a)"/>
      <rect data-fk-node="label" data-tip="Label the outcome, retrain scoring." data-group="analytics" data-n="1" x="35" y="996" width="150" height="38" rx="12" class="f-defense-tint s-defense" stroke-width="1.4"/>
      <text data-fk-node="label" data-group="analytics" x="110" y="1012.2" font-size="12" text-anchor="middle" class="f-ink">Label the outcome,</text>
      <text data-fk-node="label" data-group="analytics" x="110" y="1026.2" font-size="12" text-anchor="middle" class="f-ink">retrain scoring</text>
      <path data-fk-edge="label end" data-fk-part="f27" data-tip="Flow: Label the outcome to the activity final." data-animate="draw" data-step="19" d="M110,1034 L110,1072" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-investigation-activity-a)"/>
      <circle data-fk-node="end" data-tip="Activity final: the case is done." data-group="analytics" data-n="1" cx="110" cy="1084" r="11" fill="none" class="s-ink" stroke-width="1.6"/>
      <circle data-fk-node="end" data-group="analytics" cx="110" cy="1084" r="6" class="f-ink"/>
    </svg>
  </div>
  <figcaption><strong>Read it as:</strong> each column is one party. The black dot starts the flow, and the bullseye ends it. Bars split work into parallel branches and join it again. Diamonds pick one branch by the guard in brackets, or merge branches back into one. The circled X marks where a store below the top N leaves the flow without reaching a person.</figcaption>
</figure>

<p>The same process, seen from the case record, is a state machine. Each box is a status the case can hold, and each arrow names the event, guard, and action that move it. The design rules from sections 6 to 8 become guards here.</p>
<p>A case opens only when its expected loss beats the review cost within budget. A payout hold starts when the case enters the queue and lifts if the evidence shows no violation. Every sanction can go to appeal before the case becomes final.</p>
<figure class="fig" id="uml-ops-case-states">
  <div class="fig-head"><span class="eyebrow">Figure A3</span><h4>The life of an integrity case</h4><p class="dek">A score can open a case and hold some payouts. Only evidence moves it to a sanction, and every sanction can go to appeal.</p></div>
  <div class="fig-body">
    <svg data-fk="graph" viewBox="0 0 1000 760" role="img" aria-label="UML state machine for an integrity case. A monthly score flags a store. Below the review cutoff it gets an automated warning, delay, or cap; above it, a case opens and payouts are partly held. Under investigation, evidence is requested, reconciled, and checked by undercover buy. The case closes with no finding or is substantiated and sanctioned; a sanction can be appealed before the case becomes final, and the outcome feeds back to scoring." xmlns="http://www.w3.org/2000/svg">
      <defs><marker id="uml-ops-case-states-a" viewBox="0 0 12 12" refX="11" refY="6" markerWidth="12" markerHeight="12" markerUnits="userSpaceOnUse" orient="auto"><path d="M1,1.5 L11,6 L1,10.5" fill="none" class="s-ink" stroke-width="1.5"/></marker><marker id="uml-ops-case-states-am" viewBox="0 0 12 12" refX="11" refY="6" markerWidth="12" markerHeight="12" markerUnits="userSpaceOnUse" orient="auto"><path d="M1,1.5 L11,6 L1,10.5" fill="none" class="s-muted" stroke-width="1.5"/></marker></defs>
      <circle data-fk-node="init" data-tip="Initial state: a store enters the machine when its monthly score is above normal." data-n="1" cx="135" cy="40" r="8" class="f-ink"/>
      <path data-fk-edge="init flagged" data-fk-part="t1" data-tip="monthly score [above normal]" d="M135,48 L135,89" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-case-states-a)"/>
      <text data-fk-edge="init flagged" data-fk-part="t1" x="143" y="60" font-size="11" text-anchor="start" class="f-ink mono">monthly score</text>
      <text data-fk-edge="init flagged" data-fk-part="t1" x="143" y="73" font-size="11" text-anchor="start" class="f-ink mono">[above normal]</text>
      <rect data-fk-node="flagged" data-tip="Flagged: a store whose monthly score is above normal." data-n="1" x="70" y="90" width="130" height="40" rx="10" class="f-defense-tint s-defense" stroke-width="1.4"/>
      <text data-fk-node="flagged" x="135" y="114.2" font-size="12" text-anchor="middle" class="f-ink" font-weight="700">Flagged</text>
      <path data-fk-edge="flagged queued" data-fk-part="t2" data-tip="[loss &gt; review cost and within budget] / open case" d="M200,110 L389,110" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-case-states-a)"/>
      <text data-fk-edge="flagged queued" data-fk-part="t2" x="295" y="88" font-size="11" text-anchor="middle" class="f-ink mono">[loss &gt; review cost</text>
      <text data-fk-edge="flagged queued" data-fk-part="t2" x="295" y="101" font-size="11" text-anchor="middle" class="f-ink mono">and within budget]</text>
      <text data-fk-edge="flagged queued" data-fk-part="t2" x="295" y="126" font-size="11" text-anchor="middle" class="f-ink mono">/ open case</text>
      <rect data-fk-node="queued" data-tip="Queued for review: a case is open and part of the payouts is held." data-n="1" x="390" y="75" width="180" height="70" rx="10" class="f-defense-tint s-defense" stroke-width="1.4"/>
      <text data-fk-node="queued" x="480" y="94" font-size="12" text-anchor="middle" class="f-ink" font-weight="700">Queued for review</text>
      <path data-fk-node="queued" d="M390,103 H570" fill="none" class="s-defense" stroke-width="1"/>
      <text data-fk-node="queued" x="399" y="119" font-size="11" text-anchor="start" class="f-ink mono">entry / hold part</text>
      <text data-fk-node="queued" x="399" y="132" font-size="11" text-anchor="start" class="f-ink mono">of payouts</text>
      <path data-fk-edge="flagged auto" data-fk-part="t3" data-tip="[else]" d="M135,130 L135,209" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-case-states-a)"/>
      <text data-fk-edge="flagged auto" data-fk-part="t3" x="143" y="172" font-size="11" text-anchor="start" class="f-ink mono">[else]</text>
      <rect data-fk-node="auto" data-tip="Auto-actioned: a warning letter, payout delay, or cap, with no person involved." data-n="1" x="60" y="210" width="170" height="64" rx="10" class="f-defense-tint s-defense" stroke-width="1.4"/>
      <text data-fk-node="auto" x="145" y="229" font-size="12" text-anchor="middle" class="f-ink" font-weight="700">Auto-actioned</text>
      <path data-fk-node="auto" d="M60,238 H230" fill="none" class="s-defense" stroke-width="1"/>
      <text data-fk-node="auto" x="69" y="254" font-size="11" text-anchor="start" class="f-ink mono">entry / warning letter,</text>
      <text data-fk-node="auto" x="69" y="267" font-size="11" text-anchor="start" class="f-ink mono">payout delay, or cap</text>
      <path data-fk-edge="auto queued" data-fk-part="t4" data-tip="monthly score [rises into top N] / open case" d="M230,242 L460,242 L460,146" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-case-states-a)"/>
      <text data-fk-edge="auto queued" data-fk-part="t4" x="345" y="218" font-size="11" text-anchor="middle" class="f-ink mono">monthly score [rises</text>
      <text data-fk-edge="auto queued" data-fk-part="t4" x="345" y="231" font-size="11" text-anchor="middle" class="f-ink mono">into top N] / open case</text>
      <path data-fk-edge="auto end" data-fk-part="t5" data-tip="monthly score [back to normal] / lift action" d="M145,274 L145,696" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-case-states-a)"/>
      <text data-fk-edge="auto end" data-fk-part="t5" x="153" y="478" font-size="11" text-anchor="start" class="f-ink mono">monthly score</text>
      <text data-fk-edge="auto end" data-fk-part="t5" x="153" y="491" font-size="11" text-anchor="start" class="f-ink mono">[back to normal]</text>
      <text data-fk-edge="auto end" data-fk-part="t5" x="153" y="504" font-size="11" text-anchor="start" class="f-ink mono">/ lift action</text>
      <rect data-fk-node="investigating" data-tip="Under investigation: evidence is requested, reconciled, and checked by undercover buy." data-n="1" x="690" y="50" width="295" height="250" rx="14" class="f-caution-tint s-caution" stroke-width="1.4"/>
      <text data-fk-node="investigating" x="706" y="73" font-size="12" text-anchor="start" class="f-ink" font-weight="700">Under investigation</text>
      <path data-fk-edge="queued investigating" data-fk-part="t6" data-tip="case assigned / request records" d="M570,110 L689,110" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-case-states-a)"/>
      <text data-fk-edge="queued investigating" data-fk-part="t6" x="630" y="88" font-size="11" text-anchor="middle" class="f-ink mono">case assigned /</text>
      <text data-fk-edge="queued investigating" data-fk-part="t6" x="630" y="101" font-size="11" text-anchor="middle" class="f-ink mono">request records</text>
      <circle data-fk-node="inv-init" data-tip="Initial substate of the investigation." data-n="1" cx="722" cy="110" r="8" class="f-ink"/>
      <path data-fk-edge="inv-init evidence" data-fk-part="t7" data-tip="Starts the investigation at Evidence requested." d="M730,110 L759,110" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-case-states-a)"/>
      <rect data-fk-node="evidence" data-tip="Evidence requested: the case asks the store for its records." data-n="1" x="760" y="95" width="180" height="30" rx="8" class="f-surface s-ink" stroke-width="1.4"/>
      <text data-fk-node="evidence" x="850" y="114.2" font-size="12" text-anchor="middle" class="f-ink" font-weight="700">Evidence requested</text>
      <path data-fk-edge="evidence recon" data-fk-part="t8" data-tip="records received" d="M850,125 L850,164" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-case-states-a)"/>
      <text data-fk-edge="evidence recon" data-fk-part="t8" x="858" y="149" font-size="11" text-anchor="start" class="f-ink mono">records received</text>
      <rect data-fk-node="recon" data-tip="Reconciliation review: sales checked against purchases." data-n="1" x="760" y="165" width="180" height="30" rx="8" class="f-surface s-ink" stroke-width="1.4"/>
      <text data-fk-node="recon" x="850" y="184.2" font-size="12" text-anchor="middle" class="f-ink" font-weight="700">Reconciliation review</text>
      <path data-fk-edge="recon buy" data-fk-part="t9" data-tip="[gap unexplained]" d="M850,195 L850,234" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-case-states-a)"/>
      <text data-fk-edge="recon buy" data-fk-part="t9" x="858" y="219" font-size="11" text-anchor="start" class="f-ink mono">[gap unexplained]</text>
      <rect data-fk-node="buy" data-tip="Undercover buy: a test purchase when a gap stays unexplained." data-n="1" x="760" y="235" width="180" height="30" rx="8" class="f-surface s-ink" stroke-width="1.4"/>
      <text data-fk-node="buy" x="850" y="254.2" font-size="12" text-anchor="middle" class="f-ink" font-weight="700">Undercover buy</text>
      <path data-fk-edge="investigating subst" data-fk-part="t10" data-tip="evidence reviewed [violation shown]" d="M840,300 L840,369" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-case-states-a)"/>
      <text data-fk-edge="investigating subst" data-fk-part="t10" x="848" y="328" font-size="11" text-anchor="start" class="f-ink mono">evidence reviewed</text>
      <text data-fk-edge="investigating subst" data-fk-part="t10" x="848" y="341" font-size="11" text-anchor="start" class="f-ink mono">[violation shown]</text>
      <rect data-fk-node="subst" data-tip="Substantiated: the evidence shows a violation." data-n="1" x="760" y="370" width="160" height="36" rx="10" class="f-fraud-tint s-fraud" stroke-width="1.4"/>
      <text data-fk-node="subst" x="840" y="392.2" font-size="12" text-anchor="middle" class="f-ink" font-weight="700">Substantiated</text>
      <path data-fk-edge="investigating closed" data-fk-part="t11" data-tip="evidence reviewed [no violation]" d="M720,300 L720,340 L600,340 L600,369" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-case-states-a)"/>
      <text data-fk-edge="investigating closed" data-fk-part="t11" x="652" y="318" font-size="11" text-anchor="middle" class="f-ink mono">evidence reviewed</text>
      <text data-fk-edge="investigating closed" data-fk-part="t11" x="652" y="331" font-size="11" text-anchor="middle" class="f-ink mono">[no violation]</text>
      <rect data-fk-node="closed" data-tip="Closed, no finding: the evidence shows no violation." data-n="1" x="520" y="370" width="160" height="36" rx="10" class="f-neutral-tint s-muted" stroke-width="1.4"/>
      <text data-fk-node="closed" x="600" y="392.2" font-size="12" text-anchor="middle" class="f-ink" font-weight="700">Closed, no finding</text>
      <path data-fk-edge="subst sanctioned" data-fk-part="t12" data-tip="response reviewed / disqualify or fine" d="M840,406 L840,479" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-case-states-a)"/>
      <text data-fk-edge="subst sanctioned" data-fk-part="t12" x="848" y="438" font-size="11" text-anchor="start" class="f-ink mono">response reviewed</text>
      <text data-fk-edge="subst sanctioned" data-fk-part="t12" x="848" y="451" font-size="11" text-anchor="start" class="f-ink mono">/ disqualify or fine</text>
      <rect data-fk-node="sanctioned" data-tip="Sanctioned: the store is disqualified or fined." data-n="1" x="760" y="480" width="160" height="36" rx="10" class="f-fraud-tint s-fraud" stroke-width="1.4"/>
      <text data-fk-node="sanctioned" x="840" y="502.2" font-size="12" text-anchor="middle" class="f-ink" font-weight="700">Sanctioned</text>
      <path data-fk-edge="sanctioned appeal" data-fk-part="t13" data-tip="appeal filed" d="M840,516 L840,589" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-case-states-a)"/>
      <text data-fk-edge="sanctioned appeal" data-fk-part="t13" x="848" y="556" font-size="11" text-anchor="start" class="f-ink mono">appeal filed</text>
      <rect data-fk-node="appeal" data-tip="Under appeal: an independent review of the sanction." data-n="1" x="750" y="590" width="180" height="56" rx="10" class="f-caution-tint s-caution" stroke-width="1.4"/>
      <text data-fk-node="appeal" x="840" y="609" font-size="12" text-anchor="middle" class="f-ink" font-weight="700">Under appeal</text>
      <path data-fk-node="appeal" d="M750,618 H930" fill="none" class="s-caution" stroke-width="1"/>
      <text data-fk-node="appeal" x="759" y="634" font-size="11" text-anchor="start" class="f-ink mono">do / independent review</text>
      <path data-fk-edge="sanctioned final" data-fk-part="t14" data-tip="no appeal filed" d="M760,498 L640,498 L640,689" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-case-states-a)"/>
      <text data-fk-edge="sanctioned final" data-fk-part="t14" x="700" y="490" font-size="11" text-anchor="middle" class="f-ink mono">no appeal filed</text>
      <path data-fk-edge="closed final" data-fk-part="t15" data-tip="/ release any hold" d="M600,406 L600,689" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-case-states-a)"/>
      <text data-fk-edge="closed final" data-fk-part="t15" x="592" y="550" font-size="11" text-anchor="end" class="f-ink mono">/ release any hold</text>
      <rect data-fk-node="final" data-tip="Final: the case is decided and any hold is released." data-n="1" x="520" y="690" width="160" height="36" rx="10" class="f-surface s-ink" stroke-width="1.4"/>
      <text data-fk-node="final" x="600" y="712.2" font-size="12" text-anchor="middle" class="f-ink" font-weight="700">Final</text>
      <path data-fk-edge="appeal final" data-fk-part="t16" data-tip="decision issued / uphold or reverse" d="M840,646 L840,708 L681,708" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-case-states-a)"/>
      <text data-fk-edge="appeal final" data-fk-part="t16" x="760" y="688" font-size="11" text-anchor="middle" class="f-ink mono">decision issued /</text>
      <text data-fk-edge="appeal final" data-fk-part="t16" x="760" y="701" font-size="11" text-anchor="middle" class="f-ink mono">uphold or reverse</text>
      <path data-fk-edge="final end" data-fk-part="t17" data-tip="/ feed label to scoring" d="M520,708 L157,708" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-case-states-a)"/>
      <text data-fk-edge="final end" data-fk-part="t17" x="340" y="700" font-size="11" text-anchor="middle" class="f-ink mono">/ feed label to scoring</text>
      <circle data-fk-node="end" data-tip="Final state: the outcome is fed back to scoring as a label." data-n="1" cx="145" cy="708" r="11" fill="none" class="s-ink" stroke-width="1.6"/>
      <circle data-fk-node="end" cx="145" cy="708" r="6" class="f-ink"/>
    </svg>
<div class="fk-controls" hidden data-fk-controls><span data-fk-walk="init flagged queued investigating subst sanctioned appeal final end" data-fk-label="Step through"></span></div>
  </div>
  <figcaption><strong>Read it as:</strong> rounded boxes are states, and arrows are transitions labeled event [guard] / action. The large box groups three investigation substates, and its two exits fire from any of them. An entry line runs when the case enters that state; a do line runs while it stays there.</figcaption>
</figure>

<p>The last diagram turns to the recipient side, where the loss is theft from families. Section 6.2 proposes a recipient app with per-swipe alerts and a card lock, and section 4.4 describes how benefit replacement worked. The activity diagram joins the two.</p>
<p>A family can stop a drain within minutes of the first unknown swipe. The response then splits: the state reissues the card and decides whether the stolen benefits come back. Under the facts in section 4.4, federal replacement covered thefts only through December 20, 2024.</p>
<figure class="fig" id="uml-ops-skimming-response">
  <div class="fig-head"><span class="eyebrow">Figure A4</span><h4>When a skimmed card gets drained</h4><p class="dek">The app lets a family stop a drain within minutes. Whether the stolen money comes back depends on when the theft happened.</p></div>
  <div class="fig-body">
    <svg data-fk="graph" viewBox="0 0 1020 800" role="img" aria-label="UML activity diagram of the response to a skimmed EBT card across four swimlanes. A copied card is swiped, the app sends a per-swipe alert, and the recipient locks the card so new swipes are declined. The recipient files a report and the state opens a claim. In parallel, a replacement card is issued, chip-and-tap where available, and the state either replaces stolen benefits or tells the household no replacement exists. Federal replacement covered thefts through December 20, 2024 and was not extended." xmlns="http://www.w3.org/2000/svg">
      <defs><marker id="uml-ops-skimming-response-a" viewBox="0 0 12 12" refX="11" refY="6" markerWidth="12" markerHeight="12" markerUnits="userSpaceOnUse" orient="auto"><path d="M1,1.5 L11,6 L1,10.5" fill="none" class="s-ink" stroke-width="1.5"/></marker><marker id="uml-ops-skimming-response-am" viewBox="0 0 12 12" refX="11" refY="6" markerWidth="12" markerHeight="12" markerUnits="userSpaceOnUse" orient="auto"><path d="M1,1.5 L11,6 L1,10.5" fill="none" class="s-muted" stroke-width="1.5"/></marker></defs>
      <rect x="20" y="20" width="980" height="770" class="f-surface s-ink" stroke-width="1.4"/>
      <rect x="20" y="20" width="980" height="36" class="f-neutral-tint s-ink" stroke-width="1.4"/>
      <text data-group="lane-recipient" data-tip="Swimlane: Recipient." x="110" y="43" font-size="12.5" text-anchor="middle" class="f-ink" font-weight="700">Recipient</text>
      <path d="M200,20 V790" fill="none" class="s-ink" stroke-width="1.4"/>
      <text data-group="lane-app" data-tip="Swimlane: Recipient app." x="290" y="43" font-size="12.5" text-anchor="middle" class="f-ink" font-weight="700">Recipient app</text>
      <path d="M380,20 V790" fill="none" class="s-ink" stroke-width="1.4"/>
      <text data-group="lane-card" data-tip="Swimlane: Card and credential service." x="530" y="43" font-size="12.5" text-anchor="middle" class="f-ink" font-weight="700">Card and credential service</text>
      <path d="M680,20 V790" fill="none" class="s-ink" stroke-width="1.4"/>
      <text data-group="lane-state" data-tip="Swimlane: State agency." x="840" y="43" font-size="12.5" text-anchor="middle" class="f-ink" font-weight="700">State agency</text>
      <circle data-fk-node="start" data-tip="Start: a copied card is about to be swiped." data-n="1" cx="530" cy="88" r="8" class="f-ink"/>
      <path data-fk-edge="start approve" data-fk-part="f0" data-tip="Flow: start to Approve swipe on copied card." data-animate="draw" data-step="0" d="M530,96 L530,120" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-skimming-response-a)"/>
      <rect data-fk-node="approve" data-tip="Approve swipe on copied card: the card service cannot tell the copy from the real card." data-group="lane-card" data-n="1" x="455" y="121" width="150" height="38" rx="12" class="f-fraud-tint s-fraud" stroke-width="1.4"/>
      <text data-fk-node="approve" data-group="lane-card" x="530" y="137.2" font-size="12" text-anchor="middle" class="f-ink">Approve swipe on</text>
      <text data-fk-node="approve" data-group="lane-card" x="530" y="151.2" font-size="12" text-anchor="middle" class="f-ink">copied card</text>
      <path data-fk-edge="approve alert" data-fk-part="f1" data-tip="Flow: Approve swipe on copied card to Send per-swipe alert." data-animate="draw" data-step="1" d="M455,140 L361,140" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-skimming-response-a)"/>
      <rect data-fk-node="alert" data-tip="Send per-swipe alert: the app notifies the recipient of each swipe." data-group="lane-app" data-n="1" x="220" y="121" width="140" height="38" rx="12" class="f-defense-tint s-defense" stroke-width="1.4"/>
      <text data-fk-node="alert" data-group="lane-app" x="290" y="137.2" font-size="12" text-anchor="middle" class="f-ink">Send per-swipe</text>
      <text data-fk-node="alert" data-group="lane-app" x="290" y="151.2" font-size="12" text-anchor="middle" class="f-ink">alert</text>
      <path data-fk-edge="alert see" data-fk-part="f2" data-tip="Flow: Send per-swipe alert to See unknown transaction." data-animate="draw" data-step="2" d="M220,140 L181,140" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-skimming-response-a)"/>
      <rect data-fk-node="see" data-tip="See unknown transaction: the recipient spots a swipe they did not make." data-group="lane-recipient" data-n="1" x="40" y="121" width="140" height="38" rx="12" class="f-surface s-ink" stroke-width="1.4"/>
      <text data-fk-node="see" data-group="lane-recipient" x="110" y="137.2" font-size="12" text-anchor="middle" class="f-ink">See unknown</text>
      <text data-fk-node="see" data-group="lane-recipient" x="110" y="151.2" font-size="12" text-anchor="middle" class="f-ink">transaction</text>
      <path data-fk-edge="see lockapp" data-fk-part="f3" data-tip="Flow: See unknown transaction to Lock card in app." data-animate="draw" data-step="3" d="M110,159 L110,195" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-skimming-response-a)"/>
      <rect data-fk-node="lockapp" data-tip="Lock card in app: the recipient locks the card." data-group="lane-recipient" data-n="1" x="40" y="196" width="140" height="38" rx="12" class="f-surface s-ink" stroke-width="1.4"/>
      <text data-fk-node="lockapp" data-group="lane-recipient" x="110" y="212.2" font-size="12" text-anchor="middle" class="f-ink">Lock card</text>
      <text data-fk-node="lockapp" data-group="lane-recipient" x="110" y="226.2" font-size="12" text-anchor="middle" class="f-ink">in app</text>
      <path data-fk-edge="lockapp lock" data-fk-part="f4" data-tip="Flow: Lock card in app to Lock card." data-animate="draw" data-step="4" d="M180,215 L219,215" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-skimming-response-a)"/>
      <rect data-fk-node="lock" data-tip="Lock card: the app sends the lock to the card service." data-group="lane-app" data-n="1" x="220" y="196" width="140" height="38" rx="12" class="f-defense-tint s-defense" stroke-width="1.4"/>
      <text data-fk-node="lock" data-group="lane-app" x="290" y="219.2" font-size="12" text-anchor="middle" class="f-ink">Lock card</text>
      <path data-fk-edge="lock decline" data-fk-part="f5" data-tip="Flow: Lock card to Decline new swipes." data-animate="draw" data-step="5" d="M360,215 L454,215" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-skimming-response-a)"/>
      <rect data-fk-node="decline" data-tip="Decline new swipes: the locked card stops working." data-group="lane-card" data-n="1" x="455" y="196" width="150" height="38" rx="12" class="f-defense-tint s-defense" stroke-width="1.4"/>
      <text data-fk-node="decline" data-group="lane-card" x="530" y="212.2" font-size="12" text-anchor="middle" class="f-ink">Decline new</text>
      <text data-fk-node="decline" data-group="lane-card" x="530" y="226.2" font-size="12" text-anchor="middle" class="f-ink">swipes</text>
      <path data-fk-edge="decline report" data-fk-part="f6" data-tip="Flow: Decline new swipes to File skimming report in app." data-animate="draw" data-step="6" d="M530,234 L530,256 L110,256 L110,280" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-skimming-response-a)"/>
      <rect data-fk-node="report" data-tip="File skimming report in app: the recipient reports the theft." data-group="lane-recipient" data-n="1" x="40" y="281" width="140" height="38" rx="12" class="f-surface s-ink" stroke-width="1.4"/>
      <text data-fk-node="report" data-group="lane-recipient" x="110" y="297.2" font-size="12" text-anchor="middle" class="f-ink">File skimming</text>
      <text data-fk-node="report" data-group="lane-recipient" x="110" y="311.2" font-size="12" text-anchor="middle" class="f-ink">report in app</text>
      <path data-fk-edge="report claim" data-fk-part="f7" data-tip="Flow: File skimming report to Open a theft claim." data-animate="draw" data-step="7" d="M180,300 L764,300" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-skimming-response-a)"/>
      <rect data-fk-node="claim" data-tip="Open a theft claim: the state agency starts a claim." data-group="lane-state" data-n="1" x="765" y="281" width="150" height="38" rx="12" class="f-caution-tint s-caution" stroke-width="1.4"/>
      <text data-fk-node="claim" data-group="lane-state" x="840" y="297.2" font-size="12" text-anchor="middle" class="f-ink">Open a</text>
      <text data-fk-node="claim" data-group="lane-state" x="840" y="311.2" font-size="12" text-anchor="middle" class="f-ink">theft claim</text>
      <path data-fk-edge="claim fork" data-fk-part="f8" data-tip="Flow: Open a theft claim to the parallel fork." data-animate="draw" data-step="8" d="M840,319 L840,361" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-skimming-response-a)"/>
      <rect data-fk-node="fork" data-tip="Fork: card replacement and benefit replacement run in parallel." data-n="1" x="420" y="362" width="480" height="6" rx="1" class="f-ink"/>
      <path data-fk-edge="fork cardtype" data-fk-part="f9" data-tip="Flow: fork to the card type decision." data-animate="draw" data-step="9" d="M530,368 L530,408" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-skimming-response-a)"/>
      <path data-fk-node="cardtype" data-group="dec-card" data-tip="Guards: [chip-capable] issue a chip-and-tap card; [stripe only] issue a new stripe card." data-n="1" d="M530,409 L546,425 L530,441 L514,425 Z" class="f-surface s-ink" stroke-width="1.4"/>
      <path data-fk-edge="cardtype issuechip" data-fk-part="f10" data-tip="Flow: [chip-capable] to Issue chip-and-tap card." data-animate="draw" data-step="10" d="M514,425 L455,425 L455,490" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-skimming-response-a)"/>
      <text data-fk-edge="cardtype issuechip" data-fk-part="f10" data-tip="[chip-capable]" x="508" y="416" font-size="11" text-anchor="end" class="f-ink mono">[chip-capable]</text>
      <path data-fk-edge="cardtype issuestripe" data-fk-part="f11" data-tip="Flow: [stripe only] to Issue new stripe card." data-animate="draw" data-step="10" d="M546,425 L605,425 L605,490" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-skimming-response-a)"/>
      <text data-fk-edge="cardtype issuestripe" data-fk-part="f11" data-tip="[stripe only]" x="552" y="416" font-size="11" text-anchor="start" class="f-ink mono">[stripe only]</text>
      <rect data-fk-node="issuechip" data-tip="Issue chip-and-tap card: a replacement that blocks the next copy." data-group="lane-card" data-n="1" x="385" y="491" width="140" height="38" rx="12" class="f-defense-tint s-defense" stroke-width="1.4"/>
      <text data-fk-node="issuechip" data-group="lane-card" x="455" y="507.2" font-size="12" text-anchor="middle" class="f-ink">Issue chip-and-</text>
      <text data-fk-node="issuechip" data-group="lane-card" x="455" y="521.2" font-size="12" text-anchor="middle" class="f-ink">tap card</text>
      <rect data-fk-node="issuestripe" data-tip="Issue new stripe card: a replacement that can be copied again." data-group="lane-card" data-n="1" x="535" y="491" width="140" height="38" rx="12" class="f-caution-tint s-caution" stroke-width="1.4"/>
      <text data-fk-node="issuestripe" data-group="lane-card" x="605" y="507.2" font-size="12" text-anchor="middle" class="f-ink">Issue new</text>
      <text data-fk-node="issuestripe" data-group="lane-card" x="605" y="521.2" font-size="12" text-anchor="middle" class="f-ink">stripe card</text>
      <path data-fk-edge="issuechip mergecard" data-fk-part="f12" data-tip="Flow: Issue chip-and-tap card to merge." data-animate="draw" data-step="11" d="M455,529 L455,585 L513,585" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-skimming-response-a)"/>
      <path data-fk-edge="issuestripe mergecard" data-fk-part="f13" data-tip="Flow: Issue new stripe card to merge." data-animate="draw" data-step="11" d="M605,529 L605,585 L547,585" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-skimming-response-a)"/>
      <path data-fk-node="mergecard" data-tip="Merge: the two card branches rejoin." data-n="1" d="M530,569 L546,585 L530,601 L514,585 Z" class="f-surface s-ink" stroke-width="1.4"/>
      <path data-fk-edge="fork repldec" data-fk-part="f15" data-tip="Flow: fork to the replacement decision." data-animate="draw" data-step="13" d="M840,368 L840,408" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-skimming-response-a)"/>
      <path data-fk-node="repldec" data-group="dec-repl" data-tip="Guards: [replacement available] replace stolen benefits; [no replacement] tell the household no replacement exists." data-n="1" d="M840,409 L856,425 L840,441 L824,425 Z" class="f-surface s-ink" stroke-width="1.4"/>
      <path data-fk-edge="repldec replace" data-fk-part="f16" data-tip="Flow: [replacement available] to Replace stolen benefits." data-animate="draw" data-step="14" d="M824,425 L760,425 L760,490" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-skimming-response-a)"/>
      <text data-fk-edge="repldec replace" data-fk-part="f16" data-tip="[replacement available]" x="818" y="403" font-size="11" text-anchor="end" class="f-ink mono">[replacement</text>
      <text data-fk-edge="repldec replace" data-fk-part="f16" data-tip="[replacement available]" x="818" y="416" font-size="11" text-anchor="end" class="f-ink mono">available]</text>
      <path data-fk-edge="repldec tell" data-fk-part="f17" data-tip="Flow: [no replacement] to Tell household no replacement exists." data-animate="draw" data-step="14" d="M856,425 L920,425 L920,490" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-skimming-response-a)"/>
      <text data-fk-edge="repldec tell" data-fk-part="f17" data-tip="[no replacement]" x="862" y="416" font-size="11" text-anchor="start" class="f-caution mono">[no replacement]</text>
      <rect data-fk-node="replace" data-tip="Replace stolen benefits: the state restores the stolen amount." data-group="lane-state" data-n="1" x="690" y="491" width="140" height="38" rx="12" class="f-defense-tint s-defense" stroke-width="1.4"/>
      <text data-fk-node="replace" data-group="lane-state" x="760" y="507.2" font-size="12" text-anchor="middle" class="f-ink">Replace stolen</text>
      <text data-fk-node="replace" data-group="lane-state" x="760" y="521.2" font-size="12" text-anchor="middle" class="f-ink">benefits</text>
      <rect data-fk-node="tell" data-tip="Tell household no replacement exists: the family bears the loss." data-group="lane-state" data-n="1" x="850" y="491" width="140" height="38" rx="12" class="f-caution-tint s-caution" stroke-width="1.4"/>
      <text data-fk-node="tell" data-group="lane-state" x="920" y="507.2" font-size="12" text-anchor="middle" class="f-ink">Tell household no</text>
      <text data-fk-node="tell" data-group="lane-state" x="920" y="521.2" font-size="12" text-anchor="middle" class="f-ink">replacement exists</text>
      <path data-fk-edge="replace mergestate" data-fk-part="f18" data-tip="Flow: Replace stolen benefits to merge." data-animate="draw" data-step="15" d="M760,529 L760,585 L823,585" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-skimming-response-a)"/>
      <path data-fk-edge="tell mergestate" data-fk-part="f19" data-tip="Flow: Tell household no replacement exists to merge." data-animate="draw" data-step="15" d="M920,529 L920,585 L857,585" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-skimming-response-a)"/>
      <path data-fk-node="mergestate" data-tip="Merge: the two benefit branches rejoin." data-n="1" d="M840,569 L856,585 L840,601 L824,585 Z" class="f-surface s-ink" stroke-width="1.4"/>
      <path data-fk-edge="mergecard join" data-fk-part="f14" data-tip="Flow: card branch merge to join." data-animate="draw" data-step="12" d="M530,601 L530,636" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-skimming-response-a)"/>
      <path data-fk-edge="mergestate join" data-fk-part="f20" data-tip="Flow: benefit branch merge to join." data-animate="draw" data-step="16" d="M840,601 L840,636" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-skimming-response-a)"/>
      <rect data-fk-node="join" data-tip="Join: both parallel branches finish before the household proceeds." data-n="1" x="420" y="637" width="480" height="6" rx="1" class="f-ink"/>
      <path data-fk-edge="join use" data-fk-part="f21" data-tip="Flow: join to Use the new card." data-animate="draw" data-step="17" d="M530,643 L530,705 L181,705" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-skimming-response-a)"/>
      <rect data-fk-node="use" data-tip="Use the new card: the household resumes spending." data-group="lane-recipient" data-n="1" x="40" y="686" width="140" height="38" rx="12" class="f-surface s-ink" stroke-width="1.4"/>
      <text data-fk-node="use" data-group="lane-recipient" x="110" y="709.2" font-size="12" text-anchor="middle" class="f-ink">Use the new card</text>
      <path data-fk-edge="use end" data-fk-part="f22" data-tip="Flow: Use the new card to end." data-animate="draw" data-step="18" d="M110,724 L110,761" fill="none" class="s-ink" stroke-width="1.4" marker-end="url(#uml-ops-skimming-response-a)"/>
      <circle data-fk-node="end" data-tip="End: the household has a new card." data-n="1" cx="110" cy="773" r="11" fill="none" class="s-ink" stroke-width="1.6"/>
      <circle data-fk-node="end" cx="110" cy="773" r="6" class="f-ink"/>
      <path data-fk-node="note" data-tip="Note: federal replacement covered thefts through Dec 20, 2024, up to two months of benefits. Congress did not extend it." data-n="1" d="M700,676 H978 L990,688 V758 H700 Z" class="f-neutral-tint s-muted" stroke-width="1.2"/>
      <path data-fk-node="note" d="M978,676 V688 H990" fill="none" class="s-muted" stroke-width="1.2"/>
      <text data-fk-node="note" x="710" y="697" font-size="12" text-anchor="start" class="f-ink">Federal replacement covered thefts through</text>
      <text data-fk-node="note" x="710" y="712" font-size="12" text-anchor="start" class="f-ink">Dec 20, 2024, up to two months of benefits.</text>
      <text data-fk-node="note" x="710" y="727" font-size="12" text-anchor="start" class="f-ink">Congress did not extend it, so the family</text>
      <text data-fk-node="note" x="710" y="742" font-size="12" text-anchor="start" class="f-ink">now bears the loss.</text>
      <path data-fk-edge="note tell" data-fk-part="nt" data-tip="Note attached to Tell household no replacement exists." d="M975,676 L975,529" fill="none" class="s-muted" stroke-width="1.2" stroke-dasharray="6 4"/>
    </svg>
  </div>
  <figcaption><strong>Read it as:</strong> each column is one party. After the claim opens, two branches run in parallel: a new card, and the question of the stolen benefits. A chip card blocks the next copy; a new stripe card can be copied again. The note gives the report's facts on federal replacement.</figcaption>
</figure>

<h3 id="a2-software">A.2 Software<a class="anchor" href="#a2-software" aria-label="Link to this section"></a></h3>
<p>The component diagram recasts the layered design as software. Each box is a deployable component. A ball marks an interface the component offers, and a cup marks one it needs. The real-time path answers inside a sale: the gateway calls the eligibility, credential, and basket checks, then settles through the payout risk service.</p>
<p>Batch analytics reads the stream of sales once a month. Three evidence services feed one scoring engine. External sources sit outside the packages, because the program reads them and cannot change them.</p>
<figure class="fig" id="uml-sw-components">
  <div class="fig-head"><span class="eyebrow">Figure A5</span><h4>The platform as components</h4><p class="dek">Real-time services guard each sale, batch analytics scores stores each month, and case outcomes flow back as labels.</p></div>
  <div class="fig-body">
<svg data-fk="graph" viewBox="0 0 900 800" role="img" aria-label="UML component diagram of the fraud-prevention platform. In the real-time path, the authorization gateway requires eligibility, credential, basket check, and settlement interfaces. Batch analytics reads sale events: reference prices, ring analysis, and inventory reconciliation against supplier invoices feed a store-month scoring engine, which gives risk tiers to the payout risk service and scores to an expected-loss ranker. The ranker feeds case management, and case outcomes return to scoring as labels.">
<defs><marker id="uml-sw-components-tri-ink" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0 1 L10 5 L0 9 Z" class="f-ink"/></marker><marker id="uml-sw-components-open-ink" viewBox="0 0 10 10" refX="9.5" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0.5 0.8 L9.5 5 L0.5 9.2" fill="none" class="s-ink" stroke-width="1.3"/></marker><marker id="uml-sw-components-tri-muted" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0 1 L10 5 L0 9 Z" class="f-muted"/></marker><marker id="uml-sw-components-open-muted" viewBox="0 0 10 10" refX="9.5" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0.5 0.8 L9.5 5 L0.5 9.2" fill="none" class="s-muted" stroke-width="1.3"/></marker><marker id="uml-sw-components-tri-fraud" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0 1 L10 5 L0 9 Z" class="f-fraud"/></marker><marker id="uml-sw-components-open-fraud" viewBox="0 0 10 10" refX="9.5" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0.5 0.8 L9.5 5 L0.5 9.2" fill="none" class="s-fraud" stroke-width="1.3"/></marker><marker id="uml-sw-components-tri-caution" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0 1 L10 5 L0 9 Z" class="f-caution"/></marker><marker id="uml-sw-components-open-caution" viewBox="0 0 10 10" refX="9.5" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0.5 0.8 L9.5 5 L0.5 9.2" fill="none" class="s-caution" stroke-width="1.3"/></marker><marker id="uml-sw-components-tri-defense" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0 1 L10 5 L0 9 Z" class="f-defense"/></marker><marker id="uml-sw-components-open-defense" viewBox="0 0 10 10" refX="9.5" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0.5 0.8 L9.5 5 L0.5 9.2" fill="none" class="s-defense" stroke-width="1.3"/></marker></defs>
<rect data-group="pkg-realtime" x="10" y="10" width="109.3" height="18" class="f-neutral-tint s-muted" stroke-width="1"/>
<rect data-group="pkg-realtime" data-tip="Real-time path: services that guard each sale" x="10" y="28" width="655" height="312" class="s-muted" fill="none" stroke-width="1"/>
<rect data-group="pkg-batch" x="10" y="370" width="115.7" height="18" class="f-neutral-tint s-muted" stroke-width="1"/>
<rect data-fk-node="batch-analytics" data-group="pkg-batch" data-tip="Batch analytics: scores stores each month" x="10" y="388" width="655" height="262" class="s-muted" fill="none" stroke-width="1"/>
<rect data-group="pkg-ops" x="10" y="670" width="83.8" height="18" class="f-neutral-tint s-muted" stroke-width="1"/>
<rect data-group="pkg-ops" data-tip="Operations: people work the ranked list" x="10" y="688" width="655" height="102" class="s-muted" fill="none" stroke-width="1"/>
<path data-fk-edge="gw i-authorize" data-fk-part="p-authorize" data-tip="Authorization gateway provides IAuthorize" d="M110 108 L66 108" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="gw i-sale-events" data-fk-part="p-saleevents" data-tip="Authorization gateway provides ISaleEvents" d="M150 146 L150 170" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="batch-analytics i-sale-events" data-fk-part="d-use" data-tip="«use» dependency: Batch analytics uses ISaleEvents" d="M150 388 L150 183" class="s-muted" fill="none" stroke-width="1.2" stroke-dasharray="5 4" marker-end="url(#uml-sw-components-open-muted)"/>
<path data-fk-edge="eligibility i-eligibility" data-fk-part="p-elig" data-tip="Eligibility service provides IEligibility" d="M470 76 L422 76" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="gw i-eligibility" data-fk-part="r-elig" d="M416 86 A10 10 0 0 1 416 66" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="gw i-eligibility" data-fk-part="r-elig" data-tip="Authorization gateway requires IEligibility" d="M300 76 L406 76" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="credential i-credential" data-fk-part="p-cred" data-tip="Card and credential service provides ICredential" d="M470 140 L422 140" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="gw i-credential" data-fk-part="r-cred" d="M416 150 A10 10 0 0 1 416 130" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="gw i-credential" data-fk-part="r-cred" data-tip="Authorization gateway requires ICredential" d="M300 100 L370 100 L370 140 L406 140" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="basket i-basketcheck" data-fk-part="p-basket" data-tip="Basket validation provides IBasketCheck" d="M470 204 L422 204" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="gw i-basketcheck" data-fk-part="r-basket" d="M416 214 A10 10 0 0 1 416 194" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="gw i-basketcheck" data-fk-part="r-basket" data-tip="Authorization gateway requires IBasketCheck" d="M300 120 L350 120 L350 204 L406 204" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="payout-risk i-settlement" data-fk-part="p-settle" data-tip="Payout risk service provides ISettlement" d="M250 264 L250 246" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="gw i-settlement" data-fk-part="r-settle" d="M240 240 A10 10 0 0 1 260 240" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="gw i-settlement" data-fk-part="r-settle" data-tip="Authorization gateway requires ISettlement" d="M250 146 L250 230" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="income-match i-income" data-fk-part="p-income" data-tip="Income match provides IIncome" d="M740 76 L712 76" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="eligibility i-income" data-fk-part="r-income" d="M706 86 A10 10 0 0 1 706 66" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="eligibility i-income" data-fk-part="r-income" data-tip="Eligibility service requires IIncome" d="M650 76 L696 76" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="dup-check i-dupcheck" data-fk-part="p-dup" data-tip="Duplicate check provides IDupCheck" d="M740 140 L712 140" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="eligibility i-dupcheck" data-fk-part="r-dup" d="M706 150 A10 10 0 0 1 706 130" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="eligibility i-dupcheck" data-fk-part="r-dup" data-tip="Eligibility service requires IDupCheck" d="M650 92 L658 92 L658 140 L696 140" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="invoices i-invoices" data-fk-part="p-inv" data-tip="Supplier invoices provides IInvoices" d="M740 456 L712 456" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="inventory i-invoices" data-fk-part="r-inv" d="M706 466 A10 10 0 0 1 706 446" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="inventory i-invoices" data-fk-part="r-inv" data-tip="Inventory reconciliation requires IInvoices" d="M650 456 L696 456" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="ref-price i-refprice" data-fk-part="p-ref" data-tip="Reference price service provides IRefPrice" d="M115 488 L115 500" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="scoring i-refprice" data-fk-part="r-ref" d="M125 506 A10 10 0 0 1 105 506" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="scoring i-refprice" data-fk-part="r-ref" data-tip="Scoring engine requires IRefPrice" d="M115 560 L115 516" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="ring i-ringsignal" data-fk-part="p-ring" data-tip="Graph and ring analysis provides IRingSignal" d="M345 488 L345 500" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="scoring i-ringsignal" data-fk-part="r-ring" d="M355 506 A10 10 0 0 1 335 506" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="scoring i-ringsignal" data-fk-part="r-ring" data-tip="Scoring engine requires IRingSignal" d="M270 560 L270 526 L345 526 L345 516" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="inventory i-supplygap" data-fk-part="p-gap" data-tip="Inventory reconciliation provides ISupplyGap" d="M565 488 L565 500" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="scoring i-supplygap" data-fk-part="r-gap" d="M575 506 A10 10 0 0 1 555 506" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="scoring i-supplygap" data-fk-part="r-gap" data-tip="Scoring engine requires ISupplyGap" d="M285 560 L285 540 L565 540 L565 516" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="scoring i-risktier" data-fk-part="p-tier" data-tip="Scoring engine provides IRiskTier" d="M225 560 L225 542" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="payout-risk i-risktier" data-fk-part="r-tier" d="M215 536 A10 10 0 0 1 235 536" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="payout-risk i-risktier" data-fk-part="r-tier" data-tip="Payout risk service requires IRiskTier" d="M225 316 L225 526" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="scoring i-storescore" data-fk-part="p-score" data-tip="Scoring engine provides IStoreScore" d="M300 588 L344 588" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="ranker i-storescore" data-fk-part="r-score" d="M350 578 A10 10 0 0 1 350 598" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="ranker i-storescore" data-fk-part="r-score" data-tip="Expected-loss ranker requires IStoreScore" d="M400 588 L360 588" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="ranker i-rankedstores" data-fk-part="p-ranked" data-tip="Expected-loss ranker provides IRankedStores" d="M490 616 L490 628" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="case-mgmt i-rankedstores" data-fk-part="r-ranked" d="M500 634 A10 10 0 0 1 480 634" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="case-mgmt i-rankedstores" data-fk-part="r-ranked" data-tip="Case management requires IRankedStores" d="M490 712 L490 644" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="case-mgmt i-labels" data-fk-part="p-labels" data-tip="Case management provides ILabels" d="M400 740 L356 740" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="scoring i-labels" data-fk-part="r-labels" d="M350 750 A10 10 0 0 1 350 730" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="scoring i-labels" data-fk-part="r-labels" data-tip="Scoring engine requires ILabels: confirmed and cleared outcomes" d="M200 616 L200 740 L340 740" class="s-ink" fill="none" stroke-width="1.2"/>
<rect data-fk-node="gw" data-tip="Authorization gateway: the EBT processor component that handles each sale" x="110" y="70" width="190" height="76" class="f-defense-tint s-defense" rx="3" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="gw" x="283" y="77" width="11" height="14" class="f-surface s-defense" stroke-width="1" data-box="1"/>
<rect data-fk-node="gw" x="280" y="80" width="7" height="3" class="f-surface s-defense" stroke-width="1"/>
<rect data-fk-node="gw" x="280" y="85" width="7" height="3" class="f-surface s-defense" stroke-width="1"/>
<circle data-fk-node="i-authorize" data-tip="IAuthorize: the authorization call from POS terminals" cx="60" cy="108" r="6" class="f-surface s-ink" stroke-width="1.2"/>
<circle data-fk-node="i-sale-events" data-tip="ISaleEvents: sale events the gateway provides to batch analytics" cx="150" cy="176" r="6" class="f-surface s-ink" stroke-width="1.2"/>
<rect data-fk-node="eligibility" data-tip="Eligibility service: checks income, NAC, and household" x="470" y="50" width="180" height="52" class="f-defense-tint s-defense" rx="3" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="eligibility" x="633" y="57" width="11" height="14" class="f-surface s-defense" stroke-width="1" data-box="1"/>
<rect data-fk-node="eligibility" x="630" y="60" width="7" height="3" class="f-surface s-defense" stroke-width="1"/>
<rect data-fk-node="eligibility" x="630" y="65" width="7" height="3" class="f-surface s-defense" stroke-width="1"/>
<circle data-fk-node="i-eligibility" data-tip="IEligibility: eligibility check" cx="416" cy="76" r="6" class="f-surface s-ink" stroke-width="1.2"/>
<rect data-fk-node="credential" data-tip="Card and credential service: lock, blocks, and step-up" x="470" y="114" width="180" height="52" class="f-defense-tint s-defense" rx="3" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="credential" x="633" y="121" width="11" height="14" class="f-surface s-defense" stroke-width="1" data-box="1"/>
<rect data-fk-node="credential" x="630" y="124" width="7" height="3" class="f-surface s-defense" stroke-width="1"/>
<rect data-fk-node="credential" x="630" y="129" width="7" height="3" class="f-surface s-defense" stroke-width="1"/>
<circle data-fk-node="i-credential" data-tip="ICredential: card and credential check" cx="416" cy="140" r="6" class="f-surface s-ink" stroke-width="1.2"/>
<rect data-fk-node="basket" data-tip="Basket validation: eligible items and store catalog" x="470" y="172" width="180" height="64" class="f-defense-tint s-defense" rx="3" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="basket" x="633" y="179" width="11" height="14" class="f-surface s-defense" stroke-width="1" data-box="1"/>
<rect data-fk-node="basket" x="630" y="182" width="7" height="3" class="f-surface s-defense" stroke-width="1"/>
<rect data-fk-node="basket" x="630" y="187" width="7" height="3" class="f-surface s-defense" stroke-width="1"/>
<circle data-fk-node="i-basketcheck" data-tip="IBasketCheck: basket validation" cx="416" cy="204" r="6" class="f-surface s-ink" stroke-width="1.2"/>
<rect data-fk-node="payout-risk" data-tip="Payout risk service: tiers, delays, and caps" x="165" y="264" width="170" height="52" class="f-defense-tint s-defense" rx="3" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="payout-risk" x="318" y="271" width="11" height="14" class="f-surface s-defense" stroke-width="1" data-box="1"/>
<rect data-fk-node="payout-risk" x="315" y="274" width="7" height="3" class="f-surface s-defense" stroke-width="1"/>
<rect data-fk-node="payout-risk" x="315" y="279" width="7" height="3" class="f-surface s-defense" stroke-width="1"/>
<circle data-fk-node="i-settlement" data-tip="ISettlement: settlement through payout risk" cx="250" cy="240" r="6" class="f-surface s-ink" stroke-width="1.2"/>
<rect data-fk-node="income-match" data-tip="External income match: payroll, tax, and bank data" x="740" y="47" width="150" height="58" class="f-neutral-tint s-muted" rx="3" stroke-width="1.2" stroke-dasharray="5 3" data-box="1"/>
<rect data-fk-node="income-match" x="873" y="54" width="11" height="14" class="f-surface s-muted" stroke-width="1" data-box="1"/>
<rect data-fk-node="income-match" x="870" y="57" width="7" height="3" class="f-surface s-muted" stroke-width="1"/>
<rect data-fk-node="income-match" x="870" y="62" width="7" height="3" class="f-surface s-muted" stroke-width="1"/>
<circle data-fk-node="i-income" data-tip="IIncome: income match" cx="706" cy="76" r="6" class="f-surface s-ink" stroke-width="1.2"/>
<rect data-fk-node="dup-check" data-tip="External national duplicate check (NAC)" x="740" y="111" width="150" height="58" class="f-neutral-tint s-muted" rx="3" stroke-width="1.2" stroke-dasharray="5 3" data-box="1"/>
<rect data-fk-node="dup-check" x="873" y="118" width="11" height="14" class="f-surface s-muted" stroke-width="1" data-box="1"/>
<rect data-fk-node="dup-check" x="870" y="121" width="7" height="3" class="f-surface s-muted" stroke-width="1"/>
<rect data-fk-node="dup-check" x="870" y="126" width="7" height="3" class="f-surface s-muted" stroke-width="1"/>
<circle data-fk-node="i-dupcheck" data-tip="IDupCheck: national duplicate check" cx="706" cy="140" r="6" class="f-surface s-ink" stroke-width="1.2"/>
<rect data-fk-node="invoices" data-tip="External supplier invoices: distributor feeds" x="740" y="427" width="150" height="58" class="f-neutral-tint s-muted" rx="3" stroke-width="1.2" stroke-dasharray="5 3" data-box="1"/>
<rect data-fk-node="invoices" x="873" y="434" width="11" height="14" class="f-surface s-muted" stroke-width="1" data-box="1"/>
<rect data-fk-node="invoices" x="870" y="437" width="7" height="3" class="f-surface s-muted" stroke-width="1"/>
<rect data-fk-node="invoices" x="870" y="442" width="7" height="3" class="f-surface s-muted" stroke-width="1"/>
<circle data-fk-node="i-invoices" data-tip="IInvoices: supplier invoice feed" cx="706" cy="456" r="6" class="f-surface s-ink" stroke-width="1.2"/>
<rect data-fk-node="ref-price" data-tip="Reference price service: price by UPC, region, and month" x="30" y="424" width="170" height="64" class="f-defense-tint s-defense" rx="3" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="ref-price" x="183" y="431" width="11" height="14" class="f-surface s-defense" stroke-width="1" data-box="1"/>
<rect data-fk-node="ref-price" x="180" y="434" width="7" height="3" class="f-surface s-defense" stroke-width="1"/>
<rect data-fk-node="ref-price" x="180" y="439" width="7" height="3" class="f-surface s-defense" stroke-width="1"/>
<rect data-fk-node="ring" data-tip="Graph and ring analysis over the card-store graph" x="260" y="424" width="170" height="64" class="f-defense-tint s-defense" rx="3" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="ring" x="413" y="431" width="11" height="14" class="f-surface s-defense" stroke-width="1" data-box="1"/>
<rect data-fk-node="ring" x="410" y="434" width="7" height="3" class="f-surface s-defense" stroke-width="1"/>
<rect data-fk-node="ring" x="410" y="439" width="7" height="3" class="f-surface s-defense" stroke-width="1"/>
<rect data-fk-node="inventory" data-tip="Inventory reconciliation: sales against purchases" x="480" y="424" width="170" height="64" class="f-defense-tint s-defense" rx="3" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="inventory" x="633" y="431" width="11" height="14" class="f-surface s-defense" stroke-width="1" data-box="1"/>
<rect data-fk-node="inventory" x="630" y="434" width="7" height="3" class="f-surface s-defense" stroke-width="1"/>
<rect data-fk-node="inventory" x="630" y="439" width="7" height="3" class="f-surface s-defense" stroke-width="1"/>
<rect data-fk-node="scoring" data-tip="Scoring engine: store-month scores" x="100" y="560" width="200" height="56" class="f-defense-tint s-defense" rx="3" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="scoring" x="283" y="567" width="11" height="14" class="f-surface s-defense" stroke-width="1" data-box="1"/>
<rect data-fk-node="scoring" x="280" y="570" width="7" height="3" class="f-surface s-defense" stroke-width="1"/>
<rect data-fk-node="scoring" x="280" y="575" width="7" height="3" class="f-surface s-defense" stroke-width="1"/>
<rect data-fk-node="ranker" data-tip="Expected-loss ranker: budget-capped top list of stores" x="400" y="560" width="180" height="56" class="f-defense-tint s-defense" rx="3" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="ranker" x="563" y="567" width="11" height="14" class="f-surface s-defense" stroke-width="1" data-box="1"/>
<rect data-fk-node="ranker" x="560" y="570" width="7" height="3" class="f-surface s-defense" stroke-width="1"/>
<rect data-fk-node="ranker" x="560" y="575" width="7" height="3" class="f-surface s-defense" stroke-width="1"/>
<circle data-fk-node="i-refprice" data-tip="IRefPrice: reference prices" cx="115" cy="506" r="6" class="f-surface s-ink" stroke-width="1.2"/>
<circle data-fk-node="i-ringsignal" data-tip="IRingSignal: ring analysis signal" cx="345" cy="506" r="6" class="f-surface s-ink" stroke-width="1.2"/>
<circle data-fk-node="i-supplygap" data-tip="ISupplyGap: sales against purchases gap" cx="565" cy="506" r="6" class="f-surface s-ink" stroke-width="1.2"/>
<circle data-fk-node="i-risktier" data-tip="IRiskTier: risk tier for payouts" cx="225" cy="536" r="6" class="f-surface s-ink" stroke-width="1.2"/>
<circle data-fk-node="i-storescore" data-tip="IStoreScore: store-month scores" cx="350" cy="588" r="6" class="f-surface s-ink" stroke-width="1.2"/>
<rect data-fk-node="case-mgmt" data-tip="Case management: the investigation queue" x="400" y="712" width="180" height="56" class="f-defense-tint s-defense" rx="3" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="case-mgmt" x="563" y="719" width="11" height="14" class="f-surface s-defense" stroke-width="1" data-box="1"/>
<rect data-fk-node="case-mgmt" x="560" y="722" width="7" height="3" class="f-surface s-defense" stroke-width="1"/>
<rect data-fk-node="case-mgmt" x="560" y="727" width="7" height="3" class="f-surface s-defense" stroke-width="1"/>
<circle data-fk-node="i-rankedstores" data-tip="IRankedStores: ranked list of stores" cx="490" cy="634" r="6" class="f-surface s-ink" stroke-width="1.2"/>
<circle data-fk-node="i-labels" data-tip="ILabels: case outcomes returned as labels" cx="350" cy="740" r="6" class="f-surface s-ink" stroke-width="1.2"/>
<text data-group="pkg-realtime" x="18" y="23" class="f-ink" font-size="11" font-weight="700">Real-time path</text>
<text data-group="pkg-batch" x="18" y="383" class="f-ink" font-size="11" font-weight="700">Batch analytics</text>
<text data-group="pkg-ops" x="18" y="683" class="f-ink" font-size="11" font-weight="700">Operations</text>
<text data-group="outside" x="740" y="36" class="f-muted" font-size="11" font-style="italic">Outside the program</text>
<text data-fk-node="gw" x="120" y="92" class="f-ink" font-size="12" font-weight="700">Authorization gateway</text>
<text data-fk-node="gw" x="120" y="108" class="f-muted" font-size="11">EBT processor, each sale</text>
<text data-fk-node="i-authorize" x="60" y="94" class="f-ink mono" font-size="11" text-anchor="middle">IAuthorize</text>
<text data-fk-node="pos" data-tip="POS terminals call the gateway through IAuthorize" x="60" y="128" class="f-muted" font-size="11" text-anchor="middle">POS terminals</text>
<text data-fk-node="i-sale-events" x="140" y="180" class="f-ink mono" font-size="11" text-anchor="end">ISaleEvents</text>
<text data-fk-edge="batch-analytics i-sale-events" data-fk-part="d-use" data-tip="«use» dependency: Batch analytics uses ISaleEvents" x="158" y="362" class="f-muted" font-size="11">«use»</text>
<text data-fk-node="eligibility" x="480" y="72" class="f-ink" font-size="12" font-weight="700">Eligibility service</text>
<text data-fk-node="eligibility" x="480" y="88" class="f-muted" font-size="11">income, NAC, household</text>
<text data-fk-node="i-eligibility" x="416" y="62" class="f-ink mono" font-size="11" text-anchor="middle">IEligibility</text>
<text data-fk-node="credential" x="480" y="136" class="f-ink" font-size="12" font-weight="700">Card and credential</text>
<text data-fk-node="credential" x="480" y="152" class="f-muted" font-size="11">lock, blocks, step-up</text>
<text data-fk-node="i-credential" x="416" y="126" class="f-ink mono" font-size="11" text-anchor="middle">ICredential</text>
<text data-fk-node="basket" x="480" y="194" class="f-ink" font-size="12" font-weight="700">Basket validation</text>
<text data-fk-node="basket" x="480" y="210" class="f-muted" font-size="11">eligible items,</text>
<text data-fk-node="basket" x="480" y="224" class="f-muted" font-size="11">store catalog</text>
<text data-fk-node="i-basketcheck" x="416" y="190" class="f-ink mono" font-size="11" text-anchor="middle">IBasketCheck</text>
<text data-fk-node="payout-risk" x="175" y="286" class="f-ink" font-size="12" font-weight="700">Payout risk service</text>
<text data-fk-node="payout-risk" x="175" y="302" class="f-muted" font-size="11">tiers, delays, caps</text>
<text data-fk-node="i-settlement" x="264" y="244" class="f-ink mono" font-size="11">ISettlement</text>
<text data-fk-node="income-match" x="750" y="64" class="f-muted" font-size="11" font-style="italic">«external»</text>
<text data-fk-node="income-match" x="750" y="80" class="f-ink" font-size="12" font-weight="700">Income match</text>
<text data-fk-node="income-match" x="750" y="96" class="f-muted" font-size="11">payroll, tax, bank</text>
<text data-fk-node="i-income" x="706" y="62" class="f-ink mono" font-size="11" text-anchor="middle">IIncome</text>
<text data-fk-node="dup-check" x="750" y="128" class="f-muted" font-size="11" font-style="italic">«external»</text>
<text data-fk-node="dup-check" x="750" y="144" class="f-ink" font-size="12" font-weight="700">Duplicate check</text>
<text data-fk-node="dup-check" x="750" y="160" class="f-muted" font-size="11">national (NAC)</text>
<text data-fk-node="i-dupcheck" x="706" y="126" class="f-ink mono" font-size="11" text-anchor="middle">IDupCheck</text>
<text data-fk-node="invoices" x="750" y="444" class="f-muted" font-size="11" font-style="italic">«external»</text>
<text data-fk-node="invoices" x="750" y="460" class="f-ink" font-size="12" font-weight="700">Supplier invoices</text>
<text data-fk-node="invoices" x="750" y="476" class="f-muted" font-size="11">distributor feeds</text>
<text data-fk-node="i-invoices" x="706" y="442" class="f-ink mono" font-size="11" text-anchor="middle">IInvoices</text>
<text data-fk-node="ref-price" x="40" y="446" class="f-ink" font-size="12" font-weight="700">Reference price</text>
<text data-fk-node="ref-price" x="40" y="461" class="f-ink" font-size="12" font-weight="700">service</text>
<text data-fk-node="ref-price" x="40" y="477" class="f-muted" font-size="11">UPC, region, month</text>
<text data-fk-node="ring" x="270" y="446" class="f-ink" font-size="12" font-weight="700">Graph and ring</text>
<text data-fk-node="ring" x="270" y="461" class="f-ink" font-size="12" font-weight="700">analysis</text>
<text data-fk-node="ring" x="270" y="477" class="f-muted" font-size="11">card-store graph</text>
<text data-fk-node="inventory" x="490" y="446" class="f-ink" font-size="12" font-weight="700">Inventory</text>
<text data-fk-node="inventory" x="490" y="461" class="f-ink" font-size="12" font-weight="700">reconciliation</text>
<text data-fk-node="inventory" x="490" y="477" class="f-muted" font-size="11">sales vs purchases</text>
<text data-fk-node="scoring" x="110" y="582" class="f-ink" font-size="12" font-weight="700">Scoring engine</text>
<text data-fk-node="scoring" x="110" y="598" class="f-muted" font-size="11">store-month scores</text>
<text data-fk-node="ranker" x="410" y="582" class="f-ink" font-size="12" font-weight="700">Expected-loss ranker</text>
<text data-fk-node="ranker" x="410" y="598" class="f-muted" font-size="11">budget-capped top N</text>
<text data-fk-node="i-refprice" x="127" y="510" class="f-ink mono" font-size="11">IRefPrice</text>
<text data-fk-node="i-ringsignal" x="357" y="510" class="f-ink mono" font-size="11">IRingSignal</text>
<text data-fk-node="i-supplygap" x="577" y="510" class="f-ink mono" font-size="11">ISupplyGap</text>
<text data-fk-node="i-risktier" x="213" y="540" class="f-ink mono" font-size="11" text-anchor="end">IRiskTier</text>
<text data-fk-node="i-storescore" x="350" y="574" class="f-ink mono" font-size="11" text-anchor="middle">IStoreScore</text>
<text data-fk-node="case-mgmt" x="410" y="734" class="f-ink" font-size="12" font-weight="700">Case management</text>
<text data-fk-node="case-mgmt" x="410" y="750" class="f-muted" font-size="11">investigation queue</text>
<text data-fk-node="i-rankedstores" x="504" y="638" class="f-ink mono" font-size="11">IRankedStores</text>
<text data-fk-node="i-labels" x="350" y="726" class="f-ink mono" font-size="11" text-anchor="middle">ILabels</text>
<text data-fk-edge="scoring i-labels" data-fk-part="r-labels" data-tip="Scoring engine requires ILabels: confirmed and cleared outcomes" x="210" y="758" class="f-muted" font-size="11">confirmed and cleared outcomes</text>
</svg>
  </div>
  <figcaption><strong>Read it as:</strong> a ball is an interface a component provides, and a cup is one it requires. The gateway calls three checks on every sale and settles through payout risk. Only two outputs leave the scoring engine: a risk tier for payouts and scores for the ranker. People see only the ranked list, and their outcomes retrain the engine.</figcaption>
</figure>

<p>The sequence diagram follows one EBT purchase once the basket travels with the authorization. The processor checks every line against SNAP eligibility and the store catalog before it touches the balance. A valid basket then passes a risk check. Only a large spend soon after the monthly load triggers a step-up confirmation in the recipient app, and a recipient without a phone fails open.</p>
<p>An invalid basket is declined at the register. The processor still reports the line, because a store that keeps keying items it never stocks is a signal for the monthly score.</p>
<figure class="fig" id="uml-sw-auth-sequence">
  <div class="fig-head"><span class="eyebrow">Figure A6</span><h4>One purchase, line by line</h4><p class="dek">The processor checks every basket line before it touches the balance, and asks the recipient only when a spend looks like a load-day drain.</p></div>
  <div class="fig-body">
<svg data-fk="graph" viewBox="0 0 900 694" role="img" aria-label="UML sequence diagram of one EBT purchase with item-level basket data. The POS terminal sends the sale with its item lines to the EBT processor, which asks the basket validator to check every line. If every line is eligible and in the store catalog, the processor asks the risk service, may ask the recipient to confirm a large spend soon after the benefit load, debits the benefit ledger, and returns an itemized receipt. Otherwise the sale is declined at the register and the flagged line goes to the risk service.">
<defs><marker id="uml-sw-auth-sequence-tri-ink" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0 1 L10 5 L0 9 Z" class="f-ink"/></marker><marker id="uml-sw-auth-sequence-open-ink" viewBox="0 0 10 10" refX="9.5" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0.5 0.8 L9.5 5 L0.5 9.2" fill="none" class="s-ink" stroke-width="1.3"/></marker><marker id="uml-sw-auth-sequence-tri-muted" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0 1 L10 5 L0 9 Z" class="f-muted"/></marker><marker id="uml-sw-auth-sequence-open-muted" viewBox="0 0 10 10" refX="9.5" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0.5 0.8 L9.5 5 L0.5 9.2" fill="none" class="s-muted" stroke-width="1.3"/></marker><marker id="uml-sw-auth-sequence-tri-fraud" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0 1 L10 5 L0 9 Z" class="f-fraud"/></marker><marker id="uml-sw-auth-sequence-open-fraud" viewBox="0 0 10 10" refX="9.5" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0.5 0.8 L9.5 5 L0.5 9.2" fill="none" class="s-fraud" stroke-width="1.3"/></marker><marker id="uml-sw-auth-sequence-tri-caution" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0 1 L10 5 L0 9 Z" class="f-caution"/></marker><marker id="uml-sw-auth-sequence-open-caution" viewBox="0 0 10 10" refX="9.5" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0.5 0.8 L9.5 5 L0.5 9.2" fill="none" class="s-caution" stroke-width="1.3"/></marker><marker id="uml-sw-auth-sequence-tri-defense" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0 1 L10 5 L0 9 Z" class="f-defense"/></marker><marker id="uml-sw-auth-sequence-open-defense" viewBox="0 0 10 10" refX="9.5" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0.5 0.8 L9.5 5 L0.5 9.2" fill="none" class="s-defense" stroke-width="1.3"/></marker></defs>
<rect data-fk-area data-group="opt" data-tip="opt fragment. Guard: large spend soon after load, far from usual stores." x="32" y="328" width="370" height="88" class="s-caution" fill="none" stroke-width="1.2"/>
<path data-group="opt" d="M32 328 H65.1 V339 L58.1 346 H32 Z" class="f-caution-tint s-caution" stroke-width="1.2"/>
<rect data-fk-area data-group="alt" data-tip="alt fragment. Guards: every line eligible and in the store catalog; else, ineligible item or item not in store catalog." x="22" y="238" width="860" height="432" class="s-muted" fill="none" stroke-width="1.2"/>
<path data-group="alt" d="M22 238 H55.1 V249 L48.1 256 H22 Z" class="f-neutral-tint s-muted" stroke-width="1.2"/>
<path data-group="alt" data-tip="Else branch: ineligible item, or item not in store catalog." d="M22 550 L882 550" class="s-muted" fill="none" stroke-width="1.1" stroke-dasharray="6 4"/>
<path data-fk-node="recipient" d="M70 76 L70 686" class="s-muted" fill="none" stroke-width="1" stroke-dasharray="4 4"/>
<path data-fk-node="pos" d="M220 58 L220 686" class="s-muted" fill="none" stroke-width="1" stroke-dasharray="4 4"/>
<path data-fk-node="processor" d="M370 58 L370 686" class="s-muted" fill="none" stroke-width="1" stroke-dasharray="4 4"/>
<path data-fk-node="validator" d="M520 58 L520 686" class="s-muted" fill="none" stroke-width="1" stroke-dasharray="4 4"/>
<path data-fk-node="ledger" d="M670 58 L670 686" class="s-muted" fill="none" stroke-width="1" stroke-dasharray="4 4"/>
<path data-fk-node="risk" d="M820 58 L820 686" class="s-muted" fill="none" stroke-width="1" stroke-dasharray="4 4"/>
<rect data-fk-node="validator" x="515" y="192" width="10" height="30" class="f-defense-tint s-defense" stroke-width="1.1"/>
<rect data-fk-node="risk" x="815" y="282" width="10" height="30" class="f-defense-tint s-defense" stroke-width="1.1"/>
<rect data-fk-node="recipient" x="65" y="372" width="10" height="30" class="f-caution-tint s-caution" stroke-width="1.1"/>
<rect data-fk-node="ledger" x="665" y="446" width="10" height="30" class="f-defense-tint s-defense" stroke-width="1.1"/>
<rect data-fk-node="risk" x="815" y="596" width="10" height="14" class="f-defense-tint s-defense" stroke-width="1.1"/>
<rect data-fk-node="pos" x="215" y="106" width="10" height="556" class="f-defense-tint s-defense" stroke-width="1.1"/>
<rect data-fk-node="processor" x="365" y="149" width="10" height="483" class="f-defense-tint s-defense" stroke-width="1.1"/>
<path data-fk-edge="recipient pos" data-fk-part="m1" data-tip="1: tap card, PIN" data-animate="sequence" data-step="0" d="M70 106 L215 106" class="s-ink" fill="none" stroke-width="1.3" marker-end="url(#uml-sw-auth-sequence-tri-ink)"/>
<path data-fk-edge="pos processor" data-fk-part="m2" data-tip="2: authorize(sale), card, total, item lines" data-animate="sequence" data-step="1" d="M225 149 L365 149" class="s-ink" fill="none" stroke-width="1.3" marker-end="url(#uml-sw-auth-sequence-tri-ink)"/>
<path data-fk-edge="processor validator" data-fk-part="m3" data-tip="3: validate(basket), eligible? in catalog?" data-animate="sequence" data-step="2" d="M375 192 L515 192" class="s-ink" fill="none" stroke-width="1.3" marker-end="url(#uml-sw-auth-sequence-tri-ink)"/>
<path data-fk-edge="validator processor" data-fk-part="m4" data-tip="4: result" data-animate="sequence" data-step="3" d="M515 222 L375 222" class="s-ink" fill="none" stroke-width="1.3" stroke-dasharray="6 4" marker-end="url(#uml-sw-auth-sequence-open-ink)"/>
<path data-fk-edge="processor risk" data-fk-part="m5" data-tip="5: assess(card, store, total) Branch: every line eligible and in the store catalog." data-animate="sequence" data-step="4" d="M375 282 L815 282" class="s-ink" fill="none" stroke-width="1.3" marker-end="url(#uml-sw-auth-sequence-tri-ink)"/>
<path data-fk-edge="risk processor" data-fk-part="m6" data-tip="6: risk flags Branch: every line eligible and in the store catalog." data-animate="sequence" data-step="5" d="M815 312 L375 312" class="s-ink" fill="none" stroke-width="1.3" stroke-dasharray="6 4" marker-end="url(#uml-sw-auth-sequence-open-ink)"/>
<path data-fk-edge="processor recipient" data-fk-part="m7" data-tip="7: confirm in app Guard: large spend soon after load, far from usual stores." data-animate="sequence" data-step="6" d="M365 372 L75 372" class="s-caution" fill="none" stroke-width="1.3" marker-end="url(#uml-sw-auth-sequence-tri-caution)"/>
<path data-fk-edge="recipient processor" data-fk-part="m8" data-tip="8: confirmed Guard: large spend soon after load, far from usual stores." data-animate="sequence" data-step="7" d="M75 402 L365 402" class="s-caution" fill="none" stroke-width="1.3" stroke-dasharray="6 4" marker-end="url(#uml-sw-auth-sequence-open-caution)"/>
<path data-group="opt-note" d="M402 368 L430 368" class="s-caution" fill="none" stroke-width="1" stroke-dasharray="3 3"/>
<path data-fk-edge="processor ledger" data-fk-part="m9" data-tip="9: debit(card, total) Branch: every line eligible and in the store catalog." data-animate="sequence" data-step="8" d="M375 446 L665 446" class="s-ink" fill="none" stroke-width="1.3" marker-end="url(#uml-sw-auth-sequence-tri-ink)"/>
<path data-fk-edge="ledger processor" data-fk-part="m10" data-tip="10: new balance Branch: every line eligible and in the store catalog." data-animate="sequence" data-step="9" d="M665 476 L375 476" class="s-ink" fill="none" stroke-width="1.3" stroke-dasharray="6 4" marker-end="url(#uml-sw-auth-sequence-open-ink)"/>
<path data-fk-edge="processor pos" data-fk-part="m11" data-tip="11: approved, balance Branch: every line eligible and in the store catalog." data-animate="sequence" data-step="10" d="M365 506 L225 506" class="s-ink" fill="none" stroke-width="1.3" stroke-dasharray="6 4" marker-end="url(#uml-sw-auth-sequence-open-ink)"/>
<path data-fk-edge="pos recipient" data-fk-part="m12" data-tip="12: itemized receipt Branch: every line eligible and in the store catalog." data-animate="sequence" data-step="11" d="M215 536 L70 536" class="s-ink" fill="none" stroke-width="1.3" stroke-dasharray="6 4" marker-end="url(#uml-sw-auth-sequence-open-ink)"/>
<path data-fk-edge="processor risk" data-fk-part="m13" data-tip="13: flagLine(store, UPC) Branch: else, ineligible item or item not in store catalog." data-animate="sequence" data-step="12" d="M375 596 L815 596" class="s-ink" fill="none" stroke-width="1.3" marker-end="url(#uml-sw-auth-sequence-open-ink)"/>
<path data-fk-edge="processor pos" data-fk-part="m14" data-tip="14: declined: line n Branch: else, ineligible item or item not in store catalog." data-animate="sequence" data-step="13" d="M365 626 L225 626" class="s-fraud" fill="none" stroke-width="1.3" stroke-dasharray="6 4" marker-end="url(#uml-sw-auth-sequence-open-fraud)"/>
<path data-fk-edge="pos recipient" data-fk-part="m15" data-tip="15: declined, retry Branch: else, ineligible item or item not in store catalog." data-animate="sequence" data-step="14" d="M215 656 L70 656" class="s-fraud" fill="none" stroke-width="1.3" stroke-dasharray="6 4" marker-end="url(#uml-sw-auth-sequence-open-fraud)"/>
<circle data-fk-node="recipient" data-tip="The benefit recipient who taps the card and enters the PIN, and may confirm a large spend in the app." cx="70" cy="18" r="7" class="f-surface s-ink" stroke-width="1.2"/>
<path data-fk-node="recipient" d="M70 25 L70 40" class="s-ink" fill="none" stroke-width="1.4"/>
<path data-fk-node="recipient" d="M58 31 L82 31" class="s-ink" fill="none" stroke-width="1.4"/>
<path data-fk-node="recipient" d="M60 53 L70 40 L80 53" class="s-ink" fill="none" stroke-width="1.4"/>
<rect data-fk-node="pos" data-tip="The register terminal that sends the sale with its item lines and shows the receipt or decline." x="155" y="14" width="130" height="44" class="f-neutral-tint s-muted" rx="3" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="processor" data-tip="The EBT processor that checks every basket line before it touches the balance." x="305" y="14" width="130" height="44" class="f-defense-tint s-defense" rx="3" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="validator" data-tip="The basket validator that checks each line for eligibility and store catalog membership." x="455" y="14" width="130" height="44" class="f-defense-tint s-defense" rx="3" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="ledger" data-tip="The state benefit ledger that holds the balance and takes the debit." x="605" y="14" width="130" height="44" class="f-defense-tint s-defense" rx="3" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="risk" data-tip="The risk service that assesses the sale and hears about any flagged line." x="755" y="14" width="130" height="44" class="f-defense-tint s-defense" rx="3" stroke-width="1.2" data-box="1"/>
<path data-group="opt-note" data-tip="No phone: fail open, approve without the app." d="M430 348 H598 L608 358 V388 H430 Z" class="f-caution-tint s-caution" stroke-width="1.1"/>
<path data-group="opt-note" d="M598 348 V358 H608" class="s-caution" fill="none" stroke-width="1.1"/>
<rect data-fk-edge="recipient pos" data-fk-part="m1" data-animate="sequence" data-step="0" x="86.7" y="90" width="111.6" height="15" class="f-surface"/>
<rect data-fk-edge="pos processor" data-fk-part="m2" data-animate="sequence" data-step="1" x="232.6" y="120" width="124.8" height="15" class="f-surface"/>
<rect data-fk-edge="pos processor" data-fk-part="m2" data-animate="sequence" data-step="1" x="222.4" y="133" width="145.2" height="15" class="f-surface"/>
<rect data-fk-edge="processor validator" data-fk-part="m3" data-animate="sequence" data-step="2" x="379.3" y="163" width="131.4" height="15" class="f-surface"/>
<rect data-fk-edge="processor validator" data-fk-part="m3" data-animate="sequence" data-step="2" x="378.5" y="176" width="133.1" height="15" class="f-surface"/>
<rect data-fk-edge="validator processor" data-fk-part="m4" data-animate="sequence" data-step="3" x="412.3" y="206" width="65.4" height="15" class="f-surface"/>
<rect data-fk-edge="processor risk" data-fk-part="m5" data-animate="sequence" data-step="4" x="496.3" y="266" width="197.4" height="15" class="f-surface"/>
<rect data-fk-edge="risk processor" data-fk-part="m6" data-animate="sequence" data-step="5" x="549.1" y="296" width="91.8" height="15" class="f-surface"/>
<rect data-fk-edge="processor recipient" data-fk-part="m7" data-animate="sequence" data-step="6" x="160.9" y="356" width="118.2" height="15" class="f-surface"/>
<rect data-fk-edge="recipient processor" data-fk-part="m8" data-animate="sequence" data-step="7" x="177.4" y="386" width="85.2" height="15" class="f-surface"/>
<rect data-group="opt" x="70.1" y="331" width="320.6" height="15" class="f-surface"/>
<rect data-fk-edge="processor ledger" data-fk-part="m9" data-animate="sequence" data-step="8" x="447.7" y="430" width="144.6" height="15" class="f-surface"/>
<rect data-fk-edge="ledger processor" data-fk-part="m10" data-animate="sequence" data-step="9" x="467.5" y="460" width="105" height="15" class="f-surface"/>
<rect data-fk-edge="processor pos" data-fk-part="m11" data-animate="sequence" data-step="10" x="222.7" y="490" width="144.6" height="15" class="f-surface"/>
<rect data-fk-edge="pos recipient" data-fk-part="m12" data-animate="sequence" data-step="11" x="73.5" y="520" width="138" height="15" class="f-surface"/>
<rect data-fk-edge="processor risk" data-fk-part="m13" data-animate="sequence" data-step="12" x="512.8" y="580" width="164.4" height="15" class="f-surface"/>
<rect data-fk-edge="processor pos" data-fk-part="m14" data-animate="sequence" data-step="13" x="226" y="610" width="138" height="15" class="f-surface"/>
<rect data-fk-edge="pos recipient" data-fk-part="m15" data-animate="sequence" data-step="14" x="76.8" y="640" width="131.4" height="15" class="f-surface"/>
<rect data-group="alt" x="60.1" y="241" width="284.3" height="15" class="f-surface"/>
<rect data-group="alt" x="27" y="554" width="326.7" height="15" class="f-surface"/>
<text data-fk-node="recipient" x="70" y="70" class="f-ink" font-size="12" text-anchor="middle" font-weight="700">Recipient</text>
<text data-fk-node="pos" x="220" y="40" class="f-ink" font-size="12" text-anchor="middle" font-weight="700">POS terminal</text>
<text data-fk-node="processor" x="370" y="40" class="f-ink" font-size="12" text-anchor="middle" font-weight="700">EBT processor</text>
<text data-fk-node="validator" x="520" y="40" class="f-ink" font-size="12" text-anchor="middle" font-weight="700">Basket validator</text>
<text data-fk-node="ledger" x="670" y="33" class="f-ink" font-size="12" text-anchor="middle" font-weight="700">Benefit ledger</text>
<text data-fk-node="ledger" x="670" y="47" class="f-ink" font-size="12" text-anchor="middle" font-weight="700">(state)</text>
<text data-fk-node="risk" x="820" y="40" class="f-ink" font-size="12" text-anchor="middle" font-weight="700">Risk service</text>
<text data-fk-edge="recipient pos" data-fk-part="m1" data-animate="sequence" data-step="0" x="142.5" y="100" class="f-ink mono" font-size="11" text-anchor="middle">1: tap card, PIN</text>
<text data-fk-edge="pos processor" data-fk-part="m2" data-animate="sequence" data-step="1" x="295" y="130" class="f-ink mono" font-size="11" text-anchor="middle">2: authorize(sale)</text>
<text data-fk-edge="pos processor" data-fk-part="m2" data-animate="sequence" data-step="1" x="295" y="143" class="f-muted" font-size="11" text-anchor="middle">card, total, item lines</text>
<text data-fk-edge="processor validator" data-fk-part="m3" data-animate="sequence" data-step="2" x="445" y="173" class="f-ink mono" font-size="11" text-anchor="middle">3: validate(basket)</text>
<text data-fk-edge="processor validator" data-fk-part="m3" data-animate="sequence" data-step="2" x="445" y="186" class="f-muted" font-size="11" text-anchor="middle">eligible? in catalog?</text>
<text data-fk-edge="validator processor" data-fk-part="m4" data-animate="sequence" data-step="3" x="445" y="216" class="f-ink mono" font-size="11" text-anchor="middle">4: result</text>
<text data-fk-edge="processor risk" data-fk-part="m5" data-animate="sequence" data-step="4" x="595" y="276" class="f-ink mono" font-size="11" text-anchor="middle">5: assess(card, store, total)</text>
<text data-fk-edge="risk processor" data-fk-part="m6" data-animate="sequence" data-step="5" x="595" y="306" class="f-ink mono" font-size="11" text-anchor="middle">6: risk flags</text>
<text data-fk-edge="processor recipient" data-fk-part="m7" data-animate="sequence" data-step="6" x="220" y="366" class="f-caution mono" font-size="11" text-anchor="middle">7: confirm in app</text>
<text data-fk-edge="recipient processor" data-fk-part="m8" data-animate="sequence" data-step="7" x="220" y="396" class="f-caution mono" font-size="11" text-anchor="middle">8: confirmed</text>
<text data-group="opt" x="38" y="341" class="f-caution" font-size="11" font-weight="700">opt</text>
<text data-group="opt" x="73.1" y="341" class="f-caution" font-size="11">[large spend soon after load, far from usual stores]</text>
<text data-group="opt-note" x="438" y="365" class="f-caution" font-size="11" font-weight="700">No phone: fail open</text>
<text data-group="opt-note" x="438" y="379" class="f-ink" font-size="11">approve without the app</text>
<text data-fk-edge="processor ledger" data-fk-part="m9" data-animate="sequence" data-step="8" x="520" y="440" class="f-ink mono" font-size="11" text-anchor="middle">9: debit(card, total)</text>
<text data-fk-edge="ledger processor" data-fk-part="m10" data-animate="sequence" data-step="9" x="520" y="470" class="f-ink mono" font-size="11" text-anchor="middle">10: new balance</text>
<text data-fk-edge="processor pos" data-fk-part="m11" data-animate="sequence" data-step="10" x="295" y="500" class="f-ink mono" font-size="11" text-anchor="middle">11: approved, balance</text>
<text data-fk-edge="pos recipient" data-fk-part="m12" data-animate="sequence" data-step="11" x="142.5" y="530" class="f-ink mono" font-size="11" text-anchor="middle">12: itemized receipt</text>
<text data-fk-edge="processor risk" data-fk-part="m13" data-animate="sequence" data-step="12" x="595" y="590" class="f-ink mono" font-size="11" text-anchor="middle">13: flagLine(store, UPC)</text>
<text data-fk-edge="processor pos" data-fk-part="m14" data-animate="sequence" data-step="13" x="295" y="620" class="f-fraud mono" font-size="11" text-anchor="middle">14: declined: line n</text>
<text data-fk-edge="pos recipient" data-fk-part="m15" data-animate="sequence" data-step="14" x="142.5" y="650" class="f-fraud mono" font-size="11" text-anchor="middle">15: declined, retry</text>
<text data-group="alt" x="28" y="251" class="f-ink" font-size="11" font-weight="700">alt</text>
<text data-group="alt" x="63.1" y="251" class="f-ink" font-size="11">[every line eligible and in the store catalog]</text>
<text data-group="alt" x="30" y="564" class="f-ink" font-size="11">[else: ineligible item, or item not in store catalog]</text>
</svg>
  </div>
  <figcaption><strong>Read it as:</strong> solid arrows with filled heads are calls, dashed arrows are replies, and the open-headed arrow is a one-way signal. The alt frame runs exactly one branch; the opt frame runs only when its guard holds. A rejected line ends the sale before any money moves, and the risk service still hears about it.</figcaption>
</figure>

<p>The class diagram names the records the software keeps. Two structures mirror each other: a transaction owns its basket lines, and a supplier invoice owns its invoice lines. Both kinds of line point at the same product barcode. That shared key makes reconciliation possible, because the store writes one side and its suppliers write the other.</p>
<p>Scores attach to a retailer and a month, never to a single basket. A score can open a case or trigger an automatic action, and every action carries an expiry and an appeal.</p>
<figure class="fig" id="uml-sw-domain-model">
  <div class="fig-head"><span class="eyebrow">Figure A7</span><h4>The domain model</h4><p class="dek">Thirteen classes carry the design: sales with their lines, purchases with theirs, and scores that open cases.</p></div>
  <div class="fig-body">
<svg data-fk="graph" viewBox="0 0 900 850" role="img" aria-label="UML class diagram of the core domain. A household aggregates recipients, who hold cards that pay for transactions at retailers. A transaction is composed of basket lines and a supplier invoice is composed of invoice lines; both kinds of line point at one product barcode, which also carries regional reference prices. A retailer has store-month scores; a score can open a case or trigger automatic actions, and a case orders actions.">
<defs><marker id="uml-sw-domain-model-tri-ink" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0 1 L10 5 L0 9 Z" class="f-ink"/></marker><marker id="uml-sw-domain-model-open-ink" viewBox="0 0 10 10" refX="9.5" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0.5 0.8 L9.5 5 L0.5 9.2" fill="none" class="s-ink" stroke-width="1.3"/></marker><marker id="uml-sw-domain-model-tri-muted" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0 1 L10 5 L0 9 Z" class="f-muted"/></marker><marker id="uml-sw-domain-model-open-muted" viewBox="0 0 10 10" refX="9.5" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0.5 0.8 L9.5 5 L0.5 9.2" fill="none" class="s-muted" stroke-width="1.3"/></marker><marker id="uml-sw-domain-model-tri-fraud" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0 1 L10 5 L0 9 Z" class="f-fraud"/></marker><marker id="uml-sw-domain-model-open-fraud" viewBox="0 0 10 10" refX="9.5" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0.5 0.8 L9.5 5 L0.5 9.2" fill="none" class="s-fraud" stroke-width="1.3"/></marker><marker id="uml-sw-domain-model-tri-caution" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0 1 L10 5 L0 9 Z" class="f-caution"/></marker><marker id="uml-sw-domain-model-open-caution" viewBox="0 0 10 10" refX="9.5" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0.5 0.8 L9.5 5 L0.5 9.2" fill="none" class="s-caution" stroke-width="1.3"/></marker><marker id="uml-sw-domain-model-tri-defense" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0 1 L10 5 L0 9 Z" class="f-defense"/></marker><marker id="uml-sw-domain-model-open-defense" viewBox="0 0 10 10" refX="9.5" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0.5 0.8 L9.5 5 L0.5 9.2" fill="none" class="s-defense" stroke-width="1.3"/></marker></defs>
<path data-fk-edge="household recipient" data-fk-part="a1" data-tip="Household 1 to 1..* Recipient (aggregation, members)" d="M97.5 146 L97.5 186" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="recipient card" data-fk-part="a2" data-tip="Recipient 1 to 0..* Card (cards)" d="M97.5 264 L97.5 365" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="transaction basketline" data-fk-part="c1" data-tip="Transaction 1 to 1..* BasketLine (composition, lines)" d="M332.5 506 L332.5 546" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="invoice invoiceline" data-fk-part="c2" data-tip="SupplierInvoice 1 to 1..* InvoiceLine (composition, lines)" d="M802.5 474 L802.5 546" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="product refprice" data-fk-part="a3" data-tip="Product 1 to 0..* ReferencePrice (prices)" d="M567.5 639 L567.5 695" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="retailer score" data-fk-part="a4" data-tip="Retailer 1 to 0..* StoreMonthScore (scores)" d="M567.5 309 L567.5 365" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="case score" data-fk-part="a5" data-tip="Case 0..1 to 1 StoreMonthScore (case)" d="M567.5 130 L567.5 186" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="card transaction" data-fk-part="a6" data-tip="Card 1 to 0..* Transaction" d="M185 410 L245 410" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="transaction retailer" data-fk-part="a7" data-tip="Transaction 0..* to 1 Retailer (store)" d="M420 410 L480 410" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="retailer invoice" data-fk-part="a8" data-tip="Retailer 1 to 0..* SupplierInvoice (invoices)" d="M655 410 L715 410" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="basketline product" data-fk-part="a9" data-tip="BasketLine 0..* to 1 Product (item)" d="M420 591 L480 591" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="product invoiceline" data-fk-part="a10" data-tip="Product 1 to 0..* InvoiceLine (item)" d="M655 591 L715 591" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="case action" data-fk-part="a11" data-tip="Case 0..1 to 0..* Action" d="M655 65 L715 65" class="s-ink" fill="none" stroke-width="1.2"/>
<path data-fk-edge="score action" data-fk-part="a12" data-tip="StoreMonthScore 0..1 to 0..* Action (autoActions)" d="M655 250 L802.5 250 L802.5 130" class="s-ink" fill="none" stroke-width="1.2"/>
<rect data-fk-node="household" data-tip="Household: the benefit case that groups recipients" x="10" y="20" width="175" height="110" class="f-surface s-defense" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="household" x="10" y="20" width="175" height="26" class="f-defense-tint s-defense" stroke-width="1.2"/>
<path data-fk-node="household" d="M10 103 L185 103" class="s-defense" fill="none" stroke-width="1.2"/>
<rect data-fk-node="case" data-tip="Case: an investigation opened from a score, decided by a person" x="480" y="20" width="175" height="110" class="f-surface s-caution" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="case" x="480" y="20" width="175" height="26" class="f-caution-tint s-caution" stroke-width="1.2"/>
<path data-fk-node="case" d="M480 103 L655 103" class="s-caution" fill="none" stroke-width="1.2"/>
<rect data-fk-node="action" data-tip="Action: a step ordered by a case or triggered automatically" x="715" y="20" width="175" height="110" class="f-surface s-defense" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="action" x="715" y="20" width="175" height="26" class="f-defense-tint s-defense" stroke-width="1.2"/>
<path data-fk-node="action" d="M715 103 L890 103" class="s-defense" fill="none" stroke-width="1.2"/>
<rect data-fk-node="recipient" data-tip="Recipient: a person in a household, held as a token" x="10" y="186" width="175" height="78" class="f-surface s-defense" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="recipient" x="10" y="186" width="175" height="26" class="f-defense-tint s-defense" stroke-width="1.2"/>
<path data-fk-node="recipient" d="M10 254 L185 254" class="s-defense" fill="none" stroke-width="1.2"/>
<rect data-fk-node="score" data-tip="StoreMonthScore: a store's monthly risk scores and expected loss" x="480" y="186" width="175" height="123" class="f-surface s-defense" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="score" x="480" y="186" width="175" height="26" class="f-defense-tint s-defense" stroke-width="1.2"/>
<path data-fk-node="score" d="M480 299 L655 299" class="s-defense" fill="none" stroke-width="1.2"/>
<rect data-fk-node="card" data-tip="Card: a recipient's payment card" x="10" y="365" width="175" height="110" class="f-surface s-defense" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="card" x="10" y="365" width="175" height="26" class="f-defense-tint s-defense" stroke-width="1.2"/>
<path data-fk-node="card" d="M10 448 L185 448" class="s-defense" fill="none" stroke-width="1.2"/>
<rect data-fk-node="transaction" data-tip="Transaction: one sale authorized at a retailer" x="245" y="365" width="175" height="125" class="f-surface s-defense" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="transaction" x="245" y="365" width="175" height="26" class="f-defense-tint s-defense" stroke-width="1.2"/>
<path data-fk-node="transaction" d="M245 463 L420 463" class="s-defense" fill="none" stroke-width="1.2"/>
<rect data-fk-node="retailer" data-tip="Retailer: a store with a risk tier" x="480" y="365" width="175" height="110" class="f-surface s-defense" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="retailer" x="480" y="365" width="175" height="26" class="f-defense-tint s-defense" stroke-width="1.2"/>
<path data-fk-node="retailer" d="M480 448 L655 448" class="s-defense" fill="none" stroke-width="1.2"/>
<rect data-fk-node="invoice" data-tip="SupplierInvoice: third-party evidence of a retailer's purchases" x="715" y="365" width="175" height="93" class="f-surface s-muted" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="invoice" x="715" y="365" width="175" height="26" class="f-neutral-tint s-muted" stroke-width="1.2"/>
<path data-fk-node="invoice" d="M715 448 L890 448" class="s-muted" fill="none" stroke-width="1.2"/>
<rect data-fk-node="basketline" data-tip="BasketLine: one item line inside a transaction" x="245" y="546" width="175" height="93" class="f-surface s-defense" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="basketline" x="245" y="546" width="175" height="26" class="f-defense-tint s-defense" stroke-width="1.2"/>
<path data-fk-node="basketline" d="M245 629 L420 629" class="s-defense" fill="none" stroke-width="1.2"/>
<rect data-fk-node="product" data-tip="Product: one barcode shared by sales, purchases, and reference prices" x="480" y="546" width="175" height="93" class="f-surface s-defense" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="product" x="480" y="546" width="175" height="26" class="f-defense-tint s-defense" stroke-width="1.2"/>
<path data-fk-node="product" d="M480 629 L655 629" class="s-defense" fill="none" stroke-width="1.2"/>
<rect data-fk-node="invoiceline" data-tip="InvoiceLine: one item line inside a supplier invoice" x="715" y="546" width="175" height="78" class="f-surface s-muted" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="invoiceline" x="715" y="546" width="175" height="26" class="f-neutral-tint s-muted" stroke-width="1.2"/>
<path data-fk-node="invoiceline" d="M715 614 L890 614" class="s-muted" fill="none" stroke-width="1.2"/>
<rect data-fk-node="refprice" data-tip="ReferencePrice: regional price for a product by month and store class" x="480" y="695" width="175" height="140" class="f-surface s-defense" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="refprice" x="480" y="695" width="175" height="26" class="f-defense-tint s-defense" stroke-width="1.2"/>
<path data-fk-node="refprice" d="M480 808 L655 808" class="s-defense" fill="none" stroke-width="1.2"/>
<path data-fk-edge="household recipient" data-fk-part="a1" d="M97.5 130 L92.5 138 L97.5 146 L102.5 138 Z" class="f-surface s-ink" stroke-width="1.2"/>
<path data-fk-edge="transaction basketline" data-fk-part="c1" d="M332.5 490 L327.5 498 L332.5 506 L337.5 498 Z" class="f-ink s-ink" stroke-width="1.2"/>
<path data-fk-edge="invoice invoiceline" data-fk-part="c2" d="M802.5 458 L797.5 466 L802.5 474 L807.5 466 Z" class="f-ink s-ink" stroke-width="1.2"/>
<rect data-group="legend" data-tip="Legend: color shows the source of each class" x="12" y="562" width="16" height="11" class="f-defense-tint s-defense" stroke-width="1"/>
<rect data-group="legend" x="12" y="582" width="16" height="11" class="f-neutral-tint s-muted" stroke-width="1"/>
<rect data-group="legend" x="12" y="602" width="16" height="11" class="f-caution-tint s-caution" stroke-width="1"/>
<text data-fk-node="household" x="97.5" y="37" class="f-ink" font-size="12" text-anchor="middle" font-weight="700">Household</text>
<text data-fk-node="household" x="18" y="63" class="f-ink mono" font-size="11">- caseId: String</text>
<text data-fk-node="household" x="18" y="78" class="f-ink mono" font-size="11">- state: StateCode</text>
<text data-fk-node="household" x="18" y="93" class="f-ink mono" font-size="11">- benefit: Money</text>
<text data-fk-node="household" x="18" y="120" class="f-ink mono" font-size="11">+ recertify(): Result</text>
<text data-fk-node="case" x="567.5" y="37" class="f-ink" font-size="12" text-anchor="middle" font-weight="700">Case</text>
<text data-fk-node="case" x="488" y="63" class="f-ink mono" font-size="11">- caseId: String</text>
<text data-fk-node="case" x="488" y="78" class="f-ink mono" font-size="11">- opened: Date</text>
<text data-fk-node="case" x="488" y="93" class="f-ink mono" font-size="11">- outcome: Outcome</text>
<text data-fk-node="case" x="488" y="120" class="f-ink mono" font-size="11">+ close(o: Outcome)</text>
<text data-fk-node="action" x="802.5" y="37" class="f-ink" font-size="12" text-anchor="middle" font-weight="700">Action</text>
<text data-fk-node="action" x="723" y="63" class="f-ink mono" font-size="11">- kind: ActionKind</text>
<text data-fk-node="action" x="723" y="78" class="f-ink mono" font-size="11">- reversible: Boolean</text>
<text data-fk-node="action" x="723" y="93" class="f-ink mono" font-size="11">- expires: Date</text>
<text data-fk-node="action" x="723" y="120" class="f-ink mono" font-size="11">+ appeal(): Result</text>
<text data-fk-node="recipient" x="97.5" y="203" class="f-ink" font-size="12" text-anchor="middle" font-weight="700">Recipient</text>
<text data-fk-node="recipient" x="18" y="229" class="f-ink mono" font-size="11">- token: PersonToken</text>
<text data-fk-node="recipient" x="18" y="244" class="f-ink mono" font-size="11">- hasPhone: Boolean</text>
<text data-fk-node="score" x="567.5" y="203" class="f-ink" font-size="12" text-anchor="middle" font-weight="700">StoreMonthScore</text>
<text data-fk-node="score" x="488" y="229" class="f-ink mono" font-size="11">- month: YearMonth</text>
<text data-fk-node="score" x="488" y="244" class="f-ink mono" font-size="11">- excessShare: Decimal</text>
<text data-fk-node="score" x="488" y="259" class="f-ink mono" font-size="11">- supplyRatio: Decimal</text>
<text data-fk-node="score" x="488" y="274" class="f-ink mono" font-size="11">- pFraud: Decimal</text>
<text data-fk-node="score" x="488" y="289" class="f-ink mono" font-size="11">- /expectedLoss: Money</text>
<text data-fk-node="card" x="97.5" y="382" class="f-ink" font-size="12" text-anchor="middle" font-weight="700">Card</text>
<text data-fk-node="card" x="18" y="408" class="f-ink mono" font-size="11">- cardToken: Token</text>
<text data-fk-node="card" x="18" y="423" class="f-ink mono" font-size="11">- chip: Boolean</text>
<text data-fk-node="card" x="18" y="438" class="f-ink mono" font-size="11">- locked: Boolean</text>
<text data-fk-node="card" x="18" y="465" class="f-ink mono" font-size="11">+ lock(): void</text>
<text data-fk-node="transaction" x="332.5" y="382" class="f-ink" font-size="12" text-anchor="middle" font-weight="700">Transaction</text>
<text data-fk-node="transaction" x="253" y="408" class="f-ink mono" font-size="11">- txnId: String</text>
<text data-fk-node="transaction" x="253" y="423" class="f-ink mono" font-size="11">- time: DateTime</text>
<text data-fk-node="transaction" x="253" y="438" class="f-ink mono" font-size="11">- total: Money</text>
<text data-fk-node="transaction" x="253" y="453" class="f-ink mono" font-size="11">- status: AuthStatus</text>
<text data-fk-node="transaction" x="253" y="480" class="f-ink mono" font-size="11">+ validate(): Result</text>
<text data-fk-node="retailer" x="567.5" y="382" class="f-ink" font-size="12" text-anchor="middle" font-weight="700">Retailer</text>
<text data-fk-node="retailer" x="488" y="408" class="f-ink mono" font-size="11">- storeId: String</text>
<text data-fk-node="retailer" x="488" y="423" class="f-ink mono" font-size="11">- storeClass: StoreClass</text>
<text data-fk-node="retailer" x="488" y="438" class="f-ink mono" font-size="11">- tier: RiskTier</text>
<text data-fk-node="retailer" x="488" y="465" class="f-ink mono" font-size="11">+ setTier(t: RiskTier)</text>
<text data-fk-node="invoice" x="802.5" y="382" class="f-ink" font-size="12" text-anchor="middle" font-weight="700">SupplierInvoice</text>
<text data-fk-node="invoice" x="723" y="408" class="f-ink mono" font-size="11">- invoiceId: String</text>
<text data-fk-node="invoice" x="723" y="423" class="f-ink mono" font-size="11">- supplier: String</text>
<text data-fk-node="invoice" x="723" y="438" class="f-ink mono" font-size="11">- date: Date</text>
<text data-fk-node="basketline" x="332.5" y="563" class="f-ink" font-size="12" text-anchor="middle" font-weight="700">BasketLine</text>
<text data-fk-node="basketline" x="253" y="589" class="f-ink mono" font-size="11">- qty: Integer</text>
<text data-fk-node="basketline" x="253" y="604" class="f-ink mono" font-size="11">- unitPrice: Money</text>
<text data-fk-node="basketline" x="253" y="619" class="f-ink mono" font-size="11">- eligible: Boolean</text>
<text data-fk-node="product" x="567.5" y="563" class="f-ink" font-size="12" text-anchor="middle" font-weight="700">Product</text>
<text data-fk-node="product" x="488" y="589" class="f-ink mono" font-size="11">- upc: UPC</text>
<text data-fk-node="product" x="488" y="604" class="f-ink mono" font-size="11">- category: Category</text>
<text data-fk-node="product" x="488" y="619" class="f-ink mono" font-size="11">- snapEligible: Boolean</text>
<text data-fk-node="invoiceline" x="802.5" y="563" class="f-ink" font-size="12" text-anchor="middle" font-weight="700">InvoiceLine</text>
<text data-fk-node="invoiceline" x="723" y="589" class="f-ink mono" font-size="11">- qty: Integer</text>
<text data-fk-node="invoiceline" x="723" y="604" class="f-ink mono" font-size="11">- unitCost: Money</text>
<text data-fk-node="refprice" x="567.5" y="712" class="f-ink" font-size="12" text-anchor="middle" font-weight="700">ReferencePrice</text>
<text data-fk-node="refprice" x="488" y="738" class="f-ink mono" font-size="11">- region: Region</text>
<text data-fk-node="refprice" x="488" y="753" class="f-ink mono" font-size="11">- month: YearMonth</text>
<text data-fk-node="refprice" x="488" y="768" class="f-ink mono" font-size="11">- storeClass: StoreClass</text>
<text data-fk-node="refprice" x="488" y="783" class="f-ink mono" font-size="11">- median: Money</text>
<text data-fk-node="refprice" x="488" y="798" class="f-ink mono" font-size="11">- iqr: Money</text>
<text data-fk-node="refprice" x="488" y="825" class="f-ink mono" font-size="11">+ upperBound(): Money</text>
<text data-fk-edge="household recipient" data-fk-part="a1" x="104.5" y="148" class="f-ink mono" font-size="11">1</text>
<text data-fk-edge="household recipient" data-fk-part="a1" x="104.5" y="181" class="f-ink mono" font-size="11">1..*</text>
<text data-fk-edge="household recipient" data-fk-part="a1" data-tip="Household 1 to 1..* Recipient (aggregation, members)" x="90.5" y="181" class="f-muted" font-size="11" text-anchor="end" font-style="italic">members</text>
<text data-fk-edge="recipient card" data-fk-part="a2" x="104.5" y="278" class="f-ink mono" font-size="11">1</text>
<text data-fk-edge="recipient card" data-fk-part="a2" x="104.5" y="360" class="f-ink mono" font-size="11">0..*</text>
<text data-fk-edge="recipient card" data-fk-part="a2" data-tip="Recipient 1 to 0..* Card (cards)" x="90.5" y="360" class="f-muted" font-size="11" text-anchor="end" font-style="italic">cards</text>
<text data-fk-edge="transaction basketline" data-fk-part="c1" x="339.5" y="508" class="f-ink mono" font-size="11">1</text>
<text data-fk-edge="transaction basketline" data-fk-part="c1" x="339.5" y="541" class="f-ink mono" font-size="11">1..*</text>
<text data-fk-edge="transaction basketline" data-fk-part="c1" data-tip="Transaction 1 to 1..* BasketLine (composition, lines)" x="325.5" y="541" class="f-muted" font-size="11" text-anchor="end" font-style="italic">lines</text>
<text data-fk-edge="invoice invoiceline" data-fk-part="c2" x="809.5" y="476" class="f-ink mono" font-size="11">1</text>
<text data-fk-edge="invoice invoiceline" data-fk-part="c2" x="809.5" y="541" class="f-ink mono" font-size="11">1..*</text>
<text data-fk-edge="invoice invoiceline" data-fk-part="c2" data-tip="SupplierInvoice 1 to 1..* InvoiceLine (composition, lines)" x="795.5" y="541" class="f-muted" font-size="11" text-anchor="end" font-style="italic">lines</text>
<text data-fk-edge="product refprice" data-fk-part="a3" x="574.5" y="653" class="f-ink mono" font-size="11">1</text>
<text data-fk-edge="product refprice" data-fk-part="a3" x="574.5" y="690" class="f-ink mono" font-size="11">0..*</text>
<text data-fk-edge="product refprice" data-fk-part="a3" data-tip="Product 1 to 0..* ReferencePrice (prices)" x="560.5" y="690" class="f-muted" font-size="11" text-anchor="end" font-style="italic">prices</text>
<text data-fk-edge="retailer score" data-fk-part="a4" x="574.5" y="323" class="f-ink mono" font-size="11">0..*</text>
<text data-fk-edge="retailer score" data-fk-part="a4" x="574.5" y="360" class="f-ink mono" font-size="11">1</text>
<text data-fk-edge="retailer score" data-fk-part="a4" data-tip="Retailer 1 to 0..* StoreMonthScore (scores)" x="560.5" y="323" class="f-muted" font-size="11" text-anchor="end" font-style="italic">scores</text>
<text data-fk-edge="case score" data-fk-part="a5" x="574.5" y="144" class="f-ink mono" font-size="11">0..1</text>
<text data-fk-edge="case score" data-fk-part="a5" x="574.5" y="181" class="f-ink mono" font-size="11">1</text>
<text data-fk-edge="case score" data-fk-part="a5" data-tip="Case 0..1 to 1 StoreMonthScore (case)" x="560.5" y="144" class="f-muted" font-size="11" text-anchor="end" font-style="italic">case</text>
<text data-fk-edge="card transaction" data-fk-part="a6" x="190" y="405" class="f-ink mono" font-size="11">1</text>
<text data-fk-edge="card transaction" data-fk-part="a6" x="240" y="405" class="f-ink mono" font-size="11" text-anchor="end">0..*</text>
<text data-fk-edge="transaction retailer" data-fk-part="a7" x="425" y="405" class="f-ink mono" font-size="11">0..*</text>
<text data-fk-edge="transaction retailer" data-fk-part="a7" x="475" y="405" class="f-ink mono" font-size="11" text-anchor="end">1</text>
<text data-fk-edge="transaction retailer" data-fk-part="a7" data-tip="Transaction 0..* to 1 Retailer (store)" x="477" y="424" class="f-muted" font-size="11" text-anchor="end" font-style="italic">store</text>
<text data-fk-edge="retailer invoice" data-fk-part="a8" x="660" y="405" class="f-ink mono" font-size="11">1</text>
<text data-fk-edge="retailer invoice" data-fk-part="a8" x="710" y="405" class="f-ink mono" font-size="11" text-anchor="end">0..*</text>
<text data-fk-edge="retailer invoice" data-fk-part="a8" data-tip="Retailer 1 to 0..* SupplierInvoice (invoices)" x="712" y="424" class="f-muted" font-size="11" text-anchor="end" font-style="italic">invoices</text>
<text data-fk-edge="basketline product" data-fk-part="a9" x="425" y="586" class="f-ink mono" font-size="11">0..*</text>
<text data-fk-edge="basketline product" data-fk-part="a9" x="475" y="586" class="f-ink mono" font-size="11" text-anchor="end">1</text>
<text data-fk-edge="basketline product" data-fk-part="a9" data-tip="BasketLine 0..* to 1 Product (item)" x="477" y="605" class="f-muted" font-size="11" text-anchor="end" font-style="italic">item</text>
<text data-fk-edge="product invoiceline" data-fk-part="a10" x="660" y="586" class="f-ink mono" font-size="11">1</text>
<text data-fk-edge="product invoiceline" data-fk-part="a10" x="710" y="586" class="f-ink mono" font-size="11" text-anchor="end">0..*</text>
<text data-fk-edge="product invoiceline" data-fk-part="a10" data-tip="Product 1 to 0..* InvoiceLine (item)" x="660" y="605" class="f-muted" font-size="11" font-style="italic">item</text>
<text data-fk-edge="case action" data-fk-part="a11" x="660" y="59" class="f-ink mono" font-size="11">0..1</text>
<text data-fk-edge="case action" data-fk-part="a11" x="710" y="79" class="f-ink mono" font-size="11" text-anchor="end">0..*</text>
<text data-fk-edge="score action" data-fk-part="a12" x="660" y="244" class="f-ink mono" font-size="11">0..1</text>
<text data-fk-edge="score action" data-fk-part="a12" x="809.5" y="144" class="f-ink mono" font-size="11">0..*</text>
<text data-fk-edge="score action" data-fk-part="a12" data-tip="StoreMonthScore 0..1 to 0..* Action (autoActions)" x="795.5" y="144" class="f-muted" font-size="11" text-anchor="end" font-style="italic">autoActions</text>
<text data-group="legend" x="36" y="572" class="f-muted" font-size="11">program record</text>
<text data-group="legend" x="36" y="592" class="f-muted" font-size="11">third-party evidence</text>
<text data-group="legend" x="36" y="612" class="f-muted" font-size="11">decided by a person</text>
</svg>
  </div>
  <figcaption><strong>Read it as:</strong> a filled diamond marks a part that lives and dies with its whole, so a basket line exists only inside its transaction. Numbers at each end are multiplicities. Product is the hub: basket lines, invoice lines, and reference prices all point at one barcode, and that shared key lets sales be reconciled against purchases.</figcaption>
</figure>

<p>The second sequence diagram shows the batch side. A scheduler starts the run once a month. For each store, the scoring engine gathers three kinds of evidence: excess prices against the regional reference, sales against supplier purchases, and the store's place in the card-store graph. It scores the store-month with shrinkage, so a small store with a few odd lines does not jump the queue.</p>
<p>The ranker sorts stores by expected loss. The investigator budget fixes N before the run starts, so transaction volume never changes how many cases people see.</p>
<figure class="fig" id="uml-sw-scoring-sequence">
  <div class="fig-head"><span class="eyebrow">Figure A8</span><h4>The monthly scoring run</h4><p class="dek">Each month the engine scores every store, the ranker sorts them by expected loss, and the budget decides who gets a person.</p></div>
  <div class="fig-body">
<svg data-fk="graph" viewBox="0 0 900 998" role="img" aria-label="UML sequence diagram of the monthly scoring run. A scheduler starts the scoring engine. For each store, the engine gets excess prices from the reference price service, the supply ratio from inventory reconciliation, and a ring signal from graph analysis, then scores the store-month. The ranker sorts stores by expected loss. Per store, stores below review cost get no action, stores above cost but outside the budget get a payout delay or volume cap, and the top N within budget get a case. Later, case management sends confirmed and cleared outcomes back to the scoring engine as labels for retraining.">
<defs><marker id="uml-sw-scoring-sequence-tri-ink" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0 1 L10 5 L0 9 Z" class="f-ink"/></marker><marker id="uml-sw-scoring-sequence-open-ink" viewBox="0 0 10 10" refX="9.5" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0.5 0.8 L9.5 5 L0.5 9.2" fill="none" class="s-ink" stroke-width="1.3"/></marker><marker id="uml-sw-scoring-sequence-tri-muted" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0 1 L10 5 L0 9 Z" class="f-muted"/></marker><marker id="uml-sw-scoring-sequence-open-muted" viewBox="0 0 10 10" refX="9.5" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0.5 0.8 L9.5 5 L0.5 9.2" fill="none" class="s-muted" stroke-width="1.3"/></marker><marker id="uml-sw-scoring-sequence-tri-fraud" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0 1 L10 5 L0 9 Z" class="f-fraud"/></marker><marker id="uml-sw-scoring-sequence-open-fraud" viewBox="0 0 10 10" refX="9.5" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0.5 0.8 L9.5 5 L0.5 9.2" fill="none" class="s-fraud" stroke-width="1.3"/></marker><marker id="uml-sw-scoring-sequence-tri-caution" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0 1 L10 5 L0 9 Z" class="f-caution"/></marker><marker id="uml-sw-scoring-sequence-open-caution" viewBox="0 0 10 10" refX="9.5" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0.5 0.8 L9.5 5 L0.5 9.2" fill="none" class="s-caution" stroke-width="1.3"/></marker><marker id="uml-sw-scoring-sequence-tri-defense" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0 1 L10 5 L0 9 Z" class="f-defense"/></marker><marker id="uml-sw-scoring-sequence-open-defense" viewBox="0 0 10 10" refX="9.5" refY="5" markerWidth="10" markerHeight="10" markerUnits="userSpaceOnUse" orient="auto"><path d="M0.5 0.8 L9.5 5 L0.5 9.2" fill="none" class="s-defense" stroke-width="1.3"/></marker></defs>
<rect data-fk-area data-group="loop-store" data-tip="loop fragment. Guard: for each store s." x="125" y="104" width="435" height="264" class="s-muted" fill="none" stroke-width="1.2"/>
<path data-group="loop-store" d="M125 104 H164.5 V115 L157.5 122 H125 Z" class="f-neutral-tint s-muted" stroke-width="1.2"/>
<rect data-fk-area data-group="alt-action" data-tip="alt fragment. Guards: benefit &lt; review cost (no action); above review cost, outside budget N (payout delay or volume cap); top N, within budget (open a case)." x="574" y="507" width="312" height="258" class="s-muted" fill="none" stroke-width="1.2"/>
<path data-group="alt-action" d="M574 507 H607.1 V518 L600.1 525 H574 Z" class="f-neutral-tint s-muted" stroke-width="1.2"/>
<path data-group="alt-action" data-tip="Branch: above review cost, outside budget N." d="M574 559 L886 559" class="s-muted" fill="none" stroke-width="1.1" stroke-dasharray="6 4"/>
<path data-group="alt-action" data-tip="Branch: top N, within budget." d="M574 662 L886 662" class="s-muted" fill="none" stroke-width="1.1" stroke-dasharray="6 4"/>
<rect data-fk-area data-group="loop-rank" data-tip="loop fragment. Guard: for each store s, in rank order." x="566" y="477" width="328" height="302" class="s-muted" fill="none" stroke-width="1.2"/>
<path data-group="loop-rank" d="M566 477 H605.5 V488 L598.5 495 H566 Z" class="f-neutral-tint s-muted" stroke-width="1.2"/>
<path data-fk-node="sched" d="M60 58 L60 990" class="s-muted" fill="none" stroke-width="1" stroke-dasharray="4 4"/>
<path data-fk-node="engine" d="M171 58 L171 990" class="s-muted" fill="none" stroke-width="1" stroke-dasharray="4 4"/>
<path data-fk-node="refprice" d="M283 58 L283 990" class="s-muted" fill="none" stroke-width="1" stroke-dasharray="4 4"/>
<path data-fk-node="inventory" d="M394 58 L394 990" class="s-muted" fill="none" stroke-width="1" stroke-dasharray="4 4"/>
<path data-fk-node="graph" d="M506 58 L506 990" class="s-muted" fill="none" stroke-width="1" stroke-dasharray="4 4"/>
<path data-fk-node="ranker" d="M617 58 L617 990" class="s-muted" fill="none" stroke-width="1" stroke-dasharray="4 4"/>
<path data-fk-node="payout" d="M729 58 L729 990" class="s-muted" fill="none" stroke-width="1" stroke-dasharray="4 4"/>
<path data-fk-node="cases" d="M840 58 L840 990" class="s-muted" fill="none" stroke-width="1" stroke-dasharray="4 4"/>
<rect data-fk-node="refprice" x="278" y="148" width="10" height="30" class="f-defense-tint s-defense" stroke-width="1.1"/>
<rect data-fk-node="inventory" x="389" y="208" width="10" height="30" class="f-defense-tint s-defense" stroke-width="1.1"/>
<rect data-fk-node="graph" x="501" y="268" width="10" height="30" class="f-defense-tint s-defense" stroke-width="1.1"/>
<rect data-fk-node="engine" x="171" y="332" width="10" height="16" class="f-defense-tint s-defense" stroke-width="1.1"/>
<rect data-fk-node="ranker" x="617" y="445" width="10" height="16" class="f-defense-tint s-defense" stroke-width="1.1"/>
<rect data-fk-node="payout" x="724" y="618" width="10" height="30" class="f-defense-tint s-defense" stroke-width="1.1"/>
<rect data-fk-node="cases" x="835" y="721" width="10" height="30" class="f-caution-tint s-caution" stroke-width="1.1"/>
<rect data-fk-node="ranker" x="612" y="411" width="10" height="398" class="f-defense-tint s-defense" stroke-width="1.1"/>
<rect data-fk-node="engine" x="166" y="88" width="10" height="751" class="f-defense-tint s-defense" stroke-width="1.1"/>
<rect data-fk-node="sched" x="55" y="80" width="10" height="765" class="f-defense-tint s-defense" stroke-width="1.1"/>
<rect data-fk-node="cases" x="835" y="912" width="10" height="16" class="f-caution-tint s-caution" stroke-width="1.1"/>
<rect data-fk-node="engine" x="171" y="956" width="10" height="16" class="f-defense-tint s-defense" stroke-width="1.1"/>
<rect data-fk-node="engine" x="166" y="922" width="10" height="56" class="f-defense-tint s-defense" stroke-width="1.1"/>
<path data-fk-edge="sched engine" data-fk-part="m1" data-tip="1: runMonth(m)" data-animate="sequence" data-step="0" d="M65 88 L166 88" class="s-ink" fill="none" stroke-width="1.3" marker-end="url(#uml-sw-scoring-sequence-tri-ink)"/>
<path data-fk-edge="engine refprice" data-fk-part="m2" data-tip="2: excess(s, m) Loop: for each store s." data-animate="sequence" data-step="1" d="M176 148 L278 148" class="s-ink" fill="none" stroke-width="1.3" marker-end="url(#uml-sw-scoring-sequence-tri-ink)"/>
<path data-fk-edge="refprice engine" data-fk-part="m3" data-tip="3: excessShare Loop: for each store s." data-animate="sequence" data-step="2" d="M278 178 L176 178" class="s-ink" fill="none" stroke-width="1.3" stroke-dasharray="6 4" marker-end="url(#uml-sw-scoring-sequence-open-ink)"/>
<path data-fk-edge="engine inventory" data-fk-part="m4" data-tip="4: supplyRatio(s, m) Loop: for each store s." data-animate="sequence" data-step="3" d="M176 208 L389 208" class="s-ink" fill="none" stroke-width="1.3" marker-end="url(#uml-sw-scoring-sequence-tri-ink)"/>
<path data-fk-edge="inventory engine" data-fk-part="m5" data-tip="5: ratio Loop: for each store s." data-animate="sequence" data-step="4" d="M389 238 L176 238" class="s-ink" fill="none" stroke-width="1.3" stroke-dasharray="6 4" marker-end="url(#uml-sw-scoring-sequence-open-ink)"/>
<path data-fk-edge="engine graph" data-fk-part="m6" data-tip="6: ringSignal(s) Loop: for each store s." data-animate="sequence" data-step="5" d="M176 268 L501 268" class="s-ink" fill="none" stroke-width="1.3" marker-end="url(#uml-sw-scoring-sequence-tri-ink)"/>
<path data-fk-edge="graph engine" data-fk-part="m7" data-tip="7: ring score Loop: for each store s." data-animate="sequence" data-step="6" d="M501 298 L176 298" class="s-ink" fill="none" stroke-width="1.3" stroke-dasharray="6 4" marker-end="url(#uml-sw-scoring-sequence-open-ink)"/>
<path data-fk-edge="engine engine" data-fk-part="m8" data-tip="8: score(s), shrinkage, then model Loop: for each store s." data-animate="sequence" data-step="7" d="M176 322 L203 322 L203 338 L181 338" class="s-ink" fill="none" stroke-width="1.3" marker-end="url(#uml-sw-scoring-sequence-tri-ink)"/>
<path data-fk-edge="engine ranker" data-fk-part="m9" data-tip="9: rank(scores, N), N: reviews the budget funds" data-animate="sequence" data-step="8" d="M176 411 L612 411" class="s-ink" fill="none" stroke-width="1.3" marker-end="url(#uml-sw-scoring-sequence-tri-ink)"/>
<path data-fk-edge="ranker ranker" data-fk-part="m10" data-tip="10: sortByExpectedLoss()" data-animate="sequence" data-step="9" d="M622 435 L649 435 L649 451 L627 451" class="s-ink" fill="none" stroke-width="1.3" marker-end="url(#uml-sw-scoring-sequence-tri-ink)"/>
<path data-fk-edge="ranker payout" data-fk-part="m11" data-tip="11: setTier(s), delay, cap Loop: for each store s, in rank order. Branch: above review cost, outside budget N." data-animate="sequence" data-step="10" d="M622 618 L724 618" class="s-ink" fill="none" stroke-width="1.3" marker-end="url(#uml-sw-scoring-sequence-tri-ink)"/>
<path data-fk-edge="payout ranker" data-fk-part="m12" data-tip="12: tier set Loop: for each store s, in rank order. Branch: above review cost, outside budget N." data-animate="sequence" data-step="11" d="M724 648 L622 648" class="s-ink" fill="none" stroke-width="1.3" stroke-dasharray="6 4" marker-end="url(#uml-sw-scoring-sequence-open-ink)"/>
<path data-fk-edge="ranker cases" data-fk-part="m13" data-tip="13: openCase(s, evidence), an investigator decides Loop: for each store s, in rank order. Branch: top N, within budget." data-animate="sequence" data-step="12" d="M622 721 L835 721" class="s-caution" fill="none" stroke-width="1.3" marker-end="url(#uml-sw-scoring-sequence-tri-caution)"/>
<path data-fk-edge="cases ranker" data-fk-part="m14" data-tip="14: caseId Loop: for each store s, in rank order. Branch: top N, within budget." data-animate="sequence" data-step="13" d="M835 751 L622 751" class="s-caution" fill="none" stroke-width="1.3" stroke-dasharray="6 4" marker-end="url(#uml-sw-scoring-sequence-open-caution)"/>
<path data-fk-edge="ranker engine" data-fk-part="m15" data-tip="15: ranked list" data-animate="sequence" data-step="14" d="M612 809 L176 809" class="s-ink" fill="none" stroke-width="1.3" stroke-dasharray="6 4" marker-end="url(#uml-sw-scoring-sequence-open-ink)"/>
<path data-fk-edge="engine sched" data-fk-part="m16" data-tip="16: done" data-animate="sequence" data-step="15" d="M166 839 L65 839" class="s-ink" fill="none" stroke-width="1.3" stroke-dasharray="6 4" marker-end="url(#uml-sw-scoring-sequence-open-ink)"/>
<path data-fk-edge="cases engine" data-fk-part="m17" data-tip="17: outcomes(confirmed, cleared), labels for the next run" data-animate="sequence" data-step="16" d="M835 922 L176 922" class="s-defense" fill="none" stroke-width="1.3" marker-end="url(#uml-sw-scoring-sequence-open-defense)"/>
<path data-fk-edge="engine engine" data-fk-part="m18" data-tip="18: retrain(labels)" data-animate="sequence" data-step="17" d="M176 946 L203 946 L203 962 L181 962" class="s-defense" fill="none" stroke-width="1.3" marker-end="url(#uml-sw-scoring-sequence-tri-defense)"/>
<rect data-fk-node="sched" data-tip="The scheduler that starts the monthly scoring run." x="8" y="14" width="104" height="44" class="f-defense-tint s-defense" rx="3" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="engine" data-tip="The scoring engine that scores each store-month and retrains on case outcomes." x="119" y="14" width="104" height="44" class="f-defense-tint s-defense" rx="3" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="refprice" data-tip="The reference price service that returns the excess price share." x="231" y="14" width="104" height="44" class="f-defense-tint s-defense" rx="3" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="inventory" data-tip="Inventory reconciliation, which returns the supply ratio." x="342" y="14" width="104" height="44" class="f-defense-tint s-defense" rx="3" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="graph" data-tip="Graph analysis, which returns a ring signal." x="454" y="14" width="104" height="44" class="f-defense-tint s-defense" rx="3" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="ranker" data-tip="The ranker that sorts stores by expected loss and applies the budget." x="565" y="14" width="104" height="44" class="f-defense-tint s-defense" rx="3" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="payout" data-tip="The payout risk service that sets a payout delay or volume cap." x="677" y="14" width="104" height="44" class="f-defense-tint s-defense" rx="3" stroke-width="1.2" data-box="1"/>
<rect data-fk-node="cases" data-tip="Case management, where an investigator decides each case and sends outcomes back." x="788" y="14" width="104" height="44" class="f-defense-tint s-defense" rx="3" stroke-width="1.2" data-box="1"/>
<rect data-fk-edge="sched engine" data-fk-part="m1" data-animate="sequence" data-step="0" x="66.3" y="72" width="98.4" height="15" class="f-surface"/>
<rect data-fk-edge="engine refprice" data-fk-part="m2" data-animate="sequence" data-step="1" x="174.5" y="132" width="105" height="15" class="f-surface"/>
<rect data-fk-edge="refprice engine" data-fk-part="m3" data-animate="sequence" data-step="2" x="177.8" y="162" width="98.4" height="15" class="f-surface"/>
<rect data-fk-edge="engine inventory" data-fk-part="m4" data-animate="sequence" data-step="3" x="213.5" y="192" width="138" height="15" class="f-surface"/>
<rect data-fk-edge="inventory engine" data-fk-part="m5" data-animate="sequence" data-step="4" x="253.1" y="222" width="58.8" height="15" class="f-surface"/>
<rect data-fk-edge="engine graph" data-fk-part="m6" data-animate="sequence" data-step="5" x="282.7" y="252" width="111.6" height="15" class="f-surface"/>
<rect data-fk-edge="graph engine" data-fk-part="m7" data-animate="sequence" data-step="6" x="292.6" y="282" width="91.8" height="15" class="f-surface"/>
<rect data-fk-edge="engine engine" data-fk-part="m8" data-animate="sequence" data-step="7" x="208" y="316" width="78.6" height="15" class="f-surface"/>
<rect data-fk-edge="engine engine" data-fk-part="m8" data-animate="sequence" data-step="7" x="208" y="330" width="133.1" height="15" class="f-surface"/>
<rect data-group="loop-store" x="169.5" y="107" width="114.9" height="15" class="f-surface"/>
<rect data-fk-edge="engine ranker" data-fk-part="m9" data-animate="sequence" data-step="8" x="331.6" y="382" width="124.8" height="15" class="f-surface"/>
<rect data-fk-edge="engine ranker" data-fk-part="m9" data-animate="sequence" data-step="8" x="309.3" y="395" width="169.4" height="15" class="f-surface"/>
<rect data-fk-edge="ranker ranker" data-fk-part="m10" data-animate="sequence" data-step="9" x="654" y="429" width="164.4" height="15" class="f-surface"/>
<rect data-group="alt-action" x="587" y="535" width="217.8" height="15" class="f-surface"/>
<rect data-fk-edge="ranker payout" data-fk-part="m11" data-animate="sequence" data-step="10" x="623.8" y="589" width="98.4" height="15" class="f-surface"/>
<rect data-fk-edge="ranker payout" data-fk-part="m11" data-animate="sequence" data-step="10" x="639.8" y="602" width="66.5" height="15" class="f-surface"/>
<rect data-fk-edge="payout ranker" data-fk-part="m12" data-animate="sequence" data-step="11" x="630.4" y="632" width="85.2" height="15" class="f-surface"/>
<rect data-fk-edge="ranker cases" data-fk-part="m13" data-animate="sequence" data-step="12" x="643" y="692" width="171" height="15" class="f-surface"/>
<rect data-fk-edge="ranker cases" data-fk-part="m13" data-animate="sequence" data-step="12" x="655.9" y="705" width="145.2" height="15" class="f-surface"/>
<rect data-fk-edge="cases ranker" data-fk-part="m14" data-animate="sequence" data-step="13" x="692.5" y="735" width="72" height="15" class="f-surface"/>
<rect data-group="alt-action" x="612.1" y="510" width="145.2" height="15" class="f-surface"/>
<rect data-group="alt-action" x="579" y="563" width="229.9" height="15" class="f-surface"/>
<rect data-group="alt-action" x="579" y="666" width="139.1" height="15" class="f-surface"/>
<rect data-group="loop-rank" x="610.5" y="480" width="205.7" height="15" class="f-surface"/>
<rect data-fk-edge="ranker engine" data-fk-part="m15" data-animate="sequence" data-step="14" x="341.5" y="793" width="105" height="15" class="f-surface"/>
<rect data-fk-edge="engine sched" data-fk-part="m16" data-animate="sequence" data-step="15" x="86.1" y="823" width="58.8" height="15" class="f-surface"/>
<rect data-group="later" data-tip="Later: investigators close cases." x="350.2" y="859" width="199.6" height="15" class="f-surface"/>
<rect data-fk-edge="cases engine" data-fk-part="m17" data-animate="sequence" data-step="16" x="396.9" y="893" width="217.2" height="15" class="f-surface"/>
<rect data-fk-edge="cases engine" data-fk-part="m17" data-animate="sequence" data-step="16" x="432.9" y="906" width="145.2" height="15" class="f-surface"/>
<rect data-fk-edge="engine engine" data-fk-part="m18" data-animate="sequence" data-step="17" x="208" y="940" width="131.4" height="15" class="f-surface"/>
<text data-fk-node="sched" x="60" y="40" class="f-ink" font-size="11" text-anchor="middle" font-weight="700">Scheduler</text>
<text data-fk-node="engine" x="171" y="40" class="f-ink" font-size="11" text-anchor="middle" font-weight="700">Scoring engine</text>
<text data-fk-node="refprice" x="283" y="33" class="f-ink" font-size="11" text-anchor="middle" font-weight="700">Reference price</text>
<text data-fk-node="refprice" x="283" y="47" class="f-ink" font-size="11" text-anchor="middle" font-weight="700">service</text>
<text data-fk-node="inventory" x="394" y="33" class="f-ink" font-size="11" text-anchor="middle" font-weight="700">Inventory</text>
<text data-fk-node="inventory" x="394" y="47" class="f-ink" font-size="11" text-anchor="middle" font-weight="700">reconciliation</text>
<text data-fk-node="graph" x="506" y="40" class="f-ink" font-size="11" text-anchor="middle" font-weight="700">Graph analysis</text>
<text data-fk-node="ranker" x="617" y="40" class="f-ink" font-size="11" text-anchor="middle" font-weight="700">Ranker</text>
<text data-fk-node="payout" x="729" y="33" class="f-ink" font-size="11" text-anchor="middle" font-weight="700">Payout risk</text>
<text data-fk-node="payout" x="729" y="47" class="f-ink" font-size="11" text-anchor="middle" font-weight="700">service</text>
<text data-fk-node="cases" x="840" y="33" class="f-ink" font-size="11" text-anchor="middle" font-weight="700">Case</text>
<text data-fk-node="cases" x="840" y="47" class="f-ink" font-size="11" text-anchor="middle" font-weight="700">management</text>
<text data-fk-edge="sched engine" data-fk-part="m1" data-animate="sequence" data-step="0" x="115.5" y="82" class="f-ink mono" font-size="11" text-anchor="middle">1: runMonth(m)</text>
<text data-fk-edge="engine refprice" data-fk-part="m2" data-animate="sequence" data-step="1" x="227" y="142" class="f-ink mono" font-size="11" text-anchor="middle">2: excess(s, m)</text>
<text data-fk-edge="refprice engine" data-fk-part="m3" data-animate="sequence" data-step="2" x="227" y="172" class="f-ink mono" font-size="11" text-anchor="middle">3: excessShare</text>
<text data-fk-edge="engine inventory" data-fk-part="m4" data-animate="sequence" data-step="3" x="282.5" y="202" class="f-ink mono" font-size="11" text-anchor="middle">4: supplyRatio(s, m)</text>
<text data-fk-edge="inventory engine" data-fk-part="m5" data-animate="sequence" data-step="4" x="282.5" y="232" class="f-ink mono" font-size="11" text-anchor="middle">5: ratio</text>
<text data-fk-edge="engine graph" data-fk-part="m6" data-animate="sequence" data-step="5" x="338.5" y="262" class="f-ink mono" font-size="11" text-anchor="middle">6: ringSignal(s)</text>
<text data-fk-edge="graph engine" data-fk-part="m7" data-animate="sequence" data-step="6" x="338.5" y="292" class="f-ink mono" font-size="11" text-anchor="middle">7: ring score</text>
<text data-fk-edge="engine engine" data-fk-part="m8" data-animate="sequence" data-step="7" x="211" y="326" class="f-ink mono" font-size="11">8: score(s)</text>
<text data-fk-edge="engine engine" data-fk-part="m8" data-animate="sequence" data-step="7" x="211" y="340" class="f-muted" font-size="11">shrinkage, then model</text>
<text data-group="loop-store" x="131" y="117" class="f-ink" font-size="11" font-weight="700">loop</text>
<text data-group="loop-store" x="172.5" y="117" class="f-ink" font-size="11">[for each store s]</text>
<text data-fk-edge="engine ranker" data-fk-part="m9" data-animate="sequence" data-step="8" x="394" y="392" class="f-ink mono" font-size="11" text-anchor="middle">9: rank(scores, N)</text>
<text data-fk-edge="engine ranker" data-fk-part="m9" data-animate="sequence" data-step="8" x="394" y="405" class="f-muted" font-size="11" text-anchor="middle">N: reviews the budget funds</text>
<text data-fk-edge="ranker ranker" data-fk-part="m10" data-animate="sequence" data-step="9" x="657" y="439" class="f-ink mono" font-size="11">10: sortByExpectedLoss()</text>
<text data-group="alt-action" x="590" y="545" class="f-muted" font-size="11" font-style="italic">no action: score updates next month</text>
<text data-fk-edge="ranker payout" data-fk-part="m11" data-animate="sequence" data-step="10" x="673" y="599" class="f-ink mono" font-size="11" text-anchor="middle">11: setTier(s)</text>
<text data-fk-edge="ranker payout" data-fk-part="m11" data-animate="sequence" data-step="10" x="673" y="612" class="f-muted" font-size="11" text-anchor="middle">delay, cap</text>
<text data-fk-edge="payout ranker" data-fk-part="m12" data-animate="sequence" data-step="11" x="673" y="642" class="f-ink mono" font-size="11" text-anchor="middle">12: tier set</text>
<text data-fk-edge="ranker cases" data-fk-part="m13" data-animate="sequence" data-step="12" x="728.5" y="702" class="f-caution mono" font-size="11" text-anchor="middle">13: openCase(s, evidence)</text>
<text data-fk-edge="ranker cases" data-fk-part="m13" data-animate="sequence" data-step="12" x="728.5" y="715" class="f-muted" font-size="11" text-anchor="middle">an investigator decides</text>
<text data-fk-edge="cases ranker" data-fk-part="m14" data-animate="sequence" data-step="13" x="728.5" y="745" class="f-caution mono" font-size="11" text-anchor="middle">14: caseId</text>
<text data-group="alt-action" x="580" y="520" class="f-ink" font-size="11" font-weight="700">alt</text>
<text data-group="alt-action" x="615.1" y="520" class="f-ink" font-size="11">[benefit &lt; review cost]</text>
<text data-group="alt-action" x="582" y="573" class="f-ink" font-size="11">[above review cost, outside budget N]</text>
<text data-group="alt-action" x="582" y="676" class="f-ink" font-size="11">[top N, within budget]</text>
<text data-group="loop-rank" x="572" y="490" class="f-ink" font-size="11" font-weight="700">loop</text>
<text data-group="loop-rank" x="613.5" y="490" class="f-ink" font-size="11">[for each store s, in rank order]</text>
<text data-fk-edge="ranker engine" data-fk-part="m15" data-animate="sequence" data-step="14" x="394" y="803" class="f-ink mono" font-size="11" text-anchor="middle">15: ranked list</text>
<text data-fk-edge="engine sched" data-fk-part="m16" data-animate="sequence" data-step="15" x="115.5" y="833" class="f-ink mono" font-size="11" text-anchor="middle">16: done</text>
<text data-group="later" x="450" y="869" class="f-muted" font-size="11" text-anchor="middle" font-style="italic">later: investigators close cases</text>
<text data-fk-edge="cases engine" data-fk-part="m17" data-animate="sequence" data-step="16" x="505.5" y="903" class="f-defense mono" font-size="11" text-anchor="middle">17: outcomes(confirmed, cleared)</text>
<text data-fk-edge="cases engine" data-fk-part="m17" data-animate="sequence" data-step="16" x="505.5" y="916" class="f-muted" font-size="11" text-anchor="middle">labels for the next run</text>
<text data-fk-edge="engine engine" data-fk-part="m18" data-animate="sequence" data-step="17" x="211" y="950" class="f-defense mono" font-size="11">18: retrain(labels)</text>
</svg>
  </div>
  <figcaption><strong>Read it as:</strong> the loop frames repeat once per store, and the alt frame picks one branch per store. Only the top N within budget reach case management. The rest get a reversible automatic action or nothing. Closed cases come back weeks later as labels, and the next run trains on them.</figcaption>
</figure>

<h2 id="references">References<a class="anchor" href="#references" aria-label="Link to this section"></a></h2>
<ol>
<li>Center on Budget and Policy Priorities. &quot;Congress Must Extend Protections for SNAP Households Who Are Victims of Benefit Theft.&quot; Blog post, 2024. <a href="https://www.cbpp.org/blog/congress-must-extend-protections-for-snap-households-who-are-victims-of-benefit-theft">cbpp.org: SNAP benefit theft</a></li>
<li>Code of Federal Regulations. 7 CFR 273.16, Disqualification for intentional Program violation. Accessed via Legal Information Institute, Cornell Law School. <a href="https://www.law.cornell.edu/cfr/text/7/273.16">law.cornell.edu: 7 CFR 273.16</a></li>
<li>Code of Federal Regulations. 7 CFR 278.6, Disqualification of retail food stores and wholesale food concerns and imposition of civil money penalties in lieu of disqualifications. Accessed via Legal Information Institute, Cornell Law School. <a href="https://www.law.cornell.edu/cfr/text/7/278.6">law.cornell.edu: 7 CFR 278.6</a></li>
<li>Congressional Research Service. <em>Supplemental Nutrition Assistance Program: Errors and Fraud</em> (IF10860). Updated April 7, 2025. <a href="https://www.everycrsreport.com/files/2025-04-07_IF10860_5ae77126bb0274ff50854578e32b6633ca81d923.html">everycrsreport.com: IF10860</a></li>
<li>Congressional Research Service. <em>Errors and Fraud in the Supplemental Nutrition Assistance Program (SNAP)</em> (R45147). Updated September 28, 2018. <a href="https://www.everycrsreport.com/reports/R45147.html">everycrsreport.com: R45147</a></li>
<li>Congressional Research Service. <em>Supplemental Nutrition Assistance Program (SNAP): Benefit Theft Through Electronic Benefit Card Skimming</em> (IN12419). <a href="https://www.congress.gov/crs-product/IN12419">congress.gov: IN12419</a></li>
<li>Consolidated Appropriations Act, 2023. Public Law 117-328, Division HH, section 501.</li>
<li>Federal Register. &quot;Enhancing Retailer Standards in the Supplemental Nutrition Assistance Program (SNAP).&quot; Final rule, 81 FR 90675, December 15, 2016. <a href="https://www.federalregister.gov/documents/2016/12/15/2016-29837/enhancing-retailer-standards-in-the-supplemental-nutrition-assistance-program-snap">federalregister.gov: 81 FR 90675</a></li>
<li>Federal Register. &quot;Supplemental Nutrition Assistance Program: Requirement for Interstate Data Matching To Prevent Duplicate Issuances.&quot; Interim final rule, October 3, 2022. <a href="https://www.federalregister.gov/documents/2022/10/03/2022-21011/supplemental-nutrition-assistance-program-requirement-for-interstate-data-matching-to-prevent">federalregister.gov: interstate data matching</a></li>
<li>US Department of Agriculture, Economic Research Service. &quot;Supplemental Nutrition Assistance Program (SNAP) average monthly participation and inflation-adjusted annual program spending, fiscal years 2000-24.&quot; Chart. <a href="https://www.ers.usda.gov/data-products/chart-gallery/chart-detail?chartId=54637">ers.usda.gov: participation and spending chart</a></li>
<li>US Department of Agriculture, Food and Nutrition Service. <em>Feasibility Study of Capturing Supplemental Nutrition Assistance Program (SNAP) Purchases at the Point of Sale</em>. Prepared by IMPAQ International. November 2016.</li>
<li>US Department of Agriculture, Food and Nutrition Service. <em>Fiscal Year 2024 SNAP Quality Control Payment Error Rates</em>. 2025. <a href="https://fns-prod.azureedge.us/sites/default/files/resource-files/snap-fy24QC-PER.pdf">fns.usda.gov: FY 2024 QC report</a></li>
<li>US Department of Agriculture, Food and Nutrition Service. &quot;USDA Releases Annual SNAP Payment Error Rates for FY 2024.&quot; Press release, June 27, 2025.</li>
<li>US Department of Agriculture. &quot;USDA Announces FY 2025 State Payment Error Rates in SNAP.&quot; Press release, June 24, 2026. <a href="https://www.usda.gov/about-usda/news/press-releases/2026/06/24/usda-announces-fy-2025-state-payment-error-rates-snap">usda.gov: FY 2025 state error rates</a></li>
<li>US Department of Agriculture. &quot;USDA Announces Actions to Better Serve States, Nutrition Program Recipients, and the American Taxpayer.&quot; Press release on the Food and Nutrition Administration, April 30, 2026. <a href="https://www.usda.gov/about-usda/news/press-releases/2026/04/30/usda-announces-actions-better-serve-states-nutrition-program-recipients-and-american-taxpayer">usda.gov: FNS restructuring</a></li>
<li>US Government Accountability Office. <em>Supplemental Nutrition Assistance Program: Actions Needed to Better Measure and Address Retailer Trafficking</em> (GAO-19-167). December 2018. <a href="https://www.gao.gov/products/gao-19-167">gao.gov: GAO-19-167</a></li>
<li>Wilson, Hoke (Manhattan Strategy Group). <em>The Extent of Trafficking in the Supplemental Nutrition Assistance Program: 2015-2017</em>. USDA Food and Nutrition Service, Office of Policy Support, September 2021. <a href="https://fns-prod.azureedge.us/sites/default/files/resource-files/Trafficking2015-2017-3.pdf">fns.usda.gov: trafficking 2015-2017</a></li>
<li>Contra Costa County Employment and Human Services Department. &quot;New EBT Card Security &amp; Technology Upgrade.&quot; February 2025. <a href="https://ehsd.org/2025/02/18/new-ebt-card-security-technology-upgrade/">ehsd.org: EBT card upgrade</a></li>
</ol>
]]></content:encoded><media:thumbnail url="https://truonghan.com/og/designing-out-snap-fraud.png"/></item><item><title>The NeXT Bytes: Prologue</title><link>https://truonghan.com/the-next-bytes/</link><guid isPermaLink="true">https://truonghan.com/the-next-bytes/</guid><pubDate>Tue, 02 Jun 2020 00:00:00 +0000</pubDate><description>The NeXT Bytes is a monthly memo where we want to share these experiences, observation and research summary. Together with my crew, we intend to provide a sense of the goings-on in the tech industry, with more than thousands subscribers who are founders, makers, software engineers, designers, product guys and technologist.</description><content:encoded><![CDATA[<h2 id="the-standing-ground">The Standing-ground<a class="anchor" href="#the-standing-ground" aria-label="Link to this section"></a></h2>
<p>I'm a software engineer, innovation advocates and technology investor. I created <a href="golang.org.vn">Golang Vietnam</a> Community and helped run <a href="webuild.community">WeBuild Community</a>. On those platforms, we connect with all the makers and software engineers from different background and support them on their side projects.</p>
<p>I'm the founder of <a href="dwarves.foundation">Dwarves Foundation</a> (<a href="https://github.com/dwarvesf">@dwarvesf</a>), a technology firm that focuses on tech know-how R&amp;D and massive application/distribution via a wide range of activities and services, e.g. offshore centre, software consulting, open-source software development. At Dwarves Foundation, we choose innovation to be one of our core values that form ideas to affect the general society undoubtedly. Innovation is not for geniuses working alone; it is an activity that requires a team effort. Being innovative is a part to approach the new era.</p>
<p>I also founded <a href="dwarves.ventures">Dwarves Ventures</a>, the investment arms of Dwarves Foundation. We invested in a few startups that want to build next favorite things. One of our investment is <a href="superbits.co">Super Bits</a>. It's a product team that dedicated to making software &amp; tools for programmers and development team.</p>
<h2 id="we-are-heading-to-the-future">We are heading to the future<a class="anchor" href="#we-are-heading-to-the-future" aria-label="Link to this section"></a></h2>
<p>Throughout the years, we still believe that the best way to predict the future is to invent it. In the run to the future, we are continually working and exploring new technologies, frameworks and methodologies. While we actively involve in software development, we also have the opportunity to work with many startups, founders, business owners and software team, which make us one of the best witness to pragmatic innovation transformation.</p>
<p>The interest and area of knowledge keep expanding around technological know-how and practices to build sustainable businesses, in variety of</p>
<ul>
<li>Teamwork &amp; Leadership</li>
<li>Business and Organization Structure</li>
<li>Product Development</li>
<li>Software Design</li>
<li>Digital Transformation &amp; Automation</li>
<li>Data-driven &amp; Business Intelligent</li>
</ul>
<p>And also</p>
<ul>
<li>Working with Startups</li>
<li>Investment &amp; VC Point of View</li>
<li>Business Model Analysis</li>
<li>Innovation and Methodology</li>
<li>Everything else Science x Technology related</li>
</ul>
<p>And <strong>The NeXT Bytes</strong> is a monthly memo where we want to share these experiences, observation and research summary. Together with my crew, we intend to provide a sense of the goings-on in the tech industry, with more than thousands subscribers who are founders, makers, software engineers, designers, product guys and technologist. As we are going to explore the future, we don't know what it holds, but the journey will be exciting.</p>
<p>The name <a href="https://tinyletter.com/nextbytes/"><strong>The NeXT Bytes</strong></a> is inspired by Apple NeXTSTEP OS and also through the fact that Technology &amp; Software continues to drive the world. Everything is running on code which consists of trillion of billion of <em><strong>zero &amp; one</strong></em> bits combined. We want to look at the bright side of the future where the next generation of Bytes would be elegant, well-written and would make the world a better place.</p>
<p>Subscribing to this newsletter is a way keep up with these updates, with a message sent directly to your inbox.</p>
<blockquote>
<p>Subscribe: <strong><a href="https://tinyletter.com/nextbytes/">https://tinyletter.com/nextbytes/</a></strong></p>
</blockquote>
<p><img src="https://truonghan.com/images/blog/2020-06-02-next-bytes.png" alt=""></p>
]]></content:encoded><media:thumbnail url="https://truonghan.com/og/the-next-bytes.png"/></item><item><title>2020 Resolutions</title><link>https://truonghan.com/2020-resolutions/</link><guid isPermaLink="true">https://truonghan.com/2020-resolutions/</guid><pubDate>Sun, 01 Mar 2020 00:00:00 +0000</pubDate><description>The last 10 years is quite a ride. Turning from a kid to a man, I have grown a bit thanks to a long list of mentors, brothers, and sisters. This is 2020, and I'd like to make it special to kick start the next 10 years. Here come the resolutions for this year.</description><content:encoded><![CDATA[<p>The last 10 years was quite a ride. Turning from a kid to a man, I have grown more than my expected thanks to a long list of mentors, brothers, and sisters. It is 2020, and I'd like to make it special to kick start the next 10 years. Here come the resolutions for this year.</p>
<h4 id="dwarves-foundation">Dwarves Foundation<a class="anchor" href="#dwarves-foundation" aria-label="Link to this section"></a></h4>
<p>The company we founded in 2015 has turned 5 and reached its first milestone. It's about time to step down and let others take the throne.</p>
<ul>
<li>Transition: Some transitions are needed for the next generation to take over the company. Everything is quite in shape. The business model is firm. We have a great team of 50-ish, a growth rate of 60% since Day 1, a worth-to-pursue vision, a few missions ahead to accomplish, a playbook with all of our best practices, and a few good hires last year. I'm looking for a new CEO for Dwarves Foundation.</li>
<li>Territory: With a good record serving customers in the last 5 years and everything are in place; the business expansion expects to happen both online and offline. Most of the time, we meet and work with our clients via the internet. The pattern works so far, but I believe there's still room for improvement. Why not try to take another business approach? I'm looking for business partners to explore the market that we haven't touched.</li>
<li>Employee-owned company: Last year, we distributed company shares to the early employees and had the first batch of dividend pulled off. This year, we will continue to issue the ESOP to the rest privately. Every member who stays with the company for more than 2 years will have chances to earn company stakes.</li>
<li>Fully automation &amp; Data-driven operations: The best part of running your tech company? You have a vast workforce to try all the cool kinds of stuff. We have the data of every part of daily operations, from project development to personal development. All the hiring, branding campaigns, or even accounting &amp; payroll adjustments should base on data. It will help reveal the hidden aspect that you've ever notice.</li>
<li>A getaway office: It's my dream to have an unconventional office space where the dwarves can escape the city and hang around. One in the mountain and one near the beach. Still, a long way to go.</li>
</ul>
<h4 id="dwarves-ventures">Dwarves Ventures<a class="anchor" href="#dwarves-ventures" aria-label="Link to this section"></a></h4>
<p>Taking the build capability to the step further, Dwarves Ventures is a micro VC that makes use of our knowledge, experience for years to help tech startups with business &amp; financial models, pitch assist, product design, market testing, MVP &amp; platform development. All the materials, metrics, and tractions to achieve before getting to series A funding round.</p>
<p>Last year, we made an investment into 2 local tech startups in Vietnam in exchange for 5 to 20 percent in equity. One major in Hospitality and one in Wholesale Marketplace. This year, among with the expansion of our core business and Craftsmanship Study Program, we hope to meet more exceptional founders.</p>
<h4 id="superbits">Superbits<a class="anchor" href="#superbits" aria-label="Link to this section"></a></h4>
<p>The internet used to be a fun place. Growing up, I couldn't find the same excitement. As per the prediction for the next decade, people tend to lean over the different &amp; personalized, I decided to found the Superbits to help bring those excitement bits back. And we are not the only team want to do that, there's another that bring cool bits back to the internet for years. One of them: <a href="https://100r.co">100r.co</a></p>
<p>We started last year with a few macOS apps to help with the productivity boost. This year we will continue to look at this same topic.</p>
<h4 id="a-hedge-fund-for-indie--retail-investors">A hedge fund for indie &amp; retail investors<a class="anchor" href="#a-hedge-fund-for-indie--retail-investors" aria-label="Link to this section"></a></h4>
<p>We build an autobot &amp; apply quantitative methods into the global financial trading market, dedicated to producing exceptional returns. For the exceptional returns, I mean a few bucks for hundreds USD saving, LOL. It can't make us rich but can help make life easier, financially. It is still in beta testing, and I'm looking for friends who are interested in &amp; also want to get the first taste. Please drop me a message at <a href="mailto:han@cashbag.xyz">han@cashbag.xyz</a></p>
<h4 id="learn-another-language">Learn another language<a class="anchor" href="#learn-another-language" aria-label="Link to this section"></a></h4>
<p>I barely speak English. It's not so proficient, and I'm not a type to learn anything to excellence. So instead of getting a higher degree in English, I'd love to learn another language. Chinese &amp; Japanese are on my list for now.</p>
<h4 id="be-a-dad">Be a dad<a class="anchor" href="#be-a-dad" aria-label="Link to this section"></a></h4>
<p>And finally, be a good dad. Learn to parent. I'm going to be a dad in about a month or two. I gave him a cool name, bought him a domain, hodl a few tokens in case they will go skyrocket. I'm still not ready for this, but let see what may come in the future.</p>
]]></content:encoded><media:thumbnail url="https://truonghan.com/og/2020-resolutions.png"/></item><item><title>The Unconventional</title><link>https://truonghan.com/the-unconventional/</link><guid isPermaLink="true">https://truonghan.com/the-unconventional/</guid><pubDate>Wed, 17 Jul 2019 00:00:00 +0000</pubDate><description>My thought on the unconventional members at Dwarves Foundation</description><content:encoded><![CDATA[<p>You might have known that I'm building the software organization called The Dwarves in the last four years. The team reached the size of 50 several months ago. The average member age is around 26. They are smart and talented. They are young, well-educated, and hard-working toward success.</p>
<p>With a different expectation in engineering standard, we rarely hire people outside of our circle. We handpick and recruit the individual whom we feel they are the one matching with the engineering-driven culture that we are pursuing. We also decided to hire high potential freshers and train them from scratch. It's costly to do such a thing, but we believe good things take time. That's the way we hire for culture (and values).</p>
<p>The culture we are pursuing is the combination between the unconventional spirit and the will to do the right things in every piece of our decision, not just follow the existing of which has been said for years. From the outside perspective, it seems to be ridiculous to develop such an organization:</p>
<ul>
<li>from gather people with like mind and build the organization we'd love to work at</li>
<li>where judgments are fair and run by the right ideas</li>
<li>people payout are equal to their contribution so they can make money and be cool at the same time</li>
<li>people should commit to the quality, not the sales quantity</li>
<li>the team members have time to think about the next problem they want to solve or can join the team on self-development progress.</li>
</ul>
<p>It's like Utopia for software engineers and innovators.</p>
<p>The thought about an unusual business approach, high risk, and considerable chances to fail often appear in my head. But things are getting confident in me over time. There are few companies out there did the same thing, and they stood out.</p>
<p>Remember when Steve Job talked about why he didn't hire the professional managers? In the fast-paced industry like tech, the developer turned manager rule the works. The professional managers can't do crap about that. Managing at Dwarves Foundation is a part-time occupation, next to being involved with doing the work itself.</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
			<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube-nocookie.com/embed/rQKis2Cfpeo?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
		</div>

<p><strong>And running by ideas is the best way to hire great people.</strong></p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
			<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube-nocookie.com/embed/f60dheI4ARg?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
		</div>

<p><strong>Some companies care about the work output, not chairs.</strong></p>
<p><a href="https://basecamp.com/books/remote"><img src="https://truonghan.com/images/blog/2019-07-16-guessing.jpg" alt=""></a></p>
<p><strong>Or when they accept the truth that planning is guessing.</strong></p>
<p><a href="https://basecamp.com/books/rework"><img src="https://truonghan.com/images/blog/2019-07-16-planning.jpg" alt=""></a></p>
<p>I love the unconventional way they see and describe the world, not in the fascinating prism but the true nature.</p>
<p>We seek those kinds of people to join our team. The one which is distinctive, with a different approach and can see things in their bare form, know the truth, and be optimistic about the future are the right people we'd love to work.</p>
<p>Recently, an intern shared <a href="https://qz.com/1279371/this-physicists-ideas-of-time-will-blow-your-mind/">this post</a> about quantum physics, time and it blew my mind. It reminds me of how lucky I am to have them in the team.</p>
<p>Tech industry is the destination and interference zone between sectors. It's the most fast-pacing environment and the future where human will put all the effort to push its boundary. The Dwarves are running along with the world, and we are welcome you to join us on this journey. We can't promise you the world, but we can guarantee you it will be a fun ride.</p>
]]></content:encoded><media:thumbnail url="https://truonghan.com/og/the-unconventional.png"/></item><item><title>Software Engineering Philosophy</title><link>https://truonghan.com/software-engineering-philosophy/</link><guid isPermaLink="true">https://truonghan.com/software-engineering-philosophy/</guid><pubDate>Thu, 23 May 2019 00:00:00 +0000</pubDate><description>My thought on software development. The philosophy help to shape up the way we think and react in our daily life. They are what we follow at Dwarves Foundation.</description><content:encoded><![CDATA[<p><img src="https://truonghan.com/images/blog/2019-05-23-software.png" alt=""></p>
<h1 id="software-is-changing-the-world">Software is changing the world<a class="anchor" href="#software-is-changing-the-world" aria-label="Link to this section"></a></h1>
<p>Software is all around us, yet few people think much about the many ways that software impacts the world every day. The history of computing is often said to start with the tactical problems engineers needed to solve in World War II, but it wasn't until 1941 that engineer Konrad Zuse designed a digital computer capable of using software to solve general problems. During the 1950s, standardized programming languages emerged from the need to reproduce old software on new hardware. By the 1960s, software - until then given away for free with new computers - was being produced and sold to end users. By the 1980s, software companies saw the potential in the user-friendly graphical interfaces we are now familiar with. Apple started producing operating systems in the mid-1980s and Microsoft in the early 1990s, leading to the era of convenient and fast software. Internet was invented in 1983 with cloud computing and big data era follow up in the next 20 years. Decentralization with p2p communication, blockchain and bitcoin are booming up in the last 5 years. Today, software is all around us.</p>
<p>Software is used by businesses to communicate worldwide. It is used to operate some of the most complex medical machines. It regulates nuclear plants, electrical grids, and other utilities that provide the basics of life. Of course, it is also behind the safe operation of more than 31 million commercial airline flights in the U.S. each year.</p>
<p>Software also helps make life convenient. For example, computer software has made it easier than ever to connect with friends all over the world using social networks. As software becomes more integrated into electronics and appliances, it will continue to simplify life's challenges. As an example, everyone is familiar with how much easier it is to drive from point A to point B thanks to on-board GPS software. Refrigerators can now notify owners about spoiled food; lights around town and in your home can brighten when they detect you walking in the dark; televisions can make suggestions and record your favorite shows based on your preferences.</p>
<p>Whatever the future holds, it is a good bet that software will remain a very important part of it. As computer software becomes more sophisticated, it will help people with more complex challenges. In the future, software may even help us to regulate and improve our own bodies! Today's wearable technology and implanted medical devices are just the beginning of what the next generation of software can do.</p>
<p>It's the pass to future. The performance and survival of a large number of organizations now depend on their ability to integrate digital at the heart of their business strategy. The world can't operate without software.</p>
<h1 id="the-dwarf-principles">The Dwarf Principles<a class="anchor" href="#the-dwarf-principles" aria-label="Link to this section"></a></h1>
<p>Our definition of Engineering Philosophy, the guiding ideas that help to shape up the way we think and work, to ensure the core values and to deliver quality softwares.</p>
<p>The performance and survival of a large number of organizations now depend on their ability to integrate digital at the heart of their business strategy. This manifesto documents 15 principles that, our team believes, are the cornerstone to creating exceptional digital products that reach their objectives and stand the test of time.</p>
<h2 id="engineering-drive"><strong>Engineering Drive</strong><a class="anchor" href="#engineering-drive" aria-label="Link to this section"></a></h2>
<p><em>We build a company where software engineering discipline could shine, innovative and quality products are shipped and change the world for the better.</em></p>
<h4 id="systematic-discipline">SYSTEMATIC DISCIPLINE<a class="anchor" href="#systematic-discipline" aria-label="Link to this section"></a></h4>
<p>Software engineering is an engineering discipline that’s applied to the development of software in a systematic approach.</p>
<p>Not applying software engineering methods results in more expensive, less reliable software, and it can be detrimental on the long term, as the changes come in, the costs will dramatically increase.</p>
<h4 id="engineers-are-not-commodities">ENGINEERS ARE NOT COMMODITIES<a class="anchor" href="#engineers-are-not-commodities" aria-label="Link to this section"></a></h4>
<p>In every software, the engineering team is one of the most imporant factors to make it successful. Software is more of an artisanship, and software engineers aren't a replaceable cog.</p>
<p>People have an assembly line mindset left over from the industrial age. Don’t believe the one-size-fits-all interview process with whiteboarding problems. These serve to grind away the individuality and make us feel like an assembly line worker.</p>
<h4 id="the-best-idea-wins">THE BEST IDEA WINS<a class="anchor" href="#the-best-idea-wins" aria-label="Link to this section"></a></h4>
<p>If you have a great idea and the will to see it through, you can effect great change. Nothing is off limits—we’re constantly looking for improvements in our products, our processes, and our people.</p>
<p>All voices are equal here—we hire people to have an opinion and be creative. If your idea makes the most sense, that’s what we’re doing, regardless of your role or seniority.</p>
<p>We learned the practice from an A team, and it is beneficial by helping to strengthen the flat organizational structure.</p>
<p><img src="https://truonghan.com/images/blog/2019-05-23-dwarf.png" alt=""></p>
<h2 id="the-craftsmanship"><strong>The Craftsmanship</strong><a class="anchor" href="#the-craftsmanship" aria-label="Link to this section"></a></h2>
<h4 id="well-crafted-software">WELL-CRAFTED SOFTWARE<a class="anchor" href="#well-crafted-software" aria-label="Link to this section"></a></h4>
<p>We humbly demonstrate our expertise by delivering quality software. We work toward perfection in every single piece that we produce. We are proud of the well-crafted software that we develop together. We do not tolerate preventable defects.</p>
<h4 id="quality-pays">QUALITY PAYS<a class="anchor" href="#quality-pays" aria-label="Link to this section"></a></h4>
<p>Good things take time. Quality must be the top priority. In software work, quality problems overwhelm everything else. When quality isn't managed, entire software projects are unmanageable. Software development is intellectual work. Crafting a high quality software requires not just lots of efforts from a team but also the disciplines and proper methods.</p>
<h4 id="mind-the-details">MIND THE DETAILS<a class="anchor" href="#mind-the-details" aria-label="Link to this section"></a></h4>
<p>Details matter. We believe small details equate to big success. As such, creating a product that charms users involves adjusting, refining, and perfecting, over and over again. This attention to detail makes all the difference and creates memorable user experiences.</p>
<h4 id="choosing-the-boring-solution">CHOOSING THE BORING SOLUTION<a class="anchor" href="#choosing-the-boring-solution" aria-label="Link to this section"></a></h4>
<p>Software development is complex enough by nature. When in doubt, go with the boring solution. Using such a boring solution means that you're using simple vocabulary, which greatly increases the chances of everybody understanding you. This will make it a lot easier for everybody to follow along.</p>
<h4 id="think-long-term">THINK LONG-TERM<a class="anchor" href="#think-long-term" aria-label="Link to this section"></a></h4>
<p>We do not believe in rushing for project and working overtime. We believe in working sustainably and balanced so that we are a happy bunch of sane workers. Software requires good brains to create good codes and good processes. We do not burn out.</p>
<h4 id="applications-need-solid-foundations">APPLICATIONS NEED SOLID FOUNDATIONS<a class="anchor" href="#applications-need-solid-foundations" aria-label="Link to this section"></a></h4>
<p>Quality products always rely on a solid foundation. There will be a lot of works running on days, weeks, months, and maybe even years building that application. This constant, never-ending maintenance and extension of an application means that its foundation becomes crucial. Much like with a house, it's not a clever idea to save some money by building just a cheap foundation.</p>
<h4 id="nothing-is-permanent">NOTHING IS PERMANENT<a class="anchor" href="#nothing-is-permanent" aria-label="Link to this section"></a></h4>
<p>Software moves fast. New technology has enabled us to create new things. The technology you used yesterday maybe deprecated today. Make sure you understand both the old technology and the new one before buying into the next new thing. New technology needs to provide actual value over existing solutions.</p>
<h2 id="minifesto"><strong>Minifesto</strong><a class="anchor" href="#minifesto" aria-label="Link to this section"></a></h2>
<h4 id="running-lean">RUNNING LEAN<a class="anchor" href="#running-lean" aria-label="Link to this section"></a></h4>
<p>Lean philosophy regards everything not adding value to the customer as waste. In order to eliminate waste, one should be able to recognize it. Partially done work, extra processes like paperwork and features not often used by customers are waste. Rework or unnecessarily complex solutions are waste. Waiting for other activities, teams, processes is waste. Managerial overhead not producing real value is waste.</p>
<h4 id="deliver-as-fast-as-possible">DELIVER AS FAST AS POSSIBLE<a class="anchor" href="#deliver-as-fast-as-possible" aria-label="Link to this section"></a></h4>
<p>Make it work. Perfect is an enemy of good. Look for perfection, but not yet. First do it, then do it right, then do it better. Any sufficiently complex system cannot be built out of design, it has to be evolved. The shorter the iterations, the better the learning and communication within the team. The sooner the end product is delivered without major defects, the sooner feedback can be received, and incorporated into the next iteration.</p>
<h4 id="aim-for-simplicity">AIM FOR SIMPLICITY<a class="anchor" href="#aim-for-simplicity" aria-label="Link to this section"></a></h4>
<p>Simplicity is complicated. Simple is harder than complex. We have to write code for humans not machines. We want readability. Readable means Reliable. It's easier to understand. It's easier to work on. If it breaks, it's easier to fix.</p>
<h4 id="effective--productive">EFFECTIVE &gt; PRODUCTIVE<a class="anchor" href="#effective--productive" aria-label="Link to this section"></a></h4>
<p>Being productive is about occupying your time—filling your schedule to the brim and getting as much done as you can. Being effective is about finding more of your time unoccupied and open for other things besides work. We don’t believe in busyness. We believe in effectiveness. Know your priority and do things that matter.</p>
<h4 id="fail-fast-learn-often">FAIL FAST, LEARN OFTEN<a class="anchor" href="#fail-fast-learn-often" aria-label="Link to this section"></a></h4>
<p>Don't be afraid of starting all over again. Evaluate your work constantly. Don't be afraid of throwing to the rubbish a project you've working on last months. When you start from scratch, innovation comes organically inspired by previous experiences. Evaluate your work constantly.</p>
<p><img src="https://truonghan.com/images/blog/2019-05-23-team-images.png" alt=""></p>
<h5 id="we-are-hiring">WE ARE HIRING<a class="anchor" href="#we-are-hiring" aria-label="Link to this section"></a></h5>
<p>We are looking for more Dwarves joining us. Have the same DNA and want to build world class products with superb team? Find yourself a seat at: <a href="https://dwarves.careers">https://dwarves.careers</a></p>
<p>We build a dynamic environment where engineers can spend their time working with disruptive startups and be cool at the same time. Checkout engineering life at Dwarves Foundation: <a href="https://dwarves.careers/life/">https://dwarves.careers/life/</a></p>
<p><img src="https://truonghan.com/images/blog/2019-05-23-engineering-life.png" alt=""></p>
<blockquote>
<p>Here's our cat</p>
</blockquote>
<p><img src="https://truonghan.com/images/blog/2019-05-23-cat.JPG" alt=""></p>
<p>--</p>
<p>The web version: <a href="https://d.foundation/manifesto">https://d.foundation/manifesto</a></p>
]]></content:encoded><media:thumbnail url="https://truonghan.com/og/software-engineering-philosophy.png"/></item><item><title>2018: Year of Achievement</title><link>https://truonghan.com/2018-year-of-achievement/</link><guid isPermaLink="true">https://truonghan.com/2018-year-of-achievement/</guid><pubDate>Mon, 11 Feb 2019 00:00:00 +0000</pubDate><description>2018 has been a fulfilling year for us with many achievements and we're very thankful for all the contributions that you have made.</description><content:encoded><![CDATA[<p><img src="https://truonghan.com/images/blog/2019-01-26-homepage.png" alt=""></p>
<p>Dwarves Foundation is a software development firm that based in South East Asia. Starting in 2014, we use cutting-edge technology to help tech startups, entrepreneurs and makers to deliver world-class software products.</p>
<h1 id="the-dwarves-of-the-year">The Dwarves of the Year<a class="anchor" href="#the-dwarves-of-the-year" aria-label="Link to this section"></a></h1>
<p>Many endeavors have significantly been made by our Dwarves to keep our boat moving over the past year. We have seen the sweat they drop. We have seen the effort they give. We have seen the time we spent together and we cannot stop ourselves treasuring those moments. It gratifies the team to eventually hold an award for the Dwarves of the year.</p>
<p>We understand that success does not come by accident. Breakthrough arrives after hours of working and trying. It pushes us through many considerations to finally reach to these three top-notch nominees among those remarkable hard works:</p>
<p><img src="https://truonghan.com/images/blog/2019-01-26-hall-of-fame.png" alt=""></p>
<h3 id="huy-nguyenoutstanding">Huy Nguyen — Outstanding<a class="anchor" href="#huy-nguyenoutstanding" aria-label="Link to this section"></a></h3>
<p>Considered for his dynamic contribution, Huy distinguishes himself for being enthusiasm towards the work. His striking performance was shown throughout multiple products. Appears to be the frequent workaholic who dedicates his time more than others, Huy did achieve a remarkable growth.
Beside successfully improving the internal competency, Huy is highly recognized for being a dedicated trainer for interns and co-workers.</p>
<h3 id="thanh-phamhonorable-mention">Thanh Pham — Honorable Mention<a class="anchor" href="#thanh-phamhonorable-mention" aria-label="Link to this section"></a></h3>
<p>Thanh differentiates his work performance during the process of development which assigned him the superior skills on visual and animation. Apart from being a devoted employee, he also plays an integral part of backing up the team and training front-end juniors.
Nevertheless, Thanh finds himself hard to fit in with other Dwarves, which explains the reason his ability in leadership is still in need of improvement.</p>
<h3 id="hieu-nguyenhonorable-mention">Hieu Nguyen — Honorable Mention<a class="anchor" href="#hieu-nguyenhonorable-mention" aria-label="Link to this section"></a></h3>
<p>Along with achieving breakthrough change on technical skills and meeting deadline on time with high standard workability, Hieu is well known for being proactive and eager to learn new things. Hieu also proves himself as an adept for new Dwarves to look up and get feedback from.
Though he truly give his best for the work, Hieu’s limited ability of teamwork and leadership still prevents him from being more outstanding.</p>
<p>As there is nothing that can help us measure the contribution you have put, we decided to conduct the award by different means. They are expected to claim the prize based on their titles both in cash and in kind, along with a certificate of recognition as a proof of accomplishment, as well as being honored on our Hall of Fame. Every single piece of trying helps us gradually expand the picture. Live long and prosper has been chosen as one of the mindsets to constitute our woodland from the very first day.</p>
<p>For the sake of that to happen, we understand that their benefit comes parallel to the team. So it will be dead wrong if you think there is a benchmark for the means of award. At Dwarves Foundation, recognition does not work that way. The more they contribute, the more guerdon they receive.</p>
<p><img src="https://truonghan.com/images/blog/2019-01-26-certificate.jpg" alt=""></p>
<h1 id="2018">2018<a class="anchor" href="#2018" aria-label="Link to this section"></a></h1>
<p>2018 has been a fulfilling year for us with many achievements.</p>
<p>We adapted the new team structure; we redefined things we value.</p>
<p>We doubled the office space. We welcomed new dwarves to join us.</p>
<p>We scaled up from 15 to 40ish with all the smart people gathering around and commit to the dream of building our first global software firm.</p>
<p>We learn new ways to get things done; we live by that for a cutting edge product quality. And we wrap it up with the tedious belief of a better triumph.</p>
<p><img src="https://truonghan.com/images/blog/2019-01-26-year-end-party.jpeg" alt=""></p>
<p>Everything is just the beginning of our first chapters and it couldn’t be that great without you. I’m very thankful for all the contributions that you have made and looking forward to 2019 with all of you.</p>
<hr>
<h3 id="we-are-hiring">We are hiring<a class="anchor" href="#we-are-hiring" aria-label="Link to this section"></a></h3>
<p>As we are expanding in 2019, we are looking for more Dwarves joining us. Find yourself a seat at: <a href="https://dwarves.careers">https://dwarves.careers</a></p>
<p><img src="https://truonghan.com/images/blog/2019-01-26-tech-stack.png" alt=""></p>
<p>Read more at: <a href="http://bit.ly/dwarves-foundation-2018">Medium</a></p>
]]></content:encoded><media:thumbnail url="https://truonghan.com/og/2018-year-of-achievement.png"/></item><item><title>What Dwarves Foundation Stands For</title><link>https://truonghan.com/what-dwarves-foundation-stands-for/</link><guid isPermaLink="true">https://truonghan.com/what-dwarves-foundation-stands-for/</guid><pubDate>Mon, 19 Mar 2018 00:00:00 +0000</pubDate><description>Dwarves Foundation is an innovation service firm. We have been building an organization with high standard software practices and business growth capabilities, helping tech startups, entrepreneurs and makers deliver their innovative software product since 2013.</description><content:encoded><![CDATA[<p>Innovation happens. There are many startups found and raise fund every day. They focus on making significant positive changes, impact millions of people, lead to breakthroughs and support the foundation of economic growth.</p>
<p>Building an innovation startup requires lots of different things, and it is tough. Most of them have to deal with business development, legal, fundraising, team recruitment, and product development at the same time. Tech startups need to move fast and break things.</p>
<p>At some points, growing a startup needs a strong and experienced team behind. The software team that can help to design the system, design the database, produce the well-crafted software, keep the system stable, avoid the technical debt and deliver values is one of the critical pieces to solve the puzzle.</p>
<p>Another motivations for Dwarves Foundation was the lack of respect that service firms receive. Most software companies out there focus on digital transformation and consultancy. They do tricks. They hit and run. The codebase they produced is full of crap and it damages our pride.</p>
<p>This is where Dwarves Foundation comes in.
Despite the fact that service firms scale linearly, their ultimate size is capped and small, they don’t have life-changing exits for their founders, we start Dwarves Foundation as we want to build things right. I’ve always felt that companies like Dwarves Foundation are more important to the economy than the mildly pejorative phrase used to describe them.</p>
<p>Dwarves Foundation is an innovation service firm. We have been building an organization with high standard software practices and business growth capabilities, helping tech startups, entrepreneurs and makers deliver their innovative software product since 2013.</p>
<p><strong>We stand for the craftsmanship in software development</strong>. More than telling people how to do things, as a firm, we take responsibility for collaboratively creating the product of innovation with the client. We value the long-term partnership, and we brought the economic impact through massive product distribution and brought to the market by the clients.</p>
<p>We empower innovation with software craftsmanship. This is what we do here. It takes many forms, and we’re always working towards it.</p>
<h3 id="the-dwarves">The Dwarves<a class="anchor" href="#the-dwarves" aria-label="Link to this section"></a></h3>
<p>The story begins in Norse Mythology. After the executed of Ymir, the very first giant of the universe, by Odin has conceived a whole new universe. In the new world, Yggdrasill is a giant ash tree located at the center cradles the nine realms of the cosmos.</p>
<p><img src="https://truonghan.com/images/blog/2018-03-19-yggdrasill.png" alt=""></p>
<p>Dwarf is the small and misshapen creatures that originated as maggots from Ymir’s corpse. They dwell in the mountains deep down in the dark underground of <strong>Svartalfheim</strong>. Dwarves are wisdom, natural good at smithing, mining, and crafting. They are well known for being the best blacksmiths throughout the nine worlds, not only do they have natural abilities to craft weapons and jewelry. They are also excellent engineers and architects.</p>
<p>Dwarvish magic was fundamentally technological know how. They had an unusually high level of skill. There was no shimmering light around the Dwarves, just first rate workmanship. Dwarves greatly pride themselves in their craftsmanship, be it smithing or anything else.</p>
<p>The dwarf is an example of a creature who has the ability that surpasses the known world, presaging the rise of a universe beyond this physical realm. Whatever their origin, the dwarf’s existence plays a role in expanding the imagination.</p>
<p>Aspiring to create a positive breakthrough that can be able to rewrite the new world, we chose the name Dwarves Foundation as the commitment to the vision that we follow.</p>
<p>At our woodland, the Dwarves are encouraged to thoroughly assess and evaluate the product quality before handing it to the customers. By participate in every detail of the work, we take craftsmanship as one of the most fundamental factors of the career transformation for engineers. World-class products require world-class engineers and craftsmanship is what makes it done. Craftsmanship is what flows among the team spirit, or does not exist at all.</p>
<p>We choose innovation to be one of our core values that form ideas to affect the general society undoubtedly. Innovation is not for geniuses working alone, it is an activity that requires the team effort. Being innovative is a part to approach the new era. What that rewards, what can I say, can genuinely lead to significantly remodel the cosmo.</p>
<p><img src="https://truonghan.com/images/blog/2018-03-19-dwarves.jpg" alt=""></p>
<hr>
<p>Empower innovation is the lifestyle we choose and we are always welcome the like minded people to join us on this journey. A network with all the people who want to change the world would be really awesome. Drop us a message <a href="mailto:team@d.foundation">team@d.foundation</a> anytime.</p>
]]></content:encoded><media:thumbnail url="https://truonghan.com/og/what-dwarves-foundation-stands-for.png"/></item><item><title>Cofounder Agreement</title><link>https://truonghan.com/cofounder-agreement/</link><guid isPermaLink="true">https://truonghan.com/cofounder-agreement/</guid><pubDate>Thu, 15 Feb 2018 00:00:00 +0000</pubDate><description>Ensure Alignment of Vision As you and your cofounder (or cofounders) embark on your entrepreneurial journey together, it’s imperative that you all share the same vision for the purpose and future of …</description><content:encoded><![CDATA[<h1 id="ensure-alignment-of-vision">Ensure Alignment of Vision<a class="anchor" href="#ensure-alignment-of-vision" aria-label="Link to this section"></a></h1>
<p>As you and your cofounder (or cofounders) embark on your entrepreneurial journey together, it’s imperative that you all share the same vision for the purpose and future of your company. It’s easy to get caught up in the thrill of launching a potentially successful startup together, but you all must take the time to establish the existential foundations of the company, and as early as possible, to help prevent the possibility of a drastic falling out later on.</p>
<h3 id="what-are-your-goals">What Are Your Goals?<a class="anchor" href="#what-are-your-goals" aria-label="Link to this section"></a></h3>
<ul>
<li>Why are you creating this startup?</li>
<li>What need will it fill?</li>
<li>What will your initial product/service offering be?</li>
<li>And what is your strategy for growing the company?</li>
</ul>
<h3 id="what-are-your-company-values-and-philosophy">What Are Your Company Values and Philosophy?<a class="anchor" href="#what-are-your-company-values-and-philosophy" aria-label="Link to this section"></a></h3>
<p>Your company’s success depends on vision alignment (alignment between you and your investors, your employees, and your customers, etc.). Do you share the same priorities? Do you agree on what’s most important to you both personally and for the future of your company?</p>
<h3 id="how-do-you-measure-success">How Do You Measure Success?<a class="anchor" href="#how-do-you-measure-success" aria-label="Link to this section"></a></h3>
<ul>
<li>What do you and your co-founder(s) hope to get out of this business?</li>
<li>How will you know if you’ve achieved your goals?</li>
<li>If you don’t agree on the same goals, how will you know when you’ve achieved them?</li>
</ul>
<blockquote>
<p>&quot;Discussing these issues with your co-founder could get uncomfortable, create rifts, or even uncover deal-breakers. If you are able to successfully have these difficult conversations, this is a testament to your partnership and to the strength of your relationship -- and will serve as a solid foundation from which to grow your business.&quot; - David Ehrenberg</p>
</blockquote>
<h3 id="working-styles-and-culture">Working Styles and Culture<a class="anchor" href="#working-styles-and-culture" aria-label="Link to this section"></a></h3>
<p>Once you and your cofounder have laid down the aspirational and philosophical groundwork for your startup, this is also the ideal time to determine what kind of working environment best complements the vision of your startup.</p>
<ul>
<li>What 3 words best describe the startup culture you want to create (e.g. open, hard-working, eccentric, etc.)? Pro-tip: If you’re really serious and far along go visit few office spaces together to get a sense of what each of you likes work environments wise and why.</li>
<li>What values do we want to instill in our employees?</li>
<li>If you could pick 2 things to change about our company, and two things to bring with you from your previous experience, what would they be and why?</li>
<li>How much equity are we allocating for future employees?</li>
<li>What should we look for in our first 5 hires?</li>
<li>Describe your ideal working style.</li>
</ul>
<h1 id="legal-decisions--definitions">Legal Decisions &amp; Definitions<a class="anchor" href="#legal-decisions--definitions" aria-label="Link to this section"></a></h1>
<h3 id="general-questions">General Questions<a class="anchor" href="#general-questions" aria-label="Link to this section"></a></h3>
<ul>
<li>Is the percentage ownership subject to vesting based on continued participation in the business?</li>
<li>If one founder leaves, does the company or the other founder have the right to buy back that founder’s shares? At what price?=</li>
<li>What salaries (if any), are the founders entitled to? How can that be changed?</li>
<li>How are key decisions and day-to-day decisions of the business to be made?</li>
</ul>
<h3 id="basic-legal-definitions">Basic Legal Definitions<a class="anchor" href="#basic-legal-definitions" aria-label="Link to this section"></a></h3>
<p>Harroch's article also explores numerous basic legal terms associated with launching a startup, all of which you and your business partner should familiarize yourselves with.</p>
<ul>
<li>Confidentiality Agreements - These are also referred to as Non-Disclosure Agreements or NDAs. The purpose of the agreement is to allow the holder of confidential information to share it with a third party.</li>
<li>Copyright - A copyright gives the owner the exclusive right to make copies of the work and to prepare derivative works (such as sequels or revisions) based on the work.</li>
<li>General Partnership - If there is more than one founder, a general partnership is often chosen as the legal form of business entity.</li>
<li>Patent - A patent gives its inventor the right to prevent others from making, using, or selling the patented subjected matter described in words in the patent’s claims.</li>
<li>Service marks - Service marks resemble trademarks and are used to identify services.</li>
<li>Tax Incentive - Depending on the nature of the business, various tax incentives may be available, such as renewable energy tax credits and investment tax credits.</li>
<li>Trademark - A trademark right protects the symbolic value of a word, name, symbol, or device that the trademark owner used to identify or distinguish its good from those of others.</li>
<li>Trade Secret - A trade secret right allows the owner of the right to take action against anyone who breaches an agreement or confidential relationship or who steals or uses other improper means to obtain secret information.</li>
</ul>
<h3 id="include-a-vesting-schedule-when-issuing-shares-to-co-founders">Include a Vesting Schedule When Issuing Shares to Co-founders<a class="anchor" href="#include-a-vesting-schedule-when-issuing-shares-to-co-founders" aria-label="Link to this section"></a></h3>
<p>When you create a vesting schedule, this means that the co-founder will earn their shares over time and protects you and the company if that a co-founder leaves the company or doesn't pull their weight. The standard vesting period is four years, with monthly vesting of shares. Unvested shares should be subject to repurchase by the company if the co-founder leaves the company.</p>
<h3 id="protect-your-valuable-intellectual-property">Protect Your Valuable Intellectual Property<a class="anchor" href="#protect-your-valuable-intellectual-property" aria-label="Link to this section"></a></h3>
<p>If intellectual property is a vital element of your business, it's important to protect that IP during the formative stages of a company. You can address this issue by relying on patents, trademarks, copyrights, and trade secrets to protect your valuable IP. Another step you can take is requiring all co-founders and any third-party developers to assign to the company their rights in IP they created and that is utilized by the company. Doing this will help prevent trouble if a co-founder leaves the company and takes a crucial patent with them.</p>
<h1 id="how-to-split-equity">How to Split Equity<a class="anchor" href="#how-to-split-equity" aria-label="Link to this section"></a></h1>
<h3 id="ideas-are-valuable">Ideas are Valuable<a class="anchor" href="#ideas-are-valuable" aria-label="Link to this section"></a></h3>
<p>If you brought the original concept to the table, increase your share holdings by about 5 percent. While execution is more important in the long run, whoever conceived the original idea deserves a little extra credit.</p>
<h3 id="that-first-step-is-a-doozy">That First Step is a Doozy<a class="anchor" href="#that-first-step-is-a-doozy" aria-label="Link to this section"></a></h3>
<p>Bringing something of value to the company during its formative stages – like a filed patent (not a provisional), a compelling demo, an early version of the product, or something else that means much of the work towards financing or revenue is already done – you should receive a considerable part of the equity.</p>
<h3 id="full-time-commitment-is-expensive">Full Time Commitment is Expensive<a class="anchor" href="#full-time-commitment-is-expensive" aria-label="Link to this section"></a></h3>
<p>Bottom line, if you’re working more, then you’re risking a lot more if the project fails, which means that you are entitled to more if the project succeeds. Also, keep in mind that part-time cofounders are a big minus to someone considering an investment, so choose your partners wisely.</p>
<h3 id="the-ceo-gets-more-always">The CEO Gets More. Always.<a class="anchor" href="#the-ceo-gets-more-always" aria-label="Link to this section"></a></h3>
<p>Splitting equity equally means that no one controls the company more than anyone else. But if that’s the case, than having a CEO is useless, as well as having any form of upper management. So, whoever is the designated CEO should get another slightly more equity. If you think that’s unfair, remember that market rate for a great CEO is higher than market rate for a great CTO, which means that’s how it is everywhere.</p>
<h3 id="reputation-counts">Reputation Counts<a class="anchor" href="#reputation-counts" aria-label="Link to this section"></a></h3>
<p>If you’re a beginning entrepreneur and your partner is an experienced founder with an established reputation, they deserve more equity. If that founder has an expansive network that benefits the company, they deserve more equity. If that founder can help secure more funding for your company, they deserve more equity.</p>
<blockquote>
<p>&quot;The question of equity brings out the most fundamental differences, perceptions, and values in an aspiring startup. In fact the equity question, more than any other, may strangle a young company before it can even get started. And that’s a damn good thing.&quot; - Dan Shapiro</p>
</blockquote>
<h1 id="final-thoughts">Final Thoughts<a class="anchor" href="#final-thoughts" aria-label="Link to this section"></a></h1>
<p>Yes, talking about such topics as company roles and vision and equity and the like can be uncomfortable among founders. However, these discussions are vital to a successful partnership, as well as a successful startup, and must be held before any of you invest too much time, energy, and money into a potential company. Keep in mind that whoever you decide to launch a company with, you all will have to make countless difficult decisions once your company is actually up and running. If you take nothing else from this blog post, at least follow the bit of advice below:</p>
<blockquote>
<p>&quot;Choose co-founders the way you would choose a spouse. The reality is that you will, at least in the early days, spend far more time with your co-founders than your partner.&quot; - Danielle Newnham</p>
</blockquote>
<h4 id="source-fico">Source: <a href="https://fi.co/insight/how-to-create-the-perfect-cofounder-agreement-with-your-business-partner">fi.co</a><a class="anchor" href="#source-fico" aria-label="Link to this section"></a></h4>
]]></content:encoded><media:thumbnail url="https://truonghan.com/og/cofounder-agreement.png"/></item><item><title>How to Contribute to the Tech Community</title><link>https://truonghan.com/how-to-contribute-to-tech-community/</link><guid isPermaLink="true">https://truonghan.com/how-to-contribute-to-tech-community/</guid><pubDate>Fri, 20 Oct 2017 00:00:00 +0000</pubDate><description>Below is the short list of recommended things to do to contribute to tech community</description><content:encoded><![CDATA[<p>Even though Vietnam is one of the booming countries for tech talents, there is still a whole gap between top developers around the world and us. Most of the points are related to how professional we are, our collaboration skill or soft skill.</p>
<p>Being a part of the tech community, realizing a few issues, also wanting to contribute a bit, this blog post is my precious and is a part of the whole series to improve the quality of next generation of developers.</p>
<p>Below is the short list of recommended things to do to contribute to the tech community.</p>
<h2 id="start-by-listening">Start by listening<a class="anchor" href="#start-by-listening" aria-label="Link to this section"></a></h2>
<p>Walking into a project and saying “Hi, here’s what I think this project should be doing” is usually not taken as a good thing. Some projects may welcome that sort of approach, but if the project has been running a while, the chances of that attitude being embraced are small. Listening is the best way to know what the project needs.</p>
<p><strong>Join a mailing list</strong></p>
<p>For a large scale and open project or community that involve many people, communication through a mailing list is one of the most effective ways to not distract others and good for you to follow the entire discussion.</p>
<p>Some of the mailing lists I followed over the past two years.</p>
<ul>
<li>Viet Infosec</li>
<li>golang-dev</li>
<li>golang-nuts</li>
</ul>
<p><strong>Follow a blog</strong></p>
<p>Elite developers blog are great knowledge sources that you should follow and learn from them. Some of the rock stars they don’t have their blog. Instead, they use Twitter for daily updates.</p>
<ul>
<li>Dave Cheney: <a href="https://dave.cheney.net">Dave Cheney | The acme of foolishness</a></li>
<li>Dan Luu: <a href="https://danluu.com">Dan Luu</a></li>
<li>Rob Pike: <a href="https://commandcenter.blogspot.com">command center</a></li>
<li>Golang: <a href="https://blog.golang.org">The Go Programming Language Blog</a></li>
</ul>
<p><img src="https://truonghan.com/images/blog/2017-10-20-golang.png" alt=""></p>
<p><strong>Join online discussions via Slack or IRC</strong></p>
<p>Most of the communities have their channel to keep in touch and discuss the project or things related to the community with other members.</p>
<p>IRC is one of the most popular tools that have been used by many projects, e.g. Python, PostgreSQL, OpenStack. IRC is free software. You need to download a client and ask for the node address to log in.</p>
<p>Beside IRC, there are several modern communication platforms. They are commercial products that serve the teamwork very well. Some with a free plan has been applied to the new community: Gitter, Slack, Discord. Also, new members are always welcome there.</p>
<ul>
<li>Reactiflux: <a href="https://discordapp.com/invite/reactiflux">Discord</a></li>
<li>Golang: <a href="https://gophers.slack.com">Slack</a></li>
<li>Sails: <a href="https://gitter.im/balderdashy/sails">balderdashy/sails - Gitter</a></li>
</ul>
<p>Some communities in Vietnam I take participation in</p>
<ul>
<li>Golang Vietnam: <a href="http://golang.org.vn">http://golang.org.vn</a></li>
<li>Ruby Vietnam: <a href="http://ruby.org.vn">http://ruby.org.vn</a></li>
<li>Grokking Engineering: <a href="http://grokking.net">http://grokking.net</a></li>
</ul>
<p><img src="https://truonghan.com/images/blog/2017-10-20-golang-slack.png" alt=""></p>
<h2 id="participating">Participating<a class="anchor" href="#participating" aria-label="Link to this section"></a></h2>
<p><strong>Organizing events</strong></p>
<p>The event organizer is a difficult job and very time consuming, but it’s the most critical factor to keep the community alive. People might not find it necessary as it depends on the interest. During the days in 201x, more and more events were held by the various scale of organizations and companies. Some activities that you may find</p>
<ul>
<li>Technical Meetup (Golang, Ruby, Geeky, Grokking)</li>
<li>Product Meetup</li>
<li>Design Workshop</li>
<li>Mobile Day</li>
<li>Web Summit</li>
</ul>
<p><img src="https://truonghan.com/images/blog/2017-10-20-websummit.png" alt=""></p>
<p><strong>Sponsor an Event</strong></p>
<p>Sponsoring a participant in a program can be a great way to show your support, especially when you have your friends or employees participating in that event.</p>
<p>Usual the local events are just at a small scale from 50 - 100 participants. If they are meetup or workshop event, most of their needs are a venue to organize or food and beverage or a small amount of money for operating costs.</p>
<p><strong>Sharing your knowledge</strong></p>
<p>In every event, if you can’t afford to sponsor an event or have enough time to be an organizing member, you still can give your hand to the community by sharing your thoughts, your knowledge to other people.</p>
<p><strong>Answer a question</strong>
The best way to help build the community is by helping others. Answering someone questions especially when he is just getting his feet wet is very helpful for him. I see a lot Viet Community doesn’t have that culture. They usually piss off the newbies and don’t want to give out their hands.</p>
<p><strong>Write a blog post</strong></p>
<p>If you’ve got a blog, write about your experiences with the project that you’re using. Tell about a problem you faced utilizing the software and what you did to solve it. You’ll be helping in two ways, both by helping keep the project on the minds of others around you and by creating a record for anyone else who has your problem in the future and searches the web for the answer.</p>
<h3 id="contributing-to-open-source">Contributing to Open Source<a class="anchor" href="#contributing-to-open-source" aria-label="Link to this section"></a></h3>
<p>Open source software (OSS) is distributed under a licensing agreement which allows computer code to be shared, viewed and modified by other users and organizations.</p>
<p>Alternatively, in slightly more user-friendly language, open source software is available for the general public to use and modify from its original design free of charge. What it means is that a piece of software can evolve and be iterated upon by other developers anywhere in the world. Ideally, this says that the software is improved over time, but it can often take plenty of exciting twists and turns with all of that evolution and can change form and shape entirely. <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p><strong>Diagnose a bug</strong></p>
<p>Bugs are often poorly reported. Building an attitude to help narrow down the problem make it easier for the developers to fix it. Whatever you discover, add it to the ticket in the bug system for all to see.</p>
<p><strong>Fix a bug</strong></p>
<p>Finding an interesting bug in the ticket system and try to fix it is the best practical way to contribute to the open source project. Remember to follow the guideline and during your working period, document whatever you know to help the later contributors. Write some tests if you can.</p>
<p><img src="https://truonghan.com/images/blog/2017-10-20-issue.png" alt=""></p>
<p><strong>Write a test</strong></p>
<p>Adding the test to the open source project will help to improve the quality and make it more stable. In some cases, test cases and test suites are more like the documents for the project.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://en.wikipedia.org/wiki/Open-source_software">https://en.wikipedia.org/wiki/Open-source_software</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded><media:thumbnail url="https://truonghan.com/og/how-to-contribute-to-tech-community.png"/></item><item><title>The CEO Roles: Idea to Continuous Growth</title><link>https://truonghan.com/the-ceo-roles/</link><guid isPermaLink="true">https://truonghan.com/the-ceo-roles/</guid><pubDate>Sat, 17 Sep 2016 00:00:00 +0000</pubDate><description>A CEO role must evolve to sustain growth. Where are you in the growth journey?</description><content:encoded><![CDATA[<p>A CEO role must evolve to sustain growth. Where are you in the growth journey?</p>
<h1 id="start-up">Start Up<a class="anchor" href="#start-up" aria-label="Link to this section"></a></h1>
<p>Here CEOs are:</p>
<ul>
<li>Doers, the 'Jacks and Janes' of all trades.</li>
<li>Decision makers</li>
</ul>
<h1 id="initial-growth">Initial Growth<a class="anchor" href="#initial-growth" aria-label="Link to this section"></a></h1>
<p>CEOs must:</p>
<ul>
<li>Learn to delegate</li>
<li>Learn how to attract and recruit the right people</li>
<li>Set the company direction</li>
</ul>
<h1 id="rapid-growth">Rapid Growth<a class="anchor" href="#rapid-growth" aria-label="Link to this section"></a></h1>
<p>CEOs need to:</p>
<ul>
<li>Hire people with more knowledge than themselves in the key business functions</li>
<li>Get the team to function as a 'team', to plan and communicate</li>
</ul>
<h1 id="continuous-growth">Continuous Growth<a class="anchor" href="#continuous-growth" aria-label="Link to this section"></a></h1>
<p>CEOs need to:</p>
<ul>
<li>Become strategic innovators &amp; change catalysts</li>
<li>Lead the company culture</li>
</ul>
<p><img src="https://truonghan.com/images/blog/2016-09-17-ceo-roles.png" alt=""></p>
]]></content:encoded><media:thumbnail url="https://truonghan.com/og/the-ceo-roles.png"/></item><item><title>Git Practices at Dwarves Foundation</title><link>https://truonghan.com/git-practices-at-dwarves-foundation/</link><guid isPermaLink="true">https://truonghan.com/git-practices-at-dwarves-foundation/</guid><pubDate>Mon, 20 Jun 2016 00:00:00 +0000</pubDate><description>Git practice using at Dwarves Foundation. Git is one of the most popular source control. Github is one platform built over the top of Git and well adapted by lots of companies. Knowing the right workflow will help to increase team productivity.</description><content:encoded><![CDATA[<p>Git is one of the most popular source control. Github is one platform built over the top of Git and well adapted by lots of companies. Knowing the right workflow will help to increase team productivity. In this post, I will try to cover some of the best practices from the community and the way we applied them at <a href="https://www.dwarves.foundation">Dwarves Foundation</a>.</p>
<h1 id="know-how-to-write-good-commit-message">Know how to write good commit message<a class="anchor" href="#know-how-to-write-good-commit-message" aria-label="Link to this section"></a></h1>
<blockquote>
<p>Source: <a href="http://chris.beams.io/posts/git-commit/">http://chris.beams.io/posts/git-commit/</a></p>
</blockquote>
<p><img src="https://truonghan.com/images/blog/2016-06-27-git-commit.png" alt=""></p>
<p>Have you ever read some repos with commit messages like above?</p>
<p>While many repositories' logs look like the former, there are exceptions. The <a href="https://github.com/torvalds/linux/commits/master">Linux kernel</a> and <a href="https://github.com/git/git/commits/master">git itself</a> are great examples. Look at <a href="https://github.com/spring-projects/spring-boot/commits/master">Spring Boot</a>, or any repository managed by <a href="https://github.com/tpope/vim-pathogen/commits/master">Tim Pope</a>. The contributors to these repositories know that a well-crafted git commit message is the best way to communicate context about a change to fellow developers (and indeed to their future selves). A diff will tell you what changed, but only the commit message can properly tell you why.</p>
<p>Being known that, a project's long-term success rests (among other things) on its maintainability, reviewing others commits and pull requests is also the big reason that you should write great commit messages.</p>
<p>In the post, the author wants to address the most basic elements of keeping a healthy commit history: how to write an individual commit message. You can checkout the source article or just follow the <strong>seven rules</strong> below and you're on your way to committing like a pro</p>
<ol>
<li>Separate subject from body with a blank line</li>
<li>Limit the subject line to 50 characters</li>
<li>Capitalize the subject line</li>
<li>Do not end the subject line with a period</li>
<li>Use the imperative mood in the subject line</li>
<li>Wrap the body at 72 characters</li>
<li>Use the body to explain what and why vs. how</li>
</ol>
<h1 id="git-branching-model">Git branching model<a class="anchor" href="#git-branching-model" aria-label="Link to this section"></a></h1>
<h1 id="gitflow">Gitflow<a class="anchor" href="#gitflow" aria-label="Link to this section"></a></h1>
<p>Inspired by Vincent Driessen's branching model, git-flow are a set of git extensions to provide high-level repository operations for it. Git-flow is a merge based solution. It doesn't rebase feature branches.</p>
<ul>
<li>Checkout gitflow cheatsheet: <a href="http://danielkummer.github.io/git-flow-cheatsheet/">http://danielkummer.github.io/git-flow-cheatsheet/</a></li>
<li>Apps that support gitflow:
<ul>
<li>Source Tree: <a href="https://www.sourcetreeapp.com">https://www.sourcetreeapp.com</a></li>
<li>Git Tower: <a href="https://www.git-tower.com">https://www.git-tower.com</a></li>
</ul>
</li>
</ul>
<p>Note: Maybe after working for a long time with merge based Gitflow, you will find your git log a little bit confusing and look like that:</p>
<p><img src="https://truonghan.com/images/blog/2016-06-27-gitflow-mess.png" alt=""></p>
<p>Please take no worries about this, because there is still a part about <code>git rebase</code>. For now, you can continue to read the article <a href="http://endoflineblog.com/gitflow-considered-harmful">GitFlow considered harmful</a> to know more about the author issue.</p>
<h3 id="gitflow-pitfall">Gitflow pitfall<a class="anchor" href="#gitflow-pitfall" aria-label="Link to this section"></a></h3>
<ul>
<li>It makes the project's history completely unreadable with the <a href="https://stackoverflow.com/questions/9069061/what-is-the-difference-between-git-merge-and-git-merge-no-ff"><code>--no-ff</code> flag</a>. The --no-ff flag prevents git merge from executing a &quot;fast-forward&quot; if it detects that your current HEAD is an ancestor of the commit you're trying to merge. A fast-forward is when, instead of constructing a merge commit, git just moves your branch pointer to point at the incoming commit.</li>
<li>The master/develop split is redundant.</li>
<li>It's needlessly complex. And people make mistakes and break them by accident.</li>
</ul>
<h3 id="the-anti-gitflow">The Anti-gitflow<a class="anchor" href="#the-anti-gitflow" aria-label="Link to this section"></a></h3>
<ul>
<li>There is only one eternal branch - you can call it master, develop, current, next - whatever. I personally like &quot;master&quot;, and that's the name I'll use in the rest of the description, as it's convention by now in the Git world and immediately conveys its purpose.</li>
<li>All other branches (feature, release, hotfix, and whatever else you need) are temporary and only used as a convenience to share code with other developers and as a backup measure. They are always removed once the changes present on them land on master.</li>
<li>Features are integrated onto the master branch primarily in a way which keeps the history linear. You have a lot of leeway in how you want to enforce this. You can make it simply a convention that developers are encouraged, but not forced, to follow. On the other side of the spectrum, if you use something like Gerrit to manage your Git repositories (which I recommend, even if you don't practice code reviews - the permission system is fantastic, and if you ever decide you want code reviews, it'll be very easy to start doing them), you can set up permissions in such a way that actually forbids pushing merge commits to master, and that way ensure linear history.</li>
<li>Releases are done similarly to in GitFlow. You create a new branch for the release, branching off at the point in master that you decide has all the necessary features. From then on new work, aimed for the next release, is pushed to master as always, and any necessary changes are pushed to the release branch (in my opinion, it's an anti-pattern and a huge red flag if your release requires separate commits to work, but that's a topic for another article - for simplicity, let's assume you can't or don't want to change that). Finally, once the release is ready, you tag the top of the release branch. Then, because there is one eternal branch, there is only one way to get your release to be versioned permanently - and that is to merge the release branch into master and push that changed master. After that, all the changes that were made during the release are now part of master, and the release branch is deleted.</li>
<li>Hotfixes are very similar to releases, except you don't branch from an arbitrary commit on master, but from the release tag that you want to make the fix in. Again, work on master continues as always, and the necessary fixes are pushed to the hotfix branch. Once the fix is ready, the procedure is exactly the same as for a release - tag the top of the branch creating a new release, merge it into master, then delete the hotfix branch.</li>
</ul>
<h1 id="pull-request">Pull Request<a class="anchor" href="#pull-request" aria-label="Link to this section"></a></h1>
<p>Pull request is a feature that makes it easier for developers to collaborate. Pull request is a mechanism for a developer to notify team members that they have completed a feature. Some tricks to make Pull Requests more awesome for your project:</p>
<ul>
<li>Open a Pull Request as early as possible: Pull Requests are a great way to start a conversation of a feature, so start one as soon as possible- even before you are finished with the code. Your team can comment on the feature as it evolves, instead of providing all their feedback at the very end.</li>
<li>Pull Requests work branch to branch: No one has a fork of github/github. We make Pull Requests in the same repository by opening Pull Requests for branches.</li>
<li>A Pull Request doesn't have to be merged: Pull Requests are easy to make and a great way to get feedback and track progress on a branch. But some ideas don't make it. It's okay to close a Pull Request without merging; we do it all the time.</li>
</ul>
<p>Hint: Based on an article <a href="http://ben.balter.com/2015/12/08/types-of-pull-requests/">Type of Pull Request</a>, there are 6 types of PR. But <a href="http://ben.straub.cc/2015/04/02/wip-pull-request/"><code>WIP pattern</code></a> is the one that is using by lots of teams and companies. It follows the mantra of &quot;<strong>Open a Pull Request as early as possible</strong>&quot;.</p>
<h1 id="doing-code-review">Doing Code Review<a class="anchor" href="#doing-code-review" aria-label="Link to this section"></a></h1>
<blockquote>
<p>Source: <a href="https://github.com/thoughtbot/guides/tree/master/code-review">https://github.com/thoughtbot/guides/tree/master/code-review</a></p>
</blockquote>
<h3 id="everyone">Everyone<a class="anchor" href="#everyone" aria-label="Link to this section"></a></h3>
<ul>
<li>Accept that many programming decisions are opinions. Discuss tradeoffs, which you prefer, and reach a resolution quickly.</li>
<li>Ask questions; don't make demands. (&quot;What do you think about naming this :user_id?&quot;)</li>
<li>Ask for clarification. (&quot;I didn't understand. Can you clarify?&quot;)</li>
<li>Avoid selective ownership of code. (&quot;mine&quot;, &quot;not mine&quot;, &quot;yours&quot;)</li>
<li>Avoid using terms that could be seen as referring to personal traits. (&quot;dumb&quot;, &quot;stupid&quot;). Assume everyone is attractive, - intelligent, and well-meaning.</li>
<li>Be explicit. Remember people don't always understand your intentions online.</li>
<li>Be humble. (&quot;I'm not sure - let's look it up.&quot;)</li>
<li>Don't use hyperbole. (&quot;always&quot;, &quot;never&quot;, &quot;endlessly&quot;, &quot;nothing&quot;)</li>
<li>Don't use sarcasm.</li>
<li>Keep it real. If emoji, animated gifs, or humor aren't you, don't force them. If they are, use them with aplomb.</li>
<li>Talk synchronously (e.g. chat, screensharing, in person) if there are too many &quot;I didn't understand&quot; or &quot;Alternative solution:&quot; comments. Post a follow-up comment summarizing the discussion.</li>
</ul>
<h3 id="having-your-code-reviewed">Having Your Code Reviewed<a class="anchor" href="#having-your-code-reviewed" aria-label="Link to this section"></a></h3>
<ul>
<li>Be grateful for the reviewer's suggestions. (&quot;Good call. I'll make that change.&quot;)</li>
<li>Don't take it personally. The review is of the code, not you.</li>
<li>Explain why the code exists. (&quot;It's like that because of these reasons. Would it be more clear if I rename this class/file/- method/variable?&quot;)</li>
<li>Extract some changes and refactorings into future tickets/stories.</li>
<li>Link to the code review from the ticket/story. (&quot;Ready for review: <a href="https://github.com/organization/project/pull/1%22">https://github.com/organization/project/pull/1&quot;</a>)</li>
<li>Push commits based on earlier rounds of feedback as isolated commits to the branch. Do not squash until the branch is ready - to merge. Reviewers should be able to read individual updates based on their earlier feedback.</li>
<li>Seek to understand the reviewer's perspective.</li>
<li>Try to respond to every comment.</li>
<li>Wait to merge the branch until Continuous Integration tells you the test suite is green in the - branch.</li>
<li>Merge once you feel confident in the code and its impact on the project.</li>
</ul>
<h3 id="reviewing-code">Reviewing code<a class="anchor" href="#reviewing-code" aria-label="Link to this section"></a></h3>
<p>Understand why the change is necessary (fixes a bug, improves the user experience, refactors the existing code). Then:</p>
<ul>
<li>Communicate which ideas you feel strongly about and those you don't.</li>
<li>Identify ways to simplify the code while still solving the problem.</li>
<li>If discussions turn too philosophical or academic, move the discussion offline to a regular Friday afternoon technique - discussion. In the meantime, let the author make the final decision on alternative implementations.</li>
<li>Offer alternative implementations, but assume the author already considered them. (&quot;What do you think about using a custom - validator here?&quot;)</li>
<li>Seek to understand the author's perspective.</li>
<li>Sign off on the pull request with a 👍 or &quot;Ready to merge&quot; comment.</li>
</ul>
<h1 id="git-templates">Git templates<a class="anchor" href="#git-templates" aria-label="Link to this section"></a></h1>
<p>To make things easier, we have adopted Issue template and Pull Request template that we think they are great to help the team to improve the productivity</p>
<blockquote>
<p><a href="https://gist.githubusercontent.com/tieubao/2a9fc10be45a67ab315ef3f8ead6530d/raw/e99d04268bf83389f4f728f386f79d9da3132480/issue-template.md">https://gist.githubusercontent.com/tieubao/2a9fc10be45a67ab315ef3f8ead6530d/raw/e99d04268bf83389f4f728f386f79d9da3132480/issue-template.md</a></p>
</blockquote>
<p><a href="https://gist.github.com/tieubao/2a9fc10be45a67ab315ef3f8ead6530d">View the gist</a></p>
<blockquote>
<p><a href="https://gist.githubusercontent.com/tieubao/98a83f179d06fe3e33e4dfe4f1395904/raw/4cd5eaa3586503c9a89b7cac34886e2769db01d1/pr-template.md">https://gist.githubusercontent.com/tieubao/98a83f179d06fe3e33e4dfe4f1395904/raw/4cd5eaa3586503c9a89b7cac34886e2769db01d1/pr-template.md</a></p>
</blockquote>
<p><a href="https://gist.github.com/tieubao/98a83f179d06fe3e33e4dfe4f1395904">View the gist</a></p>
<br>
<hr>
<p><sup>And finally, Atlassian has a full tutorials from scratch, you can find it at: <a href="https://www.atlassian.com/git/tutorials/what-is-version-control">https://www.atlassian.com/git/tutorials/what-is-version-control</a><sup></p>
]]></content:encoded><media:thumbnail url="https://truonghan.com/og/git-practices-at-dwarves-foundation.png"/></item><item><title>Dwarves Foundation</title><link>https://truonghan.com/dwarves-foundation/</link><guid isPermaLink="true">https://truonghan.com/dwarves-foundation/</guid><pubDate>Fri, 29 May 2015 00:00:00 +0000</pubDate><description>Story about Dwarves Foundation</description><content:encoded><![CDATA[<p>After an interesting journey at <a href="https://truonghan.com/a/cau-chuyen-khoi-nghiep-tu-lifebox-den-cloudjay-p1/">CloudJay</a>, I took a short break for a month and feel that startup life is too hard for young people but it's really worth to be a part startup scene. Someone said, one year to live in a startup is better than five years working in the enterprise. And I think this is correct. I need to face more problems and be able to solve them myself to be more mature.</p>
<p><img src="https://truonghan.com/images/blog/2015-06-17-entrepreneur.jpg" alt=""></p>
<p>I also find that Google Golang is interesting and it might be useful for us later. I bet my investment on it and found <a href="https://golang.org.vn">Golang Vietnam</a>, a network of Golang Developers in Vietnam.</p>
<p>Based on some connections with friends in Singapore, I start asking people about other software businesses. And begin to find my first early member. I named a team <a href="https://dwarves.foundation">Dwarves Foundation</a>. Dwarf is the small and misshapen creatures in Norse Mythology that are wisdom, natural good at smithing, mining, and crafting. My plan is to provide Tech Partner service to ventures and startups.</p>
<p><img src="https://truonghan.com/images/blog/2015-06-17-dwarves.jpg" alt=""></p>
<h2 id="our-values">Our values<a class="anchor" href="#our-values" aria-label="Link to this section"></a></h2>
<p>We are a team of geeks, hustles and startup veterans. We live around the world but most of us based in Saigon, Vietnam. We are (forever) young and always eager to learn. We have passion and experience in making cool products, esp. cloud and mobile apps. We want to help entrepreneurs solve real problems using our approachs and solutions.</p>
<ul>
<li><strong>Customer first</strong>: We provide the service with high quality and no latency response time. Customers have to be happy.</li>
<li><strong>Geeky style</strong>: We can do anything, wear multiple hats, take multiple approachs from official to tricky way to achieve the goal.</li>
<li><strong>Teamwork</strong>: <strong><em>Synchronisation</em></strong> and <strong><em>Collaboration</em></strong> are the most important key things. You can read this topic: <a href="https://truonghan.com/it-is-hard-to-become-a-team-member/">It's hard to become a team member</a>. We apply agile process to keep our spirits stick together.</li>
<li><strong>Proactive</strong>: Pick your most productivity time to work. You need to figure out yourself what you want to do and what you want to contribute aligned to the team goal as a dwarf in the tribe. We are too busy to tell you.</li>
<li><strong>Growth and sharing</strong>: You can learn another skill from other teammates. We are happy to share it to you. And we believe that working hard and improving yourself everyday will make you paid back later. Read <a href="https://www.quora.com/How-can-I-motivate-myself-to-work-hard">this topic</a> to keep yourself motivated.</li>
</ul>
<h2 id="our-services">Our services<a class="anchor" href="#our-services" aria-label="Link to this section"></a></h2>
<ul>
<li>
<p><strong>Startup Factory</strong>: Our woodland is the trustworthy place where lots of ideas are validated, incubated, developed, iterated until product-market fit simultaneously and for successful ones, we turn them into new businesses. The formula repeats on its own. Moreover, we also provide startups with hands-on mentorship and seed investment programs, usually in exchange for equity.</p>
</li>
<li>
<p><strong>Development Lab</strong>: Our woodland is the trustworthy place where founders could come and acquire expertise for their business models. We offer a technical partnership that takes over their whole systems and applications from development, operation to maintenance stages. We are backed by a talented, ambitious and technology-heavy workforce with years of experience. Consequently, we guarantee world-class quality for delivered products across all platforms (Web, iOS, Android).</p>
</li>
<li>
<p><strong>Talent pool</strong>: Our woodland is the trustworthy bridge for the founders who are outsourcing products to connect with talented developers in Vietnam. We guarantee the most competitive prices among competitors in local market. It is the outcome of continuously building a wide network of trust between founders and talents. The price is worth it!</p>
</li>
</ul>
<iframe src="https://speakerdeck.com/player/1f4ffd51e8b049e6bce5dbdefad3786e" width="100%" height="420" frameborder="0" allowfullscreen loading="lazy"></iframe>
<h2 id="workflow-at-dwarves-foundation">Workflow at Dwarves Foundation<a class="anchor" href="#workflow-at-dwarves-foundation" aria-label="Link to this section"></a></h2>
<h3 id="for-project-management">For project management<a class="anchor" href="#for-project-management" aria-label="Link to this section"></a></h3>
<p>We use Trello with Kanban theory to organize tasks. Tasks will be flew from left list to right list. We also organize lists into Scrum steps. So this will be liked:</p>
<ul>
<li>Sprint Planning</li>
<li>Current Sprint</li>
<li>In Progress</li>
<li>Blocked</li>
<li>Testing</li>
<li>Bugs Report</li>
<li>Approved</li>
</ul>
<p><img src="https://truonghan.com/images/blog/2015-06-17-trello.png" alt=""></p>
<p>For each project, we will have <em>Sprint planning meeting</em> for Project Manager to explain project details to project members. User stories will be converted into cards or tickets and added to Trello list &gt; <strong>Sprint Planning</strong>.</p>
<p>Usually, one sprint is about 1 or 2 weeks, we have weekly meeting to break user stories into tasks and add them to list Current Sprint. It's also our target for this week, we have to get in done. This time, cards need to have requirement and non requirement specification. And sometimes, if this task has some special requirement that developers need to care about, the Project Manager need to add <strong>Acceptance Criteria</strong>.</p>
<p><img src="https://truonghan.com/images/blog/2015-06-17-criteria.png" alt=""></p>
<p>As a customer, you are granted permission to access Trello Board to give feedback or be a tester. And most of my customers are happy with it.</p>
<p>The template for this workflow can be found at <a href="https://trello.com/b/WYgcFBgl/sprint-template">https://trello.com/b/WYgcFBgl/sprint-template</a></p>
<h3 id="for-source-code-management">For source code management<a class="anchor" href="#for-source-code-management" aria-label="Link to this section"></a></h3>
<p>We use <code>git</code> and GitLab. It's more like GitHub but for enterprise. You can have one by downloading and deploying it yourself.</p>
<p>We apply <a href="http://danielkummer.github.io/git-flow-cheatsheet/">Gitflow</a> and <code>Merge Request</code> (some people may call it Pull Request) between developers to gain some benefits from it:</p>
<ul>
<li>Can deliver anytime because branch <code>master</code> is always stable.</li>
<li>Quality source code: always review it after it's merged into branch <code>develep</code></li>
<li>High efficiency collaboration between team members.</li>
</ul>
<p><img src="https://truonghan.com/images/blog/2015-06-17-gitflow.png" alt=""></p>
<p>There's another article about it on Atlassian site: <a href="https://www.atlassian.com/git/tutorials/comparing-workflows/feature-branch-workflow">https://www.atlassian.com/git/tutorials/comparing-workflows/feature-branch-workflow</a></p>
<p>Project Manager will be the one that init the repository on Git with some imporatant informations. For every ticket on Trello, developers need to create one issue in GitLab, create new feature branch and start the implementation on it. The <code>git commit</code> command must attach with one or more than one issue.</p>
<p><img src="https://truonghan.com/images/blog/2015-06-17-commit.png" alt=""></p>
<p>After that, one merge request will be created and the person who has responsibility to review this request need to check over the source code then <code>Accept</code> or <code>Reject</code> the <code>Merge Request</code>.</p>
<p>This card will be moved to list <code>Testing</code>, QC will be assigned to test this feature and so on.</p>
<h3 id="for-communication">For Communication<a class="anchor" href="#for-communication" aria-label="Link to this section"></a></h3>
<p>We use <a href="http://slack.com">Slack</a> as the main hub for all informations, from team meeting, hangout, manage tasks and source code ...</p>
<p><img src="https://truonghan.com/images/blog/2015-06-17-slack.png" alt=""></p>
<p>Slack support lots of methods to add integration. So the powerful it can be depends on what you integrate into it.</p>
<p>Finally, at the end of the day, we do daily meeting via hangout or face to face at the office to summary what we have done today and what we plan to do tomorrow. The project manager will be the one to write it down and make weekly report to customer.</p>
<p>All the processes out there are just to make the team stick together and development plan works. All team members need to understand their roles, team vision and know how to make things go on the right tracks.</p>
<p>Read more about <a href="https://truonghan.com/it-is-hard-to-become-a-team-member/">Teamwork</a>.</p>
<ul>
<li>About us: <a href="http://www.dwarvesf.com">http://www.dwarvesf.com</a></li>
<li>Github: <a href="https://github.com/dwarvesf">https://github.com/dwarvesf</a></li>
<li>Hiring: <a href="https://github.com/dwarvesf/WeAreHiring">https://github.com/dwarvesf/WeAreHiring</a></li>
</ul>
]]></content:encoded><media:thumbnail url="https://truonghan.com/og/dwarves-foundation.png"/></item><item><title>How We Split Equity Between Co-founders</title><link>https://truonghan.com/how-we-split-equity-between-cofounders/</link><guid isPermaLink="true">https://truonghan.com/how-we-split-equity-between-cofounders/</guid><pubDate>Mon, 09 Feb 2015 00:00:00 +0000</pubDate><description>It’s a very fair question to ask. My own co-founder Bryan and I asked the same question of ourselves when we launched our startup Embarke. Bryan and I had only known each other for a couple months, …</description><content:encoded><![CDATA[<p>It’s a very fair question to ask. My own co-founder Bryan and I asked the same question of ourselves when we launched our startup Embarke. Bryan and I had only known each other for a couple months, yet it was important to both of us to have an open and honest partnership from day one. We also knew the importance of figuring this stuff out early, while we could still look at it objectively.</p>
<p>The purpose of this post is to not just share with you what we did and the process we went through, but to also give you the tool we built to help us along the way.</p>
<h2 id="yes-it-can-be-uncomfortable-get-over-it">Yes, it can be uncomfortable. Get over it.<a class="anchor" href="#yes-it-can-be-uncomfortable-get-over-it" aria-label="Link to this section"></a></h2>
<p>I’ve seen far too many founders put this off because it’s an uncomfortable conversation. The reality is, if you’re not comfortable talking about uncomfortable things with your co-founder(s) ... you’ve got bigger problems. This relationship can’t be one of secrecy or procrastination. The nature of an entrepreneur is to take something on…head on…and right now. This equity split conversation is one of the first (of very many more) uncomfortable conversations that you’ll have with your co-founder, and it’s a great signal for how well you’ll work through future situations.</p>
<blockquote>
<p>This relationship can’t be one of secrecy or procrastination. The nature of an entrepreneur is to take something on--head on--and right now.</p>
</blockquote>
<p>So, we started digging and found countless resources talking about how equity should and shouldn’t be split among founders. The problem (with any advice) is, everyone’s got an opinion. Many of those opinions of course are based on their previous experience which may or may not be related to your situation, how far along the business is, and countless other variables. So, with all of these opinions and calculators out there, how did we come to terms with it?</p>
<p>We used them all.</p>
<p>Then we built our own tool to leverage them, and dove in.</p>
<h2 id="its-not-about-you">It’s not about you.<a class="anchor" href="#its-not-about-you" aria-label="Link to this section"></a></h2>
<p>It’s important to realize that this isn’t entirely about you. Through this process, we learned things about each other we didn’t know. We learned more about each other’s skill sets, our pasts, projects, accomplishments, and what we could each truly bring to the table. Sure, it’s a little about you. But it’s got to be about your co-founder(s) as well... so you need to have an open and receptive mind.</p>
<p>What was the end result for us? We were both very happy with the outcome, we learned a TON about each other that we didn’t know before, and walked away from the process more comfortable about working together than ever.</p>
<p>&quot;Alright already. Give me the tool!&quot;</p>
<p>Bryan and I each took our tool and started mapping out what aspects we felt were important to our business, and what we felt we each brought to the table. We did this independently of each other over a day or two. Then, we sat down and went through everything item-by-item. That is where the magic happens.</p>
<p><a href="https://al.bsharah.com/wp-content/uploads/2014/03/Embarke-CoFounder-Equity-Split-Template-2016-01.xlsx">The spreadsheet</a> has 5 different calculators in it, and a final box that averages everything out among all 5. Hopefully it’s intuitive enough for you to use. Any questions, hit me up in the comments!</p>
<h4 id="source-al-bsharah">Source: <a href="https://al.bsharah.com/co-founders-its-time-to-split-that-equity/">AL BSHARAH</a><a class="anchor" href="#source-al-bsharah" aria-label="Link to this section"></a></h4>
]]></content:encoded><media:thumbnail url="https://truonghan.com/og/how-we-split-equity-between-cofounders.png"/></item><item><title>Divide Founders' Stock Equally? Bad Idea</title><link>https://truonghan.com/divide-founders-stock-equally-bad-idea/</link><guid isPermaLink="true">https://truonghan.com/divide-founders-stock-equally-bad-idea/</guid><pubDate>Thu, 05 Feb 2015 00:00:00 +0000</pubDate><description>Many entrepreneurs think that each founder should own equal shares. This can be problematic, for many reasons.</description><content:encoded><![CDATA[<p>You and some co-venturers have come up with a business plan and an idea that you want to bring to fruition. How do you decide how much each person will own in the company?</p>
<p>Many entrepreneurs think that each founder should own equal shares. This can be problematic, for many reasons.</p>
<p>Here are a few things to think about when dividing ownership and founders stock.</p>
<ul>
<li><strong>Who is bringing the funds to the table?</strong> Money is important and without initial funding, you can't get a business off the ground. But is the bank-roller entitled to a larger proportion of the shares? The decision will depend on how much money he or she is putting in and what his or her additional roles are in the company.</li>
<li><strong>Whose idea was it?</strong> There is some value to put on the original idea. But simply coming up with the idea shouldn't give majority ownership to the founder. Was the idea patented? A patented idea carries more tangible weight than an abstract idea.</li>
<li><strong>Who will be doing the lion's share of the work?</strong> Set agreements on day one for who will be contributing work. One person may be contributing valuable managerial labor while another one might be designing important elements.</li>
<li><strong>What are your business projections?</strong> This is an important question because it helps you figure out who is in the company for the long haul. Which brings us to the final point.</li>
<li><strong>Have vesting restrictions.</strong> Your stock agreements should have provisions stating that no founder can pull out stock in the company until the lapse of a certain period of time. The last thing you want is for someone to put in the minimal and cash out early at the same proportion as the founders who are toiling away daily.</li>
</ul>
<p>Equity splitting between founders is very important and can be the source of much business litigation in the future. Remember that the startup equity sharing can also bring control issues and voting issues. You need to be careful who has control and who has voting rights in the company as well.</p>
<p>Also <strong>keep in mind that not everyone needs to be an &quot;owner.&quot;</strong> You don't want too many cooks in the kitchen. See if any of the initial contributors can have a different role, perhaps as a key employee with a different class of stock ownership.</p>
<h4 id="source-findlaw">Source: <a href="https://blogs.findlaw.com/free_enterprise/2012/04/equal-shares-for-founders-stock-bad-idea.html">FindLaw</a><a class="anchor" href="#source-findlaw" aria-label="Link to this section"></a></h4>
]]></content:encoded><media:thumbnail url="https://truonghan.com/og/divide-founders-stock-equally-bad-idea.png"/></item><item><title>Cảm giác khi trở thành CEO của một startup</title><link>https://truonghan.com/cam-giac-khi-tro-thanh-ceo-cua-mot-startup/</link><guid isPermaLink="true">https://truonghan.com/cam-giac-khi-tro-thanh-ceo-cua-mot-startup/</guid><pubDate>Thu, 22 Jan 2015 00:00:00 +0000</pubDate><description>Trích dẫn bài dịch "Cảm giác khi trở thành CEO của một startup" từ Quora.</description><content:encoded><![CDATA[<p>Cảm ơn dịch giả, anh Ninh vì bài dịch này. Xin được phép trích lại bài dịch của anh để mọi người có cơ hội hiểu thêm về những cảm giác mà một đồng sáng lập / key member (không chỉ là CEO) phải trải qua khi thực sự <em>dành hết sức của mình</em> cho công việc.</p>
<h4 id="nguồn-httpghichepninhnvcom"><strong>Nguồn:</strong> <a href="http://ghichep.ninhnv.com/2012/06/cam-giac-nao-khi-tham-gia-mot-cong-ty.html">http://ghichep.ninhnv.com/</a><a class="anchor" href="#ngu%e1%bb%93n-httpghichepninhnvcom" aria-label="Link to this section"></a></h4>
<p>Cảm giác thế nào khi bạn tham gia gây dựng một công ty startup? <a href="http://qr.ae/3YjjZ">Câu trả lời của Paul Dejoe trên Quora</a> với hơn 1400 votes (hiện tại là khoảng 5800 votes) đã tóm gọn gần như đầy đủ các cung bậc cảm xúc của những người làm startup.</p>
<hr>
<p>Cảm giác thế nào ư? Mỗi đêm bạn đều trằn trọc khó ngủ. Ngày cuối tuần chẳng còn nghĩa lý gì nữa. Các vòng đầu tư cũng chẳng giúp bạn nhẹ nhõm hơn được gì. Vì điều đó có nghĩa là càng có nhiều người trông cậy vào bạn mang lại lợi nhuận gấp 20 lần số tiền họ đã đầu tư vào bạn. Và thật là khó khăn để dứt bỏ các suy nghĩ về công ty. Ngay kể cả TV, phim ảnh hay các kỳ nghỉ lễ cũng trở nên nhàm chán khi mà tương lai, vận mệnh của công ty bạn đang ở đâu đó trong Inbox hay kết quả từ một bài test A/B bạn vừa quyết định chạy thử.</p>
<p>Bạn cảm thấy thật tội lỗi khi làm gì đó bạn thích ngoài công việc ra. Trải qua hàng năm trời vật lộn với cuộc chiến nội tâm, bạn mới nhận ra rằng, giữ cho cuộc sống &quot;cân bằng&quot; là cả một nghệ thuật. Nó quan trọng chẳng kém các kỹ năng bạn vốn hằng ao ước.</p>
<p>Bạn bắt đầu cảm thấy sáng tạo có giá trị dường nào và bạn buộc lòng phải suy nghĩ một cách khác biệt, không chỉ để thắng cuộc mà còn để nhìn ra cơ hội tốt nhất. Bạn nhận ra những ý tưởng tốt nhất nảy sinh không phải là khi bạn dán mắt vào màn hình. Những trò giải trí tiêu khiển nhẹ nhàng lành mạnh lại mang lại hiệu quả tức thì không ngờ đến.</p>
<p>Bạn bắt đầu thấy nể phục mấy con vịt. Quẫy đạp như điên dưới nước mà phía trên vẫn phải tìm cách nhẹ nhàng, ung dung khi mọi người ngước nhìn. Bạn thấm thía một điều rằng nếu bạn đánh mất đi cá tính, phong thái của mình, tức là bạn thua cuộc.</p>
<p>Bạn luôn luôn tự hỏi liệu mình có đang thay đổi thế giới ngày một tốt đẹp hơn? Hay người ta sống có tốt hơn từ ngày biết đến bạn không?</p>
<p>Bạn sáng tạo và khi bạn có một ý tưởng, dường như chẳng có gì có thể cản trở bạn cho đến khi bạn biến được nó thành sự thật. Bạn có cảm giác này đơn giản là vì bạn không thể làm khác đi được. Bạn bắt đầu cảm nhận cụm từ &quot;entrepreneur&quot; gần gũi hơn. Thật khó để giải thích cho bạn bè rằng không mạo hiểm như bạn chẳng qua là vì trong đầu họ chất đầy nỗi lo sợ không dám thúc đẩy bản thân hành động hay mang tất cả những gì đang có ra để chiến đấu cho dù thật bại luôn rình rập hàng ngày. Bạn bắt đầu biến các cuộc chuyện trò có liên quan thành câu chuyện làm thế nào có thể khai thác các cơ hội thành lợi ích nó mang lại. Những người gần gũi với bạn sẽ nhìn vào những gì bạn quan tâm theo một cách hoàn toàn khác bởi vì họ chẳng hiểu gì cả. Bạn không trách cứ gì họ. Họ không thể hiểu nếu họ chưa từng tự mình làm điều gì đó tương tự. Đó là lý do tại sao bạn cứ đi tìm cách thuyết phục người khác trở thành Entrepreneur như bạn. Bạn cảm thấy có được phần thưởng xứng đáng khi giúp đỡ các entrepreneurs khác. Đây là email của tôi: <a href="mailto:paul@ecquire.com">paul@ecquire.com</a>. Hãy cho tôi biết tôi có thể giúp gì cho bạn không.</p>
<p>Việc của bạn là tạo ra một tầm nhìn, một văn hóa, đưa đúng người lên chiếc xe cùng bạn và truyền cảm hứng cho họ. Khi bạn nhìn đồng đội của mình tin tưởng vào tầm nhìn đó, cũng nhiều chẳng kém gì bạn, và tin tưởng bạn sẽ luôn làm đúng, nó là cảm giác không thể diễn tả nổi. Nỗ lực làm việc hơn người theo cấp số mũ luôn khiến bạn ngạc nhiên. Đó là lý do tại sao tìm đúng đồng đội là điều khó khăn nhất nhưng cũng là điều quan trọng nhất. Bài học này sẽ ảnh hưởng đáng kể đến cuộc đời bạn sau này. Bạn sẽ không còn tìm cách giữ mọi thứ bình bình nữa vì bạn biết điều gì có thể xẩy ra khi bạn kiên trì mãnh liệt chiến đấu cho những gì tốt đẹp nhất và tìm thấy những cộng sự tuyệt vời. Sẽ chẳng còn là vấn đề khi thẳng thắn với mọi người mà không sợ làm tổn thương họ.</p>
<p>Bạn bắt đầu nhận ra rằng bạn là một lãnh đạo và bạn phải dẫn dắt hoặc bạn không thể theo được nữa. Bạn gạt đi những lời chào mời gạ bán vì bạn cần làm chủ cuộc chơi và bạn cảm thấy rằng các đồng đội của bạn là những người tuyệt vời nhất thế giới. Bạn có thể làm bất cứ thứ gì một cách chăm chỉ, cần mẫn. Từ bỏ không phải là một lựa chọn.</p>
<p><img src="https://truonghan.com/images/blog/2015-01-22-ceo-startup-quote.png" alt=""></p>
<p>Bạn phải sẵn sàng ngủ lại công ty và thấy thoải mái với việc đó. Bạn có thể cười vào mọi thứ vì khi bạn nghĩ về những điều tồi tệ hơn có thể xẩy đến với công ty mình, thực tế là nó hoàn toàn có thể sẽ xảy ra. Hãy tưởng tượng bạn làm một cái gì đó trong suốt 2 năm trời và sau đó đổ sông đổ bể cả chỉ vì một sai lầm nhỏ vào một ngày nào đó. Bạn nhận ra rằng trong khi đồng đội của bạn vui vẻ và lúc nào cũng có thể cười cợt rằng bạn sẽ không thể chết được, thì sự thật lại ngược lại: bạn học cách trân trọng từng chặng đường đi và hướng đến tương lai trong từng việc bạn làm hàng ngày ngay kể cả những lúc khó khăn nhất. Bạn sẽ nghe người ta nói rằng đừng để quá tụt dốc khi mọi thứ tồi tệ và cũng đừng quá phởn chí khi mọi sự đang tốt, và bạn thậm chí còn đi khuyên người khác những điều tương tự. Nhưng bạn sẽ không bao giờ chấp nhận sự lờ đờ, chẳng bứt lên mà cũng chẳng kém đi vì điều này không thú vị chút nào. Ngay kể cả khi mọi thử đảo lộn hoàn toàn những gì bạn dự định thì cũng là một điều đáng để ăn mừng. Tự nhiên bạn lại cứ thích đâm đầu vào các thử thách khó nhất chỉ vì có một mối liên hệ trực tiếp giữa độ khó của công việc với cảm giác sung sướng khi làm những điều tưởng chừng không thể.</p>
<p>Bạn nhận ra rằng sẽ thú vị hơn nhiều khi bạn không có tiền và tiền đôi khi lại là thứ tồi tệ nhất là khi lấy nó làm mục tiêu cá nhân. Nếu bạn đủ may mắn để cảm thấy như vậy, đó thật sự là một cảm giác gần với sự bình yên vì bạn nhận ra những gì bạn làm là thử thách và cũng là công việc bạn yêu quý. Tài sản của bạn là sự tự do, chủ động, trách nhiệm và sự công nhận. Những thứ này cũng sẽ là những tài sản giá trị mà những người bạn muốn chiến đấu cùng cũng tôn trọng.</p>
<p>Bạn cảm thấy như thể những ông bố bà mẹ với khách hàng và họ sẽ không bao giờ hiểu được bạn yêu họ như thế nào vì chính họ là minh chứng cho việc bạn không điên rồ. Bạn muôn ôm lấy tất cả bọn họ. Và họ có ý nghĩa như là cả thế giới đối với bạn.</p>
<p>Những gì học được phần lớn là từ sự tự trải nghiệm, nhiều hơn bất cứ yếu tố bẩm sinh nào của một Entrepreneur. Bạn nhận ra rằng bạn cần làm gì khi nhận những cú đấm tả tơi vào mặt rất rất nhiều lần. Bạn nhận ra rằng điều bạn cần làm là khi chẳng có ai để ý và chẳng có ai nhìn ra. Bạn nhận ra rằng bạn kém đủ mọi thứ, và thật may mắn nếu bạn giỏi một kỹ năng nào đó. Và thứ duy nhất tuyệt vời có lẽ được là chính mình. Đó cũng là lý do bạn không bao giờ thỏa hiệp. Bạn học được quyền lực và vị thế có thể gây nghiện đến như thế nào và vì sao nó lại có thể tha hóa nhiều người đến vậy.</p>
<p>Bạn sẽ vô cùng biết ơn những quãng thời gian tồi tệ. Trong các ngành nghề khác rất hiếm khi thấy cảnh này. Khi điều tồi tệ xẩy đến, sẽ có những người đến giúp bạn không ngần ngại. Tal Raviv, Gary Smith, Joe Reyes, Toan Dang, Vincent Cheung, Eric Elinow, Abe Marciano và những người khác nữa. Tôi nợ họ cả đời và không gì có thể đền đáp họ được cũng như họ cũng chẳng mong đợi sự đền đáp của tôi.</p>
<p>Bạn bắt đầu nhận ra trong đời, những người may mắn nhất thế giới sẽ chỉ một lần là một phần nào đó của điều gì đó vĩ đại. Biết điều này giúp bạn hợp lý hóa sự toàn tâm của bạn.</p>
<p>Tóm lại, tham gia một công ty startup rất thú vị. Mỗi ngày một khác và đều thú vị cả. Ngay cả khi đó là một ngày tồi tệ cũng vẫn rất thú vị. Biết được rằng các quyết định của bạn không chỉ tác động đến bạn mà còn rất nhiều người khác nữa sẽ là một gánh nặng bạn sẵn sàng chịu đựng hàng ngày còn hơn là không làm chủ được tương lai vận mệnh của mình. Đó là lý do tại sao tôi không thể làm gì khác.</p>
<p><img src="https://truonghan.com/images/blog/2015-01-22-paul-graham-startup-curve.png" alt=""></p>
<h1 id="original-post">Original Post<a class="anchor" href="#original-post" aria-label="Link to this section"></a></h1>
<hr>
<p>Very tough to sleep most nights of the week. Weekends don't mean anything to you anymore. Closing a round of financing is not a relief. It means more people are depending on you to turn their investment into 20 times what they gave you.</p>
<p>It's very difficult to &quot;turn it off&quot;. But at the same time, television, movies and vacations become so boring to you when your company's future might be sitting in your inbox or in the results of a new A/B test you decide to run.</p>
<p>You feel guilty when you're doing something you like doing outside of the company. Only through years of wrestling with this internal fight do you recognize how the word &quot;balance&quot; is an art that is just as important as any other skill set you could ever hope to have. You begin to see how valuable creativity is and that you must think differently not only to win, but to see the biggest opportunity. You recognize you get your best ideas when you're not staring at a screen. You see immediate returns on healthy distractions.</p>
<p>You start to respect the Duck. Paddle like hell under the water and be smooth and calm on top where everyone can see you. You learn the hard way that if you lose your cool you lose.</p>
<p>You always ask yourself if I am changing the World in a good way? Are people's lives better for having known me?</p>
<p>You are creative and when you have an idea it has no filter before it becomes a reality. This feeling is why you can't do anything else.</p>
<p>You start to see that the word &quot;entrepreneur&quot; is a personality. It's difficult to talk to your friends that are not risking the same things you are because they are content with not pushing themselves or putting it all out there in the public with the likelihood of failure staring at you everyday. You start to turn a lot of your conversations with relatives into how they might exploit opportunities for profit. Those close to you will view your focus as something completely different because they don't understand. You don't blame them. They can't understand if they haven't done it themselves. It's why you will gravitate towards other entrepreneurs. You will find reward in helping other entrepreneurs. This is my email: <a href="mailto:paul@ecquire.com">paul@ecquire.com</a>. Let me know if I can help you with anything.</p>
<p>Your job is to create a vision, a culture, to get the right people on the bus and to inspire. When you look around at a team that believes in the vision as much as you do and trusts you will do the right thing all the time, it's a feeling that can't be explained. The exponential productivity from great people will always amaze you. It's why finding the right team is the most difficult thing you will do but the most important. This learning will affect your life significantly. You will not settle for things anymore because you will see what is possible when you hold out for the best and push to find people that are the best. You don't have a problem anymore being honest with people about not cutting it.</p>
<p>You start to see that you're a leader and you have to lead or you can't be involved with it at all. You turn down acquisition offers because you need to run the show and you feel like your team is the best in the World and you can do anything with hard work. Quitting is not an option.</p>
<p>You have to be willing to sleep in your car and laugh about it. You have to be able to laugh at many things because when you think of the worse things in the World that could happen to your company, they will happen. Imagine working for something for two years and then have to throw it out completely because you see in one day that it's wrong. You realize that if your team is having fun and can always laugh that you won't die, and in fact, the opposite will happen: you will learn to love the journey and look forward to what you do everyday even at the lowest times. You'll hear not to get too low when things are bad and not to get too high when things are good and you'll even give that advice. But you'll never take it because being in the middle all the time isn't exciting and an even keel is never worth missing out on something worth celebrating. You'll become addicted to finding the hardest challenges because there's a direct relationship between how difficult something is and the euphoria of a feeling when you do the impossible.</p>
<p>You realize that it's much more fun when you didn't have money and that money might be the worse thing you could have as a personal goal. If you're lucky enough to genuinely feel this way, it is a surreal feeling that is the closest thing to peace because you realize it's the challenges and the work that you love. Your currencies are freedom, autonomy, responsibility and recognition. Those happen to be the same currencies of the people you want around you.</p>
<p>You feel like a parent to your customers in that they will never realize how much you love them and it is they who validate you are not crazy. You want to hug every one of them. They mean the World to you.</p>
<p>You learn the most about yourself more than any other vocation as an entrepreneur. You learn what you do when you get punched in the face many many times. You learn what you do when no one is looking and when no one would find out. You learn that you are bad at many things, lucky if you're good at a handful of things and the only thing you can ever be great at is being yourself which is why you can never compromise it. You learn how power and recognition can be addicting and see how it could corrupt so many.</p>
<p>You become incredibly grateful for the times that things were going as bad as they possibly could. Most people won't get to see this in any other calling. When things are really bad, there are people that come running to help and don't think twice about it. Tal Raviv, Gary Smith, Joe Reyes, Toan Dang, Vincent Cheung, Eric Elinow, Abe Marciano are some of them. I will forever be in their debt and I could never repay them nor would they want or expect to be repaid.</p>
<p>You begin to realize that in life, the luckiest people in the World only get one shot at being a part of something great. Knowing this helps you make sense of your commitment.</p>
<p>Of all the things said though, it's exciting. Every day is different and so exciting. Even when it's bad it's exciting. Knowing that your decisions will not only affect you but many others is a weight that I would rather have any day than the weight of not controlling my future. That's why I could not do anything else.</p>
<h4 id="source-quora"><strong>Source:</strong> <a href="http://qr.ae/3YjjZ">Quora</a><a class="anchor" href="#source-quora" aria-label="Link to this section"></a></h4>
]]></content:encoded><media:thumbnail url="https://truonghan.com/og/cam-giac-khi-tro-thanh-ceo-cua-mot-startup.png"/></item><item><title>How to Become a Senior Software Engineer</title><link>https://truonghan.com/how-to-become-a-senior-software-engineer/</link><guid isPermaLink="true">https://truonghan.com/how-to-become-a-senior-software-engineer/</guid><pubDate>Tue, 30 Dec 2014 00:00:00 +0000</pubDate><description>Wonder how to become a senior software engineer? I collected some helpful ideas.</description><content:encoded><![CDATA[<p>To summary, you can call yourself a Senior when:</p>
<ul>
<li>You can handle the entire software development life cycle, end to end</li>
<li>You lead others, or others look to you for guidance.</li>
<li>You can self manage your projects</li>
</ul>
<p>Software development is a curious creature unlike other fields. Sometimes, a fresh punk out of college can run circles around veterans who have 20+ years of &quot;experience&quot;. Programming is a bizarre world where code is king.</p>
<p>Some achieve the above in 2 years or less, others take 10 years. <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<hr>
<h3 id="how">How<a class="anchor" href="#how" aria-label="Link to this section"></a></h3>
<p>Software engineering is hard. The industry is always changing and you need to keep up with it. You need to put constant effort and time. There is always more to learn than you can possibly absorb. Many engineers who finish a several month boot camp don’t realize how much more they need to learn on top of it. No worries, there is a clear path to becoming a senior software engineer in almost any area of software development. <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<h3 id="step-0-choose-your-path-and-stick-to-it">Step 0: choose your path and stick to it!<a class="anchor" href="#step-0-choose-your-path-and-stick-to-it" aria-label="Link to this section"></a></h3>
<blockquote>
<p>do one thing and do it well Doug McIlroy : Unix Philosophy</p>
</blockquote>
<p>I can’t emphasize this enough! It is really important to choose a specialty and a technical stack that you will be working with. Sticking to it and not getting distracted is what will make you a specialist. It’s an important choice to make. Here is how to do it:</p>
<ul>
<li>Choose your specialty. It’s what you want to do. For example: Web development or Mobile development or DevOps etc.</li>
<li>Choose a technical stack within your specialty. For example: if you chose web development, you’ll need to choose between Ruby on Rails, Python and Django, JavaScript and MEAN etc. If you chose mobile, you’ll need to choose between iOS or Android, etc.</li>
<li>Stick to it and master it.</li>
</ul>
<p>I’m not saying that you shouldn’t get familiar with anything else outside of your main specialty. A well rounded T-Shaped Software Engineer knows about other aspects of programming too. Although he/she knows their main specialty extremely well. Now that you’ve chosen your specialty, you have a lot of things to learn. Take your time. It will take you several years to read all these books and practice necessary skills. Get through this challenge one skill at the time. I’ll be using the following technical stacks going forward: Ruby on Rails for web development, iOS for mobile development. It is just because I have experience working with it. I can speak to it personally. At the end of the says it’s just a matter of taste. If you choose some other stack, you’ll need to find different books.</p>
<h3 id="step-1-lay-the-foundations">Step 1. Lay the foundations<a class="anchor" href="#step-1-lay-the-foundations" aria-label="Link to this section"></a></h3>
<p>Read The Pragmatic Programmer book. It’s a good starting point on the path to software mastery. The book summarizes best high level practices. It’s language / technology agnostic. It doesn’t matter which tech stack you choose, the main principles stay the same.</p>
<h3 id="step-2-master-your-programming-language">Step 2. Master your programming language<a class="anchor" href="#step-2-master-your-programming-language" aria-label="Link to this section"></a></h3>
<p>You’ll be using many languages day to day depending on the technology stack of your choice. Although, majority of your code will be written in one language. For example: a Ruby on Rails developer uses Ruby primarily. An AngularJS developer uses JavaScript most of the time. Identifying the language of your choice and mastering it is crucial for your career. Learn it, master it! You’ll be using this language every day. Choose your language and read an advance book on it.</p>
<ul>
<li>For a Ruby Developer - Programming</li>
<li>Ruby For an iOS Developer - The Swift Programming Language</li>
</ul>
<h3 id="step-3-learn-your-framework">Step 3. Learn your framework<a class="anchor" href="#step-3-learn-your-framework" aria-label="Link to this section"></a></h3>
<p>Choose one, learn it:</p>
<ul>
<li>For a Rails Developer - Rails Tutorial by Michael Hartl</li>
<li>For an iOS Developer - iOS programming: The Big Nerd Ranch Guide</li>
</ul>
<h3 id="step-4-become-a-master-of-your-text-editor--ide">Step 4. Become a master of your text editor / IDE<a class="anchor" href="#step-4-become-a-master-of-your-text-editor--ide" aria-label="Link to this section"></a></h3>
<p>Again. Choose one, master it and stick to it. If you chose VIM, stay with VIM and become a master of it.</p>
<h3 id="step-5-use-your-version-control-system-like-a-pro">Step 5. Use your Version Control System like a pro<a class="anchor" href="#step-5-use-your-version-control-system-like-a-pro" aria-label="Link to this section"></a></h3>
<p>I recommend using Git. Here is the resource that I like: Pro git - free online ebook with a complete breakdown on every aspect of git</p>
<h3 id="step-6-commit-to-doing-test-driven-development">Step 6. Commit to doing Test Driven Development<a class="anchor" href="#step-6-commit-to-doing-test-driven-development" aria-label="Link to this section"></a></h3>
<p>Choose a book specific to your tech stack, learn it. Get into a habit of doing it every day. Quality code comes with writing tests.</p>
<ul>
<li>For a Ruby on Rails Developer - Rails Test Prescriptions</li>
<li>For an iOS Developer - Test Driven iOS Development</li>
</ul>
<h3 id="step-7-refactor-as-a-habit">Step 7. Refactor as a habit<a class="anchor" href="#step-7-refactor-as-a-habit" aria-label="Link to this section"></a></h3>
<p>The codebase grows in complexity as you add more functionality. In order to keep your codebase manageable you need to refactor. If you don’t refactor as you go, you’ll soon come to a sad place where even a small change is hard, bugs are inevitable and nobody wants to deal with your messy codebase. Just make your life easier. Don’t delay your refactor. Get into a habit of refactoring. Here is a good book on how to do it: Refactoring: Improving the Design of Existing Code by Martin Fowler</p>
<h3 id="step-8-learn-software-architecture">Step 8. Learn software architecture<a class="anchor" href="#step-8-learn-software-architecture" aria-label="Link to this section"></a></h3>
<p>Depending on the stack you choose, find a good book on architecture. Here is one that I recommend by Martin Fowler. It’s called Patterns of Enterprise Application Architecture</p>
<h3 id="step-9-unleash-the-power-of-the-command-line">Step 9. Unleash the power of the command line<a class="anchor" href="#step-9-unleash-the-power-of-the-command-line" aria-label="Link to this section"></a></h3>
<p>The command line can feel intimidating at first. Although, as soon as you learn how to use it, you’ll become a lot more efficient in performing basic tasks. That alone makes you a better developer since you are able to achieve more in less time. Then you can take it to the next level. Use shell to automate common tasks and build your dotfiles. If you’re working on a Mac or any Linux distribution, learn shell. Here is a good book on it by Mark Bates: Conquering the Command Line</p>
<h3 id="step-10-code-code-code">Step 10. Code code code!<a class="anchor" href="#step-10-code-code-code" aria-label="Link to this section"></a></h3>
<p>Every single day. Like in any sport, you need to practice what you learned in order for knowledge to stick.</p>
<h3 id="takeaway">Takeaway<a class="anchor" href="#takeaway" aria-label="Link to this section"></a></h3>
<p>Yes, becoming Senior Software Engineer is hard. It takes a lot of time and commitment. Different people learn at different speed, but on average, it takes about 10 years to become a solid senior dev. It’s a big investment in time for a big return. Software Engineers have the bright future. They are in high demand on the job market. Their salary grows proportionally with their experience. It’s a rapidly growing and a relatively new field. Every day of a software engineer is a challenge. A challenge of changing people’s lives. Go ahead and take on this challenge by becoming a Senior Software Engineer one skill at the time.</p>
<hr>
<p>And to me, title is just title. It can be your past experience. It can be your new goal. But you need to prove that you're worth it.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="http://programmers.stackexchange.com/questions/25564/when-should-you-call-yourself-a-senior-developer">When should you call yourself a senior developer?</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><a href="http://engineering.awesomenesstv.com/post/106521664643/10-steps-to-become-a-senior-software-engineer">Steps to become a senior software engineer</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded><media:thumbnail url="https://truonghan.com/og/how-to-become-a-senior-software-engineer.png"/></item><item><title>It Is Hard to Become a Team Member</title><link>https://truonghan.com/it-is-hard-to-become-a-team-member/</link><guid isPermaLink="true">https://truonghan.com/it-is-hard-to-become-a-team-member/</guid><pubDate>Fri, 05 Dec 2014 00:00:00 +0000</pubDate><description>You think it's easy to work with someone or to work in a team? Trust me. It is really hard to become a team member.</description><content:encoded><![CDATA[<p>I was working at a tech startup called CloudJay (former Lifebox JSC). Our team has nine including engineers and business development. As we've known, there are many factors to make a successful business and teamwork is one of them. In my opinion, this is the most important.</p>
<p><img src="https://truonghan.com/images/blog/2014-02-05-teamwork-01.jpg" alt=""></p>
<p>The startup, in common sense, is founded to solve people problems, real problems; to provide works for society, also to bring values and make money. That is a thing that people called the vision, team vision, company vision. As the team member, you should understand it clearly, love it or hate it and follow it, make it your life, your working purpose. A team does not need employees but contributors.</p>
<p>The team still need heroes, and there should be an 'I' in the team but <strong>the only way to make the boat go fast is perfect coordination</strong>, it also means perfect teamwork and synchronization. The skill set is essential, but it isn't important as vision and coordination. The 'I' must be unselfish and think about the team first. As a team member, it’s important to remember that no one is perfect; everyone has something that they could improve upon, including yourself. So, be supportive. Offer constructive criticism. Listen, listen, listen. People who always talk, but never listen will be stunted in growth. Remember that everyone knows something you don’t know.</p>
<p><img src="https://truonghan.com/images/blog/2014-02-05-teamwork-02.jpg" alt=""></p>
<p>We effectively have an environment in which competition and collaboration co-exist and feed off each other. Competition, synchronization or collaborating perfectly; make the boat go fast is always the main point. Remember, even if you work harder than anyone else, <strong>if you lose synchronization, you slow the boat down.</strong></p>
<figure class="code"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">*They told me there is no “I” in the team.*
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">I am an athlete.
</span></span><span class="line"><span class="cl">I am an individual.
</span></span><span class="line"><span class="cl">I am strong.
</span></span><span class="line"><span class="cl">I am weak.
</span></span><span class="line"><span class="cl">I have desires, hopes, and dreams.
</span></span><span class="line"><span class="cl">I have goals.
</span></span><span class="line"><span class="cl">I have fears.
</span></span><span class="line"><span class="cl">As a team, my opponent will never see my weaknesses.
</span></span><span class="line"><span class="cl">Only my strength, never my fears, only my goals as they unfold before them.
</span></span><span class="line"><span class="cl">I am not afraid that my team will see my fears, my hopes, dreams, or desires. 
</span></span><span class="line"><span class="cl">I trust them to an unlimited level.
</span></span><span class="line"><span class="cl">I am not afraid that my team will see my faults, because
</span></span><span class="line"><span class="cl">With them I can overcome my faults, with them I am 
</span></span><span class="line"><span class="cl">fearless, with them I have hopes and dreams.
</span></span><span class="line"><span class="cl">With my team I am not weak, I do not have the strength
</span></span><span class="line"><span class="cl">of one athlete, but many, combined, focused, 
</span></span><span class="line"><span class="cl">And dangerous to my un-united opponent.
</span></span><span class="line"><span class="cl">I become my team, and my team becomes me.
</span></span><span class="line"><span class="cl">I do not judge, and I am not judged.
</span></span><span class="line"><span class="cl">I have a goal, and the team has a goal.
</span></span><span class="line"><span class="cl">The team goal is my goal.
</span></span><span class="line"><span class="cl">All that matters is that the team reaches its goal.
</span></span><span class="line"><span class="cl">They always told me there is no “I” in “Team”.
</span></span><span class="line"><span class="cl">They were wrong.
</span></span><span class="line"><span class="cl">I am the team.
</span></span><span class="line"><span class="cl">I became the team.
</span></span><span class="line"><span class="cl">The team became me.
</span></span><span class="line"><span class="cl">The team becomes an entity unto itself.
</span></span><span class="line"><span class="cl">The team is strong, creative, compassionate, caring,
</span></span><span class="line"><span class="cl">Authoritative, disciplined.
</span></span><span class="line"><span class="cl">The team absorbs “I”, and then there is “I” in “Team”.
</span></span><span class="line"><span class="cl">The “I” becomes part of something much more powerful.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">They were right there is no “I” in “Team”,
</span></span><span class="line"><span class="cl">But there is “Team” in me.</span></span></code></pre></div></figure>
]]></content:encoded><media:thumbnail url="https://truonghan.com/og/it-is-hard-to-become-a-team-member.png"/></item></channel></rss>