<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Fault Lines Blog</title>
        <link>http://localhost:3001/stories</link>
        <description>Fault Lines Blog</description>
        <lastBuildDate>Sun, 09 Aug 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <copyright>Copyright © 2026 Fault Lines</copyright>
        <item>
            <title><![CDATA[The export that passed every permission check]]></title>
            <link>http://localhost:3001/stories/the-export-that-passed-every-permission-check</link>
            <guid>http://localhost:3001/stories/the-export-that-passed-every-permission-check</guid>
            <pubDate>Sun, 09 Aug 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A fictional production incident about composed agent authority, effect-bound approval, and releasing exact bytes to the right person.]]></description>
            <content:encoded><![CDATA[<div class="storyStatus"><span>Architecture story</span><span class="storyLevelChip storyLevelChip--professional" title="Defend a cross-boundary production decision under ambiguity." aria-label="Professional level: Defend a cross-boundary production decision under ambiguity.">Professional</span>A fictional production incident about composed agent authority, effect-bound approval, and releasing exact bytes to the right person.</div>
<aside class="storyLearningGoals" aria-labelledby="story-learning-goals-title"><div><span>Before the incident</span><h2 id="story-learning-goals-title">What you'll learn from this story</h2></div><ul><li><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg><span>Explain why several individually least-privileged tools can compose into a capability that none of them exposes alone.</span></li><li><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg><span>Separate a model’s semantic record match from the authoritative evidence required to release that record to a person.</span></li><li><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg><span>Bind human approval to an exact release proposal, immutable package version, recipient, purpose, source evidence, redaction policy, delivery channel, and expiry.</span></li><li><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg><span>Place execution-time source-to-sink checks at a deterministic release boundary and evaluate prohibited bytes separately from task completion.</span></li></ul></aside>
<p><strong>Harborlight People</strong> is a fictional workforce-management SaaS company. Its employer customers use the platform for payroll, scheduling, employee relations, support, and access administration. They can also configure a workflow through which current and former workers request copies of their personal data.</p>
<p>The employer customer defines the request policy, the source categories in scope, and who may make the final release decision. Harborlight operates the software and a managed privacy-operations team under that customer-defined policy. The process in this story is Harborlight's fictional design, not a universal legal requirement.</p>
<p>Harborlight handled about 3,800 worker-data requests per month. Most looked simple from the request portal: prove who you are, describe the employment period, wait while the records are assembled, and collect a package from an authenticated portal.</p>
<p>The work behind that path was not a single database query.</p>
<p>A worker might have a legal name, a preferred name, a former surname, several email addresses, and more than one worker identifier after a rehire or contractor conversion. A support ticket might mention a person without being about that person. An attachment might be linked to a ticket whose requester and uploader were neither its subject. A fixed join could recover the obvious records. It could not reliably resolve every free-text reference, copied attachment, or derived record.</p>
<p>That was why Harborlight built <strong>Lumen</strong>, a bounded AI privacy worker. Lumen read case-scoped records through narrow adapters, resolved aliases and free-text references, proposed which records concerned the requester, explained the evidence behind each proposal, and suggested redactions. Lumen could produce candidates. It did not own a person's stable identity, record-subject truth, case policy, approval, delivery credential, or the release of bytes.</p>
<p><strong>Mara Chen</strong>, the Senior Privacy Operations Specialist responsible for defining request scope and resolving ambiguous record matches, supervised the managed workflow. Under each employer's policy, she could approve an exact release.</p>
<p>The distinction sounded conservative enough: Lumen proposed; Harborlight decided.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-a-verified-worker-normally-receives-an-export">How a verified worker normally receives an export<a href="http://localhost:3001/stories/the-export-that-passed-every-permission-check#how-a-verified-worker-normally-receives-an-export" class="hash-link" aria-label="Direct link to How a verified worker normally receives an export" title="Direct link to How a verified worker normally receives an export" translate="no">​</a></h2>
<div class="failoverTopology failoverTopology--loading">Loading interactive story context…</div>
<details class="conceptPanel"><summary><strong>Concepts in this story</strong><span class="conceptPanel__count">5<!-- --> concepts</span></summary><div class="conceptPanel__list"><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#security.privilege-composition">Privilege composition</a><span>Identity and security · Capability containment</span></div><p>The broader capability created when several individually permitted operations can be chained. Local least-privilege checks do not establish that the complete path or resulting effect is authorized.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#security.source-to-sink-authorization">Source-to-sink authorization</a><span>Identity and security · Information-flow control</span></div><p>An authorization decision over the complete path from protected input through transformations to a consequential destination. It checks whether this data may reach this sink for the current purpose, not only whether each intermediate tool call is allowed.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#data.record-subject-binding">Record-subject binding</a><span>Data systems · Provenance and identity</span></div><p>The supported relationship between a versioned record and the person or entity it concerns. A model may propose this relationship from semantic evidence, but release authority requires corroborating source metadata, policy, or explicit review.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#agents.effect-bound-approval">Effect-bound approval</a><span>Agentic systems · Approval binding</span></div><p>Human authorization tied to one canonical proposed effect: its target, parameters, evidence, constraints, destination, and expiry. A material change creates a new approval question instead of inheriting consent from the surrounding task.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#agents.execution-time-authorization">Execution-time authorization</a><span>Agentic systems · Effect authorization</span></div><p>Re-evaluating current principal, workload, resource, purpose, policy, approval, and effect immediately before a consequential action. Permission observed during planning or approval is historical evidence, not automatic present authority.</p></div></div></details>
<p>The normal journey crossed four responsibility groups:</p>
<ol>
<li class="">A worker authenticated in Harborlight's request portal and opened a case.</li>
<li class="">The case service bound the employer tenant, requester, configured purpose, approved source categories, employment period, and current case status. Mara reviewed that scope and permitted candidate discovery.</li>
<li class="">Case-scoped adapters retrieved records and versioned metadata. Lumen proposed record inclusion and redactions from stable identifiers, aliases, references, attachments, and contextual evidence.</li>
<li class="">Redaction and export services built a candidate package inside an encrypted case staging area.</li>
<li class="">A delivery service published an approved immutable package version into a protected worker-portal namespace.</li>
<li class="">The portal reauthorized each read, and the worker received the package or an explicit ambiguity or delay notice.</li>
</ol>
<p>Source permission answered whether a service could read a tenant, record class, or time range. Lumen's semantic judgment answered whether a record was a plausible candidate. Release authorization was a different question: whether the exact bytes in the package could go to the exact current recipient for this case and purpose.</p>
<p>The effect boundary was the component that made those bytes available outside Harborlight's internal review path.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-design-the-team-trusted">The design the team trusted<a href="http://localhost:3001/stories/the-export-that-passed-every-permission-check#the-design-the-team-trusted" class="hash-link" aria-label="Direct link to The design the team trusted" title="Direct link to The design the team trusted" translate="no">​</a></h2>
<p>Harborlight's implementation looked like a careful separation of duties.</p>
<p>The request portal and privacy-operations console were React applications. A NestJS service called <code>privacy-case-api</code> stored cases in PostgreSQL. A Python <code>lumen-worker</code> ran in Temporal workflows on Amazon EKS. Case-scoped adapters could read only the approved tenant and source categories in the employer's HR database, payroll service, scheduling service, support index, access logs, and versioned S3 attachments.</p>
<p>A controlled PostgreSQL identity graph mapped typed, versioned identifiers across those systems. It treated <code>primary_subject</code>, <code>mentioned_person</code>, <code>requester</code>, <code>author</code>, <code>approver</code>, and <code>manager_of_subject</code> as different relationships instead of collapsing them into one <code>person_id</code>.</p>
<p>Separate containers ran the redaction service and <code>export-builder</code>. Candidate packages went into encrypted S3 prefixes isolated by case. A notification service could contact only the verified address or portal subject on that case. OpenTelemetry traces and an append-only event store recorded the workflow.</p>
<p>Each component enforced a narrow policy:</p>
<ul>
<li class="">Search could read only the current employer tenant, the approved source categories, and the approved employment period.</li>
<li class="">Lumen received case-scoped source access, never a general publication or portal credential.</li>
<li class="">Redaction ran under a versioned policy.</li>
<li class=""><code>export-builder</code> could write only inside the requester's isolated case prefix.</li>
<li class="">Notification could contact only the verified recipient bound to the case.</li>
<li class="">The portal authenticated the recipient and checked access to the case package.</li>
</ul>
<p>The initial human approval covered the purpose and discovery scope: which tenant, sources, and period the workflow could inspect. Lumen's selected candidate set then flowed into redaction and packaging as if selection had also established eligibility for release.</p>
<p>The package record stored the case and recipient. It did not store an authoritative record-subject decision for every included source. The delivery service checked the case and recipient before publication, but it did not ask which evidence allowed every source record's bytes to reach that recipient.</p>
<p>The team's mental model was reasonable:</p>
<blockquote>
<p>Source read, package staging, and protected publication are each least-privileged. The workflow assembled from them is therefore least-privileged.</p>
</blockquote>
<p>There was no broad email tool, public object-storage credential, or model-controlled portal session to make the flaw look obvious.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="tuesday-at-0900">Tuesday at 09:00<a href="http://localhost:3001/stories/the-export-that-passed-every-permission-check#tuesday-at-0900" class="hash-link" aria-label="Direct link to Tuesday at 09:00" title="Direct link to Tuesday at 09:00" translate="no">​</a></h2>
<p>At <strong>09:00</strong>, <strong>Jana Li</strong>, a verified former worker and the intended recipient, authenticated, completed Harborlight's fictional identity-verification flow, and opened case <code>PR-2048</code> for her former employment.</p>
<p>At <strong>09:18</strong>, Mara approved candidate discovery across payroll, scheduling, support, and access logs during Jana's employment period. She approved a search scope, not an exact package that did not yet exist.</p>
<p>At <strong>09:31</strong>, Lumen loaded Jana's stable HR person ID, former worker ID, legal name, former surname, email history, and known source relationships. It began candidate discovery through the scoped adapters.</p>
<p>At <strong>09:44</strong>, an allowed support ticket produced an ambiguous lead. The ticket contained “J. Li” and linked a versioned salary-adjustment attachment. It was in the correct employer tenant, an approved source category, and the relevant period. Its structured requester and attachment uploader did not identify the person the attachment described.</p>
<p>The attachment concerned <strong>Jian Li</strong>, a different current employee in the same tenant with a different stable person ID and worker ID.</p>
<p>At <strong>09:46</strong>, Lumen combined name similarity, topic, date, and organizational context. It proposed that the attachment concerned Jana. That was a plausible semantic mistake, not an invented identity merge and not a bypass of a source control.</p>
<p>At <strong>09:49</strong>, the support adapter correctly returned the allowed ticket and attachment. The redaction service removed bank details under its configured rule. It retained Jian's salary and performance explanation because the service assumed that candidate eligibility had already been established upstream.</p>
<p>At <strong>09:55</strong>, <code>export-builder</code> correctly wrote the package into Jana's encrypted case prefix. The package stayed inside the correct tenant and case.</p>
<p>At <strong>10:02</strong>, the delivery service confirmed that <code>PR-2048</code> was open and that Jana's portal subject was its recipient. It published the package and sent a notification. Every local policy decision returned <code>allow</code>.</p>
<p>At <strong>10:11</strong>, Jana authenticated in the portal and opened the exact package prepared for her case. Alongside her own records, she found Jian's salary-adjustment attachment. The bank details were gone. Jian's salary and performance explanation were not.</p>
<p>Jana reported it.</p>
<p>The package had reached the correct verified person. It contained the wrong person's sensitive employment data.</p>
<section class="failoverSequence harborlightIncidentSequence" aria-labelledby="harborlight-sequence-title"><header class="failoverSequence__header"><span class="failoverSequence__kicker">Incident diagnosis · six local decisions</span><h3 id="harborlight-sequence-title">Six green checks, one unauthorized path</h3><p>Read downward. Every check is genuinely valid; each card also names the release fact that the check never established.</p></header><ol class="harborlightIncidentSequence__list"><li class="harborlightIncidentSequence__check"><span class="harborlightIncidentSequence__number" aria-hidden="true">1</span><article><div class="harborlightIncidentSequence__checkHeader"><span class="harborlightIncidentSequence__allow"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg> Local check passed</span><time>09:00</time></div><div class="harborlightIncidentSequence__title"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-key-round" aria-hidden="true"><path d="M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z"></path><circle cx="16.5" cy="7.5" r=".5" fill="currentColor"></circle></svg><h4>Requester identity: allow</h4></div><p>Jana authenticates, completes Harborlight’s fictional verification flow, and opens case PR-2048.</p><dl><div><dt>It proves</dt><dd>Jana may receive an authorized package.</dd></div><div><dt>It does not prove</dt><dd>It does not prove every candidate record concerns Jana.</dd></div></dl></article></li><li class="harborlightIncidentSequence__check"><span class="harborlightIncidentSequence__number" aria-hidden="true">2</span><article><div class="harborlightIncidentSequence__checkHeader"><span class="harborlightIncidentSequence__allow"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg> Local check passed</span><time>09:18</time></div><div class="harborlightIncidentSequence__title"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-shield-check" aria-hidden="true"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"></path><path d="m9 12 2 2 4-4"></path></svg><h4>Case scope: allow</h4></div><p>Mara permits discovery across the employer, sources, purpose, and employment period recorded on the case.</p><dl><div><dt>It proves</dt><dd>The search territory and purpose are approved.</dd></div><div><dt>It does not prove</dt><dd>It does not approve an exact future release object.</dd></div></dl></article></li><li class="harborlightIncidentSequence__check"><span class="harborlightIncidentSequence__number" aria-hidden="true">3</span><article><div class="harborlightIncidentSequence__checkHeader"><span class="harborlightIncidentSequence__allow"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg> Local check passed</span><time>09:46</time></div><div class="harborlightIncidentSequence__title"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-search" aria-hidden="true"><path d="m21 21-4.34-4.34"></path><circle cx="11" cy="11" r="8"></circle></svg><h4>Search authorization: allow</h4></div><p>Lumen proposes a salary-adjustment attachment after matching “J. Li,” topic, date, and organization context.</p><dl><div><dt>It proves</dt><dd>The workflow may read the attachment as a candidate.</dd></div><div><dt>It does not prove</dt><dd>It does not prove Jian’s bytes may be released to Jana.</dd></div></dl></article></li><li class="harborlightIncidentSequence__check"><span class="harborlightIncidentSequence__number" aria-hidden="true">4</span><article><div class="harborlightIncidentSequence__checkHeader"><span class="harborlightIncidentSequence__allow"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg> Local check passed</span><time>09:49</time></div><div class="harborlightIncidentSequence__title"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-file-check-corner" aria-hidden="true"><path d="M10.5 22H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v6"></path><path d="M14 2v5a1 1 0 0 0 1 1h5"></path><path d="m14 20 2 2 4-4"></path></svg><h4>Redaction policy: allow</h4></div><p>The configured transformation removes bank details but retains Jian’s salary and performance explanation.</p><dl><div><dt>It proves</dt><dd>The configured sensitive fields were removed.</dd></div><div><dt>It does not prove</dt><dd>It does not prove the remaining record belongs in Jana’s case.</dd></div></dl></article></li><li class="harborlightIncidentSequence__check"><span class="harborlightIncidentSequence__number" aria-hidden="true">5</span><article><div class="harborlightIncidentSequence__checkHeader"><span class="harborlightIncidentSequence__allow"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg> Local check passed</span><time>09:55</time></div><div class="harborlightIncidentSequence__title"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-folder-lock" aria-hidden="true"><rect width="8" height="5" x="14" y="17" rx="1"></rect><path d="M10 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v2.5"></path><path d="M20 17v-2a2 2 0 1 0-4 0v2"></path></svg><h4>Export storage: allow</h4></div><p>The export builder writes the candidate package only to Jana’s encrypted case prefix.</p><dl><div><dt>It proves</dt><dd>The package stayed inside the intended case storage boundary.</dd></div><div><dt>It does not prove</dt><dd>It does not prove every included byte is authorized.</dd></div></dl></article></li><li class="harborlightIncidentSequence__check"><span class="harborlightIncidentSequence__number" aria-hidden="true">6</span><article><div class="harborlightIncidentSequence__checkHeader"><span class="harborlightIncidentSequence__allow"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg> Local check passed</span><time>10:11</time></div><div class="harborlightIncidentSequence__title"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-archive" aria-hidden="true"><rect width="20" height="5" x="2" y="3" rx="1"></rect><path d="M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8"></path><path d="M10 12h4"></path></svg><h4>Protected publication and access: allow</h4></div><p>At 10:02 the service publishes to Jana’s protected portal namespace; at 10:11 Jana authenticates and the portal serves the package.</p><dl><div><dt>It proves</dt><dd>The package was bound to Jana’s case, and Jana was authenticated when its bytes were served.</dd></div><div><dt>It does not prove</dt><dd>It does not prove the package content was safe for Jana.</dd></div></dl></article></li><li class="harborlightIncidentSequence__outcome"><span class="harborlightIncidentSequence__number" aria-hidden="true">7</span><article><div class="harborlightIncidentSequence__outcomeLabel"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-alert" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" x2="12" y1="8" y2="12"></line><line x1="12" x2="12.01" y1="16" y2="16"></line></svg> Unauthorized outcome</div><h4>Exact bytes about Jian reach Jana</h4><p>The verified recipient receives another worker’s salary and performance explanation. The workflow can show six allows, but no owned relationship that authorized those bytes for Jana.</p></article></li></ol><p class="failoverSequence__takeaway"><strong>The missing control:</strong> permission to read a candidate never became source-to-sink authorization to release its exact bytes.</p></section>
<p>The ordered path was all green until the outcome:</p>
<ol>
<li class="">Jana's identity was valid. That proved she could receive a package authorized for Jana; it did not prove every candidate concerned her.</li>
<li class="">The case scope was valid. That proved the tenant, sources, period, and purpose were approved; it did not define an exact release object.</li>
<li class="">Search authorization was valid. That proved Lumen could read the attachment as a candidate; it did not permit Jian's bytes to go to Jana.</li>
<li class="">Redaction was locally valid. That proved configured fields were removed; it did not prove the remaining record belonged in Jana's case.</li>
<li class="">Export storage was valid. That proved the package stayed in Jana's isolated case area; it did not authorize every byte inside it.</li>
<li class="">Portal authorization was valid. That proved Jana opened the case package; it did not prove that package was safe for Jana.</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="six-green-checks-and-one-unowned-relationship">Six green checks and one unowned relationship<a href="http://localhost:3001/stories/the-export-that-passed-every-permission-check#six-green-checks-and-one-unowned-relationship" class="hash-link" aria-label="Direct link to Six green checks and one unowned relationship" title="Direct link to Six green checks and one unowned relationship" translate="no">​</a></h2>
<p>At <strong>10:36</strong>, <strong>Elias Ward</strong>, the Staff Security Engineer responsible for Harborlight's data-release control plane, joined the source, Lumen, redaction, export, and portal logs.</p>
<p>The trace was unusually complete. It proved which workload had called each adapter, which policy version had run, where the package had been written, and which authenticated portal subject had opened it.</p>
<p>It could not answer the question Elias cared about:</p>
<blockquote>
<p>Which authoritative evidence permitted bytes derived from Jian's attachment to become available to Jana?</p>
</blockquote>
<p>No component owned that relationship.</p>
<p>The first proposed fix was to improve entity resolution. The team could add more identifiers, tune Lumen's prompt and evaluator, raise a confidence threshold, and retrieve metadata before content. All were useful improvements. None repaired the authority boundary.</p>
<p>Suppose Lumen reached 99.9% accuracy on Harborlight's evaluation set. The remaining rare false inclusion would still travel automatically from allowed search, through allowed redaction and allowed case storage, to an allowed recipient. Model accuracy changed how often the system proposed a dangerous package. It did not change who was allowed to release it.</p>
<p>Elias redrew the workflow as a graph of reachable effects instead of a list of tool scopes.</p>
<span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_dqldeh_">Privilege composition<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span>
<p>Search alone could reveal records only inside Harborlight's case workflow. <code>export-builder</code> alone could stage only a case package. Protected publication and portal serving alone could expose only the package bound to Jana's case. Composed as <strong>read → stage → publish/serve</strong>, the three narrow permissions created a broader capability: select protected source bytes and make them available to a person. Notification changed discoverability; because email was only a locator, it was not the content-release permission.</p>
<p>No local credential had widened. The reachable path had.</p>
<p>That made the next control question unavoidable.</p>
<span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_eqldeh_">Source-to-sink authorization<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span>
<p>Harborlight had authorized each edge locally. It had not authorized the whole path from a protected source record, through transformations and staging, to the exact released bytes and authenticated recipient.</p>
<p>The rule Elias wrote at the top of the incident document was shorter:</p>
<blockquote>
<p>Permission to find a record is not permission to release it, and three narrow permissions do not make a narrow path.</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-candidate-was-not-a-subject-decision">A candidate was not a subject decision<a href="http://localhost:3001/stories/the-export-that-passed-every-permission-check#a-candidate-was-not-a-subject-decision" class="hash-link" aria-label="Direct link to A candidate was not a subject decision" title="Direct link to A candidate was not a subject decision" translate="no">​</a></h2>
<p>The Jana/Jian collision exposed another overloaded word: “match.”</p>
<p>Lumen had matched text and context to Jana's case. It had not changed Harborlight's stable person identifiers. Nor had it established that the attachment's role relative to Jana was <code>primary_subject</code> rather than <code>mentioned_person</code>, <code>requester</code>, or no supported relationship at all.</p>
<span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_gildeh_">Record-subject binding<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span>
<p>For structured records, Harborlight could consume typed, versioned relationships from the domain that owned them. Core HR owned its person and worker identifiers. A payroll domain could own an explicit payroll-subject relationship. Object storage owned an attachment's bytes and version, but its folder did not establish who the attachment was about.</p>
<p>For unstructured “aboutness,” no universal automatic table existed. Lumen could supply a candidate relationship, confidence, rationale, conflicts, and stable source reference. Deterministic policy could reject known tenant or person contradictions. Where evidence remained uncertain, the workflow needed an explicit <code>AMBIGUOUS_REVIEW</code> state and Mara needed the canonical metadata, not only Lumen's summary.</p>
<p>Names, email addresses, and similarity scores remained evidence. They did not become stable subject keys because they looked convincing.</p>
<p>Harborlight also stopped reducing eligibility to <code>SUPPORTED</code> or <code>reviewerApproved</code>. A relationship assertion described how evidence connected a record to a person; it did not itself decide what this case and purpose could release. A separate typed <code>ReleaseEligibility</code> recorded the policy rule and version, <code>ALLOW | DENY | UNKNOWN | CONFLICT</code>, reviewer and rationale when needed, and whether the decision covered a whole immutable record, named fields or byte ranges, or one exact transformed output. Multi-subject records could therefore be partly eligible. Generated filenames, cover pages, summaries, archive metadata, previews, and redacted output carried lineage and eligibility too.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="approval-had-to-name-the-effect">Approval had to name the effect<a href="http://localhost:3001/stories/the-export-that-passed-every-permission-check#approval-had-to-name-the-effect" class="hash-link" aria-label="Direct link to Approval had to name the effect" title="Direct link to Approval had to name the effect" translate="no">​</a></h2>
<p>Mara had approved a legitimate case and reasonable discovery scope. Asking her to “review more carefully” would repeat the same defect if the review screen still described a general intent instead of an exact future release.</p>
<p>Harborlight stopped calling several different records one “envelope.” It defined them separately:</p>
<ul>
<li class=""><strong><code>ReleaseProposalV1</code></strong> was an immutable body containing schema and canonicalization versions; case, tenant, requester, recipient, purpose, and source scope; exact source and evidence versions; relationship assertions; typed, scoped release-eligibility decisions; transformation and redaction versions; immutable package object version, payload digest, length, media type, and package-format version; destination, channel, and validity constraints. It contained no digest, approver, receipt, nonce, or mutable status.</li>
<li class="">The canonicalizer encoded that body as UTF-8 using <a href="https://www.rfc-editor.org/rfc/rfc8785.html" target="_blank" rel="noopener noreferrer" class="">RFC 8785 JSON canonicalization</a> plus Harborlight's <code>ReleaseProposalV1</code> schema. SHA-256 covered those exact canonical bytes. The schema defined omitted and default values; unknown fields, duplicate keys, aliases, non-canonical numbers, and unsupported versions failed closed.</li>
<li class="">A separate <strong><code>ApprovalReceipt</code></strong> named the proposal digest, schema and canonicalization versions, approver and role, approval time, expiry, one-use nonce, and decision. The approval store separately owned current <code>APPROVED</code>, <code>REVOKED</code>, <code>SUPERSEDED</code>, <code>RELEASING</code>, or <code>SPENT</code> state and its revision.</li>
<li class="">A <strong><code>ReleaseReceipt</code></strong> later recorded the operation, proposal and payload digests, immutable object version, gateway workload, task and tenant, employer-policy decision and revision, configured recipient subject, destination, publication event and result, and time. An <strong><code>AccessReceipt</code></strong> recorded the authenticated portal subject, proposal and payload digests, exact object version, served range and byte count, and whether access completed or stopped.</li>
</ul>
<p>The reviewer UI and gateway deserialized the same canonical proposal bytes. Mara saw the exact package content—or a complete, byte-accounted rendering of it—rather than only Lumen's summary. Her approval attested to the separate proposal digest.</p>
<span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_j2ldeh_">Effect-bound approval<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span>
<p>Approval of “process Jana's request” no longer authorized any package the workflow later assembled. It authorized one proposal, one evidence snapshot, one immutable package version, one recipient, one purpose, one redaction implementation, one delivery channel, and one validity window.</p>
<p>Adding or replacing a record was material. So was changing a source version, subject decision, redaction policy, payload, recipient, destination, or delivery channel. A material change moved the case to <code>REAUTHORIZE</code> unless an explicit reviewed policy said that particular change was immaterial.</p>
<p>The digest protected equality after Harborlight defined and canonicalized the fields. It did not prove that a relationship inside the proposal was true. A perfectly hashed mistake was still a mistake.</p>
<p>The proposal, complete review rendering, receipts, and preview also became sensitive derived artifacts. Harborlight had to restrict and monitor access to them, encrypt them, apply configured retention and deletion, and avoid copying raw records into logs just to make an audit convenient.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-release-boundary-in-code">The release boundary in code<a href="http://localhost:3001/stories/the-export-that-passed-every-permission-check#the-release-boundary-in-code" class="hash-link" aria-label="Direct link to The release boundary in code" title="Direct link to The release boundary in code" translate="no">​</a></h2>
<p>Before the incident, publication code treated storage inside an approved case as sufficient evidence:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Tempting: the package inherits release authority from its case.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">publish</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">packageId</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> session</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">PortalSession</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> pkg </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> packages</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">load</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">packageId</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> cases</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">requireOpenForRecipient</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pkg</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">caseId</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> session</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">subject</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> delivery</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">publish</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pkg</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">stagedObject</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> session</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">subject</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Correct: reserve current authority, then publish one immutable version.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">publishApproved</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cmd</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">ReleaseCommand</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> caller</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">WorkloadIdentity</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> workloads</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">requireReleaseWorker</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">caller</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cmd</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">taskId</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cmd</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">tenantId</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> proposalBytes </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> proposals</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">loadCanonicalBytes</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cmd</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">proposalId</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> proposal </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token maybe-class-name">ReleaseProposalV1</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">decodeStrict</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">proposalBytes</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> canonicalBytes </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">canonicalizeV1</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">proposal</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> proposalDigest </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">sha256</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">canonicalBytes</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> approval </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> approvals</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">loadReceipt</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">proposalDigest</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> approvalState </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> approvals</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">loadCurrent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">approval</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">nonce</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> currentCase </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> cases</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">load</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">proposal</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">caseId</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> currentPolicy </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> policies</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">load</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">proposal</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">tenantId</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> object </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> packages</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">headVersion</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">proposal</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">package</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">objectVersion</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">bytes</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">equal</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">proposalBytes</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> canonicalBytes</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      approval</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">proposalDigest</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!==</span><span class="token plain"> proposalDigest </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      approval</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">schemaVersion</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!==</span><span class="token plain"> proposal</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">schemaVersion</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      approval</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">canonicalizationVersion</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!==</span><span class="token plain"> proposal</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">canonicalizationVersion</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      approval</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">decision</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!==</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'ALLOW'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> approval</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">expiresAt</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;=</span><span class="token plain"> clock</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">now</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      approvalState</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">status</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!==</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'APPROVED'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token operator" style="color:#393A34">!</span><span class="token plain">currentPolicy</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">canApprove</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">approval</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">approver</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> approval</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">role</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      currentCase</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">recipientSubject</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!==</span><span class="token plain"> proposal</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">recipientSubject</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      object</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">version</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!==</span><span class="token plain"> proposal</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">package</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">objectVersion</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      object</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">digest</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!==</span><span class="token plain"> proposal</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">package</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">digest</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      object</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">length</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!==</span><span class="token plain"> proposal</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">package</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">length</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      object</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">mediaType</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!==</span><span class="token plain"> proposal</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">package</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">mediaType</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      object</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">formatVersion</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!==</span><span class="token plain"> proposal</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">package</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">formatVersion</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> cases</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">moveTo</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'REAUTHORIZE'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">proposal</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">outputs</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">some</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">o</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> o</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">eligibility</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">decision</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!==</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'ALLOW'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> cases</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">moveTo</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'AMBIGUOUS_REVIEW'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// One transaction authenticates policy authority, compares every current</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// revision, and moves this one-use nonce APPROVED -&gt; RELEASING.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> reservation </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> releaseLedger</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">reserve</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    operationId</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> cmd</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">operationId</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> taskId</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> cmd</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">taskId</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> caller</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    proposalDigest</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> approval</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> approvalState</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> currentCase</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> currentPolicy</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    objectVersion</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> object</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">version</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> destination</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> proposal</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">destination</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">reservation</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">acquired</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> cases</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">moveTo</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">reservation</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">nextState</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// operationId makes publication retry-idempotent. Readers remain denied</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// until the ledger and ReleaseReceipt say this exact version is RELEASED.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">try</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> publication </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> delivery</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">publishImmutableVersion</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      operationId</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> cmd</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">operationId</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> objectVersion</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> object</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">version</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      proposalDigest</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> recipientSubject</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> proposal</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">recipientSubject</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      destination</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> proposal</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">destination</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> releaseLedger</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">confirmReleased</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      reservation</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token maybe-class-name">ReleaseReceipt</span><span class="token punctuation" style="color:#393A34">.</span><span class="token keyword module" style="color:#00009f">from</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">publication</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> reconciliation</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">enqueue</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cmd</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">operationId</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> cases</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">moveTo</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'RELEASE_PENDING'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">serve</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">grantId</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> session</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">PortalSession</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> range</span><span class="token operator" style="color:#393A34">?</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">ByteRange</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> grant </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> releaseLedger</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">loadReleased</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">grantId</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> currentCase </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> cases</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">load</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">grant</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">caseId</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">session</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">subject</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!==</span><span class="token plain"> grant</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">recipientSubject</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      session</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">subject</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!==</span><span class="token plain"> currentCase</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">recipientSubject</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">throw</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name maybe-class-name">Deny</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> portalPolicy</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">authorizeCurrentRead</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">grant</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> currentCase</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> session</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> range</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> portal</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">streamExactVersion</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">grant</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">objectVersion</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> range</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token maybe-class-name">AccessReceipt</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">record</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>The reservation was the authorization point, not a check followed by an unrelated write. In one authoritative operation-ledger transaction it authenticated the Harborlight release workload, preserved the employer-policy context, compared current case, policy, approval, recipient, evidence, redaction, destination, channel, and object revisions, and conditionally spent the single-use nonce. The workload acted under the employer's configured policy; it was not Jana, it did not receive her token, and this design had no further multi-hop delegated actor.</p>
<p>Object storage and the ledger did not share a transaction. Publication therefore used the operation ID as an idempotency key and the exact immutable object version named by the proposal. A timeout, successful object write followed by receipt failure, or uncertain response moved the case durably to <code>RELEASE_PENDING</code>. Reconciliation checked object visibility, serving-key/grant state, and any <code>ReleaseReceipt</code> before completing or retrying; it never blindly republished. Readers remained denied until the control plane marked that exact version <code>RELEASED</code>.</p>
<p>Harborlight chose <strong>protected publication</strong>. At publication, the gateway placed one immutable encrypted version into a protected namespace and emitted a <code>ReleaseReceipt</code> bound to the configured recipient subject. That was not proof Jana had authenticated or read it. The portal rechecked the current release grant, case, recipient binding, and revocation state on every full or range read, served only that object version without archive regeneration, re-encoding, redirects, share links, or CDN substitution, and emitted an <code>AccessReceipt</code> for actual bytes served. Notification was only a locator.</p>
<span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_lqldeh_">Execution-time authorization<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span>
<p>Approval could be correct when Mara clicked and stale when publication began. The atomic reservation closed replay and the authorize-then-publish race for current control-plane state; idempotent publication and reconciliation closed uncertain completion. Revocation stopped future reads or destroyed the serving key for an unretrieved package, but Harborlight did not claim it could retract bytes already served. Partial and interrupted ranges remained in access receipts.</p>
<p>A fail-closed gateway introduced an availability dependency. Missing policy, lineage, labels, proposal bytes, receipt, current approval state, exact object version, or gateway availability moved the case durably to <code>AMBIGUOUS_REVIEW</code>, <code>REAUTHORIZE</code>, or <code>RELEASE_PENDING</code>. No operator or service could fall back to the old delivery path or publish directly from staging. A future break-glass route would have to be a separate, dual-controlled effect with its own exact proposal, short-lived authority, receipts, and post-event review.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="two-designs-harborlight-could-defend">Two designs Harborlight could defend<a href="http://localhost:3001/stories/the-export-that-passed-every-permission-check#two-designs-harborlight-could-defend" class="hash-link" aria-label="Direct link to Two designs Harborlight could defend" title="Direct link to Two designs Harborlight could defend" translate="no">​</a></h2>
<p>The incident did not produce a perfect identity graph or perfect data-flow labels. It left Harborlight with two credible architectures and different operating costs.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="design-a-protected-publication">Design A: protected publication<a href="http://localhost:3001/stories/the-export-that-passed-every-permission-check#design-a-protected-publication" class="hash-link" aria-label="Direct link to Design A: protected publication" title="Direct link to Design A: protected publication" translate="no">​</a></h3>
<p>In the protected-publication design, source adapters returned versioned records, typed relationships where available, and provenance metadata. Lumen proposed candidates, relationship types, evidence, conflicts, and redactions. Deterministic policy created case- and purpose-specific <code>ReleaseEligibility</code> decisions, rejected known contradictions, and sent <code>UNKNOWN</code> or <code>CONFLICT</code> to <code>AMBIGUOUS_REVIEW</code>; no confidence threshold converted a proposal into authority.</p>
<p>The versioned transformer created one write-once package and complete output lineage, including generated metadata. The manifest service created <code>ReleaseProposalV1</code>. Mara reviewed the unresolved evidence, exact output, and proposal. The approval store issued a separate expiring receipt for its digest.</p>
<p>At publication, an authenticated release workload supplied task and operation IDs. <code>data-release-gateway</code> recomputed equality, verified the exact object version, rechecked current case state, employer policy, scoped eligibility, source and redaction versions, recipient, destination, channel, expiry, and current approval state, then atomically reserved the nonce and revisions. It alone held publication authority. Idempotent publication and reconciliation handled the non-transactional object-store boundary; the portal separately authorized and receipted exact-version access.</p>
<p>This design concentrated enforcement immediately before final release. It was legible to reviewers and could be introduced without teaching every source system a complete shared label language.</p>
<p>It was not free. Harborlight's fictional operating model had previously completed 86% of requests without ambiguous-match review. The corrected path sent an additional 9% of all cases to review and raised median completion time from 11 to 19 hours. Source changes caused reapproval. Proposals, renderings, receipts, and previews added sensitive data to govern. The gateway and reviewer queue became availability and capacity dependencies.</p>
<p>Those numbers describe Harborlight, not an industry baseline.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="design-b-closed-world-continuous-information-flow-enforcement">Design B: closed-world continuous information-flow enforcement<a href="http://localhost:3001/stories/the-export-that-passed-every-permission-check#design-b-closed-world-continuous-information-flow-enforcement" class="hash-link" aria-label="Direct link to Design B: closed-world continuous information-flow enforcement" title="Direct link to Design B: closed-world continuous information-flow enforcement" translate="no">​</a></h3>
<p>In the continuous design, source adapters attached tenant, subject or relationship, classification, purpose, version, and provenance labels wherever authoritative evidence existed. Lumen could propose a new relationship, but the proposal stayed explicitly untrusted until policy or review resolved it.</p>
<p>Harborlight first declared a closed world of typed transformations and mandatory mediated sinks: redaction, OCR, summaries, archives, filenames, previews, logs, caches, notification bodies, protected publication, and portal service. These interfaces preserved input references, labels, transformation and policy versions, and output digests; uninstrumented connectors and outputs were denied. <code>UNKNOWN</code> was absorbing across joins and transformations until an explicit deterministic rule or scoped human decision resolved it. Lumen could not declassify its own output.</p>
<p>A deterministic controller evaluated every edge inside that declared boundary. Covert and unmodeled channels remained outside the claim, not silently assumed safe. The final exact-byte gateway from Design A still bound the immutable object version, recipient, case, purpose, destination, and expiry.</p>
<p>This design could reject a forbidden path earlier and maintain policy across more than one sink. Its cost moved outward. Every connector and transformation needed a shared relationship and provenance model. Labels were incomplete. Propagation through unstructured content was difficult. Conservative rules rejected some valid records. Connector integration, false rejection, and policy maintenance became permanent operating work.</p>
<p>Continuous enforcement did not promise perfect taint tracking or stand alone as a universal solution. It made unknowns explicit, denied new connectors without the contract, and added earlier defense around the same protected-publication boundary.</p>
<p>Both designs kept an ambiguity state. A system that silently omitted every uncertain record might prevent this disclosure while still failing the product obligation to deliver a complete, usable export or a clear ambiguity response.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-tests-that-separated-a-completed-task-from-a-safe-one">The tests that separated a completed task from a safe one<a href="http://localhost:3001/stories/the-export-that-passed-every-permission-check#the-tests-that-separated-a-completed-task-from-a-safe-one" class="hash-link" aria-label="Direct link to The tests that separated a completed task from a safe one" title="Direct link to The tests that separated a completed task from a safe one" translate="no">​</a></h2>
<p>Before the incident, Harborlight's strongest visible success signal was a completed package opened by the verified requester. The incident satisfied that signal.</p>
<p>The new evaluation harness graded eight questions independently for each trial:</p>
<ol>
<li class=""><strong>Candidate retrieval:</strong> Did Lumen propose the relevant records without drowning review in false candidates?</li>
<li class=""><strong>Scoped release eligibility:</strong> Did current evidence and policy permit every included whole record, field/range, transformed output, and generated metadata item for this requester, case, and purpose?</li>
<li class=""><strong>Redaction correctness:</strong> Did prohibited fields or third-party content remain in an otherwise eligible record?</li>
<li class=""><strong>Approval equality:</strong> Did the executed proposal, immutable object version, recipient, purpose, source versions, redaction policy, channel, and expiry equal what Mara approved?</li>
<li class=""><strong>Execution-time authorization:</strong> Were the workload, case, policy, recipient, eligibility, proposal, current approval state, and single-use reservation still valid at publication?</li>
<li class=""><strong>Prohibited-byte outcome:</strong> Did any unauthorized byte become available through a package, preview, archive, cache, link, or alternate channel?</li>
<li class=""><strong>Task outcome:</strong> Did the requester receive a complete, accessible export or an explicit ambiguity response?</li>
<li class=""><strong>Evidence reconstruction:</strong> Could Harborlight join source version, Lumen rationale, eligibility, policy or review, approved proposal, publication receipt, served bytes, and authenticated recipient without retaining bearer tokens or complete exports in logs?</li>
</ol>
<p>Task completion never compensated for a prohibited-byte failure.</p>
<p>The fault matrix included namesakes, former names, rehires, reassigned email, quoted names, multi-subject and partially releasable records, reviewer error, OCR collisions, conflicting attachment metadata, and generated filenames, cover pages, summaries, and archive metadata. It changed source objects, scoped eligibility, redaction versions, recipients, purposes, channels, policy versions, approval status, and expiry after review.</p>
<p>Canonicalization property tests permuted field order and exercised omitted/default values, Unicode, aliases, duplicate and unknown keys, schema and canonicalization versions, and every material field. Release tests used a receipt for the wrong proposal, replayed or concurrently reserved nonces, wrong-task/tenant/deployment/purpose workloads, cancellation after approval, policy change during reservation, publication-before-pickup revocation, recipient rebinding, duplicate workers, receipt-write failure, and retry after uncertain publication.</p>
<p>Serving tests substituted an object under the same case and package ID, rolled back versions, regenerated archives, introduced post-check transformations and stale CDN/cache content, and issued partial and range requests. Closed-world tests omitted labels, joined multiple inputs, and exercised OCR, summaries, previews, logs, archives, notification-derived output, and newly deployed connectors. Dependency timeouts, stale control-plane caches, partial outages, and operator bypass attempts had to land in the named fail-closed states without making the old delivery path reachable.</p>
<p>The harness tested <code>ALLOW</code>, <code>DENY</code>, and explicit review outcomes. It also failed designs that denied every ambiguous record and quietly called the export complete.</p>
<p>Harborlight measured review rate and queue age, decision dwell time, false inclusion and exclusion, safe completion latency, reapproval, gateway availability, temporary-artifact lifecycle, audit reconstruction, accessible completion, cost per safely accepted export, bulk/override rate, reversal, and reviewer disagreement. Admission control protected a staffed queue-age objective, with forecasted capacity and a second-review escalation pool. Saturation returned an explicit delay or ambiguity notice; it did not raise a confidence threshold, enable bulk approval, or reopen direct staging publication. Retrieval quality remained a model metric. It no longer stood in for release safety.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-question-elias-used-next">The question Elias used next<a href="http://localhost:3001/stories/the-export-that-passed-every-permission-check#the-question-elias-used-next" class="hash-link" aria-label="Direct link to The question Elias used next" title="Direct link to The question Elias used next" translate="no">​</a></h2>
<p>Elias tested whether the team had learned a privacy-export trick or a general authority model.</p>
<p>A manufacturing company's procurement agent could read purchase orders and supplier master data, write a settlement proposal to an internal case store, and ask a payment service to schedule an approved settlement. Each tool was narrow.</p>
<p>While resolving an invoice discrepancy, the model linked a valid factoring notice to the wrong supplier account. It proposed the correct amount to a different payee. A human had approved only “resolve the invoice discrepancy.”</p>
<p>Elias asked four questions:</p>
<ol>
<li class="">What composed path turned read and proposal permissions into a payment capability?</li>
<li class="">Which authoritative evidence had to bind supplier, invoice, and payee?</li>
<li class="">What exact proposal had to bind supplier, invoice, amount, currency, payee, evidence versions, purpose, and expiry?</li>
<li class="">Which deterministic settlement boundary had to stop or execute the payment after checking the current world?</li>
</ol>
<p>“Narrow the scopes,” “ask accounts payable,” and “improve the match” were useful supporting actions. None answered all four questions.</p>
<p>The surface had changed from employment data to supplier settlement. The rule had not:</p>
<blockquote>
<p>A model may propose the path. Only a deterministic boundary with current, effect-specific authority may open the sink.</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="where-release-authority-lives">Where release authority lives<a href="http://localhost:3001/stories/the-export-that-passed-every-permission-check#where-release-authority-lives" class="hash-link" aria-label="Direct link to Where release authority lives" title="Direct link to Where release authority lives" translate="no">​</a></h2>
<p>The incident sequence showed how six correct local decisions formed an unauthorized path. The final topology separates proposal, evidence, approval, and effect ownership, and shows the direct path from Lumen or staging to external delivery as blocked.</p>
<div class="failoverTopology failoverTopology--loading">Loading interactive production topology…</div>
<p>The responsibility ledger for that topology is:</p>
<ol>
<li class="">Source adapters own versioned retrieval and source provenance; they do not authorize delivery.</li>
<li class="">Employer domains and the controlled identity graph provide stable identifiers and typed relationship evidence where available; uncertainty stays explicit.</li>
<li class="">Lumen proposes candidates, linkage evidence, conflicts, and redactions; it cannot approve its own relationship or release bytes.</li>
<li class="">Deterministic policy and Mara's ambiguity review resolve supported, rejected, and unresolved relationships.</li>
<li class="">The manifest service produces versioned canonical <code>ReleaseProposalV1</code> bytes and governs the sensitive proposal and review-rendering lifecycle.</li>
<li class="">The reviewer UI renders the exact proposal/output; the approval store issues a separate <code>ApprovalReceipt</code> and owns current revocation, supersession, reservation, and spend state. Approval evidence is not a delivery credential.</li>
<li class="">An authenticated Harborlight release workload carries task, tenant, purpose, and operation identity under the employer-policy context; it does not impersonate Jana.</li>
<li class=""><code>data-release-gateway</code> revalidates current authority, atomically reserves the one-use nonce and revisions, idempotently publishes the exact immutable object version, reconciles uncertain results, and emits a <code>ReleaseReceipt</code>. It alone owns publication authority.</li>
<li class="">The authenticated portal reauthorizes every read and range for the current grant, case, recipient, and exact object version, then emits an <code>AccessReceipt</code> for actual bytes served.</li>
<li class="">Audit and evaluation join source version, Lumen rationale, eligibility, policy, reviewer, proposal, approval, workload, publication, bytes served, and recipient while minimizing retained sensitive content.</li>
</ol>
<p>Harborlight could now prove more than who called every tool. It could explain why the exact bytes released were authorized for the exact person who received them—or stop in ambiguity when it could not.</p>
<details class="conceptPanel"><summary><strong>Concepts in this story</strong><span class="conceptPanel__count">5<!-- --> concepts</span></summary><div class="conceptPanel__list"><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#security.privilege-composition">Privilege composition</a><span>Identity and security · Capability containment</span></div><p>The broader capability created when several individually permitted operations can be chained. Local least-privilege checks do not establish that the complete path or resulting effect is authorized.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#security.source-to-sink-authorization">Source-to-sink authorization</a><span>Identity and security · Information-flow control</span></div><p>An authorization decision over the complete path from protected input through transformations to a consequential destination. It checks whether this data may reach this sink for the current purpose, not only whether each intermediate tool call is allowed.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#data.record-subject-binding">Record-subject binding</a><span>Data systems · Provenance and identity</span></div><p>The supported relationship between a versioned record and the person or entity it concerns. A model may propose this relationship from semantic evidence, but release authority requires corroborating source metadata, policy, or explicit review.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#agents.effect-bound-approval">Effect-bound approval</a><span>Agentic systems · Approval binding</span></div><p>Human authorization tied to one canonical proposed effect: its target, parameters, evidence, constraints, destination, and expiry. A material change creates a new approval question instead of inheriting consent from the surrounding task.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#agents.execution-time-authorization">Execution-time authorization</a><span>Agentic systems · Effect authorization</span></div><p>Re-evaluating current principal, workload, resource, purpose, policy, approval, and effect immediately before a consequential action. Permission observed during planning or approval is historical evidence, not automatic present authority.</p></div></div></details>
<hr>
<p><em>Harborlight People, Lumen, Jana Li, Jian Li, Mara Chen, Elias Ward, case <code>PR-2048</code>, the incident, and all operating metrics are fictional. The workflow is a customer-configured product design, not a universal statement of legal requirements. Technical grounding includes <a href="https://www.anthropic.com/engineering/how-we-contain-claude" target="_blank" rel="noopener noreferrer" class="">Anthropic's 2026 containment report</a>, <a href="https://openai.com/index/designing-agents-to-resist-prompt-injection/" target="_blank" rel="noopener noreferrer" class="">OpenAI's 2026 source-to-sink agent design</a>, the <a href="https://www.nist.gov/privacy-framework/privacy-framework" target="_blank" rel="noopener noreferrer" class="">NIST Privacy Framework</a>, <a href="https://csrc.nist.gov/Pubs/sp/800/53/r5/upd1/Final" target="_blank" rel="noopener noreferrer" class="">NIST SP 800-53 Rev. 5</a>, <a href="https://pages.nist.gov/800-63-4/" target="_blank" rel="noopener noreferrer" class="">NIST Digital Identity Guidelines</a>, <a href="https://www.w3.org/TR/prov-overview/" target="_blank" rel="noopener noreferrer" class="">W3C PROV</a>, <a href="https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html" target="_blank" rel="noopener noreferrer" class="">OWASP authorization guidance</a>, <a href="https://cheatsheetseries.owasp.org/cheatsheets/Insecure_Direct_Object_Reference_Prevention_Cheat_Sheet.html" target="_blank" rel="noopener noreferrer" class="">OWASP IDOR prevention</a>, <a href="https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html" target="_blank" rel="noopener noreferrer" class="">OWASP logging guidance</a>, <a href="https://www.rfc-editor.org/rfc/rfc8707.html" target="_blank" rel="noopener noreferrer" class="">RFC 8707 resource indicators</a>, and <a href="https://www.rfc-editor.org/rfc/rfc9396.html" target="_blank" rel="noopener noreferrer" class="">RFC 9396 rich authorization requests</a>. These sources support system principles; they do not describe Harborlight, its incident, its metrics, or one universal privacy process.</em></p>]]></content:encoded>
            <category>LLM engineering</category>
            <category>Agentic systems</category>
            <category>System design</category>
            <category>Common misconception</category>
        </item>
        <item>
            <title><![CDATA[The failover agent that mistook no answer for no action]]></title>
            <link>http://localhost:3001/stories/the-failover-agent-that-mistook-no-answer-for-no-action</link>
            <guid>http://localhost:3001/stories/the-failover-agent-that-mistook-no-answer-for-no-action</guid>
            <pubDate>Sun, 09 Aug 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A fictional production incident about durable AI workers, uncertain cloud operations, and recovering external truth.]]></description>
            <content:encoded><![CDATA[<div class="storyStatus"><span>Architecture story</span><span class="storyLevelChip storyLevelChip--professional" title="Defend a cross-boundary production decision under ambiguity." aria-label="Professional level: Defend a cross-boundary production decision under ambiguity.">Professional</span>A fictional production incident about durable AI workers, uncertain cloud operations, and recovering external truth.</div>
<aside class="storyLearningGoals" aria-labelledby="story-learning-goals-title"><div><span>Before the incident</span><h2 id="story-learning-goals-title">What you'll learn from this story</h2></div><ul><li><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg><span>Separate what a durable workflow recorded from what an external system actually did.</span></li><li><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg><span>Design stable operation identity and reconciliation for cloud calls with uncertain outcomes.</span></li><li><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg><span>Explain why choosing a current worker with a lease does not fence an older worker from new effects.</span></li><li><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg><span>Evaluate the safety of an agent’s full trajectory, not only its final green state.</span></li></ul></aside>
<p><strong>Northstar Ledger</strong> (a fictional B2B commerce-infrastructure company) routes checkout and inventory updates for regional grocery chains. Its customers do not run one Northstar checkout page. They use Northstar behind their own mobile apps, self-checkout kiosks, and delivery sites.</p>
<p>At the Saturday peak, those systems send roughly 48,000 order operations per minute. A short outage creates queues. An incorrect recovery can create something worse: orders accepted through a path that the company no longer believes is authoritative.</p>
<p>Northstar stored checkout state in an Amazon Aurora PostgreSQL global database. The primary Region served writes. A secondary Region stayed ready for disaster recovery. The operating rule was strict: Northstar could advertise only the writer confirmed by Aurora's current topology.</p>
<p><strong>Imani</strong> (the Staff Reliability Engineer responsible for Northstar's database-recovery control plane) had spent the previous six months reducing the time between an incident page and a safe recovery decision.</p>
<p>Her team built <strong>Relay</strong>, a bounded AI incident worker. Relay read approved telemetry, compared an incident with reviewed runbooks, and proposed a typed recovery plan. It could not promote a database or change traffic. A deterministic policy service checked every proposal. A human incident commander then approved the exact target and the maximum tolerated data loss before any effect was allowed.</p>
<p>This mattered because Relay was useful precisely where incidents were messy. It could gather replication lag, recent deployments, health probes, and runbook constraints in seconds. It could explain why one recovery target fitted the evidence better than another.</p>
<p>It could not make an ambiguous cloud operation unambiguous by thinking harder.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-a-checkout-normally-reaches-the-writer">How a checkout normally reaches the writer<a href="http://localhost:3001/stories/the-failover-agent-that-mistook-no-answer-for-no-action#how-a-checkout-normally-reaches-the-writer" class="hash-link" aria-label="Direct link to How a checkout normally reaches the writer" title="Direct link to How a checkout normally reaches the writer" translate="no">​</a></h2>
<p>Before looking at the failover, it helps to place Relay beside the customer path. A shopper's request normally passes through Northstar's regional endpoint and order services before Aurora accepts the write. Relay is not in that request path. During an incident, it feeds a separate recovery control path that can change the Aurora writer and the Route 53 target serving future requests.</p>
<div class="failoverTopology failoverTopology--loading">Loading interactive story context…</div>
<details class="conceptPanel"><summary><strong>Concepts in this story</strong><span class="conceptPanel__count">5<!-- --> concepts</span></summary><div class="conceptPanel__list"><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#agents.state-persistence-and-checkpointing">State persistence and checkpointing</a><span>Agentic systems · Durable execution</span></div><p>Persisting workflow progress so a long-running agent can resume after waiting or process loss. A checkpoint records completed orchestration state; it does not prove that an unacknowledged external effect failed or that the outside world is still unchanged.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#effects.idempotency-key">Idempotency key</a><span>Distributed systems · Effect safety</span></div><p>A stable caller-provided identifier for one logical operation. Repeating the same request with the same key lets the receiver suppress an additional effect within its documented scope and retention window; a new key represents new intent.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#effects.exactly-once-semantics">Exactly-once semantics</a><span>Distributed systems · Effect safety</span></div><p>A guarantee that one logical operation is applied once inside a stated boundary, usually by combining atomic state changes, deduplication, or idempotency. A workflow or broker guarantee does not automatically make a separate external side effect exactly once.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#coordination.fencing-token">Fencing token</a><span>Distributed systems · Coordination</span></div><p>A monotonically increasing epoch attached to work on a protected resource. The resource or gateway rejects requests carrying an older epoch, preventing a stale lease holder from beginning new effects after a successor takes over.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#evaluation.trajectory-vs-end-state">Trajectory vs end-state evaluation</a><span>AI evaluation · Agent trajectories</span></div><p>End-state evaluation checks where the task finished. Trajectory evaluation also checks the actions, observations, policy decisions, costs, and prohibited intermediate effects used to get there. A correct final state can follow an unacceptable path.</p></div></div></details>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-recovery-path-they-trusted">The recovery path they trusted<a href="http://localhost:3001/stories/the-failover-agent-that-mistook-no-answer-for-no-action#the-recovery-path-they-trusted" class="hash-link" aria-label="Direct link to The recovery path they trusted" title="Direct link to The recovery path they trusted" translate="no">​</a></h2>
<p>The system around Relay looked cautious.</p>
<p>The incident console was React. A NestJS service called <code>incident-api</code> assembled the request. A Python <code>relay-worker</code> called the language model and returned a typed proposal, not free-form shell commands.</p>
<p>A Temporal workflow on Amazon EKS owned the recovery sequence. Temporal activity workers sent heartbeats while they ran. Kubernetes Leases helped the team identify the current controller. PostgreSQL stored the incident, plan, policy result, and approval. A separate NestJS service called <code>effect-gateway</code> owned the AWS credentials and exposed narrow RDS and Route 53 operations.</p>
<p>No Relay worker held a general AWS credential. AWS STS minted short-lived credentials for the gateway's adapters. OpenTelemetry joined traces across the workflow, and CloudTrail recorded AWS API activity.</p>
<p>That was a competent design. The team had durable state, bounded tools, least-privilege credentials, deterministic policy, and a human approval gate.</p>
<p>The recovery activity still contained one quiet mistake. The naive workflow placed the promotion and its follow-on Route 53 update inside one coarse, heartbeating <code>recoverRegion</code> activity. That allowed overlapping activity attempts to perform both effects. If traffic had been a later workflow-scheduled activity, worker A could not have advanced to it independently after the first activity timed out.</p>
<p>When Temporal retried an activity, that attempt generated a fresh request key. The gateway checked whether the caller was permitted to invoke the adapter, but it did not keep one authoritative record for the logical promotion. It also did not reject an old worker by workflow epoch.</p>
<p>The team's mental model was:</p>
<blockquote>
<p>Temporal remembers the step. Kubernetes knows which worker is current. If a call fails, retrying the activity will either recover the result or safely perform the same work.</p>
</blockquote>
<p>They had discussed duplicate requests. They had not designed for a request that succeeded elsewhere while leaving no answer locally.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="saturday-at-0213">Saturday at 02:13<a href="http://localhost:3001/stories/the-failover-agent-that-mistook-no-answer-for-no-action#saturday-at-0213" class="hash-link" aria-label="Direct link to Saturday at 02:13" title="Direct link to Saturday at 02:13" translate="no">​</a></h2>
<p>At 02:12, the primary Region developed a network impairment. Checkout latency climbed. Health probes disagreed by source Region. Replication lag to the secondary remained inside Northstar's approved emergency loss bound, but the primary was becoming unreachable from enough application nodes to threaten the order queue.</p>
<p>At <strong>02:13:00</strong>, Relay assembled the evidence. Its proposal named the secondary cluster, measured lag, included Northstar's digest and observation time for the Aurora topology snapshot, and stated the maximum loss bound. It recommended the reviewed regional-promotion runbook.</p>
<p>The proposal was correct.</p>
<p>At <strong>02:13:18</strong>, <strong>Darius</strong> (the incident commander with authority to approve database recovery) reviewed the evidence and approved that exact digest. He did not approve “fix checkout.” He approved one target under one stated loss envelope.</p>
<p>The policy service accepted it. The Temporal workflow recorded it. Activity worker A began the promotion.</p>
<p>At <strong>02:13:24</strong>, worker A's <code>recoverRegion</code> handler created local request key <code>attempt-a</code> and called the RDS adapter through <code>effect-gateway</code>. The gateway sent the approved promotion request to AWS.</p>
<p>At <strong>02:13:27</strong>, AWS accepted the promotion and returned an acknowledgement to worker A's handler. Before the handler could persist that receipt or report its activity result to Temporal, its connection to Northstar's control services failed. The acknowledgement survived only in the still-running process's memory.</p>
<p>The workflow history now contained an honest fact: no result had been recorded.</p>
<p>It did not contain another honest fact: the promotion had already crossed Northstar's boundary and AWS had acknowledged it to the isolated handler.</p>
<p>At <strong>02:13:33</strong>, Temporal's configured activity heartbeat timeout expired. Under the activity's retry policy, Temporal scheduled another attempt, which worker B polled. Independently, Northstar's controller Lease expired and B became the current controller. Neither mechanism terminated worker A's local activity handler.</p>
<p>At <strong>02:13:40</strong>, worker B loaded the durable workflow history, saw the missing activity result, and created request key <code>attempt-b</code>. Its first Northstar-cached topology observation was stale: it still named the impaired original cluster as writer. Following the recovery code, B retried the coarse activity and prepared a “hold on the known writer” traffic fallback toward the original cluster.</p>
<p>The replacement worker was not ignoring the checkpoint. It was following it.</p>
<p>At <strong>02:13:46</strong>, Aurora's authoritative topology identified the secondary cluster as the writer. The original promotion had happened. The missing response had concealed an accepted effect, not a failed one.</p>
<p>At <strong>02:13:50</strong>, worker A's still-running <code>recoverRegion</code> handler regained control-service connectivity and continued to its embedded Route 53 call. It submitted change <code>A-184</code>, pointing checkout to the newly promoted secondary cluster. Temporal could reject A's late activity completion; it could not undo an external call the handler had already made. Nothing at <code>effect-gateway</code> rejected A's next action as stale.</p>
<p>Worker A and worker B now held different beliefs about the same recovery:</p>
<ul>
<li class="">worker A held the unpersisted AWS acknowledgement and intended to point traffic to the newly promoted secondary cluster;</li>
<li class="">worker B held a stale snapshot naming the impaired original cluster as writer and intended to point traffic back there.</li>
</ul>
<p>Both could still begin a new Route 53 action.</p>
<section class="failoverSequence" aria-labelledby="failover-sequence-title"><header class="failoverSequence__header"><span class="failoverSequence__kicker">Incident sequence · first 10 minutes</span><h3 id="failover-sequence-title">Three histories stop agreeing</h3><p>Read downward on a phone or left to right on a larger screen. Each card names the record or control surface whose state matters.</p></header><ol class="failoverSequence__list"><li class="failoverSequence__moment failoverSequence__moment--safe"><span class="failoverSequence__number" aria-hidden="true">1</span><article><div class="failoverSequence__momentHeader"><span class="failoverSequence__icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-check" aria-hidden="true"><path d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z"></path><path d="m9 12 2 2 4-4"></path></svg></span><time>02:13:18</time></div><h4>The correct plan is approved</h4><p>Relay proposes the secondary cluster, and the incident commander approves that exact target and loss bound.</p><footer><span>Proposal + approval</span><strong>The intended action is valid.</strong></footer></article></li><li class="failoverSequence__moment failoverSequence__moment--warning"><span class="failoverSequence__number" aria-hidden="true">2</span><article><div class="failoverSequence__momentHeader"><span class="failoverSequence__icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-cloud-cog" aria-hidden="true"><path d="m10.852 19.772-.383.924"></path><path d="m13.148 14.228.383-.923"></path><path d="M13.148 19.772a3 3 0 1 0-2.296-5.544l-.383-.923"></path><path d="m13.53 20.696-.382-.924a3 3 0 1 1-2.296-5.544"></path><path d="m14.772 15.852.923-.383"></path><path d="m14.772 18.148.923.383"></path><path d="M4.2 15.1a7 7 0 1 1 9.93-9.858A7 7 0 0 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.2"></path><path d="m9.228 15.852-.923-.383"></path><path d="m9.228 18.148-.923.383"></path></svg></span><time>02:13:27</time></div><h4>AWS accepts; the receipt stays local</h4><p>The Aurora acknowledgement reaches worker A, but its control-service connection fails before the handler records the receipt.</p><footer><span>External authority</span><strong>Promotion accepted.</strong></footer></article></li><li class="failoverSequence__moment failoverSequence__moment--warning"><span class="failoverSequence__number" aria-hidden="true">3</span><article><div class="failoverSequence__momentHeader"><span class="failoverSequence__icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-refresh-cw" aria-hidden="true"><path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"></path><path d="M21 3v5h-5"></path><path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"></path><path d="M8 16H3v5"></path></svg></span><time>02:13:40</time></div><h4>A replacement worker sees no result</h4><p>Temporal retries the coarse activity. Worker B has durable history, but no proof of what happened after dispatch.</p><footer><span>Workflow history</span><strong>No receipt recorded.</strong></footer></article></li><li class="failoverSequence__moment failoverSequence__moment--danger"><span class="failoverSequence__number" aria-hidden="true">4</span><article><div class="failoverSequence__momentHeader"><span class="failoverSequence__icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-git-compare-arrows" aria-hidden="true"><circle cx="5" cy="6" r="3"></circle><path d="M12 6h5a2 2 0 0 1 2 2v7"></path><path d="m15 9-3-3 3-3"></path><circle cx="19" cy="18" r="3"></circle><path d="M12 18H7a2 2 0 0 1-2-2V9"></path><path d="m9 15 3 3-3 3"></path></svg></span><time>02:13:50</time></div><h4>Two workers can still change traffic</h4><p>Worker A targets the promoted secondary while worker B targets the original cluster from a stale snapshot. The gateway has no fencing epoch.</p><footer><span>Traffic control</span><strong>Competing Route 53 effects.</strong></footer></article></li><li class="failoverSequence__moment failoverSequence__moment--hidden"><span class="failoverSequence__number" aria-hidden="true">5</span><article><div class="failoverSequence__momentHeader"><span class="failoverSequence__icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-radio-tower" aria-hidden="true"><path d="M4.9 16.1C1 12.2 1 5.8 4.9 1.9"></path><path d="M7.8 4.7a6.14 6.14 0 0 0-.8 7.5"></path><circle cx="12" cy="9" r="2"></circle><path d="M16.2 4.8c2 2 2.26 5.11.8 7.47"></path><path d="M19.1 1.9a9.96 9.96 0 0 1 0 14.1"></path><path d="M9.5 18h5"></path><path d="m8 22 4-11 4 11"></path></svg></span><time>02:23:00</time></div><h4>A green ending hides the unsafe path</h4><p>Aurora becomes healthy, but the checkout target changed Regions three times and orders queued during recovery.</p><footer><span>Final-state dashboard</span><strong>Green; trajectory unsafe.</strong></footer></article></li></ol><p class="failoverSequence__takeaway"><strong>The ambiguity:</strong> “No result was recorded” is true, but it does not mean “no external action occurred.”</p></section>
<p>At <strong>02:14:22</strong>, after change <code>A-184</code> had left provider processing, Route 53 accepted B's sequential change <code>B-771</code>, pointing the same record back to the original cluster. At <strong>02:21:08</strong>, after joining Aurora's writer topology with both change records, the incident commander approved repair change <code>R-009</code> toward the promoted secondary. Route 53 processed the three batches in submission order; Northstar's contradictory controllers, not Route 53, had created the oscillation.</p>
<p>Long-lived application connections did not all move when DNS changed. Some clients held existing pools while others resolved the newer target. Checkout moved to the promoted secondary, back toward the impaired original cluster, then finally to the authoritative writer.</p>
<p>Aurora did not casually create two managed-database primaries. Its topology converged on one writer. The incident was in Northstar's recovery control plane: two workers were allowed to issue traffic-changing effects from different beliefs about that topology.</p>
<p>The checkout target changed Regions three times. The order service placed 11,842 operations into its retry queue. A total of 1,906 shoppers saw a temporary checkout warning.</p>
<p>At <strong>02:23</strong>, the workflow reached a green terminal state. Aurora was healthy. The final traffic target matched the final writer. The top-level recovery dashboard called the run successful.</p>
<p>The customer path had still been unsafe.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="three-histories-all-telling-the-truth">Three histories, all telling the truth<a href="http://localhost:3001/stories/the-failover-agent-that-mistook-no-answer-for-no-action#three-histories-all-telling-the-truth" class="hash-link" aria-label="Direct link to Three histories, all telling the truth" title="Direct link to Three histories, all telling the truth" translate="no">​</a></h2>
<p>Imani did not start with the model transcript. She put three timelines side by side.</p>
<p>The first was <strong>Relay's proposal history</strong>. It showed the evidence Relay had used, the chosen secondary, Northstar's observed-topology digest and timestamp, the loss bound, and Darius's approval digest. Nothing in it explained the oscillation. Relay had recommended the right action.</p>
<p>The second was <strong>Temporal's durable workflow history</strong>. It showed the coarse <code>recoverRegion</code> activity scheduled, worker A's heartbeat loss, no recorded receipt, a timeout, and a replacement attempt. That record was also correct.</p>
<p>This was <span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_2dildeh_">state persistence and checkpointing<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span>: the workflow had preserved its own progress well enough to resume. A checkpoint could say what had been scheduled, observed, or recorded. It could not prove what AWS had done after the request crossed the network.</p>
<p>The third was <strong>external authority</strong>. Aurora's current topology established which cluster was the writer. CloudTrail later corroborated that the original API call reached AWS. Route 53 held three sequential change resources, each with its own status and timestamps. Those records showed effects the workflow had not understood when it retried.</p>
<p>The contradiction disappeared once the team stopped asking which history was wrong.</p>
<p>They were histories of different things.</p>
<p>The phrase from the design review—“durable means exactly once”—finally sounded as dangerous as it was.</p>
<p><span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_1eqldeh_">Exactly-once semantics<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span> can exist inside a stated boundary when the participants cooperate through atomic state changes, deduplication, or idempotency. Temporal could durably record and replay workflow history. PostgreSQL could make one ledger transition atomic. Neither mechanism could silently include an arbitrary AWS operation that had already crossed the network.</p>
<p>The timeout meant only this: Northstar did not know the result yet.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-key-had-named-the-attempt">The key had named the attempt<a href="http://localhost:3001/stories/the-failover-agent-that-mistook-no-answer-for-no-action#the-key-had-named-the-attempt" class="hash-link" aria-label="Direct link to The key had named the attempt" title="Direct link to The key had named the attempt" translate="no">​</a></h2>
<p>The code review made the first bug painfully small:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">src/failover/promote.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Tempting: every retried activity invents a new intent.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">promoteActivity</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">plan</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">ApprovedPlan</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> effectGateway</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">promote</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token spread operator" style="color:#393A34">...</span><span class="token plain">plan</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    requestKey</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> crypto</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">randomUUID</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Correct boundary: allocate this record once, before dispatch.</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">resumePromotion</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">operationId</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> epoch</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">number</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> op </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> operations</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">operationId</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">switch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">op</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">status</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">case</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'CONFIRMED'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> op</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">receipt</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">case</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'DISPATCHING'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">case</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'UNKNOWN'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">reconcileWithAws</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">op</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">case</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'AUTHORIZED'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> effectGateway</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">dispatch</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">        operationId</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> op</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">id</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">        parameters</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> op</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">parameters</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">        approvalDigest</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> op</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">approvalDigest</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">        topologySnapshotDigest</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> op</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">topologySnapshotDigest</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">        topologyObservedAt</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> op</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">topologyObservedAt</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">        fencingEpoch</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> epoch</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword module" style="color:#00009f">default</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">stopForReview</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">op</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>An <span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_2g2ldeh_">idempotency key<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span> should identify one logical operation, not one transport attempt. Reusing a stable identity gives a cooperating receiver the chance to recognize a repeat within its documented scope and retention window. Generating <code>attempt-b</code> told the gateway that worker B had brought new intent.</p>
<p>But replacing <code>randomUUID()</code> with a stable value was not the whole fix.</p>
<p>Not every provider operation exposes the same idempotency or lookup contract. The RDS promotion path still needed reconciliation against Aurora's current topology and AWS evidence. Route 53 returned a change ID that Northstar could persist and poll from <code>PENDING</code> to <code>INSYNC</code>. Each adapter needed to respect the destination's actual contract.</p>
<p>An idempotency key could help suppress a duplicate dispatch. It could not tell Northstar whether an unobserved operation had completed, whether the world had changed since approval, or whether an old worker was still alive.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="an-operation-could-be-unknown">An operation could be unknown<a href="http://localhost:3001/stories/the-failover-agent-that-mistook-no-answer-for-no-action#an-operation-could-be-unknown" class="hash-link" aria-label="Direct link to An operation could be unknown" title="Direct link to An operation could be unknown" translate="no">​</a></h2>
<p>Imani's team added a PostgreSQL operation ledger in a failure-independent Region. Before any effect, the workflow created one stable <code>operation_id</code> and bound it to:</p>
<ul>
<li class="">the exact effect type and target;</li>
<li class="">a canonical digest of its parameters;</li>
<li class="">the policy version and approval digest;</li>
<li class="">Northstar's digest and observation time for the Aurora topology snapshot;</li>
<li class="">the current workflow epoch;</li>
<li class="">any AWS request, audit, resource, or change identifiers returned later.</li>
</ul>
<p>The ledger did not force every operation into <code>SUCCEEDED</code> or <code>FAILED</code>. A normal dispatch could confirm directly; an ambiguous one branched into an explicit uncertainty state:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">PROPOSED → AUTHORIZED → DISPATCHING → CONFIRMED</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                                  ↘ UNKNOWN</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                                      ├→ CONFIRMED</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                                      ├→ SUPERSEDED</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                                      └→ MANUAL_REVIEW</span><br></div></code></pre></div></div>
<p>It also allowed <code>REJECTED</code> before dispatch. <code>SUPERSEDED</code> and <code>MANUAL_REVIEW</code> were resolved outcomes for an uncertain operation, not mandatory stops on the successful path.</p>
<p><code>UNKNOWN</code> was not an error message. It was a statement about evidence. The call might have failed before leaving Northstar. It might have been accepted and still be running. It might have completed while its response disappeared. Until reconciliation established more, all three remained possible.</p>
<p>For an unknown promotion, the replacement worker did not manufacture a new logical operation. It read Aurora's current global-cluster topology, joined CloudTrail and request evidence where available, checked the intended target, and recorded the observation time. Only then could it confirm the original operation, retry under the same safe identity when the contract allowed it, supersede it, or stop for manual review.</p>
<p>The next action faced a second barrier. Before changing traffic, the workflow re-read topology, policy, and approval. Northstar's topology digest supported this changed-world check; it was not an RDS <code>ETag</code>, resource version, or atomic API precondition. The checkpointed plan remained valuable historical evidence. It was not present authority.</p>
<p>The team gave each Route 53 change a separate operation identity. The gateway stored the returned change ID and polled <code>GetChange</code>. <code>PENDING</code> meant AWS had accepted a change that had not yet propagated. <code>INSYNC</code> established propagation through Route 53's authoritative DNS servers. Northstar still required regional DNS-resolution checks and connection-pool turnover before declaring the customer path complete.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-lease-was-not-a-fence">A lease was not a fence<a href="http://localhost:3001/stories/the-failover-agent-that-mistook-no-answer-for-no-action#a-lease-was-not-a-fence" class="hash-link" aria-label="Direct link to A lease was not a fence" title="Direct link to A lease was not a fence" translate="no">​</a></h2>
<p>The Kubernetes Lease had done its job. It helped the cluster choose worker B after worker A stopped sending heartbeats.</p>
<p>It had not revoked worker A's memory, stopped its process, cancelled an in-flight AWS request, or told Route 53 to reject its next call.</p>
<p>Northstar added a monotonically increasing <span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_2kaldeh_">fencing token<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span>—called a fencing epoch in the implementation—to every claimed dispatch. The gateway atomically claimed the tuple <code>{operation_id, epoch, effect}</code> in the ledger. After a successful claim, the gateway—not the worker—owned that dispatch. A newer epoch prevented another claim but could not cancel a call already claimed or sent; an ownership change during dispatch moved the operation to <code>UNKNOWN</code> for reconciliation.</p>
<p>When worker B took epoch <code>240</code>, a later request from worker A carrying epoch <code>239</code> was rejected at the gateway.</p>
<p>That boundary was deliberately narrow:</p>
<ul>
<li class="">it stopped a stale worker from initiating a new effect through Northstar's gateway;</li>
<li class="">it did not cancel a request already sent to AWS;</li>
<li class="">it did not turn Aurora or Route 53 into participants in Northstar's transaction;</li>
<li class="">it did not remove the need for provider request IDs, topology checks, or reconciliation.</li>
</ul>
<p>Workers still received no general RDS or Route 53 credentials. If a worker could bypass the gateway, the fence would be a number in a log rather than an enforced safety mechanism.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-system-they-took-back-to-review">The system they took back to review<a href="http://localhost:3001/stories/the-failover-agent-that-mistook-no-answer-for-no-action#the-system-they-took-back-to-review" class="hash-link" aria-label="Direct link to The system they took back to review" title="Direct link to The system they took back to review" translate="no">​</a></h2>
<p>The corrected design separated four ownership boundaries.</p>
<p>The <strong>human and incident interface</strong> contained the React console, <code>incident-api</code>, Relay's evidence, and the incident commander's exact approval.</p>
<p>The <strong>AI analysis and durable workflow boundary</strong> contained the Python <code>relay-worker</code>, Temporal, Kubernetes controller election, and versioned activity workers on EKS. Relay proposed. Temporal remembered orchestration. The Lease helped choose a current controller. None of them owned AWS truth or enforced the effect fence.</p>
<p>The <strong>operation control and evidence boundary</strong> contained the failure-independent PostgreSQL ledger, deterministic policy service, <code>effect-gateway</code>, reconciliation workers, OpenTelemetry, and CloudTrail correlation. This was where stable operation identity, current epoch, approval binding, and effect evidence met.</p>
<p>The <strong>AWS data and traffic boundary</strong> contained Aurora's authoritative topology and Route 53's asynchronous change resources. Northstar observed those systems through their own contracts rather than treating its last checkpoint as a copy of them.</p>
<p>The recovery rule became simple enough to print beside the incident console:</p>
<blockquote>
<p>Resume the task from durable history. Reconcile an uncertain effect with its authority. Revalidate the world before beginning the next effect.</p>
</blockquote>
<p>Simple did not mean cheap.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-tests-that-cared-how-it-finished">The tests that cared how it finished<a href="http://localhost:3001/stories/the-failover-agent-that-mistook-no-answer-for-no-action#the-tests-that-cared-how-it-finished" class="hash-link" aria-label="Direct link to The tests that cared how it finished" title="Direct link to The tests that cared how it finished" translate="no">​</a></h2>
<p>The original dashboard evaluated the end state: was Aurora healthy, was one writer present, did the workflow finish, and did checkout recover?</p>
<p>Those questions still mattered. They were not enough.</p>
<p>Imani added <span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_2oildeh_">Trajectory vs end-state evaluation<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span>. End-state evaluation checked where the system finished. Trajectory evaluation checked the observations, approvals, dispatches, policy decisions, and prohibited intermediate transitions used to get there.</p>
<p>The test environment repeatedly injected the failures that ordinary happy-path tests hid:</p>
<ul>
<li class="">drop the provider response after dispatch;</li>
<li class="">terminate a worker before it persists an external receipt;</li>
<li class="">stop heartbeats, start a replacement, then wake the old worker;</li>
<li class="">duplicate or delay audit events;</li>
<li class="">change topology between approval and dispatch;</li>
<li class="">deploy a new worker build while an old execution remains open;</li>
<li class="">leave a Route 53 change <code>PENDING</code> longer than expected;</li>
<li class="">let an operator modify the same recovery resource.</li>
</ul>
<p>Deterministic graders checked one stable promotion intent, no new stale-epoch dispatch, and no completion without authoritative evidence. Every <code>UNKNOWN</code> operation had to become reconciled or explicitly escalated. The final writer and traffic target had to agree. A run failed if traffic made a prohibited transition, even when the final dashboard was green.</p>
<p>Model evaluation ran separately. Relay was graded on evidence classification, loss-bound extraction, and plan proposal. Improving those scores could not compensate for an unsafe effect trajectory.</p>
<p>Across Northstar's 1,200 fictional fault-injection trials, every unknown operation either reconciled to authoritative evidence or stopped in <code>MANUAL_REVIEW</code>; zero stale-epoch dispatches crossed the gateway. None were converted into a fresh logical promotion merely because a response was missing.</p>
<p>The result that mattered most was also the least attractive on a status slide: safe write resumption became slower.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-the-safety-cost">What the safety cost<a href="http://localhost:3001/stories/the-failover-agent-that-mistook-no-answer-for-no-action#what-the-safety-cost" class="hash-link" aria-label="Direct link to What the safety cost" title="Direct link to What the safety cost" translate="no">​</a></h2>
<p>The previous path optimized for recovery time. If the first call truly had failed before reaching AWS, an immediate replay was fast.</p>
<p>The new path deliberately paused to establish external truth. During ambiguous cases, Northstar kept checkout read-only for another 45 to 90 seconds. More orders entered a queue instead of receiving an immediate answer.</p>
<p>The team now operated a failure-independent control database, an effect gateway, provider-specific reconcilers, epoch management, and richer evidence retention. A damaged control store could delay recovery. A conservative precondition could send an incident to a human even when an automatic action would have worked.</p>
<p>Operations accepted that cost because the alternative spent correctness without measuring it. Product accepted it because a visible “please retry” message was recoverable. Conflicting order histories were not.</p>
<p>The design did not promise exactly-once cloud effects. It promised something more testable:</p>
<ul>
<li class="">one stable intent;</li>
<li class="">explicit uncertainty;</li>
<li class="">no new action from a stale owner;</li>
<li class="">no completion claim without evidence from the system that owned the result.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-question-imani-used-next">The question Imani used next<a href="http://localhost:3001/stories/the-failover-agent-that-mistook-no-answer-for-no-action#the-question-imani-used-next" class="hash-link" aria-label="Direct link to The question Imani used next" title="Direct link to The question Imani used next" translate="no">​</a></h2>
<p>The incident changed how Imani reviewed every durable agent.</p>
<p>For a payment gateway, she asked what happened when capture succeeded but its response disappeared.</p>
<p>For a secret-rotation worker, she asked which credential actually existed when a checkpoint and the target account disagreed.</p>
<p>For a deployment agent, she asked whether the approved plan still described the cluster after a human changed it during a pause.</p>
<p>The surface changed. The review question did not:</p>
<blockquote>
<p>If this worker wakes with no answer, what proves whether the outside world already acted?</p>
</blockquote>
<p>A durable workflow can remember where its computation stopped. It cannot remember a fact it never received.</p>
<p>That fact has to be recovered from the authority that owns it.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-production-topology">The production topology<a href="http://localhost:3001/stories/the-failover-agent-that-mistook-no-answer-for-no-action#the-production-topology" class="hash-link" aria-label="Direct link to The production topology" title="Direct link to The production topology" translate="no">​</a></h2>
<p>The incident sequence earlier showed when Northstar's histories diverged. The final deployment view shows where each responsibility lives, and lets the reader switch between the failed path and the corrected control boundary.</p>
<div class="failoverTopology failoverTopology--loading">Loading interactive architecture…</div>
<details class="conceptPanel"><summary><strong>Concepts in this story</strong><span class="conceptPanel__count">5<!-- --> concepts</span></summary><div class="conceptPanel__list"><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#agents.state-persistence-and-checkpointing">State persistence and checkpointing</a><span>Agentic systems · Durable execution</span></div><p>Persisting workflow progress so a long-running agent can resume after waiting or process loss. A checkpoint records completed orchestration state; it does not prove that an unacknowledged external effect failed or that the outside world is still unchanged.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#effects.idempotency-key">Idempotency key</a><span>Distributed systems · Effect safety</span></div><p>A stable caller-provided identifier for one logical operation. Repeating the same request with the same key lets the receiver suppress an additional effect within its documented scope and retention window; a new key represents new intent.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#effects.exactly-once-semantics">Exactly-once semantics</a><span>Distributed systems · Effect safety</span></div><p>A guarantee that one logical operation is applied once inside a stated boundary, usually by combining atomic state changes, deduplication, or idempotency. A workflow or broker guarantee does not automatically make a separate external side effect exactly once.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#coordination.fencing-token">Fencing token</a><span>Distributed systems · Coordination</span></div><p>A monotonically increasing epoch attached to work on a protected resource. The resource or gateway rejects requests carrying an older epoch, preventing a stale lease holder from beginning new effects after a successor takes over.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#evaluation.trajectory-vs-end-state">Trajectory vs end-state evaluation</a><span>AI evaluation · Agent trajectories</span></div><p>End-state evaluation checks where the task finished. Trajectory evaluation also checks the actions, observations, policy decisions, costs, and prohibited intermediate effects used to get there. A correct final state can follow an unacceptable path.</p></div></div></details>
<hr>
<p><em>Northstar Ledger, its people, its incident, and its metrics are fictional. Technical grounding: <a href="https://docs.aws.amazon.com/durable-execution/patterns/best-practices/idempotency/" target="_blank" rel="noopener noreferrer" class="">AWS guidance on idempotency and retries in durable execution</a>, <a href="https://kubernetes.io/docs/concepts/architecture/leases/" target="_blank" rel="noopener noreferrer" class="">Kubernetes Leases</a>, <a href="https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_FailoverGlobalCluster.html" target="_blank" rel="noopener noreferrer" class="">the Amazon RDS global-cluster failover API</a>, <a href="https://docs.aws.amazon.com/Route53/latest/APIReference/API_ChangeResourceRecordSets.html" target="_blank" rel="noopener noreferrer" class="">Route 53 change batches</a>, <a href="https://docs.aws.amazon.com/Route53/latest/APIReference/API_GetChange.html" target="_blank" rel="noopener noreferrer" class="">Route 53 change status</a>, and <a href="https://google.aip.dev/154" target="_blank" rel="noopener noreferrer" class="">resource freshness validation</a>. These sources establish technical behavior and boundaries; they do not describe the fictional incident.</em></p>]]></content:encoded>
            <category>LLM engineering</category>
            <category>Agentic systems</category>
            <category>Distributed workflows</category>
            <category>Cloud operations</category>
            <category>Common misconception</category>
        </item>
        <item>
            <title><![CDATA[The fine-tune that answered with last week’s policy]]></title>
            <link>http://localhost:3001/stories/the-fine-tune-that-answered-with-last-weeks-policy</link>
            <guid>http://localhost:3001/stories/the-fine-tune-that-answered-with-last-weeks-policy</guid>
            <pubDate>Sun, 09 Aug 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A fictional launch about stale model knowledge, retrieval-augmented generation, and choosing RAG versus fine-tuning.]]></description>
            <content:encoded><![CDATA[<div class="storyStatus"><span>Architecture story</span><span class="storyLevelChip storyLevelChip--beginner" title="Learn and recognize one principal mechanism." aria-label="Beginner level: Learn and recognize one principal mechanism.">Beginner</span>A fictional production incident about keeping an AI answer tied to current policy.</div>
<aside class="storyLearningGoals" aria-labelledby="story-learning-goals-title"><div><span>Before the incident</span><h2 id="story-learning-goals-title">What you'll learn from this story</h2></div><ul><li><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg><span>Distinguish knowledge stored in model weights from knowledge retrieved from an external source.</span></li><li><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg><span>Choose between retrieval-augmented generation and fine-tuning based on what needs to change.</span></li><li><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg><span>Separate the indexing pipeline from the per-question query pipeline in a production RAG system.</span></li><li><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg><span>Test whether generated policy claims are grounded in current, versioned evidence.</span></li></ul></aside>
<p><strong>Ashvale Benefits</strong> (a fictional employee-benefits administration company) helps mid-sized employers manage health-plan enrollment, eligibility rules, and employee policy questions. It had eleven days before its busiest open-enrollment season.</p>
<p>The company had built an AI support assistant inside its member portal. The assistant was supposed to answer routine questions from each employer’s approved policy handbook, show the supporting section, and hand uncertain cases to the support team.</p>
<p>During open enrollment, that team expected eighty thousand questions. Most would be some version of the same thing: <em>Does my plan cover this? When does coverage begin? Which form do I need?</em></p>
<p>The business goal was simple. Answer the routine questions immediately. Send the hard ones to a human. Never invent a benefit that did not exist.</p>
<p>There was one more rule from legal: every answer had to point to the policy section that supported it.</p>
<!-- -->
<details class="conceptPanel"><summary><strong>Concepts in this story</strong><span class="conceptPanel__count">5<!-- --> concepts</span></summary><div class="conceptPanel__list"><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#knowledge.parametric-vs-external">Parametric vs non-parametric knowledge</a><span>Generative AI · Knowledge systems</span></div><p>Parametric knowledge is encoded implicitly in a model’s learned weights. Non-parametric knowledge lives outside the model—such as in documents or an index—and is fetched when needed. External knowledge can be revised without retraining the model.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#knowledge.retrieval-augmented-generation">Retrieval-augmented generation</a><span>Generative AI · Retrieval</span></div><p>A generation pattern that retrieves relevant external information for the current query and includes it in the model’s input before the answer is generated. Retrieval changes the context, not the model’s weights.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#knowledge.rag-vs-fine-tuning">RAG vs fine-tuning</a><span>Generative AI · Retrieval</span></div><p>RAG supplies external knowledge at inference time; fine-tuning changes model weights through training. Use retrieval as the default for changing, sourceable facts and fine-tuning for learned behavior, format, or task adaptation. They can be combined.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#knowledge.indexing-vs-query-pipeline">Indexing pipeline vs query pipeline</a><span>Generative AI · Retrieval</span></div><p>The indexing pipeline prepares source content before questions arrive: load, split, enrich, and store it. The query pipeline runs per request: interpret the question, retrieve evidence, assemble context, generate, and validate the answer.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#knowledge.grounding">Grounding</a><span>Generative AI · Retrieval</span></div><p>Connecting generated claims to supplied evidence or an authoritative external source. Grounding makes answers inspectable and can reduce unsupported claims, but retrieval and generation can still fail.</p></div></div></details>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-obvious-shortcut">The obvious shortcut<a href="http://localhost:3001/stories/the-fine-tune-that-answered-with-last-weeks-policy#the-obvious-shortcut" class="hash-link" aria-label="Direct link to The obvious shortcut" title="Direct link to The obvious shortcut" translate="no">​</a></h2>
<p><strong>Rhea</strong> (the AI engineer leading the assistant project) was responsible for taking that system from an internal demo to production. Her service sat behind the member portal, received an employee’s question and plan details, called a language model, and returned either an answer or an escalation to a human agent.</p>
<p>The base model wrote clear answers, but it did not know Ashvale’s plans. The company had 4,800 approved question-and-answer pairs from past enrollment seasons. They were clean, reviewed, and already written in the support team’s voice.</p>
<p>Training on those examples felt like the shortest path. If the model did not know the handbook, teach it the handbook.</p>
<p>Rhea removed personal data, balanced the common and rare question types, and held back 600 examples for evaluation. Two days later, the new model looked much better:</p>
<ul>
<li class="">answer accuracy rose from 71% to 93% on the held-back set;</li>
<li class="">the required JSON shape passed 99.6% of the time;</li>
<li class="">answers were shorter and matched the support team’s tone;</li>
<li class="">the model stopped recommending forms from other plan families.</li>
</ul>
<p>The team called that a launch candidate.</p>
<p>The phrase they kept using was: “Now the model knows our policies.”</p>
<p>That sentence survived every review.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-answer-from-last-week">The answer from last week<a href="http://localhost:3001/stories/the-fine-tune-that-answered-with-last-weeks-policy#the-answer-from-last-week" class="hash-link" aria-label="Direct link to The answer from last week" title="Direct link to The answer from last week" translate="no">​</a></h2>
<p>On Friday afternoon, legal approved a policy amendment. New spouses would become eligible after 30 days, not 60. The document team published version 7.4 of the handbook and archived version 7.3.</p>
<p>On Monday morning, a support analyst ran the launch checklist.</p>
<blockquote>
<p><strong>Question:</strong> I got married last month. Can I add my spouse now?</p>
<p><strong>Assistant:</strong> Spouses become eligible after a 60-day waiting period. See section 8.2 of the Ashvale Standard Plan handbook.</p>
</blockquote>
<p>Section 8.2 did not say that anymore.</p>
<p>Worse, the answer looked excellent. It was direct. It used the approved tone. It included a citation-shaped sentence. Nothing in the wording signalled that the fact was stale.</p>
<p>Rhea tried the question twelve ways. Nine answers used 60 days. Two avoided a number. One used 30 days but cited the wrong section.</p>
<p>The launch dashboard still showed 93% accuracy because its questions came from version 7.3—the same policy era as the training data.</p>
<p>The model had passed the test it was given. The product had failed the test the business cared about.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-had-actually-changed">What had actually changed<a href="http://localhost:3001/stories/the-fine-tune-that-answered-with-last-weeks-policy#what-had-actually-changed" class="hash-link" aria-label="Direct link to What had actually changed" title="Direct link to What had actually changed" translate="no">​</a></h2>
<p>Rhea’s manager asked a blunt question.</p>
<p>“We replaced the handbook on Friday. Why is the assistant still reading the old one?”</p>
<p>It was not reading either handbook.</p>
<p>The training run had changed the model’s weights. The old examples had influenced statistical patterns inside those weights. There was no row Rhea could update from <code>60</code> to <code>30</code>, no source record she could inspect, and no reliable pointer from an answer back to the training example that shaped it.</p>
<p>That was the distinction Rhea had missed: <span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_282ldeh_">parametric knowledge and non-parametric knowledge<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span> are stored differently.</p>
<p>The fine-tuned model held knowledge implicitly in its parameters. The handbook was explicit knowledge outside the model. Rhea had copied some of the handbook’s patterns into the parameters, then mistaken that process for connecting the model to the handbook.</p>
<p>Fine-tuning can teach facts. It is not physically incapable of doing so. But learning a fact from examples is not the same as maintaining a current, inspectable source of truth. Coverage can be uneven. Updates require more training. Provenance is weak. Old and new facts can compete.</p>
<p>Ashvale’s problem was not “make the model speak differently.” It was “answer from whichever policy is active for this employee today.”</p>
<p>That reframed <span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_292ldeh_">RAG versus fine-tuning<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span>.</p>
<p>Fine-tuning had been useful for behaviour: tone, response shape, and when to escalate. The changing policy facts needed a different path.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="two-pipelines-not-one-training-run">Two pipelines, not one training run<a href="http://localhost:3001/stories/the-fine-tune-that-answered-with-last-weeks-policy#two-pipelines-not-one-training-run" class="hash-link" aria-label="Direct link to Two pipelines, not one training run" title="Direct link to Two pipelines, not one training run" translate="no">​</a></h2>
<p>The team stopped asking the model to remember the handbook.</p>
<p>They built an <span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_2a2ldeh_">indexing pipeline and a query pipeline<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span>.</p>
<p>Ashvale already ran its customer-facing services on Amazon EKS. The member portal was React. A NestJS service called <code>assistant-api</code> handled questions. Versioned policy PDFs lived in Amazon S3, PostgreSQL held plan and policy-version metadata, and a dedicated OpenSearch domain held searchable policy sections. A Python worker called <code>policy-indexer</code> prepared each new handbook.</p>
<p>The indexing pipeline ran when the document team published a policy. It extracted each approved section, attached the tenant, plan, version, and effective dates, then stored a searchable representation. Publishing version 7.4 created new records and retired the old records. It did not retrain the model.</p>
<p>The query pipeline ran for every question. It identified the employee’s plan and date, searched only the active policy records, and placed the best matching passages beside the question.</p>
<figure class="zoomableDiagram"><button type="button" class="zoomableDiagram__preview" aria-label="Open fullscreen diagram: Two-panel architecture diagram. Policy publishing sends a handbook through Amazon S3, Amazon SQS, the policy-indexer, an embedding API, OpenSearch, and PostgreSQL. Live questions pass from the React portal through assistant-api, PostgreSQL, OpenSearch, and the LLM API before returning a cited answer."><img src="http://localhost:3001/img/stories/current-policy-rag-lifecycle.webp" alt="Two-panel architecture diagram. Policy publishing sends a handbook through Amazon S3, Amazon SQS, the policy-indexer, an embedding API, OpenSearch, and PostgreSQL. Live questions pass from the React portal through assistant-api, PostgreSQL, OpenSearch, and the LLM API before returning a cited answer." loading="lazy" decoding="async"><span class="zoomableDiagram__cue" aria-hidden="true">Expand diagram ↗</span></button><figcaption>The publishing path updates the evidence. The live-question path retrieves from those same shared services before the model generates an answer. Select the image to inspect it full-screen.</figcaption></figure>
<p>The model still generated the prose. But it now generated with current evidence in the request. That pattern was <span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_2baldeh_">retrieval-augmented generation<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span>.</p>
<p>Rhea’s first implementation was deliberately boring:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">src/answers/answer-policy-question.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">answerPolicyQuestion</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">input</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">PolicyQuestion</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> activePolicy </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> policyVersions</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">findActive</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    tenantId</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">tenantId</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    planId</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">planId</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    asOf</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">asOfDate</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> evidence </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> openSearch</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">search</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    index</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'policy-sections-v1'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    query</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">question</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    filters</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">      tenantId</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">tenantId</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">      planId</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">planId</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">      policyVersion</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> activePolicy</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">version</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    limit</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">5</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token function" style="color:#d73a49">hasEnoughEvidence</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">evidence</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">escalate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'No active policy passage supports an answer'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> answer </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> model</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">generate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    instructions</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">      </span><span class="token string" style="color:#e3116c">'Answer only from the supplied policy passages.'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">      </span><span class="token string" style="color:#e3116c">'Cite the source ID after every policy claim.'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">      </span><span class="token string" style="color:#e3116c">'If the passages do not answer the question, say so.'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    question</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">question</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    evidence</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">citationsResolveToEvidence</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">answer</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> evidence</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> answer</span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">escalate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'The answer contains an unsupported citation'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>The important line was not <code>model.generate</code>. It was the refusal before it and the check after it.</p>
<p>Ashvale did not accept “the model probably knows” as evidence. Each policy claim had to connect to a retrieved, active passage. That connection was <span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_2caldeh_">grounding<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span>.</p>
<p>Grounding did not make the model truthful by definition. The search could retrieve the wrong section. The model could misread the right section. A citation could point to a passage that did not actually support the sentence.</p>
<p>So the team tested the pieces separately. Could the system retrieve the correct active section? Given that section, did the answer stay within it? Did every citation resolve? Did no-evidence questions escalate?</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-second-launch-review">The second launch review<a href="http://localhost:3001/stories/the-fine-tune-that-answered-with-last-weeks-policy#the-second-launch-review" class="hash-link" aria-label="Direct link to The second launch review" title="Direct link to The second launch review" translate="no">​</a></h2>
<p>By Thursday, the updated-policy test set had 240 questions. Half depended on a recent policy change. The other half were designed to catch accidental regressions on stable rules.</p>
<p>The numbers were less pretty than the original 93%, but more useful:</p>
<ul>
<li class="">91% of questions produced a correct answer supported by the active policy;</li>
<li class="">7% escalated because the retrieved evidence was weak or conflicting;</li>
<li class="">2% failed and stayed blocked from launch for investigation;</li>
<li class="">every displayed citation resolved to a versioned policy section.</li>
</ul>
<p>The spouse question now returned 30 days and linked to version 7.4, section 8.2. When the team marked version 7.4 inactive in a staging test, the assistant did not quietly fall back to 7.3. It escalated.</p>
<p>That behaviour cost them something.</p>
<p>Median response time rose from 720 milliseconds to 980. The 99th percentile rose by almost a second. Each answer consumed more input tokens. The team now operated a document parser, an index, freshness alarms, access filters, and two evaluation suites. A bad effective date could hide the right policy even when every other component worked.</p>
<p>The fallback also sent more conversations to humans. Legal considered that a feature. Finance saw a higher support cost.</p>
<p>Both were right.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-rhea-kept-from-the-fine-tune">What Rhea kept from the fine-tune<a href="http://localhost:3001/stories/the-fine-tune-that-answered-with-last-weeks-policy#what-rhea-kept-from-the-fine-tune" class="hash-link" aria-label="Direct link to What Rhea kept from the fine-tune" title="Direct link to What Rhea kept from the fine-tune" translate="no">​</a></h2>
<p>The team did not throw the fine-tuned model away.</p>
<p>It still followed Ashvale’s response format better than the base model. It used the right tone. It made cleaner escalation decisions. Those were changes in behaviour, and the training examples were good evidence for that behaviour.</p>
<p>The policy text remained outside the model, where it could be versioned, filtered, inspected, and replaced.</p>
<p>That was the answer Rhea carried into the next design review:</p>
<blockquote>
<p>If the knowledge changes, retrieve it. If the behaviour changes, fine-tune it. If both change, combine them—but keep the source of truth outside the weights.</p>
</blockquote>
<p>The rule was not absolute. It was a better starting question than “Which AI technique should we use?”</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-architecture-they-took-to-production">The architecture they took to production<a href="http://localhost:3001/stories/the-fine-tune-that-answered-with-last-weeks-policy#the-architecture-they-took-to-production" class="hash-link" aria-label="Direct link to The architecture they took to production" title="Direct link to The architecture they took to production" translate="no">​</a></h2>
<p>The lifecycle view earlier in the story showed <em>when</em> the indexing and query pipelines ran. For the final design review, Rhea also needed a deployment view that answered a different question: <em>where does each responsibility live?</em></p>
<p>The member and legal portals remained user-facing systems. The Python <code>policy-indexer</code> and NestJS <code>assistant-api</code> ran inside the Amazon EKS cluster. Versioned documents, publish events, active-version records, and searchable vectors lived in managed data services. Embeddings and generation crossed a separate AI API boundary.</p>
<div class="productionTopology productionTopology--loading">Loading interactive architecture…</div>
<details class="conceptPanel"><summary><strong>Concepts in this story</strong><span class="conceptPanel__count">5<!-- --> concepts</span></summary><div class="conceptPanel__list"><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#knowledge.parametric-vs-external">Parametric vs non-parametric knowledge</a><span>Generative AI · Knowledge systems</span></div><p>Parametric knowledge is encoded implicitly in a model’s learned weights. Non-parametric knowledge lives outside the model—such as in documents or an index—and is fetched when needed. External knowledge can be revised without retraining the model.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#knowledge.retrieval-augmented-generation">Retrieval-augmented generation</a><span>Generative AI · Retrieval</span></div><p>A generation pattern that retrieves relevant external information for the current query and includes it in the model’s input before the answer is generated. Retrieval changes the context, not the model’s weights.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#knowledge.rag-vs-fine-tuning">RAG vs fine-tuning</a><span>Generative AI · Retrieval</span></div><p>RAG supplies external knowledge at inference time; fine-tuning changes model weights through training. Use retrieval as the default for changing, sourceable facts and fine-tuning for learned behavior, format, or task adaptation. They can be combined.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#knowledge.indexing-vs-query-pipeline">Indexing pipeline vs query pipeline</a><span>Generative AI · Retrieval</span></div><p>The indexing pipeline prepares source content before questions arrive: load, split, enrich, and store it. The query pipeline runs per request: interpret the question, retrieve evidence, assemble context, generate, and validate the answer.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#knowledge.grounding">Grounding</a><span>Generative AI · Retrieval</span></div><p>Connecting generated claims to supplied evidence or an authoritative external source. Grounding makes answers inspectable and can reduce unsupported claims, but retrieval and generation can still fail.</p></div></div></details>
<hr>
<p><em>Ashvale Benefits, its people, and its metrics are fictional. Technical grounding: <a href="https://arxiv.org/abs/2005.11401" target="_blank" rel="noopener noreferrer" class="">the original RAG paper</a>, <a href="https://aclanthology.org/2024.emnlp-main.444/" target="_blank" rel="noopener noreferrer" class="">research on fine-tuning with new factual knowledge</a>, and <a href="https://aclanthology.org/2024.findings-naacl.100/" target="_blank" rel="noopener noreferrer" class="">an empirical study of groundedness in retrieval-augmented generation</a>.</em></p>]]></content:encoded>
            <category>LLM engineering</category>
            <category>Retrieval-augmented generation</category>
            <category>Knowledge systems</category>
            <category>Common misconception</category>
        </item>
        <item>
            <title><![CDATA[The retry that made the outage worse]]></title>
            <link>http://localhost:3001/stories/the-retry-that-made-the-outage-worse</link>
            <guid>http://localhost:3001/stories/the-retry-that-made-the-outage-worse</guid>
            <pubDate>Sun, 09 Aug 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A fictional production incident about layered retries, abandoned AI inference, and why a fleet of local circuit breakers failed to protect recovery.]]></description>
            <content:encoded><![CDATA[<div class="storyStatus"><span>Architecture story</span><span class="storyLevelChip storyLevelChip--advanced" title="Trace interacting mechanisms and compare production tradeoffs." aria-label="Advanced level: Trace interacting mechanisms and compare production tradeoffs.">Advanced</span>A fictional production incident about retry ownership across an AI risk-decision path.</div>
<aside class="storyLearningGoals" aria-labelledby="story-learning-goals-title"><div><span>Before the incident</span><h2 id="story-learning-goals-title">What you'll learn from this story</h2></div><ul><li><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg><span>Calculate how independent retry policies multiply across service, agent, and network boundaries.</span></li><li><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg><span>Explain why a timeout can stop the wait while expensive model inference continues elsewhere.</span></li><li><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg><span>Design one deadline, retry budget, and circuit-breaker boundary for a distributed AI request.</span></li><li><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg><span>Choose a degraded mode by pricing its business cost and its downstream operating capacity.</span></li></ul></aside>
<p><strong>Kiteframe Pay</strong> (a fictional payment-risk infrastructure company) helps online marketplaces decide whether a card order should be approved, declined, or reviewed by a human. Merchants call Kiteframe during checkout, before they capture money or promise inventory to a shopper.</p>
<p>Most orders never need generative AI. Deterministic rules settle obvious cases in under 80 milliseconds. The difficult eight percent—new devices, unusual delivery patterns, sparse account histories—enter <strong>Aster</strong>, Kiteframe's AI-assisted risk analyst.</p>
<p>Aster was not a chatbot and could not approve a payment. It gathered approved evidence, used a language model to produce a typed risk recommendation with cited signals, and passed that recommendation to a deterministic merchant-policy engine. Without Aster, those ambiguous orders went to a manual-review queue. During a large sale, that queue could grow faster than Kiteframe's analysts could empty it.</p>
<p>At <strong>02:13 on Tuesday</strong>, model latency rose sharply. Seven minutes later, Kiteframe's entire checkout success rate had fallen from 99.4% to 71%.</p>
<p>The model provider was recovering.</p>
<p>Kiteframe's retries would not let it.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="where-ai-sits-in-a-checkout">Where AI sits in a checkout<a href="http://localhost:3001/stories/the-retry-that-made-the-outage-worse#where-ai-sits-in-a-checkout" class="hash-link" aria-label="Direct link to Where AI sits in a checkout" title="Direct link to Where AI sits in a checkout" translate="no">​</a></h2>
<p><strong>Mina Rao</strong> (the Principal Reliability Engineer responsible for Kiteframe's risk-decision platform) began the incident by drawing the useful path, not the failure. One merchant request entered <code>risk-api</code>. A bounded Python worker assembled evidence. The model returned a schema-constrained recommendation. A policy service—not the model—owned the payment decision.</p>
<p>The architecture below lets you switch between that normal journey, the amplification incident, and the protected design. The numbered explanation remains readable on a phone; full-screen mode exposes the production boundaries and zoom controls.</p>
<div class="failoverTopology failoverTopology--loading">Loading interactive production architecture…</div>
<details class="conceptPanel"><summary><strong>Concepts in this story</strong><span class="conceptPanel__count">8<!-- --> concepts</span></summary><div class="conceptPanel__list"><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#reliability.timeout">Timeout</a><span>Reliability · Failure control</span></div><p>A limit on how long a caller will wait before treating an operation as failed. A timeout ends the wait; it only stops the underlying work when cancellation is propagated.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#reliability.retry">Retry</a><span>Reliability · Failure control</span></div><p>A new attempt after a failed one. Retries help with transient faults, but they add load and can repeat side effects unless the operation is safe to retry.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#reliability.exponential-backoff">Exponential backoff</a><span>Reliability · Failure control</span></div><p>A retry schedule in which the delay grows exponentially after each failure, usually up to a cap. It reduces pressure on a dependency that is still unhealthy.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#reliability.jitter">Jitter</a><span>Reliability · Failure control</span></div><p>Random variation added to retry delays so many clients do not retry in lockstep. It spreads recovery traffic over time instead of creating another spike.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#reliability.retry-storm">Retry storm</a><span>Reliability · Failure control</span></div><p>A feedback loop where failed requests trigger enough retries to raise load, deepen the failure, and delay recovery.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#reliability.circuit-breaker">Circuit breaker</a><span>Reliability · Failure control</span></div><p>A stateful guard that stops calls to a failing dependency for a limited time, then probes for recovery. It protects both systems; it is not another retry mechanism.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#reliability.circuit-breaker-states">Closed / open / half-open states</a><span>Reliability · Failure control</span></div><p>Closed allows calls and records outcomes. Open rejects calls immediately. After a cooldown, half-open admits a small number of probes and uses their results to close or reopen the circuit.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#reliability.degraded-response">Fallback / degraded response</a><span>Reliability · Failure control</span></div><p>A deliberately reduced result returned when the preferred dependency or path is unavailable—for example, cached data without personalization.</p></div></div></details>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="three-teams-had-each-added-a-sensible-retry">Three teams had each added a sensible retry<a href="http://localhost:3001/stories/the-retry-that-made-the-outage-worse#three-teams-had-each-added-a-sensible-retry" class="hash-link" aria-label="Direct link to Three teams had each added a sensible retry" title="Direct link to Three teams had each added a sensible retry" translate="no">​</a></h2>
<p>Kiteframe's merchant-facing services ran on Amazon EKS. A NestJS <code>checkout-api</code> called <code>risk-api</code>; a Python <code>risk-agent</code> assembled device, account, and merchant evidence; and an internal AI gateway called the external model API. Envoy sidecars handled service-to-service networking. PostgreSQL held merchant policies. Amazon SQS held manual-review cases. OpenTelemetry joined an order ID to traces across the path.</p>
<p>Each component had been tested in isolation.</p>
<p>Three retry policies had accumulated over eighteen months:</p>
<ol>
<li class=""><code>checkout-api</code> retried the whole risk evaluation once if it had no answer after 2.4 seconds.</li>
<li class=""><code>risk-agent</code> allowed three generation attempts because transient model failures were common during deploys.</li>
<li class="">Envoy retried selected connection resets and HTTP 503 responses once.</li>
</ol>
<p>No team had intended twelve model calls. Yet the theoretical multiplication for one risk decision was:</p>
<blockquote>
<p>2 whole evaluations × 3 agent attempts × 2 transport attempts = <strong>12 provider requests</strong></p>
</blockquote>
<p>A <span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_25aldeh_">retry<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span> is another attempt after a failure. The word “another” sounds harmless until several layers are each allowed to manufacture attempts for the same original work.</p>
<p>The policies also disagreed about time.</p>
<p>The merchant request had a four-second deadline. <code>checkout-api</code> spent 2.4 seconds before its retry. Each agent attempt allowed two seconds. Envoy knew nothing about either budget. A later layer could therefore begin work that had no chance of returning before the merchant had left.</p>
<p>At the model boundary, Kiteframe's 2-second <span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_262ldeh_">timeout<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span> stopped its local wait. It did not prove the provider had stopped computing. Cancellation was not propagated through every hop, and some requests had already entered the provider's queue. Their tokens continued to consume capacity even though Kiteframe would discard the late answers.</p>
<p>That distinction changed the arithmetic. The dashboards counted timed-out calls as finished client work. The provider still counted them as admitted inference.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-autoscaler-made-the-breaker-forget">The autoscaler made the breaker forget<a href="http://localhost:3001/stories/the-retry-that-made-the-outage-worse#the-autoscaler-made-the-breaker-forget" class="hash-link" aria-label="Direct link to The autoscaler made the breaker forget" title="Direct link to The autoscaler made the breaker forget" translate="no">​</a></h2>
<p>At 02:13, the provider's 99th-percentile latency rose from 1.1 seconds to 5.8 seconds and some requests returned 503. Kiteframe was receiving roughly 390 AI-routed orders per second.</p>
<p>By 02:16, it was starting more than 3,400 provider requests per second. The exact amplification varied because some calls completed and not every response qualified for every retry. The direction did not vary:</p>
<ul>
<li class="">queues increased latency;</li>
<li class="">latency crossed more local timeouts;</li>
<li class="">timeouts created more attempts;</li>
<li class="">those attempts deepened the queues.</li>
</ul>
<p>The recovery mechanism had become a <span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_27ildeh_">retry storm<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span>.</p>
<p>The team believed it already had a <span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_27qldeh_">circuit breaker<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span>. After twelve failures, each <code>risk-agent</code> pod opened its local breaker and stopped calling the model for thirty seconds.</p>
<p>Then Kubernetes did exactly what Kiteframe had configured it to do. CPU and in-flight work rose, so the Horizontal Pod Autoscaler expanded the deployment from 24 pods to 78. Each new pod started with an empty memory and a <strong>closed</strong> breaker. It admitted model traffic while older pods were rejecting it.</p>
<p>The breaker had protected one process. The dependency was being attacked by a fleet.</p>
<p>At 02:18, the provider restored enough capacity for about 2,200 request starts per second. Kiteframe was still offering far more. A health chart turned green for forty seconds, then red again as delayed retries and new pods arrived together.</p>
<p>Mina stopped the autoscaler at 78 replicas. That prevented another wave of fresh breaker state, but it did not establish which layer owned retries or how much work the fleet was permitted to admit.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-trace-looked-like-a-tree-not-a-request">The trace looked like a tree, not a request<a href="http://localhost:3001/stories/the-retry-that-made-the-outage-worse#the-trace-looked-like-a-tree-not-a-request" class="hash-link" aria-label="Direct link to The trace looked like a tree, not a request" title="Direct link to The trace looked like a tree, not a request" translate="no">​</a></h2>
<p>The incident review began with one order, <code>ord_81K2</code>.</p>
<p>Its root trace had two <code>risk.evaluate</code> spans. Beneath them were six generation spans. Transport telemetry showed eleven provider request starts; one connection had failed before Envoy could send a body. Four model requests finished after their parent spans had timed out. Two produced identical valid recommendations that nobody read.</p>
<p>Nothing in Aster's reasoning caused the outage. The prompts were valid. The tool evidence was current. The typed responses passed schema validation.</p>
<p>AI was still load-bearing in the incident because model inference was the scarce, slow, externally scheduled work being amplified. Replacing the model with a database call would have changed the cost and latency profile. It would not have fixed retry ownership.</p>
<p>Mina's first rule for the repair was therefore architectural:</p>
<blockquote>
<p>One customer intent gets one deadline and one retry budget. Crossing a boundary does not create more of either.</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="one-place-was-allowed-to-try-again">One place was allowed to try again<a href="http://localhost:3001/stories/the-retry-that-made-the-outage-worse#one-place-was-allowed-to-try-again" class="hash-link" aria-label="Direct link to One place was allowed to try again" title="Direct link to One place was allowed to try again" translate="no">​</a></h2>
<p>Kiteframe moved model-attempt policy into the AI gateway. The other layers stopped retrying generation.</p>
<p><code>risk-api</code> created an absolute deadline and an attempt budget when it accepted the order. <code>risk-agent</code> inherited them. Envoy retries were disabled for the model cluster. The AI gateway could make one additional attempt only for a narrow set of transient failures, only when budget remained, and only when enough deadline remained for the result to be useful.</p>
<p>The delay used capped <span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_2bildeh_">exponential backoff<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span>. Its maximum grew after a failure. The actual wait used full <span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_4bildeh_">jitter<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span>, choosing a random delay below that cap so every pod did not wake on the same boundary.</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">src/ai/generate-risk-recommendation.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">generateRiskRecommendation</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">DecisionContext</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// Allocated once at ingress; inherited by every downstream hop.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> budget </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">retryBudget</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> deadline </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">deadline</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">while</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">budget</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">tryConsumeAttempt</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> remainingMs </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> deadline</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">remainingMs</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">remainingMs </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">MIN_USEFUL_ATTEMPT_MS</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">break</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> permit </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> aiAdmission</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">tryAcquire</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      merchantId</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">merchantId</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      deadline</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">permit</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">break</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">try</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> model</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">generate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">prompt</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">        signal</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> deadline</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">abortSignal</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        timeoutMs</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token known-class-name class-name">Math</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">min</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1_600</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> remainingMs</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token function" style="color:#d73a49">isTransient</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!</span><span class="token plain">budget</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">canRetry</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">throw</span><span class="token plain"> error</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> capMs </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token known-class-name class-name">Math</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">min</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">200</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">**</span><span class="token plain"> budget</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">attemptsUsed</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1_200</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">sleep</span><span class="token punctuation" style="color:#393A34">(</span><span class="token known-class-name class-name">Math</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">random</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> capMs</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> deadline</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">abortSignal</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">finally</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">      permit</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">release</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">degradedDecision</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>The abort signal made cancellation explicit through Kiteframe's own code. It still could not guarantee that a remote provider would erase already-admitted work. The gateway therefore treated cancellation as a capacity hint, not as proof of zero downstream cost.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-breaker-for-the-destination-not-the-pod">A breaker for the destination, not the pod<a href="http://localhost:3001/stories/the-retry-that-made-the-outage-worse#a-breaker-for-the-destination-not-the-pod" class="hash-link" aria-label="Direct link to A breaker for the destination, not the pod" title="Direct link to A breaker for the destination, not the pod" translate="no">​</a></h2>
<p>The repaired gateway maintained one logical model-destination policy across its replicas. A Redis-backed token bucket enforced a fleet-wide concurrency ceiling and fair per-merchant admission. If Redis was unavailable, the AI path failed closed into degraded mode instead of silently admitting unbounded inference.</p>
<p>The breaker's <span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_2cqldeh_">closed, open, and half-open states<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span> now described the gateway's relationship with the model destination:</p>
<ul>
<li class=""><strong>Closed:</strong> admit calls up to the concurrency limit and measure outcomes.</li>
<li class=""><strong>Open:</strong> admit no normal calls; return degraded decisions immediately.</li>
<li class=""><strong>Half-open:</strong> after a cooldown, allow at most twenty probes per second across the fleet. Successful probes closed the circuit gradually; failures reopened it.</li>
</ul>
<p>This design introduced coordination cost. Redis became part of the admission path. Shared state could be delayed. A breaker threshold could open during a brief provider wobble and reject work that might have succeeded.</p>
<p>Kiteframe accepted those costs because the alternative allowed every new pod to rediscover the same outage by spending provider capacity.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-fallback-needed-its-own-capacity-model">The fallback needed its own capacity model<a href="http://localhost:3001/stories/the-retry-that-made-the-outage-worse#the-fallback-needed-its-own-capacity-model" class="hash-link" aria-label="Direct link to The fallback needed its own capacity model" title="Direct link to The fallback needed its own capacity model" translate="no">​</a></h2>
<p>Opening the breaker did not make risk disappear.</p>
<p>For established merchants and low-risk returning shoppers, the policy engine could use a stricter deterministic rule set. New or conflicting identities went to SQS for human review. Checkout displayed a pending-review state instead of pretending an AI recommendation existed.</p>
<p>That was a <span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_2eildeh_">fallback, or degraded response<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span>: a deliberately reduced service when the preferred path was unavailable.</p>
<p>The first version was operationally unsafe. It could enqueue 390 cases per second while the review team could resolve only 42. A “successful” fallback would merely move the outage to tomorrow's queue.</p>
<p>Product, fraud operations, and reliability set an explicit degraded-mode budget:</p>
<ul>
<li class="">deterministic policy could approve only a reviewed low-risk segment;</li>
<li class="">the manual queue admitted cases until its projected age reached forty minutes;</li>
<li class="">after that bound, remaining ambiguous checkouts received an honest retry-later response;</li>
<li class="">large merchants received fair shares so one flash sale could not consume the whole review team.</li>
</ul>
<p>During the next provider impairment, 404 ambiguous orders per second reached Kiteframe. The gateway capped provider starts at 520 per second. When the breaker opened, only twenty global probes per second tested recovery. Checkout remained available for deterministic decisions, while 31,406 orders entered review and 6,218 received retry-later responses.</p>
<p>Conversion for the affected segment fell 4.8%. Fraud operations worked an elevated queue for three hours.</p>
<p>Those were not footnotes to the technical solution. They were the price of keeping the system inside known capacity while its preferred intelligence was unavailable.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-production-path-after-the-incident">The production path after the incident<a href="http://localhost:3001/stories/the-retry-that-made-the-outage-worse#the-production-path-after-the-incident" class="hash-link" aria-label="Direct link to The production path after the incident" title="Direct link to The production path after the incident" translate="no">​</a></h2>
<p>Switch the architecture to <strong>Protected path</strong> to see the final ownership boundaries. The most important component is not the retry loop. It is the single admission point before expensive inference, backed by a deadline that still represents the merchant's request.</p>
<div class="failoverTopology failoverTopology--loading">Loading interactive production architecture…</div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-mina-carried-into-the-next-review">What Mina carried into the next review<a href="http://localhost:3001/stories/the-retry-that-made-the-outage-worse#what-mina-carried-into-the-next-review" class="hash-link" aria-label="Direct link to What Mina carried into the next review" title="Direct link to What Mina carried into the next review" translate="no">​</a></h2>
<p>A timeout says, “this caller stopped waiting.” It does not say, “the work stopped.”</p>
<p>A retry can improve availability when the failure is transient, the operation remains useful, and one layer owns the policy. Repeated independently across a call graph, it can turn a partial provider impairment into a platform outage.</p>
<p>A circuit breaker protects the boundary where it observes and rejects work. If every autoscaled process owns a fresh breaker, the fleet may have no breaker at all.</p>
<p>And a fallback is not free availability. It is another production system with a quality limit, a queue, and a finite team behind it.</p>
<details class="conceptPanel"><summary><strong>Concepts in this story</strong><span class="conceptPanel__count">8<!-- --> concepts</span></summary><div class="conceptPanel__list"><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#reliability.timeout">Timeout</a><span>Reliability · Failure control</span></div><p>A limit on how long a caller will wait before treating an operation as failed. A timeout ends the wait; it only stops the underlying work when cancellation is propagated.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#reliability.retry">Retry</a><span>Reliability · Failure control</span></div><p>A new attempt after a failed one. Retries help with transient faults, but they add load and can repeat side effects unless the operation is safe to retry.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#reliability.exponential-backoff">Exponential backoff</a><span>Reliability · Failure control</span></div><p>A retry schedule in which the delay grows exponentially after each failure, usually up to a cap. It reduces pressure on a dependency that is still unhealthy.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#reliability.jitter">Jitter</a><span>Reliability · Failure control</span></div><p>Random variation added to retry delays so many clients do not retry in lockstep. It spreads recovery traffic over time instead of creating another spike.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#reliability.retry-storm">Retry storm</a><span>Reliability · Failure control</span></div><p>A feedback loop where failed requests trigger enough retries to raise load, deepen the failure, and delay recovery.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#reliability.circuit-breaker">Circuit breaker</a><span>Reliability · Failure control</span></div><p>A stateful guard that stops calls to a failing dependency for a limited time, then probes for recovery. It protects both systems; it is not another retry mechanism.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#reliability.circuit-breaker-states">Closed / open / half-open states</a><span>Reliability · Failure control</span></div><p>Closed allows calls and records outcomes. Open rejects calls immediately. After a cooldown, half-open admits a small number of probes and uses their results to close or reopen the circuit.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#reliability.degraded-response">Fallback / degraded response</a><span>Reliability · Failure control</span></div><p>A deliberately reduced result returned when the preferred dependency or path is unavailable—for example, cached data without personalization.</p></div></div></details>
<div class="storySources"><p>Kiteframe Pay, its people, and its metrics are fictional. Technical grounding: <a href="https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/" target="_blank" rel="noopener noreferrer" class="">AWS Builders' Library on timeouts, retries, backoff, and jitter</a>, <a href="https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/circuit_breaking" target="_blank" rel="noopener noreferrer" class="">Envoy retry and circuit-breaking configuration</a>, <a href="https://kubernetes.io/docs/concepts/workloads/autoscaling/horizontal-pod-autoscale/" target="_blank" rel="noopener noreferrer" class="">Kubernetes horizontal pod autoscaling</a>, and <a href="https://opentelemetry.io/docs/concepts/context-propagation/" target="_blank" rel="noopener noreferrer" class="">OpenTelemetry context propagation</a>.</p></div>]]></content:encoded>
            <category>LLM engineering</category>
            <category>Agentic systems</category>
            <category>Resilience</category>
            <category>Reliability</category>
            <category>Common misconception</category>
        </item>
        <item>
            <title><![CDATA[The shadow model judged by the old model’s evidence]]></title>
            <link>http://localhost:3001/stories/the-shadow-model-judged-by-the-old-models-evidence</link>
            <guid>http://localhost:3001/stories/the-shadow-model-judged-by-the-old-models-evidence</guid>
            <pubDate>Sun, 09 Aug 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A fictional production incident about selective labels, shadow evaluation, and the field evidence a candidate model cannot create for itself.]]></description>
            <content:encoded><![CDATA[<div class="storyStatus"><span>Architecture story</span><span class="storyLevelChip storyLevelChip--advanced" title="Trace interacting mechanisms and compare production tradeoffs." aria-label="Advanced level: Trace interacting mechanisms and compare production tradeoffs.">Advanced</span>A fictional production incident about a shadow model evaluated on field evidence selected by the incumbent.</div>
<aside class="storyLearningGoals" aria-labelledby="story-learning-goals-title"><div><span>Before the incident</span><h2 id="story-learning-goals-title">What you'll learn from this story</h2></div><ul><li><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg><span>Distinguish an uninspected asset from an observed negative condition.</span></li><li><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg><span>Explain why a shadow evaluation can inherit the incumbent model's selective-label policy.</span></li><li><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg><span>Trace how thresholds and finite inspection capacity shape today's actions and tomorrow's evidence.</span></li><li><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg><span>Find unobserved cells in an incumbent-versus-candidate score matrix before trusting the comparison.</span></li><li><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-check" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path></svg><span>Compare sentinel sampling with a bounded disagreement canary and state what each cannot prove.</span></li></ul></aside>
<p><strong>BrambleGrid</strong> is a fictional grid-asset inspection company. Regional electric utilities hire it to combine drone imagery, asset records, and certified field inspection so scarce crews visit pole-top equipment that most needs a closer look.</p>
<p>A drone could photograph thousands of connector assemblies in a morning. A physical visit was different. It needed access coordination, a qualified crew, and enough time to perform the same close-range inspection protocol on every asset. BrambleGrid had capacity for 240 flexible visits per week. Twenty always remained reserved for manual safety reports that did not come from a model.</p>
<p>That made field inspection both an operating resource and an evidence resource.</p>
<p>BrambleGrid's ML-assisted product was called <strong>Spanwatch</strong>. For one recent connector evidence snapshot, Spanwatch estimated the probability that a protocol-C3 inspection within seven days would find an actionable connector condition. It did not predict an outage, and it could not authorize a repair.</p>
<p><strong>Dalia Moravec</strong> (the ML reliability engineer responsible for calibration, evaluation cohorts, and launch recommendations) owned Spanwatch's model evidence.</p>
<p><strong>Jon Ibarra</strong> (the field-inspection planning lead responsible for the weekly 240-visit capacity ledger) owned which proposed visits entered the field workflow.</p>
<p><strong>Mara Venn</strong> (the lead asset-integrity engineer responsible for protocol-C3 adjudication and label revision) owned the final condition record. A utility duty engineer—not Spanwatch, Dalia, Jon, or Mara—separately decided whether to restrict or repair an asset.</p>
<p>The boundaries looked fussy until the week BrambleGrid tried to replace its model.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-one-connector-becomes-evidence">How one connector becomes evidence<a href="http://localhost:3001/stories/the-shadow-model-judged-by-the-old-models-evidence#how-one-connector-becomes-evidence" class="hash-link" aria-label="Direct link to How one connector becomes evidence" title="Direct link to How one connector becomes evidence" translate="no">​</a></h2>
<div class="failoverTopology failoverTopology--loading">Loading interactive story context…</div>
<p>The journey matters because its handoffs prevent a fluent model score from
quietly becoming an operational fact. Admission, physical visit state,
condition adjudication, and utility action remain separate authorities; the
evaluation can make a bounded claim only by retaining those handoffs.</p>
<p>Three things in that path were easy to collapse in conversation.</p>
<p>The <strong>score</strong> estimated what a C3 inspection would find. The <strong>condition label</strong> recorded what a completed C3 inspection did find. Between them sat a <span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_63qldeh_">decision policy<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span>: a versioned rule that combined the score, thresholds, capacity, and current context to choose inspect, review, or defer.</p>
<p>The threshold was not part of the model. A score did not spend a visit. A visit did not authorize repair. Repair did not prove that a connector would otherwise have failed.</p>
<p>Those distinctions were already reflected in BrambleGrid's implementation. Large images lived in restricted object storage, while small events carried evidence references. Point-in-time features fed two PyTorch models behind a scoring service. A Kotlin policy service owned thresholds. A PostgreSQL-backed FieldOps service and capacity ledger owned admission and visit state. A separate Condition Registry owned C3 observations and append-only revisions. Iceberg tables, dbt jobs, and MLflow identities supported evaluation.</p>
<p>The tool names were replaceable. The authority boundaries were not.</p>
<details class="conceptPanel"><summary><strong>Concepts in this story</strong><span class="conceptPanel__count">5<!-- --> concepts</span></summary><div class="conceptPanel__list"><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#decision.decision-policy">Decision policy</a><span>ML systems · Operational decisions</span></div><p>A versioned rule that maps model evidence, thresholds, current context, capacity, and constraints into an action such as automate, inspect, review, defer, or abstain. The model supplies evidence; the policy owns the action boundary.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#evaluation.shadow-evaluation">Shadow evaluation</a><span>ML systems · Evaluation evidence</span></div><p>Running a candidate model beside the live system without letting it control production actions. This limits exposure, but observed outcomes still reflect the incumbent policy unless the evaluation creates an independent evidence path.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#decision.selective-labels">Selective labels</a><span>ML systems · Feedback and labels</span></div><p>Labels observed for a non-random subset because a prior decision policy determines which cases receive the action, inspection, approval, review, or follow-up that can reveal an outcome.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#decision.sampling-frame">Sampling frame</a><span>ML systems · Evaluation evidence</span></div><p>The explicit register of eligible units available for selection into a sample. It is distinct from the target population, the design that chooses units from the frame, and the observed cohort that actually produces usable labels.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#decision.disagreement-canary">Disagreement canary</a><span>ML systems · Evaluation evidence</span></div><p>A bounded live policy that deliberately acquires evidence where an incumbent and candidate would choose different actions. It can estimate value in that disagreement region, but it does not establish whole-population model quality.</p></div></div></details>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-safe-way-to-test-s5">The safe way to test S5<a href="http://localhost:3001/stories/the-shadow-model-judged-by-the-old-models-evidence#the-safe-way-to-test-s5" class="hash-link" aria-label="Direct link to The safe way to test S5" title="Direct link to The safe way to test S5" translate="no">​</a></h2>
<p>Spanwatch's production model was <strong>S4</strong>.</p>
<p>Each week, policy <code>P17.4</code> divided the 240 flexible visits like this:</p>
<table><thead><tr><th>Route</th><th style="text-align:right">Visits per week</th></tr></thead><tbody><tr><td>S4 score at least 0.86: urgent field admission</td><td style="text-align:right">120</td></tr><tr><td>S4 score at least 0.58 and below 0.86: deterministic desk-review route</td><td style="text-align:right">100</td></tr><tr><td>Manual and safety reserve</td><td style="text-align:right">20</td></tr><tr><td><strong>Total</strong></td><td style="text-align:right"><strong>240</strong></td></tr></tbody></table>
<p>The threshold branch was deterministic: every connector satisfying <code>0.58 &lt;= S4 &lt; 0.86</code> entered the review band. The review desk could examine 500 review-band cases each week, but it could escalate at most 100 into the field budget. A reviewed case that did not receive a visit remained unobserved for the C3 condition target. It did not become <code>CLEAR</code>.</p>
<p>Dalia's candidate model, <strong>S5</strong>, detected a condition pattern on newer composite cross-arms that S4 often scored below 0.58. On BrambleGrid's older test data, S5 looked promising.</p>
<p>The team did not let it control field work immediately. For four weeks, S4 and S5 scored the same evidence snapshots, but only S4 entered <code>P17.4</code>.</p>
<p>That was a <span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_26qldeh_">shadow evaluation<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span>: the candidate produced predictions beside the live model without controlling the live action.</p>
<p>It was a competent safety choice. S5 could not unexpectedly consume field capacity. It could not delay S4's urgent inspections. It could not change a utility decision while the team was still evaluating it.</p>
<p>The team made one assumption that felt equally safe:</p>
<blockquote>
<p>Because S5 cannot change production, the condition labels created during the shadow are neutral evidence for comparing S4 and S5.</p>
</blockquote>
<p>That assumption survived the launch checklist.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-report-that-rejected-s5">The report that rejected S5<a href="http://localhost:3001/stories/the-shadow-model-judged-by-the-old-models-evidence#the-report-that-rejected-s5" class="hash-link" aria-label="Direct link to The report that rejected S5" title="Direct link to The report that rejected S5" translate="no">​</a></h2>
<p>Across four stable weeks, the locked eligibility register—the sampling frame—contained 40,000 unique eligible connector assemblies. In the team's matrix, <strong>screen positive</strong> meant only that a model scored the case at or above 0.58 and would send it toward the evidence-acquisition path. It did not mean the connector was faulty.</p>
<table><thead><tr><th>Cell</th><th>Reader-facing meaning</th><th>S4 screen</th><th>S5 screen</th><th style="text-align:right">Connectors</th></tr></thead><tbody><tr><td>A</td><td>Both would inspect or review</td><td>positive</td><td>positive</td><td style="text-align:right">1,600</td></tr><tr><td>B</td><td>S4 only</td><td>positive</td><td>negative</td><td style="text-align:right">400</td></tr><tr><td>C</td><td>S5 only</td><td>negative</td><td>positive</td><td style="text-align:right">800</td></tr><tr><td>D</td><td>Neither</td><td>negative</td><td>negative</td><td style="text-align:right">37,200</td></tr><tr><td><strong>Total</strong></td><td></td><td></td><td></td><td style="text-align:right"><strong>40,000</strong></td></tr></tbody></table>
<p>S4's policy created 880 protocol-C3 observations through the model-selected route:</p>
<table><thead><tr><th>Cell</th><th style="text-align:right">C3 visits selected by S4</th><th style="text-align:right">Final actionable</th><th style="text-align:right">Final clear</th><th style="text-align:right">No model-route C3 label</th></tr></thead><tbody><tr><td>A: both screen positive</td><td style="text-align:right">720</td><td style="text-align:right">360</td><td style="text-align:right">360</td><td style="text-align:right">880</td></tr><tr><td>B: S4 only</td><td style="text-align:right">160</td><td style="text-align:right">112</td><td style="text-align:right">48</td><td style="text-align:right">240</td></tr><tr><td>C: S5 only</td><td style="text-align:right">0</td><td style="text-align:right">—</td><td style="text-align:right">—</td><td style="text-align:right">800 unknown</td></tr><tr><td>D: neither</td><td style="text-align:right">0</td><td style="text-align:right">—</td><td style="text-align:right">—</td><td style="text-align:right">37,200 unknown</td></tr></tbody></table>
<p>The launch report performed a technically correct join between final C3 labels and the two score receipts.</p>
<p>S4 had selected all 880 visited cases in A and B. Of those, 472 were actionable:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">S4 observed yield = 472 / 880 = 53.6%</span><br></div></code></pre></div></div>
<p>Among the 720 visited cases in A that S5 also screened positive, 360 were actionable:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">S5-positive observed yield = 360 / 720 = 50.0%</span><br></div></code></pre></div></div>
<p>Fifty-three point six beat fifty.</p>
<p>The report concluded that S5 found no extra field value. The launch council rejected it and proposed raising S4's admission threshold, which would spend still fewer visits near the uncertain boundary.</p>
<p>Nothing in the arithmetic was fabricated. Every one of the 880 condition labels could be correct.</p>
<p>The problem was where the labels existed.</p>
<div class="failoverTopology failoverTopology--loading">Loading interactive incident sequence…</div>
<p>The sequence exposes the asymmetry the report hid: correct arithmetic over the
S4-created cohort said nothing about the candidate-only region. Cell C's 800
connectors remained unknown, and later routine-route evidence could reopen that
question without filling the original cohort.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-incumbent-policy-controlled-label-observability">The incumbent policy controlled label observability<a href="http://localhost:3001/stories/the-shadow-model-judged-by-the-old-models-evidence#the-incumbent-policy-controlled-label-observability" class="hash-link" aria-label="Direct link to The incumbent policy controlled label observability" title="Direct link to The incumbent policy controlled label observability" translate="no">​</a></h2>
<p>The deployment had created <span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_2caldeh_">selective labels<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span>: outcomes were observed for a non-random subset chosen by the existing score and policy.</p>
<p>S4 did not change whether a physical connector condition existed. It changed which conditions BrambleGrid paid to observe under protocol C3.</p>
<p>That distinction bounded the report.</p>
<p>Among connectors S4 selected for inspection, the S5-positive subset had lower observed actionable-condition yield. The result could describe those visited cases.</p>
<p>It could not compare an S4-controlled policy with an S5-controlled policy across 40,000 eligible connectors. S5's claimed value was in C—the exact cell where shadow mode prevented it from causing observations.</p>
<p>The <span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_2dildeh_">sampling frame<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span> was the locked eligibility register of 40,000 connectors. Their point-in-time S4/S5 score receipts assigned them to A, B, C, or D; those attributes did not turn the observed subset into a different frame. <code>P17.4</code> was the selection and observation design applied to the frame: it determined which connectors had a non-zero path to a model-route inspection. The 880 completed visits in A and B with final binary C3 labels were the observed cohort.</p>
<p>That vocabulary bounded the inference precisely. The <code>53.6%</code> and <code>50.0%</code> yields described subsets of the S4-selected observed cohort. They did not compare an S4-controlled policy with an S5-controlled policy over the 40,000-member sampling frame, because <code>P17.4</code> created no model-route observations in C or D.</p>
<p>This was deeper than replacing missing values with zero. BrambleGrid had not done that. Its dataset correctly marked unvisited assets as unknown. The team had still overextended a valid narrow result into a release decision about a different action policy.</p>
<p>The proposed threshold increase made the mistake self-reinforcing. S4 would send fewer boundary cases into FieldOps. Those cases would produce fewer future C3 labels. A later candidate would be judged in an evidence world shaped even more strongly by S4.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-the-annual-route-didand-did-notshow">What the annual route did—and did not—show<a href="http://localhost:3001/stories/the-shadow-model-judged-by-the-old-models-evidence#what-the-annual-route-didand-did-notshow" class="hash-link" aria-label="Direct link to What the annual route did—and did not—show" title="Direct link to What the annual route did—and did not—show" translate="no">​</a></h2>
<p>BrambleGrid also operated a routine annual inspection program. It had its own selection policy, <code>AR-2026.3</code>, based on inspection age, route geography, access windows, and planned utility work. Many not-yet-due assets had no chance of inclusion. The annual route was selected, versioned evidence—not a random control group.</p>
<p>About two months after the launch review, visits under a successor route plan found actionable conditions on composite-arm connectors in the score region S5 had elevated and S4 had left alone.</p>
<p>That discovery mattered because it falsified the council's confidence that the S4-selected report had closed the question.</p>
<p>It did not retroactively label cell C.</p>
<p>Those later visits observed connector condition at a later time, under a different route version and outside the original seven-day target window. They did not prove that S5 was globally more accurate. They did not prove that any repaired connector would have failed. They told Dalia only that the omitted region deserved a valid evidence path.</p>
<p>The council withdrew its threshold proposal and asked a better question:</p>
<blockquote>
<p>Which decision are we trying to make, and what observation policy would let us make that claim?</p>
</blockquote>
<p>Dalia answered by reconstructing two exact worked counterfactuals. Each design
would have had to be assigned at the <strong>original four-week frame lock</strong>, before
the target labels were visible. The tables below are worked alternative
outcomes for those predeclared designs, not measurements BrambleGrid collected
after the annual-route discovery. Neither alternative actually ran during the
incident, they could not both have shared one capacity ledger, and the old score
receipts and cohort gained no labels from this reconstruction.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="worked-counterfactual-a-a-sentinel-frame">Worked counterfactual A: a sentinel frame<a href="http://localhost:3001/stories/the-shadow-model-judged-by-the-old-models-evidence#worked-counterfactual-a-a-sentinel-frame" class="hash-link" aria-label="Direct link to Worked counterfactual A: a sentinel frame" title="Direct link to Worked counterfactual A: a sentinel frame" translate="no">​</a></h2>
<p>If BrambleGrid had wanted to compare the S4-positive and S5-positive score regions across the declared four-week frame, it would have needed non-zero observation probability in every cell from the original frame lock.</p>
<p>The first worked design would have reserved 60 of the 240 weekly visits for a predeclared sentinel sample:</p>
<table><thead><tr><th>Weekly route</th><th style="text-align:right">Visits</th></tr></thead><tbody><tr><td>S4 urgent</td><td style="text-align:right">120</td></tr><tr><td>S4 lower-priority review-band escalations</td><td style="text-align:right">40</td></tr><tr><td>Sentinel A / B / C / D</td><td style="text-align:right">10 / 10 / 20 / 20</td></tr><tr><td>Manual and safety reserve</td><td style="text-align:right">20</td></tr><tr><td><strong>Total</strong></td><td style="text-align:right"><strong>240</strong></td></tr></tbody></table>
<p>The sampling service would have created a keyed random order independently within A, B, C, and D before condition evidence was visible. Within each cell, every eligible connector would have had one equal inclusion probability. Geography and asset attributes could balance route execution only after that keyed order was fixed; they were operational attributes, not statistical sub-strata with separate inclusion probabilities or weights. A cancellation would have remained missing unless the system used the next pre-ranked case from the same cell before the seven-day window closed.</p>
<p>After four weeks, the exact worked counterfactual would have contained 240 completed sentinel visits:</p>
<table><thead><tr><th>Cell</th><th style="text-align:right">Sentinel visits</th><th style="text-align:right">Actionable</th><th style="text-align:right">Observed sample yield</th></tr></thead><tbody><tr><td>A</td><td style="text-align:right">40</td><td style="text-align:right">20</td><td style="text-align:right">50.0%</td></tr><tr><td>B</td><td style="text-align:right">40</td><td style="text-align:right">12</td><td style="text-align:right">30.0%</td></tr><tr><td>C</td><td style="text-align:right">80</td><td style="text-align:right">56</td><td style="text-align:right">70.0%</td></tr><tr><td>D</td><td style="text-align:right">80</td><td style="text-align:right">1</td><td style="text-align:right">1.25%</td></tr></tbody></table>
<p>Because the cell-level inclusion probabilities would have been known and non-zero, the evaluation could have accounted for the unequal sampling. For this declared frame, the worked estimate was:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">S4-positive region yield = 46.0%</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">S5-positive region yield = 56.7%</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">difference               = +10.7 percentage points</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">approximate 95% range    = +6.0 to +15.3 points</span><br></div></code></pre></div></div>
<p>This result would have changed the release discussion. It would not have proved that S5 was globally safer or would prevent more service events. The both-low D estimate remained especially imprecise: one worked actionable observation carried a large weight. The range described uncertainty under the locked sampling assumptions, not certainty about the future.</p>
<p>The design would have cost 60 lower-priority S4 review-band visits each week for four weeks—25% of all flexible capacity. Urgent inspections and the 20 manual/safety slots would have remained protected, but some incumbent boundary cases would have waited in the normal cycle. BrambleGrid would also have paid for more travel to apparently routine assets and waited through the final 48-hour adjudication window.</p>
<p>The sentinel would have answered a broad score-region comparison by buying broader evidence at the original frame lock.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="worked-counterfactual-b-expose-only-the-disagreement">Worked counterfactual B: expose only the disagreement<a href="http://localhost:3001/stories/the-shadow-model-judged-by-the-old-models-evidence#worked-counterfactual-b-expose-only-the-disagreement" class="hash-link" aria-label="Direct link to Worked counterfactual B: expose only the disagreement" title="Direct link to Worked counterfactual B: expose only the disagreement" translate="no">​</a></h2>
<p>In the reconstruction, Jon posed a narrower question:</p>
<blockquote>
<p>Before comparing both models over the whole frame, can we learn whether cell C is valuable enough to keep investigating?</p>
</blockquote>
<p>For that question, the second worked alternative was a <span class="inlineConcept"><button type="button" class="inlineConcept__trigger" aria-expanded="false" aria-controls="_R_2kildeh_">disagreement canary<span aria-hidden="true" class="inlineConcept__symbol">+</span></button></span>: a bounded live policy that would have acquired evidence where incumbent and candidate actions differed, had it been assigned at the original frame lock.</p>
<p>S4 would have remained the policy everywhere except cell C. Inside C, the system would have randomly selected 40 of roughly 200 eligible connectors each week for field inspection. Every selection would have carried its <code>P17-CANARY.1</code> policy version and assignment probability.</p>
<table><thead><tr><th>Weekly route</th><th style="text-align:right">Visits</th></tr></thead><tbody><tr><td>S4 urgent</td><td style="text-align:right">120</td></tr><tr><td>S4 lower-priority review-band escalations</td><td style="text-align:right">60</td></tr><tr><td>Randomly assigned cell-C disagreements</td><td style="text-align:right">40</td></tr><tr><td>Manual and safety reserve</td><td style="text-align:right">20</td></tr><tr><td><strong>Total</strong></td><td style="text-align:right"><strong>240</strong></td></tr></tbody></table>
<p>After two weeks and the final 48-hour adjudication window, the exact worked counterfactual had 56 of 80 inspected C connectors as actionable:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">local C yield          = 56 / 80 = 70.0%</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">compatible 95% range  = 59.2% to 78.9%</span><br></div></code></pre></div></div>
<p>That result could have supported continuing, expanding, or stopping a bounded C-region rule. It could have rejected the statement that S5's exclusive region had produced no measured gain.</p>
<p>It could not estimate S5 in A or D. It could not detect a blind spot shared by both models. It could not establish whole-population precision, recall, calibration, safety, or future utility.</p>
<p>The canary would have cost 40 lower-priority S4 review-band visits each week. For two weeks, BrambleGrid would have operated mixed policies. If the canary had paused, new C admissions would have stopped, but already completed visits and labels would have kept their original canary receipts. Rollback could have restored future admission to <code>P17.4</code>; it could not have rewritten the evidence already created.</p>
<p>The counterfactual canary would have answered a local disagreement question faster by buying evidence only in C.</p>
<p>The two designs were alternatives, not layers of a universal fix, and neither was retroactively executed. If the original question had been “compare score-positive regions across the declared frame,” the sentinel would have fit. If it had been “is C worth bounded exposure before a broader comparison,” the canary would have fit. If field operations could not have accepted either capacity cost, “not enough evidence yet” would have remained an honest release decision.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-join-had-to-remember-who-selected-the-label">The join had to remember who selected the label<a href="http://localhost:3001/stories/the-shadow-model-judged-by-the-old-models-evidence#the-join-had-to-remember-who-selected-the-label" class="hash-link" aria-label="Direct link to The join had to remember who selected the label" title="Direct link to The join had to remember who selected the label" translate="no">​</a></h2>
<p>The old evaluation table joined two scores to a final condition. That was not enough. It lost the route that made the condition observable.</p>
<p>Dalia reduced the production receipt to a teaching shape:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">src/evaluation/decision-evidence.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token class-name maybe-class-name">DecisionEvidence</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  evidenceSnapshotId</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  s4</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">ScoreReceipt</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  s5</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">ScoreReceipt</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  policyId</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  capacitySnapshotId</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  selectionSource</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'S4'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'manual'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'annual'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'sentinel'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'canary'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  samplingFrameId</span><span class="token operator" style="color:#393A34">?</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  assignmentProbability</span><span class="token operator" style="color:#393A34">?</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">number</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  fieldVisit</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'completed'</span><span class="token plain"></span><br></div><div class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'expired-unobserved'</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'cancelled'</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'not-selected'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  inspectionProtocolId</span><span class="token operator" style="color:#393A34">?</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  conditionLabelVersion</span><span class="token operator" style="color:#393A34">?</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p>The real receipt also retained connector lineage, evidence and feature versions, both calibrations, threshold branch, FieldOps case state, raw observation digest, adjudication, repair reference, delayed outcome contract, cohort version, and release decision.</p>
<p>The compact type carried the central rule: the evaluation needed the policy, frame, and assignment process that made a label observable.</p>
<p>An inner join from score rows to final labels could be technically correct and still erase every connector that never had a chance to produce a label. The corrected cohort began with all 40,000 frame members, then made <code>completed</code>, <code>indeterminate</code>, <code>expired</code>, <code>cancelled</code>, and <code>not-selected</code> visible before choosing any binary denominator.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="where-each-responsibility-lives">Where each responsibility lives<a href="http://localhost:3001/stories/the-shadow-model-judged-by-the-old-models-evidence#where-each-responsibility-lives" class="hash-link" aria-label="Direct link to Where each responsibility lives" title="Direct link to Where each responsibility lives" translate="no">​</a></h2>
<p>The incident sequence showed when the inference escaped the observed cohort and was generalized to its sampling frame. The production topology shows where the corrected responsibilities would live and lets the reader compare two counterfactual evidence designs anchored to the original frame lock.</p>
<div class="failoverTopology failoverTopology--loading">Loading interactive production topology…</div>
<p>Switching the visual changes the hypothetical observation policy, capacity
receipt, and claim—not the systems that own visit state, condition evidence, or
utility action. The <strong>Sentinel</strong> view spends 60 weekly slots across A–D for four
weeks; the <strong>Canary</strong> view spends 40 weekly slots only in C for two weeks. They
are alternative what-if deployments at the original frame lock, never
simultaneous layers and never a way to write later discoveries into old labels.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-question-dalia-used-next">The question Dalia used next<a href="http://localhost:3001/stories/the-shadow-model-judged-by-the-old-models-evidence#the-question-dalia-used-next" class="hash-link" aria-label="Direct link to The question Dalia used next" title="Direct link to The question Dalia used next" translate="no">​</a></h2>
<p>The next model review was not about grid connectors. A warranty team wanted to evaluate a candidate that flagged battery packs the incumbent rarely sent to diagnostic teardown.</p>
<p>Dalia did not begin with the candidate's headline precision. She drew two score columns and asked who controlled access to the authoritative diagnostic label.</p>
<p>The domain changed. The review question did not:</p>
<blockquote>
<p>Your candidate raises cases the incumbent always leaves untouched. In shadow mode, which outcomes can the candidate never create, and what evidence would you need before changing the live threshold?</p>
</blockquote>
<p>A useful answer has five parts:</p>
<ol>
<li class="">Name the incumbent policy that selects observations.</li>
<li class="">Mark the candidate-only disagreement region.</li>
<li class="">State which target remains unknown there.</li>
<li class="">Choose a bounded evidence-acquisition design and pay its capacity cost.</li>
<li class="">Limit the conclusion to the population and decision that design can support.</li>
</ol>
<p>A shadow model can leave production unchanged while being graded on outcomes selected by the incumbent.</p>
<p>The safest release decision is not always “ship” or “reject.” Sometimes it is:</p>
<blockquote>
<p>We have a result for the old policy's evidence. We do not yet have evidence for the policy we are being asked to approve.</p>
</blockquote>
<details class="conceptPanel"><summary><strong>Concepts in this story</strong><span class="conceptPanel__count">5<!-- --> concepts</span></summary><div class="conceptPanel__list"><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#decision.decision-policy">Decision policy</a><span>ML systems · Operational decisions</span></div><p>A versioned rule that maps model evidence, thresholds, current context, capacity, and constraints into an action such as automate, inspect, review, defer, or abstain. The model supplies evidence; the policy owns the action boundary.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#evaluation.shadow-evaluation">Shadow evaluation</a><span>ML systems · Evaluation evidence</span></div><p>Running a candidate model beside the live system without letting it control production actions. This limits exposure, but observed outcomes still reflect the incumbent policy unless the evaluation creates an independent evidence path.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#decision.selective-labels">Selective labels</a><span>ML systems · Feedback and labels</span></div><p>Labels observed for a non-random subset because a prior decision policy determines which cases receive the action, inspection, approval, review, or follow-up that can reveal an outcome.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#decision.sampling-frame">Sampling frame</a><span>ML systems · Evaluation evidence</span></div><p>The explicit register of eligible units available for selection into a sample. It is distinct from the target population, the design that chooses units from the frame, and the observed cohort that actually produces usable labels.</p></div><div class="conceptPanel__item"><div><a href="http://localhost:3001/concepts#decision.disagreement-canary">Disagreement canary</a><span>ML systems · Evaluation evidence</span></div><p>A bounded live policy that deliberately acquires evidence where an incumbent and candidate would choose different actions. It can estimate value in that disagreement region, but it does not establish whole-population model quality.</p></div></div></details>
<hr>
<p><em>BrambleGrid, Spanwatch, its people, incident, protocol names, data, and metrics are fictional. Technical grounding: the 2026 study <a href="https://pubmed.ncbi.nlm.nih.gov/41627192/" target="_blank" rel="noopener noreferrer" class="">“Discovery of an Artificial Intelligence Label Feedback Loop”</a> shows that labels influenced by a deployed AI workflow can reverse the apparent comparison of a later model; <a href="https://academic.oup.com/jalm/article/10/2/305/7928785" target="_blank" rel="noopener noreferrer" class="">the associated confidence-gated production workflow</a> documents selective automatic action and human review; <a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC5958915/" target="_blank" rel="noopener noreferrer" class="">“The Selective Labels Problem”</a> establishes why a prior decision policy can determine which outcomes become observable; and <a href="https://proceedings.mlr.press/v119/perdomo20a.html" target="_blank" rel="noopener noreferrer" class="">“Performative Prediction”</a> provides the broader foundation for deployed predictions changing later evidence. These sources establish mechanisms and limits. They do not describe BrambleGrid or prove a grid-asset incident, queue collapse, outage, injury, or the superiority of S5.</em></p>]]></content:encoded>
            <category>ML systems</category>
            <category>System design</category>
            <category>Common misconception</category>
        </item>
    </channel>
</rss>