<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Aviad Rozenhek</title>
    <description>The latest articles on DEV Community by Aviad Rozenhek (@aviadr1).</description>
    <link>https://dev.to/aviadr1</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3594498%2Ff47a592b-c7ff-483e-ad05-8a64bbc23d08.jpg</url>
      <title>DEV Community: Aviad Rozenhek</title>
      <link>https://dev.to/aviadr1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aviadr1"/>
    <language>en</language>
    <item>
      <title>Django Tasks with Due: start simple, grow with Acme Printworks</title>
      <dc:creator>Aviad Rozenhek</dc:creator>
      <pubDate>Sat, 24 Oct 2026 06:56:04 +0000</pubDate>
      <link>https://dev.to/aviadr1/django-tasks-with-due-commit-the-work-recover-after-a-crash-n5k</link>
      <guid>https://dev.to/aviadr1/django-tasks-with-due-commit-the-work-recover-after-a-crash-n5k</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F29d7sdum2bnfgz1c5qqf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F29d7sdum2bnfgz1c5qqf.png" alt="Due: An attempt can end. The obligation remains." width="800" height="80"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The maintained tutorial and runnable app also live in &lt;a href="https://due.readthedocs.io/en/latest/docs/acme-printworks-tutorial/" rel="noopener noreferrer"&gt;Due’s documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Using &lt;code&gt;django-due&lt;/code&gt; helps you start with a background task you can explain in a few lines. But it can help you grow and become more robust preventing lost or incorrect work in edge cases, that can be very hard to test for or prevent.&lt;/p&gt;

&lt;p&gt;This tutorial shows that progression with &lt;strong&gt;Due's backend for Django Tasks&lt;/strong&gt;, installed as &lt;code&gt;django-due&lt;/code&gt;. You'll begin with a familiar &lt;code&gt;@task&lt;/code&gt; and &lt;code&gt;.enqueue()&lt;/code&gt;, then evolve the parts that need stronger guarantees into native Due capabilities. The payoff is practical: a small first feature, application contracts that give you reusable failure tests immediately, and coordination that expands with the product. Always build the contract alongside the feature. It is one of the main reasons to use Due.&lt;/p&gt;

&lt;p&gt;Meet &lt;strong&gt;Acme Printworks&lt;/strong&gt;, a fictional company building a Django application for small businesses ordering brochures. Picture its first customer, &lt;strong&gt;Willow Cafe&lt;/strong&gt;, uploading copy for &lt;strong&gt;500 brochures, job 418&lt;/strong&gt;. Acme initially needs a word count for an estimate. Later, Willow Cafe wants a proof, changes its opening date from June 12 to June 19, approves the revision, and sends it to a printing partner.&lt;/p&gt;

&lt;p&gt;One premise matters for the rest of the story: &lt;strong&gt;rendering a PDF can take a surprisingly long time&lt;/strong&gt;. Even a small document can trigger a long render with some inputs, as &lt;a href="https://doc.courtbouillon.org/weasyprint/stable/first_steps.html#long-renderings" rel="noopener noreferrer"&gt;WeasyPrint's rendering documentation&lt;/a&gt; explains. For our story, assume Willow Cafe's troublesome PDF takes &lt;strong&gt;10 minutes&lt;/strong&gt; to render.&lt;/p&gt;

&lt;p&gt;Ten minutes gives the world time to change around a running task. The customer might upload a correction. A rare power outage might stop the machine, or an unexpected server restart might kill the worker seven minutes into the render. None of these needs to happen often for Acme to need an answer: which proof should the customer see, what work is still owed, and who will remove the temporary files? We'll use that same ten-minute window throughout the story.&lt;/p&gt;

&lt;p&gt;Each chart uses the same clock as Acme adds features: r17 starts at minute 0, the customer corrects the date and font at minute 2, and the faster r18 publishes June 19 at minute 3. Then we examine two branches. A restart at minute 7 kills the old worker; without that restart, its old render returns at minute 10. These are alternative histories. A killed worker cannot also finish at minute 10. The portal and printer sections show what can still happen outside the restarted process.&lt;/p&gt;

&lt;p&gt;We'll follow that same order through each requirement. You'll see the code change, the specific failure it must handle, and a test that makes the failure happen.&lt;/p&gt;

&lt;p&gt;At the first stage, many task runners would work. Acme's reason to choose Due is that the same PostgreSQL-backed work model can support the later requirements without Acme inventing its own ownership, recovery, and evidence machinery. Due uses Celery for execution; its advantage here is the application coordination built around it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h2&gt;
  
  
  Run the whole story
&lt;/h2&gt;

&lt;p&gt;There's instructions at the end of this article on how to run the whole story, and The &lt;a href="https://github.com/gigaverse-app/due/tree/main/examples/acme_printworks" rel="noopener noreferrer"&gt;complete runnable project&lt;/a&gt; includes every model, migration, setting, provider simulator, command, and test. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  1. Launch: count Willow Cafe's words
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft0fh0tfzrewznjrk5oes.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft0fh0tfzrewznjrk5oes.png" alt="Stage 1: minutes 0, 2, 3, 7 and 10; restart and late-success branches; customer outcome and Due recovery." width="800" height="1080"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first requirement is modest: count each uploaded revision so Acme can prepare an estimate. Revision 17 says &lt;code&gt;Opening day: June 12&lt;/code&gt;, which has four words.&lt;/p&gt;

&lt;p&gt;The model keeps each revision separate. All application write paths must treat its text as immutable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# acme/models.py
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Revision&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Job&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;on_delete&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PROTECT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PositiveIntegerField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TextField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;word_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PositiveIntegerField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;constraints&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;UniqueConstraint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;job&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;number&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;acme_revision_number&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The task is ordinary Django Tasks code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# acme/tasks.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.conf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.tasks&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Revision&lt;/span&gt;


&lt;span class="nd"&gt;@task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queue_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CELERY_TASK_DEFAULT_QUEUE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;count_words&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;revision_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;revision&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Revision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;revision_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;revision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;Revision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;revision_id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the worker stores the count and then dies before acknowledging success, another attempt writes the same count. That is why this function is safe to replay.&lt;/p&gt;

&lt;p&gt;Commit the revision and its task together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# acme/services.py
&lt;/span&gt;&lt;span class="nd"&gt;@transaction.atomic&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_revision&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;revision&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Revision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;count_words&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enqueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;revision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;revision&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Due, enqueueing inside this transaction stores the task in the same PostgreSQL transaction. A rollback removes both. The fast worker notification happens after commit. If that notification disappears, the registered recovery pass can still find the committed task.&lt;/p&gt;

&lt;p&gt;Imagine the application loses electricity just after the upload commits. The customer can see their revision, but the fast notification never reached a worker. After power returns, the database still describes the owed word count.&lt;/p&gt;

&lt;p&gt;This is a concrete first-day benefit: Acme does not need to build a separate outbox just to keep this task admission consistent with the revision. It depends on both writes using the same database connection.&lt;/p&gt;

&lt;p&gt;Select the backend and declare the function safe to repeat:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# project/settings.py
&lt;/span&gt;&lt;span class="n"&gt;INSTALLED_APPS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;due.django&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;due.django.tasks&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;acme.apps.AcmeConfig&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;USE_TZ&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="n"&gt;CELERY_BROKER_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;redis://127.0.0.1:56399/0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;CELERY_TASK_DEFAULT_QUEUE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;default&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;TASKS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;default&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BACKEND&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;due.django.tasks.backend.DueBackend&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;QUEUES&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;default&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OPTIONS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;replay_safe_tasks&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;acme.tasks.count_words&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The complete settings include PostgreSQL credentials for the local Compose service. At this first stage, startup needs one registration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# acme/apps.py, initial version
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.apps&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AppConfig&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AcmeConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppConfig&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;acme&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;due.django.runtime.registry&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;configure_durable_work&lt;/span&gt;
        &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;due.django.tasks.runtime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;django_tasks_registration&lt;/span&gt;

        &lt;span class="nf"&gt;configure_durable_work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="nf"&gt;django_tasks_registration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;recovery_queue&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;default&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cadence_seconds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://docs.djangoproject.com/en/6.1/topics/tasks/" rel="noopener noreferrer"&gt;Django Tasks&lt;/a&gt; supplies the task API; the backend supplies execution and storage behavior. Due's configuration still needs PostgreSQL, Celery, a broker, and Beat. If you already operate that stack, the entry cost is particularly small. The saving demonstrated here is application code and future coordination work, not a claim that every deployment has the lowest infrastructure bill.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build the first contract now
&lt;/h3&gt;

&lt;p&gt;The allowlist says this task is safe to replay. Make that claim executable. Acme binds three shared pytest-obligation proofs to the actual function and reads the word count from the revision. The call counter checks that both executions really entered the function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tests/test_task_contract.py
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Application-specific replay proofs also apply to the initial Django task.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;due.django.conformance.provider&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ProviderProbe&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;due.django.testing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hold_nudges&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pytest_obligation.profiles.harmless_replay&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;REPLAY_SAFETY_PROOFS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ReplaySafeEffect&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;acme.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Job&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Revision&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;acme.services&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;add_revision&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;acme.tasks&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;count_words&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;hold_nudges&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;add_revision&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Opening day: June 12&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;


&lt;span class="nd"&gt;@pytest.mark.django_db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@pytest.mark.parametrize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;proof&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;REPLAY_SAFETY_PROOFS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;proof&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;proof&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_word_count_contract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proof&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;execute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ProviderProbe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;count_words&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;revision_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;revision_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;proof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;ReplaySafeEffect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Acme immutable revision word count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;prepare&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;observe&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;revision_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Revision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;revision_id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;word_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;execution_count_for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pytest &lt;span class="nt"&gt;-q&lt;/span&gt; tests/test_task_contract.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The suite checks that execution reaches the implementation, produces the expected four-word result, and preserves it on a real second call. Transaction rollback and lost-notification recovery are tested in &lt;code&gt;tests/test_story.py&lt;/code&gt;. The shared Django Tasks runtime cannot infer these application-specific promises for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Customers want proofs, and an old worker finishes late
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6xnzfogereucjsa60k2j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6xnzfogereucjsa60k2j.png" alt="Stage 2: minutes 0, 2, 3, 7 and 10; restart and late-success branches; customer outcome and Due recovery." width="800" height="1080"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Revision 17 says June 12 and starts its ten-minute render. Two minutes later, Willow Cafe uploads revision 18 with the corrected opening date, June 19, and a repaired font. Another worker renders that corrected file quickly and publishes it at minute three. At minute ten, the original worker finally returns with the old date.&lt;/p&gt;

&lt;p&gt;That is the delayed-success case. In the restart branch, a power outage or server restart kills the original worker at minute seven. Revision 18 is already published, so Due keeps June 19 current and does not resurrect revision 17. If the currently requested proof is still unfinished in another run, recovery starts a fresh attempt after its lease expires. That attempt uses the captured input and reruns rendering; Due does not checkpoint a PDF renderer halfway through its work.&lt;/p&gt;

&lt;p&gt;A task that blindly updates &lt;code&gt;job.published_key&lt;/code&gt; might put June 12 back in front of the customer. Acme now needs two properties: each revision writes its own immutable output, and only the current requested revision can publish the pointer.&lt;/p&gt;

&lt;p&gt;Add a native Due work model for that obligation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# acme/models.py
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProofWork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UniverseDurableWork&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;work_owner_field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ClassVar&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;owner&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;OneToOneField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Job&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;on_delete&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SET_NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;revision_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PositiveBigIntegerField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TextField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;indexes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;constraints&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;durable_work_schema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;acme_proof&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;UniverseDurableWork&lt;/code&gt;, &lt;code&gt;durable_work_schema&lt;/code&gt;, and the other imports are included in &lt;code&gt;acme/models.py&lt;/code&gt; in the project. The work captures its input text; a worker does not fetch whatever copy happens to be current later.&lt;/p&gt;

&lt;p&gt;Here is the early proof capability in full:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# acme/stage2.py
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;The early proof implementation, before portal handoff and temporary spooling.

The final application registers effects.publish_proof. This intermediate version
uses the same work schema and is exercised directly by test_stage2.py.
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;due.django.public&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ReplaySafeEffect&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;providers&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Job&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ProofWork&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.rendering&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RENDER_POLICY&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PublishProof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ReplaySafeEffect&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Job&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ProofWork&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;perform_effect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write_proof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;provider_idempotency_key&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;finalize_success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proof_key&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;Job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;owner_id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;published_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;proof_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;publish_proof&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PublishProof&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;for_owner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Job&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ProofWork&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;input_fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;universe&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;revision_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;RENDER_POLICY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;providers.write_proof()&lt;/code&gt; stores text under an immutable key. Repeating the same key with the same text succeeds; different text under that key is rejected. Due checks the captured revision and ownership before calling &lt;code&gt;finalize_success()&lt;/code&gt; inside its settlement transaction.&lt;/p&gt;

&lt;p&gt;The producer becomes a service that records the desired proof:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# acme/services.py
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;request_proof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;revision_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;revision&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Revision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;revision_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;publish_proof&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;atomic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;revision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;locked_owner&lt;/span&gt;
        &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_revision&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;revision&lt;/span&gt;
        &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;published_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;  &lt;span class="c1"&gt;# Approval waits for this revision's proof.
&lt;/span&gt;        &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;update_fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;current_revision&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;published_key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request_work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nc"&gt;ProofWork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;revision_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;revision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;revision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;universe&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;acme&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add a native application contract as soon as you add this capability. Acme binds real proof requests, independently stored proof bytes, provider failures, and superseding revisions; stage 6 shows the final contract and generated suite.&lt;/p&gt;

&lt;p&gt;The word-count task stays exactly as it was. Proof publication gets the richer API because it has a richer requirement. There is no need to force every background function into the same abstraction.&lt;/p&gt;

&lt;p&gt;Try the specific regression:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pytest &lt;span class="nt"&gt;-q&lt;/span&gt; tests/test_story.py::test_old_proof_cannot_replace_new_proof
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The test compresses that ten-minute window: revision 18 finishes while revision 17 is returning from its renderer. The executable provider renders text previews and injects the relevant ordering and interruptions, so running the example does not require waiting ten minutes. The test reads the published proof text and requires June 19. Checking only a task's success status would miss the actual bug.&lt;/p&gt;

&lt;p&gt;The runnable app gives proof rendering an explicit budget: 11 minutes to the soft worker limit, 12 to the hard limit, and a 15-minute ownership lease. Both proof implementations share this policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# acme/rendering.py
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Give the story&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s ten-minute render room to finish and settle.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;timedelta&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;due.django.public&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DurableWorkPolicy&lt;/span&gt;

&lt;span class="n"&gt;RENDER_SOFT_LIMIT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;
&lt;span class="n"&gt;RENDER_HARD_LIMIT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;
&lt;span class="n"&gt;RENDER_POLICY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DurableWorkPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;retention&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lease_duration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Register the final proof capability with the matching execution limits. This entry in &lt;code&gt;AcmeConfig.ready()&lt;/code&gt; replaces its default entry; the other capabilities keep their own budgets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# acme/apps.py, inside ready()
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.rendering&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RENDER_HARD_LIMIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RENDER_SOFT_LIMIT&lt;/span&gt;

&lt;span class="nc"&gt;DurableWorkRegistration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;acme_proof&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;capability&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;publish_proof&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CELERY_TASK_DEFAULT_QUEUE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;cadence_seconds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;execution_soft_time_limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;RENDER_SOFT_LIMIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;execution_time_limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;RENDER_HARD_LIMIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a claim made at minute 0 with no renewal, that lease expires at minute 15. A minute-7 crash therefore does not cause an immediate replacement render. Once recovery is running and the work is eligible, a later tick can dispatch a new attempt. With r18 already settled at minute 3, there is no current proof left to render. Independent cleanup can still be owed. The five-second cadence is a polling interval, not a completion deadline. Set a real renderer's bounded call timeout below the soft limit; the text simulator has no ten-minute delay. See &lt;a href="https://due.readthedocs.io/en/latest/package/docs/operations/" rel="noopener noreferrer"&gt;execution budgets&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The approval portal can be overwritten remotely
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzq7eoqrcl6oiwuhvhaba.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzq7eoqrcl6oiwuhvhaba.png" alt="Stage 3: minutes 0, 2, 3, 7 and 10; restart and late-success branches; customer outcome and Due recovery." width="800" height="1080"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Acme adds a hosted approval portal. Its API accepts &lt;code&gt;PUT(job_id, proof_key)&lt;/code&gt;, but provides no conditional version check. For this stage, suppose an earlier proof, r16, already exists with June 12. Its portal request is held in flight before our clock starts. While r17 is rendering, the minute-2 correction produces r18 and its portal update at minute 3. Restarting Acme at minute 7 cannot retract the older HTTP request. If that request lands at minute 10, the portal shows June 12 again, even though the killed r17 renderer never returned.&lt;/p&gt;

&lt;p&gt;The database fence from stage 2 protects Acme's local pointer. The portal needs repair after an escaped old write. Model that requirement with a &lt;code&gt;Projection&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# acme/models.py
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PortalWork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UniverseDurableWork&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;work_owner_field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ClassVar&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;owner&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;OneToOneField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Job&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;on_delete&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SET_NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;proof_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;indexes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;constraints&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;durable_work_schema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;acme_portal&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# acme/effects.py
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PortalProjection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Projection&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Job&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PortalWork&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;write_projection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_portal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;owner_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;proof_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;on_projected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;Job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;owner_id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;portal_failed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;finalize_retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;DurableWorkState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EXHAUSTED&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;Job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;owner_id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;portal_failed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;project_portal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PortalProjection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;for_owner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Job&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PortalWork&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;input_fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;universe&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;proof_key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;DurableWorkPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;retention&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repair_interval&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seconds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When proof publication settles, also admit the desired portal update. Replace the proof capability's &lt;code&gt;finalize_success()&lt;/code&gt; with this method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Method on PublishProof, in acme/effects.py
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;finalize_success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proof_key&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;Job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;owner_id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;published_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;proof_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;project_portal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;atomic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;owner_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request_work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PortalWork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proof_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;proof_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;universe&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;acme&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The complete class appears in stage 5 below. The pointer and portal obligation commit together. A lost follow-up notification leaves a recoverable obligation.&lt;/p&gt;

&lt;p&gt;Due's projection machinery retains evidence about older calls and tracks repair that is still owed. After a late write, recovery reapplies the current desired proof. The portal can briefly show the old value; the promise here is eventual repair, with the necessary history retained.&lt;/p&gt;

&lt;p&gt;Run the test that actually puts the wrong proof back on the portal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pytest &lt;span class="nt"&gt;-q&lt;/span&gt; tests/test_story.py::test_late_portal_write_is_repaired
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It first verifies that June 12 overwrote June 19, then runs recovery and requires the portal to show June 19 again. Acme has gained remote repair without writing its own stale-attempt ledger and reconciliation scheduler.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. A timeout after ordering 500 brochures
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fupi8145gsek6f2ga0jmm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fupi8145gsek6f2ga0jmm.png" alt="Stage 4: minutes 0, 2, 3, 7 and 10; restart and late-success branches; customer outcome and Due recovery." width="800" height="1080"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Willow Cafe approves revision 18 after it publishes at minute 3. Acme submits 500 copies to a printing partner just before minute 7. The printer creates the order and commits it. Then the minute-7 restart kills Acme's waiting client before it records the reply. A power outage at the printer's network gateway would leave Acme with the same uncertainty. At minute 10, the question is whether the order was accepted, regardless of whether the obsolete r17 render survived.&lt;/p&gt;

&lt;p&gt;This provider creates a new order on &lt;strong&gt;every POST&lt;/strong&gt;. Its reference can be searched later, but it is not an idempotency key. Blindly repeating the request might buy another 500 brochures.&lt;/p&gt;

&lt;p&gt;The simulator makes that failure real:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# acme/providers.py
&lt;/span&gt;&lt;span class="nd"&gt;@dataclass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frozen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PrintReceipt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;proof_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;submit_print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proof_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;database&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INSERT INTO orders(reference,proof_key,quantity) VALUES (?,?,?)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proof_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lastrowid&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
        &lt;span class="n"&gt;receipt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PrintReceipt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lastrowid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proof_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;lost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DELETE FROM faults WHERE name=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;print_reply&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;rowcount&lt;/span&gt;
    &lt;span class="c1"&gt;# The provider has COMMITTED. Rolling back Django cannot undo this order.
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;lost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;TimeoutError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Printer accepted the order; response was lost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;receipt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The timeout happens after an independent provider commit. A rollback in Acme's PostgreSQL transaction cannot undo that order.&lt;/p&gt;

&lt;p&gt;Store the approved purchase intent separately from the mutable job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# acme/models.py
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PrintOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;reference&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;UUIDField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;editable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;revision&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Revision&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;on_delete&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PROTECT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;proof_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PositiveIntegerField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pending&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;provider_order_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PositiveBigIntegerField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PrintWork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ObservedExternalOperationWork&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;work_owner_field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ClassVar&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;owner&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;OneToOneField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PrintOrder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;on_delete&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SET_NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;reference&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;UUIDField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;proof_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PositiveIntegerField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;indexes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;constraints&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;durable_work_schema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;acme_print&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;indexes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;observation_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;acme_print_observe&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PrintEvidence&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StoredAppliedEffectEvidence&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use an &lt;code&gt;ObservedExternalOperation&lt;/code&gt; to distinguish uncertainty from permission to retry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# acme/effects.py
&lt;/span&gt;&lt;span class="n"&gt;mirror&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StatusMirror&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PrintOrder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;owner_id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderPrinting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ObservedExternalOperation&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;PrintOrder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PrintWork&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PrintReceipt&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
    &lt;span class="n"&gt;observation_interval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seconds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;evidence_store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;JsonAppliedEffectEvidenceStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PrintEvidence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PrintReceipt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;on_applied&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;outcome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;accepted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;provider_order_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;on_unknown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;outcome&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;on_retry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;outcome&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;retrying&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;on_exhausted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;outcome&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stopped&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;on_terminal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;outcome&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stopped&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;perform_effect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submit_print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;proof_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PrinterRejected&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Only a definite refusal before acceptance permits retrying a purchase.
&lt;/span&gt;            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;EffectNotAppliedError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;observe_effect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;receipt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe_print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;receipt&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reference&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;proof_key&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;proof_key&lt;/span&gt;
            &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;
        &lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Receipt does not match the captured print request&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;receipt&lt;/span&gt;


&lt;span class="n"&gt;order_printing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OrderPrinting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;for_owner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PrintOrder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PrintWork&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;input_fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;universe&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reference&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;proof_key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quantity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;POLICY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;POLICY&lt;/code&gt; is &lt;code&gt;DurableWorkPolicy(retention=None)&lt;/code&gt; in this sample. The capability retains the applied receipt in &lt;code&gt;PrintEvidence&lt;/code&gt;. Observation checks the reference, exact proof key, and quantity before treating the receipt as evidence for this request.&lt;/p&gt;

&lt;p&gt;The approval service locks the job and rejects a stale proof approval:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# acme/services.py
&lt;/span&gt;&lt;span class="nd"&gt;@transaction.atomic&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;approve_print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected_proof_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select_for_update&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;published_key&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;published_key&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;expected_proof_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Proof changed; review the current proof before approving&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PrintOrder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;revision&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_revision&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;proof_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;published_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;order_printing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;atomic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request_work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nc"&gt;PrintWork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;proof_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;proof_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;universe&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;acme&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once admitted, this print request captures the approved proof. Later edits to the job do not change the purchase that was approved. Each call creates a distinct purchase intent; an HTTP endpoint should also deduplicate repeated browser submissions.&lt;/p&gt;

&lt;p&gt;After the simulated timeout, the order becomes &lt;code&gt;unknown&lt;/code&gt;. Recovery searches for a correlated receipt and records &lt;code&gt;accepted&lt;/code&gt;, while the provider's order count stays one. A missing receipt alone does not authorize another POST. Also, “accepted” says nothing yet about printing or shipping.&lt;/p&gt;

&lt;p&gt;Here is the application test, including its independent provider-side count:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tests/test_story.py
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_accepted_print_with_lost_reply_is_observed_without_resubmit&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;published&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lose_next_print_reply&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;hold_nudges&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;approve_print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;published_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raises&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EffectOutcomeUnknownError&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;order_printing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attempt_execution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;printwork&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;refresh_from_db&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;print_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;WorkTableDelivery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_printing&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;delivery&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;delivery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recover&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;refresh_from_db&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;accepted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;print_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;receipt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe_print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;receipt&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;provider_order_id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now consider a different incident: the printer is out of ink and explicitly rejects the request &lt;strong&gt;before accepting an order&lt;/strong&gt;. The adapter raises &lt;code&gt;PrinterRejected&lt;/code&gt;; &lt;code&gt;OrderPrinting&lt;/code&gt; translates only that definite refusal into &lt;code&gt;EffectNotAppliedError&lt;/code&gt;. Due can retry under its policy after supplies return. The contract exercises both histories:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tests/contracts/printer.py
&lt;/span&gt;&lt;span class="n"&gt;rejected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SEAM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rejection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PrinterRejected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Out of ink; order NOT accepted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;retrying&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;WorkFailureDisposition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RETRYABLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@contextmanager&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lost_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;with &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lose_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TimeoutError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Printer UPS failed after acceptance; reply lost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="n"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;receipts_visible&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt;


&lt;span class="n"&gt;uncertain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SEAM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;outcome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;lost_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@contextmanager&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;observation&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;uncertain&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reveal&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;receipts_visible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;print_receipt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nc"&gt;ObservationExample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;delivery&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;accepted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;reveal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;reveal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;observation_calls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An ink shortage after acceptance is a fulfillment delay. Acme must follow the existing order's production status; submitting another order would compound the problem. Due's acceptance receipt does not mean paper was printed or shipped.&lt;/p&gt;

&lt;p&gt;That is a strong reason to choose Due when your Django app makes consequential external requests: uncertainty becomes a state you can inspect and resolve with evidence, using a different policy from an ordinary replayable task.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Rendering gets temporary storage, and workers can die
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm16g2i4m14wrq6luweu3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm16g2i4m14wrq6luweu3.png" alt="Stage 5: minutes 0, 2, 3, 7 and 10; restart and late-success branches; customer outcome and Due recovery." width="800" height="1080"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Acme's renderer now creates temporary spool data during those ten minutes of preparing a proof. Suppose a power outage or unexpected server restart stops it at minute seven, after it has written a multi-gigabyte spool file. Deleting that file only in a Python &lt;code&gt;finally&lt;/code&gt; block is insufficient: the worker is gone before the block can run. A week of stranded files can leave too little space for the next customer. The corrected r18 proof published at minute 3 remains valid; cleaning r17's temporary files must not delete it. After the old attempt's reservation expires, Due can recover its independent cleanup. It removes disposable bytes and keeps checking until the renderer provides completion evidence that rules out later writes.&lt;/p&gt;

&lt;p&gt;Reserve a cleanup obligation &lt;strong&gt;before&lt;/strong&gt; calling the renderer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# acme/models.py
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CleanupWork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UniverseDurableWork&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;spool_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;indexes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;constraints&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;durable_work_schema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;acme_cleanup&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# acme/effects.py
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SpoolCleanup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IndependentCompensation&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ProofWork&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CleanupWork&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;capture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;CleanupWork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spool_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;universe&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;acme&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;compensate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cleanup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spool_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;cleanup_spool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SpoolCleanup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;producer_model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ProofWork&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;CleanupWork&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;nudge&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;registered_nudge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CleanupWork&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;DurableWorkPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;retention&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_rearms&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;inspection_interval&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seconds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final &lt;code&gt;acme/effects.py&lt;/code&gt; imports &lt;code&gt;RENDER_POLICY&lt;/code&gt; from &lt;code&gt;.rendering&lt;/code&gt;, just as stage 2 does. The cleanup identity uses the producer's attempt token, so separate attempts do not share temporary storage. The renderer keeps a terminal completion record. After that record exists, the attempt cannot write more spool data.&lt;/p&gt;

&lt;p&gt;Cleanup checks that evidence before reporting completion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# acme/providers.py
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cleanup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spool_key&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;database&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT finished FROM renders WHERE key=?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spool_key&lt;/span&gt;&lt;span class="p"&gt;,)).&lt;/span&gt;&lt;span class="nf"&gt;fetchone&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;  &lt;span class="c1"&gt;# Absence is not proof that a delayed write cannot arrive.
&lt;/span&gt;        &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UPDATE renders SET spool=NULL WHERE key=?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spool_key&lt;/span&gt;&lt;span class="p"&gt;,))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An absent object does not prove that an earlier write cannot arrive later. Here, a missing completion record keeps cleanup owed. A real renderer needs an equally explicit completion or cancellation contract.&lt;/p&gt;

&lt;p&gt;Now replace the early &lt;code&gt;ReplaySafeEffect&lt;/code&gt; subclass with &lt;code&gt;OwnedDurableWorkCapability&lt;/code&gt;, whose &lt;code&gt;apply()&lt;/code&gt; receives the execution context needed to reserve cleanup. This is the final proof capability:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# acme/effects.py
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;render_proof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;provider_idempotency_key&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fencing_token&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PublishProof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OwnedDurableWorkCapability&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Job&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ProofWork&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;cleanup_spool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;record_cleanup_before_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;render_proof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cleanup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fencing_token&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
                &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;confirm_compensated&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;finalize_success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proof_key&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;Job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;owner_id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;published_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;proof_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proof_failed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# Both the pointer and the portal obligation commit in this settlement.
&lt;/span&gt;        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;project_portal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;atomic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;owner_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request_work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PortalWork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proof_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;proof_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;universe&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;acme&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;finalize_retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;DurableWorkState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EXHAUSTED&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;Job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;owner_id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proof_failed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;publish_proof&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PublishProof&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;for_owner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Job&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ProofWork&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;input_fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;universe&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;revision_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;RENDER_POLICY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ordinary path renders, removes the temporary spool, and acknowledges compensation. If the process dies after rendering, the separately committed cleanup obligation remains. Recovery can clean it after its reservation expires. The immutable proof itself is retained.&lt;/p&gt;

&lt;p&gt;The same class also keeps stage 3's transactional portal handoff. This is how the example grows: the publication input and settlement remain recognizable, while the provider boundary gains the specific protection it now needs.&lt;/p&gt;

&lt;p&gt;Register all the capabilities alongside the original Django task backend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# acme/apps.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.apps&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AppConfig&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.conf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AcmeConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppConfig&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;acme&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;due.django.runtime.registry&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;DurableWorkRegistration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;configure_durable_work&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;due.django.tasks.runtime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;django_tasks_registration&lt;/span&gt;

        &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.effects&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;cleanup_spool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;order_printing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;project_portal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;publish_proof&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.rendering&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RENDER_HARD_LIMIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RENDER_SOFT_LIMIT&lt;/span&gt;

        &lt;span class="nf"&gt;configure_durable_work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="nf"&gt;django_tasks_registration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;recovery_queue&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CELERY_TASK_DEFAULT_QUEUE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cadence_seconds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nc"&gt;DurableWorkRegistration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;acme_proof&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;capability&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;publish_proof&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CELERY_TASK_DEFAULT_QUEUE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;cadence_seconds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;execution_soft_time_limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;RENDER_SOFT_LIMIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;execution_time_limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;RENDER_HARD_LIMIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="nc"&gt;DurableWorkRegistration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;capability&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;capability&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CELERY_TASK_DEFAULT_QUEUE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;cadence_seconds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capability&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;acme_portal&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;project_portal&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;acme_print&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_printing&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;acme_cleanup&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cleanup_spool&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each registration gives recovery an owner, queue, and cadence. The project's Celery setup uses the same Django settings and database. &lt;code&gt;run_demo.py&lt;/code&gt; starts both the worker and Beat, including these recovery schedules.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Keep the failure guarantees executable
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyw2oixn44p5wvvb02r71.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyw2oixn44p5wvvb02r71.png" alt="Stage 6: minutes 0, 2, 3, 7 and 10; restart and late-success branches; customer outcome and Due recovery." width="800" height="1080"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Acme changes its printer adapter six months later. A developer sees a timeout and adds a retry. The happy-path demo still works, but the next power failure could order a second 500 brochures. A contract should catch that before deployment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Always build the application contract.&lt;/strong&gt; Due and pytest-obligation provide reusable failure histories and assertions. You supply the application meaning: how work is admitted, which provider call matters, what a refusal means, and what the customer should actually see. You get valuable runnable tests without designing every lease, ownership, recovery and replay interleaving yourself.&lt;/p&gt;

&lt;p&gt;Start by configuring the test host:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tests/conftest.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;due.django.conformance.host&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;work_table_host&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pytest_obligation&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;configure&lt;/span&gt;

&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;work_table_host&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;production_packages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;acme&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;


&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;autouse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;isolated_provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tmp_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PROVIDER_DB&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tmp_path&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;provider.sqlite3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For proof publication, the seam observes the published bytes and independently stored provider bytes. &lt;code&gt;ProviderProbe&lt;/code&gt; wraps the real local provider transaction; its refusal control simulates an unavailable renderer while preserving real Due execution and PostgreSQL settlement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tests/contracts/proof.py
&lt;/span&gt;&lt;span class="n"&gt;SEAM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ProviderSeam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;make_work&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;make_proof&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;observe&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;proof_contents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;delivered&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;COPY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;provider_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;provider_contents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;provider_expected&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;COPY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;calls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;calls_for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;provider_idempotency_key&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="n"&gt;refuse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;OSError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Renderer power supply unavailable&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The helpers live in &lt;code&gt;tests/contracts/support.py&lt;/code&gt;. They call &lt;code&gt;add_revision&lt;/code&gt; and &lt;code&gt;request_proof&lt;/code&gt;, count actual provider I/O, and read the provider's separate SQLite database. The remaining bindings connect terminal outcomes, supersession, admission rollback, and cleanup composition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tests/contracts/proof.py
&lt;/span&gt;&lt;span class="n"&gt;EXAMPLES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;seam_examples&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;publish_proof&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;SEAM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;replay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;SEAM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Acme immutable rendering&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;render_proof&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;terminal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TerminalExample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;observe&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;owner_id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;proof_failed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;rearm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;retry_proof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;owner_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;snapshot&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;compensation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;compensation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;producers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;request proof&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;admission_failures&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;partial proof request&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;admission_failure&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;convergence_families&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;NotApplicable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Immutable proof keys keep old bytes separate; the current revision&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s fenced pointer is the only mutable result. There is no merge or dependency gate.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;family&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ConvergenceFamily&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;family&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;STALE_SNAPSHOTS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, supersession changes the desired proof through the production service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tests/contracts/proof.py
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;work&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;make_proof&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;SupersessionExample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;request_proof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;add_revision&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;owner_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NEW_COPY&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;observe&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;proof_contents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The complete &lt;code&gt;proof.py&lt;/code&gt; contains the producer and compensation helpers referenced above. &lt;code&gt;portal.py&lt;/code&gt;, &lt;code&gt;printer.py&lt;/code&gt;, and &lt;code&gt;cleanup.py&lt;/code&gt; bind the other capabilities. The cleanup lifecycle fixture begins with an owed row and real disposable bytes; the producer's composition histories and the subprocess test separately prove that cleanup was admitted before rendering. The bindings do not implement Due's recovery or settlement logic.&lt;/p&gt;

&lt;p&gt;These four declarations generate tests from the registered production capabilities and their actual policies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tests/test_contracts.py
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;These classes collect real tests, selected by each capability&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s policies.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;due.django.conformance.suite&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;effect_contract_suite&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;acme.effects&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cleanup_spool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_printing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;project_portal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;publish_proof&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.contracts&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cleanup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;portal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;printer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proof&lt;/span&gt;


&lt;span class="nd"&gt;@effect_contract_suite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;publish_proof&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;examples&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;proof&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EXAMPLES&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestProofContract&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="nd"&gt;@effect_contract_suite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;project_portal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;examples&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;portal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EXAMPLES&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestPortalContract&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="nd"&gt;@effect_contract_suite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_printing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;examples&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;printer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EXAMPLES&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestPrinterContract&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="nd"&gt;@effect_contract_suite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cleanup_spool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;examples&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cleanup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EXAMPLES&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestCleanupContract&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run them and keep the evidence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pytest &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; due.django.conformance.reporting &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--due-work-require-assessed&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--due-work-profile-report&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;profiles.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--durable-work-report&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;effects.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The current example runs &lt;strong&gt;286 tests&lt;/strong&gt;. Of those, &lt;strong&gt;270 are generated native contract tests&lt;/strong&gt; and three are the first task's shared replay proofs. Ten scenario tests cover the application's specific story, including a real subprocess exit; one budget check verifies that the registered worker allows the ten-minute render; two enrollment guards check that native contracts cannot disappear unnoticed.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Contract&lt;/th&gt;
&lt;th&gt;Generated checks&lt;/th&gt;
&lt;th&gt;Customer promise exercised&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Proof publication&lt;/td&gt;
&lt;td&gt;66&lt;/td&gt;
&lt;td&gt;Current proof wins; failed rendering remains recoverable; cleanup survives the producer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Approval portal&lt;/td&gt;
&lt;td&gt;70&lt;/td&gt;
&lt;td&gt;Late remote writes are repaired to the current proof&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Print ordering&lt;/td&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;td&gt;Definite refusal can retry; ambiguous acceptance requires observation; receipts repair local state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Temporary spool cleanup&lt;/td&gt;
&lt;td&gt;54&lt;/td&gt;
&lt;td&gt;Cleanup survives loss of ownership and retries until it has completion evidence&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The actual policies determine the applicable proofs. Acme also supplies explicit reasons for inapplicable families. A green contract says these application bindings passed their declared histories; it cannot establish undocumented behavior of a real printing vendor.&lt;/p&gt;

&lt;p&gt;Keep an inventory independent of the test declarations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tests/test_enrollment.py
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;A missing or disabled contract must not turn into a smaller green suite.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;due.django.conformance.enrollment&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;assert_complete_enrollment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;discover_enrollment&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;due.django.runtime.registry&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;installed&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;due.django.tasks.runtime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TaskCapability&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;native_capabilities&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# Inventory comes from production registration, independently of test classes.
&lt;/span&gt;    &lt;span class="c1"&gt;# Django Tasks has a shared runtime capability; its application task bodies
&lt;/span&gt;    &lt;span class="c1"&gt;# need their own replay/transaction tests (see test_story.py).
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;capability&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;installed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;capability&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TaskCapability&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_every_native_capability_has_a_contract&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;assert_complete_enrollment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;native_capabilities&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;discover_enrollment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_deleted_contract_is_rejected&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;enrolled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;discover_enrollment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;enrolled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;omitted&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;enrolled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raises&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;AssertionError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;conformance enrollment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="nf"&gt;assert_complete_enrollment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;native_capabilities&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;enrolled&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;omitted&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CI additionally uses Due's &lt;code&gt;scripts/check_adoption_report.py&lt;/code&gt; to require every expected proof to be collected, selected, executed and passed for all four named capabilities. &lt;code&gt;--due-work-require-assessed&lt;/code&gt; catches missing bindings; the inventory and report gates catch missing suites or filtered execution. Do not replace those checks with a test-count assertion.&lt;/p&gt;

&lt;p&gt;For the physical process-death scenario:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pytest &lt;span class="nt"&gt;-q&lt;/span&gt; tests/test_story.py::test_independent_cleanup_survives_process_death
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That test compresses a process-death history: it runs a separate Python process, commits the renderer output and calls &lt;code&gt;os._exit(79)&lt;/code&gt; before publication. It covers death after provider completion; the generated compensation histories also cover cleanup while completion is unresolved. Together with the supersession test, these exercise the distinct boundaries in our minute-7 story, without claiming a real ten-minute PDF run. Its parent verifies the crash point, recovers cleanup, checks the spool bytes disappeared, and recovers publication. &lt;code&gt;run_demo.py&lt;/code&gt; separately exercises Redis, Celery workers and Beat, so the contract suite and the live journey cover different parts of the integration.&lt;/p&gt;

&lt;p&gt;When Acme adds a provider, update its contract in the same change. A new lookup, idempotency or completion promise should have a binding and an observable verdict. This is how the initial small task grows into a system whose reliability claims remain reviewable and runnable.&lt;/p&gt;

&lt;h2&gt;
  
  
  When should you choose Due over the alternatives?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Choose Due when you expect background work to become part of your product's correctness.&lt;/strong&gt; Acme's roadmap is a good example: edits can overtake each other, remote writes can arrive late, and a repeated request can place a second physical order. Due gives those requirements explicit models and reusable recovery behavior.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Acme's requirement&lt;/th&gt;
&lt;th&gt;What Due lets the application reuse&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Commit a revision and its task together&lt;/td&gt;
&lt;td&gt;PostgreSQL task admission and scheduled recovery of lost notifications&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Publish the latest requested proof&lt;/td&gt;
&lt;td&gt;Captured inputs, revision ownership, and fenced local settlement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repair an unconditional remote portal&lt;/td&gt;
&lt;td&gt;Retained attempt evidence and desired-state projection repair&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Avoid repeating an uncertain purchase&lt;/td&gt;
&lt;td&gt;An unknown state, correlated receipts, and observation before further action&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Finish after a process dies&lt;/td&gt;
&lt;td&gt;Durable follow-up admission and independent cleanup obligations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Preserve those guarantees during changes&lt;/td&gt;
&lt;td&gt;Application contracts generate policy-specific failure tests; enrollment and report gates prevent silent coverage loss&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If your app only needs a few disposable background jobs, a simpler worker may be enough. Celery also supplies broad task execution features and is Due's worker engine here. The reason to add Due is that you would otherwise have to build and maintain the coordination in this table yourself, or integrate another system that provides it.&lt;/p&gt;

&lt;p&gt;For Acme, that investment starts small: keep the estimator's &lt;code&gt;@task&lt;/code&gt;, configure one backend, and commit the task with the revision. As requirements grow, add the native capability that matches the problem. There is no seperate migration of every task, and no need to anticipate every future workflow on day one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/gigaverse-app/due/tree/main/examples/acme_printworks" rel="noopener noreferrer"&gt;Get the complete Acme Printworks project&lt;/a&gt; · &lt;a href="https://github.com/gigaverse-app/due/blob/v0.8.0/package/docs/capabilities.md" rel="noopener noreferrer"&gt;Due capabilities and contracts&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Run the whole story
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/gigaverse-app/due/tree/main/examples/acme_printworks" rel="noopener noreferrer"&gt;complete runnable project&lt;/a&gt; includes every model, migration, setting, provider simulator, command, and test. The snippets above explain its evolution. The downloadable application contains the final version; &lt;code&gt;acme/stage2.py&lt;/code&gt; also preserves and tests the intermediate proof implementation.&lt;/p&gt;

&lt;p&gt;Use Python 3.14 and Docker Compose on Linux, macOS, or WSL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/gigaverse-app/due.git
&lt;span class="nb"&gt;cd &lt;/span&gt;due/examples/acme_printworks
python3.14 &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;span class="nb"&gt;.&lt;/span&gt; .venv/bin/activate
python &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--wait&lt;/span&gt;
python run_demo.py
python &lt;span class="nt"&gt;-m&lt;/span&gt; pytest &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; due.django.conformance.reporting &lt;span class="nt"&gt;--due-work-require-assessed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The demo applies migrations, starts a Celery worker and Beat, runs the order, then stops those processes. The first run prints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Acme Printworks job 418: Willow Cafe, 500 brochures
1. Estimate: 4 words, recovered after a lost notification
2. Proof: revision 18, June 19
3. Portal: displays revision 18
4. Printer: accepted 500 copies; lost reply recovered; exactly one order
5. Cleanup: temporary spool removed; immutable proof retained
6. Run pytest -q for late writes, process death, and replay contracts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Subsequent runs create a new job. Run &lt;code&gt;docker compose down&lt;/code&gt; when finished. These are disposable local development services. The README also explains how to use existing dedicated PostgreSQL and Redis instances.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The providers are local simulators with independent SQLite commits. Django and Due use PostgreSQL. The proof is a text preview, keeping the example small; no PDF library, printer account, or API key is needed. We deliberately give the portal and printer difficult contracts so the failure cases are reproducible.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>opensource</category>
      <category>django</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Stop writing platform checks around uvloop</title>
      <dc:creator>Aviad Rozenhek</dc:creator>
      <pubDate>Mon, 28 Sep 2026 09:08:01 +0000</pubDate>
      <link>https://dev.to/aviadr1/stop-writing-platform-checks-around-uvloop-555f</link>
      <guid>https://dev.to/aviadr1/stop-writing-platform-checks-around-uvloop-555f</guid>
      <description>&lt;p&gt;If you write asyncio code that has to run on both Windows and Linux, you've probably written this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;platform&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;win32&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;winloop&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;fastloop&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uvloop&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;fastloop&lt;/span&gt;

&lt;span class="n"&gt;fastloop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's four lines. It works. Nobody thinks about it the first time.&lt;/p&gt;

&lt;p&gt;But it doesn't stay four lines, and it doesn't stay in one place.&lt;/p&gt;

&lt;p&gt;This post is about that branch: why it keeps showing up, what it usually leaves out, and &lt;a href="https://github.com/aviadr1/winuvloop" rel="noopener noreferrer"&gt;winuvloop&lt;/a&gt;, the small package I wrote so it lives in one place.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;uvloop isn't the Windows backend. winloop is. Code that runs on both ends up picking one at runtime, at every entry point.&lt;/li&gt;
&lt;li&gt;The hand-written branch usually leaves out install markers, a readable error on PyPy, the 3.16 policy change, and a way to log which loop you got.&lt;/li&gt;
&lt;li&gt;winuvloop is that branch plus those details, packaged once. It doesn't make anything faster. uvloop and winloop do.&lt;/li&gt;
&lt;li&gt;If you only target one OS, you don't need it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why the branch exists
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/MagicStack/uvloop" rel="noopener noreferrer"&gt;uvloop&lt;/a&gt; is the standard high-performance asyncio event loop on Linux and macOS. It isn't the Windows backend, so &lt;code&gt;import uvloop&lt;/code&gt; fails on Windows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Vizonex/Winloop" rel="noopener noreferrer"&gt;winloop&lt;/a&gt; fills that gap. It provides a uvloop-compatible API for Windows.&lt;/p&gt;

&lt;p&gt;So a project whose developers are on Windows laptops and whose production runs on Linux needs both, and it has to pick one at runtime. That's the setup I kept running into: developing on Windows, deploying to Linux.&lt;/p&gt;

&lt;p&gt;The upstream projects publish their own numbers for why you'd bother. uvloop reports making asyncio &lt;strong&gt;2-4x faster&lt;/strong&gt; on its echo-server benchmarks. winloop reports this TCP connection benchmark:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event loop policy (winloop's benchmark)&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WinLoopPolicy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.493s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WindowsProactorEventLoopPolicy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2.510s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WindowsSelectorEventLoopPolicy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2.723s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Those are upstream figures for specific workloads, not a promise for your app. Measure your own.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the hand-written version leaves out
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;if sys.platform&lt;/code&gt; branch handles the import. A real cross-platform setup needs a few more things.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Dependency markers.&lt;/strong&gt; You don't want pip installing uvloop on Windows or winloop on Linux, so your &lt;code&gt;pyproject.toml&lt;/code&gt; needs environment markers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s"&gt;"uvloop; sys_platform != 'win32' and platform_python_implementation == 'CPython'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;"winloop; sys_platform == 'win32' and platform_python_implementation == 'CPython'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;platform_python_implementation&lt;/code&gt; part is there because both backends are native CPython extensions. Without it, installing on PyPy tries to pull in a backend that doesn't support PyPy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Every entry point.&lt;/strong&gt; The branch gets copied into the CLI, the worker, the benchmark script, the README example, and the test helper. In my own projects I found near-identical copies of it in several places. Each copy is one more place for things to drift.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. A useful error.&lt;/strong&gt; When the backend isn't installed (on PyPy, say, or when someone installed with &lt;code&gt;--no-deps&lt;/code&gt;), you get a bare &lt;code&gt;ModuleNotFoundError: No module named 'uvloop'&lt;/code&gt;. That tells the reader &lt;em&gt;what&lt;/em&gt; failed and nothing about &lt;em&gt;why&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. The policy API is going away.&lt;/strong&gt; A lot of code calls &lt;code&gt;uvloop.install()&lt;/code&gt; or &lt;code&gt;asyncio.set_event_loop_policy(...)&lt;/code&gt; so a framework picks up the loop. asyncio deprecated its event-loop policy system in Python 3.14 and &lt;a href="https://docs.python.org/3/deprecations/pending-removal-in-3.16.html" rel="noopener noreferrer"&gt;will remove it in 3.16&lt;/a&gt;. The replacement is &lt;code&gt;asyncio.run(..., loop_factory=...)&lt;/code&gt;. Hand-written branches that call &lt;code&gt;install()&lt;/code&gt; will need to change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Knowing which loop you actually got.&lt;/strong&gt; When someone files a bug, "which event loop were you on, and which version?" should take one line to answer.&lt;/p&gt;

&lt;p&gt;None of this is hard. It's just easy to get slightly wrong in five places.&lt;/p&gt;

&lt;h2&gt;
  
  
  What winuvloop does
&lt;/h2&gt;

&lt;p&gt;winuvloop is that branch plus those details, packaged once.&lt;/p&gt;

&lt;p&gt;The runtime module is small: platform detection, a clear error, re-exports, and typing stubs. It doesn't vendor or patch either loop.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;winuvloop&lt;/span&gt;


&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;


&lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;SystemExit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;winuvloop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Windows that runs on winloop. On Linux, macOS, and other POSIX systems it runs on uvloop.&lt;/p&gt;

&lt;p&gt;What's inside:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dependency markers.&lt;/strong&gt; winuvloop's own dependencies are the markers shown above: uvloop on non-Windows CPython, winloop on Windows CPython. &lt;code&gt;pip install winuvloop&lt;/code&gt; or &lt;code&gt;uv add winuvloop&lt;/code&gt; pulls in only the backend for the current platform.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direct aliases, not wrappers.&lt;/strong&gt; &lt;code&gt;winuvloop.run&lt;/code&gt;, &lt;code&gt;winuvloop.new_event_loop&lt;/code&gt; and &lt;code&gt;winuvloop.Loop&lt;/code&gt; are the backend's own objects, so on Linux &lt;code&gt;winuvloop.Loop&lt;/code&gt; &lt;em&gt;is&lt;/em&gt; &lt;code&gt;uvloop.Loop&lt;/code&gt;. There's no wrapper layer to debug. Any other attribute is delegated to the selected backend module.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A versioned policy API.&lt;/strong&gt; &lt;code&gt;install()&lt;/code&gt; and &lt;code&gt;EventLoopPolicy&lt;/code&gt; still work for code that needs a global policy. For example, the README shows launching Uvicorn with &lt;code&gt;loop="asyncio"&lt;/code&gt; after &lt;code&gt;winuvloop.install()&lt;/code&gt;. Both come straight from the backend. Since 0.2.5, winuvloop lists them as public only on Python &amp;lt; 3.16 and avoids loading them at import time on 3.14.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A real error on unsupported interpreters.&lt;/strong&gt; If the backend is missing, the import error names the selected backend, the platform, and the Python implementation, and says the backends target CPython. On PyPy you get told &lt;em&gt;why&lt;/em&gt; instead of just &lt;em&gt;what&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Diagnostics.&lt;/strong&gt; &lt;code&gt;backend_name()&lt;/code&gt; and &lt;code&gt;backend_version()&lt;/code&gt;, shown below.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Typing.&lt;/strong&gt; It ships &lt;code&gt;py.typed&lt;/code&gt; and stubs for the public API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI on all three OSes.&lt;/strong&gt; Linux, macOS and Windows, with unit tests that mock both backends and smoke tests that call &lt;code&gt;winuvloop.run()&lt;/code&gt; on the real one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The diagnostics take one line each:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;winuvloop&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;winuvloop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backend_name&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;     &lt;span class="c1"&gt;# "uvloop" or "winloop"
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;winuvloop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backend_version&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They also make cross-platform tests simpler. Instead of branching on the OS all over your test suite, you assert the backend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;winuvloop&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_optimized_backend_is_available&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;winloop&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;platform&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;win32&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uvloop&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;winuvloop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backend_name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;winuvloop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backend_version&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For frameworks that create their own loop, install before the framework starts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# serve.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uvicorn&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;winuvloop&lt;/span&gt;

&lt;span class="n"&gt;winuvloop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;install&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;uvicorn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;myapp:app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;127.0.0.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;asyncio&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  When not to use it
&lt;/h2&gt;

&lt;p&gt;I'd skip winuvloop in these cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You only target one OS.&lt;/strong&gt; Use uvloop (Linux/macOS) or winloop (Windows) directly. A selector buys you nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need backend-specific APIs&lt;/strong&gt; and don't want a layer in between.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You're debugging an event-loop bug.&lt;/strong&gt; Take out every wrapper, winuvloop included, and import the backend directly. If the bug still reproduces, it belongs upstream (&lt;a href="https://github.com/MagicStack/uvloop/issues" rel="noopener noreferrer"&gt;uvloop issues&lt;/a&gt;, &lt;a href="https://github.com/Vizonex/Winloop/issues" rel="noopener noreferrer"&gt;winloop issues&lt;/a&gt;), not in winuvloop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You run on PyPy.&lt;/strong&gt; Neither backend supports it. winuvloop only fails more clearly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your deployment platform already configures the loop.&lt;/strong&gt; Use the platform's own setting.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One more honest caveat: most of winuvloop's maturity is really uvloop's and winloop's. It's marked Beta on PyPI. The selector itself is small, but runtime behavior is entirely the backend's.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is anyone using it?
&lt;/h2&gt;

&lt;p&gt;According to pypistats.org, winuvloop had &lt;strong&gt;6,837 downloads&lt;/strong&gt; in the last month (as of 2026-09-28).&lt;/p&gt;

&lt;p&gt;Download counts include CI and mirrors, so I read that as "some projects depend on it", not as a user count. I haven't promoted it anywhere until now, so that usage is organic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bottom line
&lt;/h2&gt;

&lt;p&gt;winuvloop doesn't make anything faster. The speed comes from uvloop and winloop.&lt;/p&gt;

&lt;p&gt;What it removes is a small branch that gets copied into every entry point, plus the details that usually get left out of it: install markers, a readable error on PyPy, the 3.16 policy change, and a way to log which loop you're on.&lt;/p&gt;

&lt;p&gt;If your asyncio code only runs on one OS, you don't need it. If it runs on both, give it a try:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;winuvloop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source, examples and issues: &lt;a href="https://github.com/aviadr1/winuvloop" rel="noopener noreferrer"&gt;github.com/aviadr1/winuvloop&lt;/a&gt;. Happy to hear feedback in the issues. I'd especially like to hear from anyone running winloop in production on Windows.&lt;/p&gt;

</description>
      <category>python</category>
      <category>asyncio</category>
      <category>windows</category>
      <category>performance</category>
    </item>
    <item>
      <title>Keep Every LangSmith Trace Without the 10 Retention Bill</title>
      <dc:creator>Aviad Rozenhek</dc:creator>
      <pubDate>Fri, 21 Aug 2026 00:38:17 +0000</pubDate>
      <link>https://dev.to/aviadr1/keep-every-langsmith-trace-without-the-10x-retention-bill-2gl1</link>
      <guid>https://dev.to/aviadr1/keep-every-langsmith-trace-without-the-10x-retention-bill-2gl1</guid>
      <description>&lt;p&gt;LangSmith is excellent for debugging live AI systems. But keeping every trace in its extended-retention tier can turn observability into a surprisingly large line item.&lt;/p&gt;

&lt;p&gt;Today we merged a new archive workflow into &lt;a href="https://github.com/gigaverse-app/langsmith-cli" rel="noopener noreferrer"&gt;&lt;code&gt;langsmith-cli&lt;/code&gt;&lt;/a&gt; that changes that tradeoff: keep LangSmith for live debugging, continuously archive verified traces to organization-owned private S3, and query the retained Parquet directly with DuckDB.&lt;/p&gt;

&lt;p&gt;In other words, you can preserve your complete trace history without placing every trace on LangSmith's extended-retention tier.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cost-overrun risk
&lt;/h2&gt;

&lt;p&gt;LangSmith currently documents two trace-retention tiers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Retention&lt;/th&gt;
&lt;th&gt;Published trace price&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Base&lt;/td&gt;
&lt;td&gt;14 days&lt;/td&gt;
&lt;td&gt;0.05¢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extended&lt;/td&gt;
&lt;td&gt;400 days&lt;/td&gt;
&lt;td&gt;0.50¢ total&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The 0.45¢ extended-retention upgrade makes an extended trace cost 10× as much as a base trace.&lt;/p&gt;

&lt;p&gt;That difference becomes material at production volume:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Monthly traces&lt;/th&gt;
&lt;th&gt;Base, 14 days&lt;/th&gt;
&lt;th&gt;Extended, 400 days&lt;/th&gt;
&lt;th&gt;Added retention cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;100,000&lt;/td&gt;
&lt;td&gt;$50&lt;/td&gt;
&lt;td&gt;$500&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$450&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,000,000&lt;/td&gt;
&lt;td&gt;$500&lt;/td&gt;
&lt;td&gt;$5,000&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$4,500&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10,000,000&lt;/td&gt;
&lt;td&gt;$5,000&lt;/td&gt;
&lt;td&gt;$50,000&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$45,000&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These examples use the published per-trace rates before free allowances, plan terms, negotiated pricing, or taxes. Always check the &lt;a href="https://docs.langchain.com/langsmith/usage-and-billing#data-retention" rel="noopener noreferrer"&gt;official LangSmith usage and billing documentation&lt;/a&gt; before making budget decisions.&lt;/p&gt;

&lt;p&gt;There is another subtle risk: online evaluators and automation rules can upgrade matching traces when retention extension is enabled. A rule that matches one run upgrades the whole trace, and a thread-level rule can upgrade every trace in that thread. LangSmith currently enables retention extension by default for new online evaluators and automation rules, although you can opt out.&lt;/p&gt;

&lt;p&gt;At scale, an innocent-looking evaluator or rule can therefore create a much larger bill than expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  The new &lt;code&gt;langsmith-cli&lt;/code&gt; archive workflow
&lt;/h2&gt;

&lt;p&gt;The new workflow separates live observability from long-term retention:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LangSmith live traces (14 days)
        │
        ├── D+2 primary export ───────┐
        └── D+12 reconciliation ──────┤ deduplicate by run ID
                                      ▼
                            private S3 / Parquet
                                      │
                                      ▼
                         runs ... --archive (DuckDB)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The D+2 export captures traces after normal late-arriving data has settled. The D+12 reconciliation pass catches delayed updates before the 14-day base-retention window closes. Both passes deduplicate by run ID, so retries are safe and the archive converges on the latest exported data.&lt;/p&gt;

&lt;p&gt;The result is long-term trace history in storage your organization owns and controls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Route projects to private S3
&lt;/h2&gt;

&lt;p&gt;Projects can be routed to separate buckets or prefixes—for example, to isolate development and production data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# archive.yaml&lt;/span&gt;
&lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev&lt;/span&gt;
    &lt;span class="na"&gt;project_pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dev/**"&lt;/span&gt;
    &lt;span class="na"&gt;archive_uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;s3://my-langsmith-traces-dev/langsmith&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
    &lt;span class="na"&gt;project_pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prd/**"&lt;/span&gt;
    &lt;span class="na"&gt;archive_uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;s3://my-langsmith-traces-prd/langsmith&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Point the CLI at that configuration and schedule the sync using cron, a Kubernetes CronJob, or your existing scheduler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;LANGSMITH_ARCHIVE_CONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/langsmith-cli/archive.yaml

langsmith-cli &lt;span class="nt"&gt;--json&lt;/span&gt; archive &lt;span class="nb"&gt;sync&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--all-routes&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--retention-days&lt;/span&gt; 14
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The organization supplies the scheduler, LangSmith API key used during export, AWS workload identity, private buckets, encryption, lifecycle policies, and reader IAM. The CLI owns the archive and query mechanism—not your infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Query historical traces without the LangSmith API
&lt;/h2&gt;

&lt;p&gt;Archived traces remain searchable through familiar commands. Reads use DuckDB over Parquet in S3 and do not require a LangSmith API key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List production runs from the last 90 days&lt;/span&gt;
langsmith-cli &lt;span class="nt"&gt;--json&lt;/span&gt; runs list &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--archive&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--project&lt;/span&gt; prd/my-agent &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--last&lt;/span&gt; 90d

&lt;span class="c"&gt;# Search historical runs&lt;/span&gt;
langsmith-cli &lt;span class="nt"&gt;--json&lt;/span&gt; runs search &lt;span class="s2"&gt;"timeout"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--archive&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--project&lt;/span&gt; prd/my-agent

&lt;span class="c"&gt;# Inspect a run and its children&lt;/span&gt;
langsmith-cli &lt;span class="nt"&gt;--json&lt;/span&gt; runs get &amp;lt;run-id&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--archive&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--follow-children&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means incident investigations, regression analysis, audits, and offline analytics can continue long after a base-tier trace has disappeared from the live LangSmith API.&lt;/p&gt;

&lt;h2&gt;
  
  
  A better default for high-volume tracing
&lt;/h2&gt;

&lt;p&gt;For many teams, the most economical model is now straightforward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Keep base retention for day-to-day debugging.&lt;/li&gt;
&lt;li&gt;Disable automatic retention extension where it is not intentionally needed.&lt;/li&gt;
&lt;li&gt;Export every trace to private S3 before the 14-day window closes.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;--archive&lt;/code&gt; for older investigations and analysis.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You keep LangSmith's excellent live debugging experience, retain your full historical dataset under your own controls, and make long-term storage costs predictable.&lt;/p&gt;

&lt;p&gt;The merged change and implementation notes are in &lt;a href="https://github.com/gigaverse-app/langsmith-cli/pull/163" rel="noopener noreferrer"&gt;langsmith-cli PR #163&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you operate LangSmith at meaningful volume, this is worth setting up before your next evaluator or automation rule quietly multiplies the retention bill.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>langchain</category>
      <category>devops</category>
      <category>opensource</category>
    </item>
    <item>
      <title>The Bitter Lesson for Backend Engineering</title>
      <dc:creator>Aviad Rozenhek</dc:creator>
      <pubDate>Thu, 28 May 2026 13:26:46 +0000</pubDate>
      <link>https://dev.to/aviadr1/the-bitter-lesson-for-backend-engineering-hof</link>
      <guid>https://dev.to/aviadr1/the-bitter-lesson-for-backend-engineering-hof</guid>
      <description>&lt;p&gt;Every senior backend engineer has sat in a war room while smart people argue&lt;br&gt;
over instincts.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"It is the ASGI event loop."&lt;br&gt;
"No, it is the database proxy."&lt;br&gt;
"Just throw more pods at it."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Core Thesis
&lt;/h2&gt;

&lt;p&gt;We treat optimization as a test of intuition. But in "The Bitter Lesson,"&lt;br&gt;
Richard Sutton's account of 70 years of AI research, the durable winners were&lt;br&gt;
not the systems that best encoded human intuition. They were the general methods&lt;br&gt;
that scaled with computation, especially search and learning. Human knowledge&lt;br&gt;
helped locally and felt satisfying, but it tended to plateau. Compute-scaled&lt;br&gt;
methods kept improving as compute got cheaper.&lt;/p&gt;

&lt;p&gt;Backend engineering is facing its own bitter lesson. The future of systems&lt;br&gt;
engineering is not "replacing judgment with agents." It is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Build the system of work so that more computation can be turned into more&lt;br&gt;
correct engineering progress.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For this backend effort, that means using agent compute to run a systematic&lt;br&gt;
engineering loop. The human sets the goal, picks the risk boundary, and decides&lt;br&gt;
what evidence is good enough. The agents spend parallel compute on the tedious&lt;br&gt;
but decisive work: reading call paths, building harnesses, running experiments,&lt;br&gt;
fitting scaling curves, checking payload equivalence, and keeping a durable log.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem We Unleashed It On
&lt;/h2&gt;

&lt;p&gt;The immediate problem was not "make one slow endpoint faster." It was harsher:&lt;br&gt;
we needed a path to 500-1000 RPS on endpoints we had not even written yet.&lt;/p&gt;

&lt;p&gt;At the same time, many existing endpoints were already struggling at&lt;br&gt;
single-digit RPS. Horizontal scaling was ineffective: each additional CPU or pod&lt;br&gt;
contributed too little throughput. There was no single obvious culprit. It&lt;br&gt;
might have been Python CPU, serializer shape, the ORM, database compute,&lt;br&gt;
connection fan-out, external calls, worker slots, event-loop blocking, load&lt;br&gt;
shedding, or some interaction between them.&lt;/p&gt;

&lt;p&gt;That made local cleverness the wrong tool. We did not need one more confident&lt;br&gt;
guess. We needed a methodology that could discover what had to be better before&lt;br&gt;
the next generation of endpoints was designed around the same bottlenecks.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Limits Of The 30 W Brain
&lt;/h2&gt;

&lt;p&gt;A human body runs at roughly 100 W; the brain is often estimated around 20-30 W.&lt;br&gt;
That biological efficiency is remarkable, but it is also a bottleneck. A single&lt;br&gt;
engineer can only hold so many hypotheses, command histories, query plans,&lt;br&gt;
deploy states, and before/after measurements in working memory.&lt;/p&gt;

&lt;p&gt;Backend engineers pride themselves on being empirical. We talk in latency&lt;br&gt;
histograms, queue depths, lock contention, p95s, CPU profiles, connection pools,&lt;br&gt;
and query plans. Yet we still fall into vibe-based optimization because real&lt;br&gt;
systems exceed human working memory.&lt;/p&gt;

&lt;p&gt;The irony is that backend performance is governed by rigid laws: discrete math,&lt;br&gt;
queuing theory, network physics, storage behavior, CPU scheduling, cache&lt;br&gt;
invalidation, and contention. Unlike product taste or visual design, these&lt;br&gt;
systems eventually answer to measurement. But a 30 W brain cannot accurately&lt;br&gt;
simulate an asymmetric network bottleneck, a contended connection pool, and a&lt;br&gt;
multi-join query plan under load.&lt;/p&gt;

&lt;p&gt;It certainly cannot do that while also tracking deploy state, cache behavior,&lt;br&gt;
and error budgets.&lt;/p&gt;

&lt;p&gt;Cheap-enough agent compute changes the shape of the work. Three agents can&lt;br&gt;
simultaneously:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;design instrumentation;&lt;/li&gt;
&lt;li&gt;build harnesses and runbooks;&lt;/li&gt;
&lt;li&gt;inspect hot paths;&lt;/li&gt;
&lt;li&gt;prove behavior equivalence;&lt;/li&gt;
&lt;li&gt;watch readiness, proxy metrics, app metrics, and logs;&lt;/li&gt;
&lt;li&gt;maintain shared coordination state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rational move is to spend compute to reduce uncertainty. The scarce&lt;br&gt;
resource is no longer typing time. It is experimental discipline.&lt;/p&gt;

&lt;p&gt;So we reach for plausible stories. The event loop. The ORM. The GIL. The proxy.&lt;br&gt;
The pods. The cache. The database. Each story may contain truth, but treating&lt;br&gt;
the story as the strategy is exactly the human-centric mistake Sutton described.&lt;/p&gt;

&lt;p&gt;The scalable strategy is to turn those stories into experiments and let compute&lt;br&gt;
do the search.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Ego Cost Is Familiar
&lt;/h2&gt;

&lt;p&gt;Sutton's essay is uncomfortable because it says researchers repeatedly preferred&lt;br&gt;
approaches that embedded human insight, even after scalable computation kept&lt;br&gt;
winning. The pain was not just technical; it was identity. It feels better to&lt;br&gt;
believe progress comes from our clever model of the domain than from a general&lt;br&gt;
method that searches harder, learns more, or evaluates more candidates.&lt;/p&gt;

&lt;p&gt;Backend engineering has the same temptation. The scalable engineering method is&lt;br&gt;
to make each plausible story falsifiable and let the measurements decide.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Case Study
&lt;/h2&gt;

&lt;p&gt;Three specialized agents were turned loose on one hot endpoint — a paginated&lt;br&gt;
list view central to the product — with instructions to find the binding&lt;br&gt;
constraint, not the prettiest fix. A gentle 3 RPS live stress run did the&lt;br&gt;
work: the database hit ~100% CPU while the proxy and its connections sat&lt;br&gt;
flat. The bottleneck was DB compute. Python CPU was falsified separately by&lt;br&gt;
a local sweep (parallel efficiency held at ~93% across four-way parallelism&lt;br&gt;
with negligible variance). Connection fan-out and the serving stack were&lt;br&gt;
not loaded enough at 3 RPS to matter. The stress test, not the code review,&lt;br&gt;
is what named the axis.&lt;/p&gt;

&lt;p&gt;With the axis identified, the agents went after the query shapes that were&lt;br&gt;
burning the CPU. The serializer N+1 collapsed by an order of magnitude&lt;br&gt;
(179 → 15 queries at page size 10, byte-identical payload). The paginated&lt;br&gt;
&lt;code&gt;COUNT(*)&lt;/code&gt; over a distinct multi-join was the bigger win (~639 ms → ~1.5 ms,&lt;br&gt;
~400× faster, same value). A forced &lt;code&gt;GROUP BY&lt;/code&gt; rewritten as a correlated&lt;br&gt;
subquery took the full local view path from about 1590 ms to about 174 ms;&lt;br&gt;
on a warm pod the median dropped from about 2.25 s to about 1.57 s.&lt;/p&gt;

&lt;p&gt;Those wins matter as evidence that &lt;em&gt;the DB CPU axis is reducible&lt;/em&gt; — that&lt;br&gt;
the binding constraint stress testing surfaced is also a tractable one.&lt;br&gt;
They are not the lesson. The lesson is what the search exposed when it ran&lt;br&gt;
past one endpoint. The system is not bound by a single bottleneck. It is&lt;br&gt;
bound by five, each with a different blast radius, a different toolchain&lt;br&gt;
to investigate, and a different treatment cost:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The DB CPU wall.&lt;/strong&gt; The 2-vCPU writer hits ~100% long before connections
saturate. The surface fixes compress one query class on one endpoint;
underneath, dozens of unrelated endpoints share the same query shapes,
with the same low-RPS CPU cost. A hotspot inventory pairing
&lt;code&gt;pg_stat_statements&lt;/code&gt; with &lt;code&gt;EXPLAIN&lt;/code&gt; is where the next ten- and
twenty-times wins live.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The serving-layer ceiling.&lt;/strong&gt; Even with the database idle, an in-cluster
harness reproduces a ~1k-RPS wall per instance: four GIL-bound gunicorn
workers paying a per-request cost. Adding pods does not buy RPS until
that ratio improves.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The middleware tax.&lt;/strong&gt; A cache-hit response has no DB cost and no
business logic, yet still measures in tens of milliseconds. That floor —
auth resolution, DRF rendering, observability middleware, session and
CSRF on routes that need neither — is what the cache-hit ceiling cares
about.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching strategy.&lt;/strong&gt; A cache that is not stampede-protected fans out
a miss into exactly the DB compute the surface fixes were trying to
avoid. The cache only matters if it is reachable cheaply and protected
end-to-end.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3rd-party blocking on the request path.&lt;/strong&gt; Some endpoints still make
synchronous calls to external services during request handling. Those
endpoints cannot scale linearly with workers — and offloading them is
a behavior change that needs product sign-off rather than a perf-only
PR.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Pareto matters because each scenario hits a different axis. The 50K&lt;br&gt;
simultaneous-join burst is dominated by one endpoint and probably goes the&lt;br&gt;
&lt;em&gt;caching plus middleware-bypass&lt;/em&gt; route. The sustained 1K livestream uses&lt;br&gt;
six endpoints driven by FE polling cadence and is dominated by the &lt;em&gt;DB CPU&lt;br&gt;
wall&lt;/em&gt; and &lt;em&gt;3rd-party blocking&lt;/em&gt; axes; middleware optimization buys it less.&lt;br&gt;
"Make the backend faster" is a wish; "make &lt;em&gt;this&lt;/em&gt; scenario's binding axis&lt;br&gt;
cheaper" is a ranked list of PRs.&lt;/p&gt;

&lt;p&gt;The interesting part is not any single optimization, or even the cascade of&lt;br&gt;
falsifications and confirmations that produced the surface wins. It is the&lt;br&gt;
shape of what was found underneath: not one bottleneck, but a &lt;em&gt;class&lt;/em&gt; of&lt;br&gt;
them, with different scenarios bound by different axes. The agents narrowed&lt;br&gt;
the search space with evidence and let the bottleneck move without defending&lt;br&gt;
the previous theory — which is the only way a system this shape gets mapped&lt;br&gt;
at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Evidence At Scale
&lt;/h2&gt;

&lt;p&gt;One endpoint's hypothesis cascade is a vignette. The harder claim — the&lt;br&gt;
uncomfortable one — is that this kind of work &lt;em&gt;scales&lt;/em&gt;: in roughly&lt;br&gt;
thirty-six hours, a small team plus parallel agents did the shape and&lt;br&gt;
volume of investigation that a senior engineer would historically have&lt;br&gt;
spread across a quarter. The most important output is not any single&lt;br&gt;
fix. It is the &lt;em&gt;kinds of work&lt;/em&gt; that suddenly become routine.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Investigation At Scale
&lt;/h3&gt;

&lt;p&gt;The agents read across four production codebases plus a PRD for a system&lt;br&gt;
that does not exist yet — the API backend, the web frontend (to extract&lt;br&gt;
real polling cadences and request fan-out), the AI/livestream service,&lt;br&gt;
and the mobile/edge client paths, alongside the product requirements for&lt;br&gt;
a 50K-person event flow with no working implementation. They produced,&lt;br&gt;
per traffic scenario, the &lt;strong&gt;cadences&lt;/strong&gt;, the &lt;strong&gt;at-risk factors&lt;/strong&gt;, and the&lt;br&gt;
&lt;strong&gt;treatments&lt;/strong&gt;, and turned each into durable artifacts: a capacity model&lt;br&gt;
deriving per-instance RPS from worker count and CPU-per-request; a&lt;br&gt;
DB-CPU hotspot inventory ranking query shapes by cost so the ten- and&lt;br&gt;
twenty-times wins land before the 1.2× ones; a four-scenario event-day&lt;br&gt;
load profile (bursty purchase, 50K simultaneous join, initial-pressure&lt;br&gt;
livestream, sustained livestream — each with a different binding axis);&lt;br&gt;
and a synthesis collapsing dozens of hypotheses into the five axes the&lt;br&gt;
case study named.&lt;/p&gt;

&lt;p&gt;That analysis fed a hypothesis pipeline rather than a stack of one-off&lt;br&gt;
optimizations. Roughly sixty tracking tickets opened under the parent&lt;br&gt;
audit — about twenty-five explicit hypothesis tickets (each naming the&lt;br&gt;
smell, the affected endpoints, and a falsifier), about seven paired&lt;br&gt;
data-verification tickets whose only job is to confirm or kill a sibling&lt;br&gt;
hypothesis, and the rest execution sub-projects (per-PR side backend,&lt;br&gt;
read-replica routing, perf budgets, observability automation, DevOps&lt;br&gt;
unblocks). Several stories were confirmed by measured wins of the kind&lt;br&gt;
the case-study table chronicles — orders of magnitude on serializer&lt;br&gt;
fan-out and paginated counts, an order of magnitude on a forced&lt;br&gt;
aggregation — extended to more than one endpoint. Several others were&lt;br&gt;
&lt;em&gt;explicitly killed&lt;/em&gt;, which matters more than it sounds: the&lt;br&gt;
Python-CPU-as-primary-limiter story (falsified by a local sweep that&lt;br&gt;
scaled near-linearly); the load-source story for the ~1k-RPS ceiling&lt;br&gt;
(the ceiling is real and lives in the worker count × CPU-per-request&lt;br&gt;
budget); the token-cache premise (raw signature verification was too&lt;br&gt;
cheap for the cache to matter); a codec swap assumed to be a meaningful&lt;br&gt;
win that turned out to be noise. About two dozen hypotheses remain open&lt;br&gt;
in triage, each queued behind its own data-verification ticket. The&lt;br&gt;
point is not that the audit is finished. The point is that the &lt;em&gt;queue&lt;br&gt;
itself&lt;/em&gt; — falsifiable, prioritized, paired with a verification recipe —&lt;br&gt;
is the durable output, built in parallel with the work that closed the&lt;br&gt;
first batch. A traditional team builds this queue &lt;em&gt;or&lt;/em&gt; builds the fixes;&lt;br&gt;
this team built both.&lt;/p&gt;

&lt;h3&gt;
  
  
  Treatments At Scale
&lt;/h3&gt;

&lt;p&gt;The shipped treatments span the categories the analysis pointed at&lt;br&gt;
rather than clustering on a single favorite. In the same window:&lt;br&gt;
&lt;strong&gt;query-shape rewrites&lt;/strong&gt; (N+1 collapse, cheap-count pagination, GROUP-BY&lt;br&gt;
elimination); &lt;strong&gt;per-request-cost reductions&lt;/strong&gt; (eager-loading on the auth&lt;br&gt;
path, explicit connection budgeting); &lt;strong&gt;platform plumbing&lt;/strong&gt; (a per-PR&lt;br&gt;
side backend so each branch can be benchmarked in isolation, an&lt;br&gt;
in-cluster load harness, an observability-bundle validator, a fixed-rate&lt;br&gt;
stress client); and &lt;strong&gt;methodology infrastructure&lt;/strong&gt; (a clean perf-PR&lt;br&gt;
pattern, an opt-in perf measurement toolkit, a Django caching playbook,&lt;br&gt;
a per-scenario load-profile doc, a stress-test report for review by&lt;br&gt;
humans who were not in the experiment loop).&lt;/p&gt;

&lt;p&gt;Each treatment shipped as a small, byte-identical, single-anti-pattern&lt;br&gt;
PR off main rather than as a bundled lab branch. That discipline is&lt;br&gt;
itself a result. A senior engineer doing all of this serially does not&lt;br&gt;
get to be that disciplined — they bundle, because bundling is how&lt;br&gt;
working memory preserves context across changes. Parallel agents can&lt;br&gt;
afford the discipline, and the team inherits a reviewable trail instead&lt;br&gt;
of a heroic mega-merge. Beyond the PRs themselves, the cycle produced&lt;br&gt;
roughly fifteen durable documents — capacity models, methodology specs,&lt;br&gt;
optimization playbooks, validity gates, worked examples, runbooks, and&lt;br&gt;
coordination logs — so the reasoning lives in the repository instead of&lt;br&gt;
in chat history.&lt;/p&gt;




&lt;p&gt;The honest read of this is not that the agents won. It is that the&lt;br&gt;
parts of senior engineering work that used to be scarce — exhaustive&lt;br&gt;
analysis across codebases, parallel falsification of competing stories,&lt;br&gt;
ranked treatment lists with measured magnitudes, and writeups that&lt;br&gt;
survive past the conversation that produced them — are no longer&lt;br&gt;
rate-limited by typing time or by working memory. They are rate-limited&lt;br&gt;
by whether the team sets the goal clearly, defines the falsifier&lt;br&gt;
honestly, and accepts the evidence when it arrives.&lt;/p&gt;

&lt;p&gt;That last one is where the bitter part lives. Some of the killed&lt;br&gt;
hypotheses were stories the team had defended. Some of the confirmed&lt;br&gt;
wins were not in anyone's top three guesses. The method does not care&lt;br&gt;
which story was satisfying; it cares which story survives contact with&lt;br&gt;
measurement. Accepting that — and learning to take pride in defining&lt;br&gt;
goals and falsifiers rather than in being the one who &lt;em&gt;also&lt;/em&gt; typed every&lt;br&gt;
fix — is what makes the rest of it possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Meta-Method And Architecture
&lt;/h2&gt;

&lt;p&gt;The work succeeded because it was not run as a pile of disconnected chat turns.&lt;br&gt;
It was run as a long-running goal architecture with a shared objective,&lt;br&gt;
specialized agents, explicit containment, and durable state.&lt;/p&gt;

&lt;p&gt;The first phase was deliberately constrained: prepare PRs, tests, hypotheses,&lt;br&gt;
data, scripts, runbooks, and reasoning, but do not deploy and do not touch the&lt;br&gt;
shared database. The agents did not burn the target environment just to feel&lt;br&gt;
productive. They built the lab: local provider stubs, fresh-migrate workarounds,&lt;br&gt;
isolated data paths, baseline controls, instrumentation, load tooling, parsers,&lt;br&gt;
runbooks, and a coordination protocol.&lt;/p&gt;

&lt;p&gt;After live execution was approved, the operating model shifted from isolation&lt;br&gt;
to controlled target-environment work. The agents imported target-shaped data&lt;br&gt;
into local Postgres, coordinated deployment windows, recorded deployed SHAs,&lt;br&gt;
watched stop conditions, and pushed the system only until the next useful data&lt;br&gt;
point appeared.&lt;/p&gt;

&lt;p&gt;When a 3 RPS run saturated the environment, they treated it not as a crisis or&lt;br&gt;
a reason to push harder, but as evidence: wall time times worker slots was&lt;br&gt;
already enough to tip the system over.&lt;/p&gt;

&lt;p&gt;That architecture had five essential parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;A shared objective:&lt;br&gt;
make 500-1000 RPS plausible with effective horizontal scaling, not just make&lt;br&gt;
one benchmark look better.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A falsifiable model:&lt;br&gt;
use Amdahl's law, Little's Law (in-flight work ≈ throughput × latency),&lt;br&gt;
and the Universal Scalability Law to ask&lt;br&gt;
whether throughput is capped by CPU, DB compute, DB connections, event-loop&lt;br&gt;
blocking, external calls, or queueing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Isolation before risk:&lt;br&gt;
local Docker, an imported target-environment clone, local provider stubs, and&lt;br&gt;
explicit live-execution gates allowed fast iteration without turning the&lt;br&gt;
shared environment into the lab by default.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Agent specialization:&lt;br&gt;
one agent owned app-code endpoint proofs and synthesis; another owned&lt;br&gt;
harnesses, deployment, observability, and scaling sweeps; another owned&lt;br&gt;
independent review and adjacent endpoint proofs. The taskboard and&lt;br&gt;
append-only channel made collisions visible.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Proof over assertion:&lt;br&gt;
changes were paired with query counts, timings, tests, payload hashes,&lt;br&gt;
isolated worktree comparisons, target-environment samples, and explicit&lt;br&gt;
caveats.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Backend optimization is longer than one prompt. It crosses code reading,&lt;br&gt;
experiment design, harness construction, local reproduction, PR preparation,&lt;br&gt;
deploy orchestration, observability capture, measurement correction, endpoint&lt;br&gt;
fixes, and synthesis.&lt;/p&gt;

&lt;p&gt;A stateless chat loop tends to forget the global objective or collapse into the&lt;br&gt;
next visible task. Long-running goals kept the agents attached to the durable&lt;br&gt;
outcome and gave them a place to preserve context across hours of asynchronous&lt;br&gt;
code generation, deployment windows, and log analysis.&lt;/p&gt;

&lt;p&gt;The operating loop was simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;State the bottleneck hypothesis in physical terms.&lt;/li&gt;
&lt;li&gt;Define the falsifier.&lt;/li&gt;
&lt;li&gt;Build the smallest diagnostic.&lt;/li&gt;
&lt;li&gt;Measure wall, CPU, DB time, external waits, queueing, errors, and saturation.&lt;/li&gt;
&lt;li&gt;Change one lever.&lt;/li&gt;
&lt;li&gt;Prove behavior, ideally with byte-identical payloads.&lt;/li&gt;
&lt;li&gt;Re-measure the same way.&lt;/li&gt;
&lt;li&gt;Update the model when the bottleneck moves.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That last step is the hardest culturally. If the bottleneck moves, that is&lt;br&gt;
success. The point is not to defend the first theory. The point is to make the&lt;br&gt;
next limiter visible.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Guardrails
&lt;/h2&gt;

&lt;p&gt;Cheap parallel compute is an accelerant, which means bad goal architecture will&lt;br&gt;
amplify engineering errors just as quickly as it amplifies useful work. An&lt;br&gt;
agent running an undisciplined optimization loop will happily DDoS a shared&lt;br&gt;
environment, burn through observability spend, hide a regression behind a&lt;br&gt;
cached response, or hallucinate a benchmarking victory by measuring failures as&lt;br&gt;
throughput.&lt;/p&gt;

&lt;p&gt;The human role therefore does not disappear. It moves from "manually perform&lt;br&gt;
every step" to "define the containment boundary." The safety rails are not&lt;br&gt;
optional process decoration; they are what make autonomous exploration usable:&lt;br&gt;
local-first execution, explicit live-execution gates, one load window at a&lt;br&gt;
time, stop conditions, deployed SHA capture, query-count and payload-equivalence&lt;br&gt;
proofs, error-rate thresholds, and append-only coordination logs.&lt;/p&gt;

&lt;p&gt;The measurement bug caught in this work is the small version of the same risk:&lt;br&gt;
the first load harness reported achieved RPS in a way that counted failures.&lt;br&gt;
Without experimental guardrails, cheap compute would have made that wrong&lt;br&gt;
number look more convincing. With guardrails, the agents corrected the harness,&lt;br&gt;
reclassified the run, and improved the method.&lt;/p&gt;

&lt;p&gt;This method also has boundaries. It is strongest when the system is observable,&lt;br&gt;
the goal is measurable, and improvement can proceed through small,&lt;br&gt;
behavior-preserving changes. It is weaker when the task is green-field&lt;br&gt;
architecture, product judgment, or a novel failure mode with poor telemetry.&lt;br&gt;
There, human architectural taste and risk judgment still lead. The agents can&lt;br&gt;
map the terrain, but they cannot decide what terrain is worth owning.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Lesson
&lt;/h2&gt;

&lt;p&gt;The human contribution is still essential, but it moves up a level. The human&lt;br&gt;
should not spend most of the scarce 30 W on manually remembering every path and&lt;br&gt;
guessing which bottleneck matters. The human should set the objective, enforce&lt;br&gt;
experimental hygiene, choose acceptable risk, and judge the evidence.&lt;/p&gt;

&lt;p&gt;The agents should burn compute on the scalable parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;exhaustive code reading;&lt;/li&gt;
&lt;li&gt;independent hypothesis generation;&lt;/li&gt;
&lt;li&gt;repeated benchmark runs;&lt;/li&gt;
&lt;li&gt;query-plan and metric collection;&lt;/li&gt;
&lt;li&gt;before/after equivalence checks;&lt;/li&gt;
&lt;li&gt;deployment state verification;&lt;/li&gt;
&lt;li&gt;runbook and taskboard maintenance;&lt;/li&gt;
&lt;li&gt;synthesis into a capacity model.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the backend version of Sutton's lesson: do not over-invest in a human's&lt;br&gt;
favorite explanation of the system. Invest in the meta-process that searches the&lt;br&gt;
system, measures it, and improves as more compute is applied.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The discomfort is the lesson. Mature backend engineering will feel less like&lt;br&gt;
winning arguments about the system and more like building machinery that makes&lt;br&gt;
those arguments unnecessary.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The result may sting. A single senior engineer's intuition is valuable, but it&lt;br&gt;
is not a substitute for three agents running disciplined experiments in&lt;br&gt;
parallel. That is not an insult to engineering judgment. It is what engineering&lt;br&gt;
judgment should choose when compute becomes cheap enough.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Counter Factual&lt;/strong&gt;&lt;br&gt;
Estimate for an staff engineer — twenty years of wins across many varied fields data engineering, applied AI, MongoDB at scale, video codecs you've actually heard of&amp;nbsp; — none of which necessarily teaches you Django querysets, Postgres profiling, stampede-protected Redis, gunicorn worker math, or how a Kubernetes liveness probe interacts with a saturated worker pool.&lt;/p&gt;

&lt;p&gt;Ramp-up to credible-not-expert across that stack: 2–3 months. Investigation work on top of the ramp-up: another 1–2 months. Call it half a year, assuming nothing else catches fire. They'd ship ~10 heroic merged PRs and an oral folklore in their head — because under deadline the writeups are the first thing that gets cut. They'd also miss at least one axis with high probability;&lt;/p&gt;

&lt;p&gt;Versus ~36 hours with agents in parallel, all five axes named, the writeups extant. Call it a ~100× compression in wall time, plus the writeups exist at all — which, on a year-long solo project, they wouldn't.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  External Reference
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Richard Sutton, "The Bitter Lesson", March 13, 2019:
&lt;a href="http://www.incompleteideas.net/IncIdeas/BitterLesson.html" rel="noopener noreferrer"&gt;http://www.incompleteideas.net/IncIdeas/BitterLesson.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>backend</category>
      <category>software</category>
      <category>agents</category>
    </item>
    <item>
      <title>Turning Linear and Figma Into a Searchable Knowledge Layer with `aviadr1/issueclaw` and `aviadr1/figmaclaw`</title>
      <dc:creator>Aviad Rozenhek</dc:creator>
      <pubDate>Fri, 03 Apr 2026 12:13:47 +0000</pubDate>
      <link>https://dev.to/aviadr1/turning-linear-and-figma-into-a-searchable-knowledge-layer-with-aviadr1issueclaw-and-29c5</link>
      <guid>https://dev.to/aviadr1/turning-linear-and-figma-into-a-searchable-knowledge-layer-with-aviadr1issueclaw-and-29c5</guid>
      <description>&lt;p&gt;Most teams don’t lack information — they lack a way to search product, design, and engineering context as one system; this article explores what happens when you turn Linear and Figma into searchable markdown and let humans and AI reason across both.&lt;/p&gt;

&lt;p&gt;Product intent is in Linear. Design detail is in Figma. Implementation reality is in code. Each system is useful on its own, but the moment you need to answer a question that crosses all three, things get awkward. You open tabs, search manually, ask in Slack, and reconstruct the answer from fragments.&lt;/p&gt;

&lt;p&gt;That is tolerable for humans. It is much worse for AI.&lt;/p&gt;

&lt;p&gt;At Gigaverse, we wanted something different: not a new source of truth, and not a replacement for Linear or Figma, but a &lt;strong&gt;shared text layer&lt;/strong&gt; that makes product and design context searchable, diffable, and usable by both humans and agents.&lt;/p&gt;

&lt;p&gt;That is the role of two repositories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;aviadr1/issueclaw&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;aviadr1/figmaclaw&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, they turn Linear and Figma into git-native markdown that can be searched alongside code. Once that exists, a different class of workflow becomes possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;This article is about a simple idea with surprisingly large consequences:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Mirror Linear and Figma into markdown, keep that mirror up to date, and use it as a searchable working memory layer for your team.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you read on, you should come away with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a clear mental model for what &lt;code&gt;issueclaw&lt;/code&gt; and &lt;code&gt;figmaclaw&lt;/code&gt; do&lt;/li&gt;
&lt;li&gt;a practical sense of why this is useful beyond “exporting markdown”&lt;/li&gt;
&lt;li&gt;real examples of the kinds of questions this unlocks&lt;/li&gt;
&lt;li&gt;a private-by-default way to adopt the same pattern in your own team&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The short version is that this makes it much easier to answer questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where does this feature already exist in design?&lt;/li&gt;
&lt;li&gt;What did product actually decide here?&lt;/li&gt;
&lt;li&gt;Is this designed on both web and mobile?&lt;/li&gt;
&lt;li&gt;Does the design still match the current implementation?&lt;/li&gt;
&lt;li&gt;What changed in one system that has not propagated to the others?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is where the real value starts.&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem: product knowledge is fragmented by tool boundaries
&lt;/h2&gt;

&lt;p&gt;Modern product development is full of cross-system questions.&lt;/p&gt;

&lt;p&gt;Not “what does this ticket say?”&lt;br&gt;
Not “what does this Figma page look like?”&lt;br&gt;
But questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is the current shape of this feature from spec to design to implementation?&lt;/li&gt;
&lt;li&gt;Which design patterns are duplicated or inconsistent?&lt;/li&gt;
&lt;li&gt;Is this bug contradicting the spec, the design, or the code?&lt;/li&gt;
&lt;li&gt;Which features have been explored deeply in design but barely implemented?&lt;/li&gt;
&lt;li&gt;Where are we at risk of building from stale assumptions?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are not rare edge cases. They are everyday questions.&lt;/p&gt;

&lt;p&gt;And most tools are not built to answer them well.&lt;/p&gt;

&lt;p&gt;Linear is good at tickets, docs, and status.&lt;br&gt;
Figma is good at visual design and flows.&lt;br&gt;
Git is good at code and history.&lt;/p&gt;

&lt;p&gt;The problem is everything that lives between them.&lt;/p&gt;


&lt;h2&gt;
  
  
  The core idea
&lt;/h2&gt;

&lt;p&gt;The idea behind &lt;code&gt;aviadr1/issueclaw&lt;/code&gt; and &lt;code&gt;aviadr1/figmaclaw&lt;/code&gt; is straightforward.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;issueclaw&lt;/code&gt; mirrors the Linear side into markdown&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;figmaclaw&lt;/code&gt; mirrors the Figma side into markdown&lt;/li&gt;
&lt;li&gt;that markdown lives in git, alongside the rest of your technical context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you do that, you get something new:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Linear stops being only a UI you browse&lt;/li&gt;
&lt;li&gt;Figma stops being only a canvas you click through&lt;/li&gt;
&lt;li&gt;git becomes a searchable memory layer for product, design, and engineering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the key shift.&lt;/p&gt;

&lt;p&gt;This is not about replacing the original tools. The source systems remain the source of truth. The markdown mirror exists to make them more searchable, more comparable, and more usable in workflows that span multiple systems.&lt;/p&gt;


&lt;h2&gt;
  
  
  What &lt;code&gt;aviadr1/figmaclaw&lt;/code&gt; does
&lt;/h2&gt;

&lt;p&gt;At a high level, &lt;code&gt;figmaclaw&lt;/code&gt; turns Figma pages into markdown.&lt;/p&gt;

&lt;p&gt;Each page becomes a &lt;code&gt;.md&lt;/code&gt; file with two layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a structural layer with things like page IDs, frame IDs, flow information, and metadata&lt;/li&gt;
&lt;li&gt;a readable layer with summaries, sections, descriptions, and flow representations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That gives you something that is simultaneously machine-friendly and human-friendly.&lt;/p&gt;

&lt;p&gt;Machines can diff it, hash it, update it incrementally, and search it cheaply.&lt;/p&gt;

&lt;p&gt;Humans can read it, grep it, review it, and paste it into AI workflows without having to browse Figma frame by frame.&lt;/p&gt;

&lt;p&gt;In the Gigaverse setup, this created a large text mirror of the design workspace: hundreds of markdown files representing pages across dozens of Figma files. That was enough to turn design discovery into a search problem instead of a browsing problem.&lt;/p&gt;


&lt;h2&gt;
  
  
  What &lt;code&gt;aviadr1/issueclaw&lt;/code&gt; does
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;issueclaw&lt;/code&gt; does the complementary job on the Linear side.&lt;/p&gt;

&lt;p&gt;It mirrors issues, docs, and related artifacts into markdown so they can live in git as searchable text. That makes product context easier to diff, easier to inspect historically, and far easier to combine with design and code context.&lt;/p&gt;

&lt;p&gt;The important point is not just that the data is exported.&lt;/p&gt;

&lt;p&gt;It is that once the product layer and the design layer are both present as text, they can be searched together.&lt;/p&gt;

&lt;p&gt;That is what unlocks the more interesting workflows.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why this is more useful than it sounds
&lt;/h2&gt;

&lt;p&gt;If you hear “we exported Figma and Linear into markdown,” it sounds mildly convenient.&lt;/p&gt;

&lt;p&gt;That undersells it.&lt;/p&gt;

&lt;p&gt;The real benefit is that you get a &lt;strong&gt;new reasoning layer&lt;/strong&gt; across systems that normally do not compose well.&lt;/p&gt;

&lt;p&gt;At Gigaverse, this led to a few capabilities that stood out immediately.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Full-text search across designs
&lt;/h3&gt;

&lt;p&gt;This was the clearest immediate win.&lt;/p&gt;

&lt;p&gt;Once Figma was mirrored into markdown, questions like these became trivial:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where do polls appear across the product?&lt;/li&gt;
&lt;li&gt;Which screens mention notifications?&lt;/li&gt;
&lt;li&gt;How much dark mode coverage do we have?&lt;/li&gt;
&lt;li&gt;What onboarding UI already exists?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are annoying questions in raw Figma. They become easy when design is searchable as text.&lt;/p&gt;

&lt;p&gt;This alone is a strong enough use case to justify the mirror.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Feature tracing from spec to design to implementation
&lt;/h3&gt;

&lt;p&gt;One of the strongest tests was tracing Gigaverse’s “going live” feature end to end.&lt;/p&gt;

&lt;p&gt;By combining Linear docs, Figma markdown, and implementation issues, we could reconstruct:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the intended product behavior&lt;/li&gt;
&lt;li&gt;the relevant web and mobile flows&lt;/li&gt;
&lt;li&gt;the implementation work across teams&lt;/li&gt;
&lt;li&gt;and contradictions between different sources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is not just convenient retrieval. That is actual cross-system understanding.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Reconstructing design decisions
&lt;/h3&gt;

&lt;p&gt;Another strong use case was looking at stage widgets.&lt;/p&gt;

&lt;p&gt;The Figma side showed iterations, comparisons, and changing layout ideas. The Linear side showed what had been decided and how implementation tracked those decisions.&lt;/p&gt;

&lt;p&gt;That let us answer not just “what screens exist?” but “what was explored, what direction won, and how far did it get?”&lt;/p&gt;

&lt;p&gt;That is a much more interesting kind of query.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Platform parity analysis
&lt;/h3&gt;

&lt;p&gt;This turned out to be a very practical question:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which features exist on both mobile and web?&lt;/li&gt;
&lt;li&gt;Which are intentionally platform-specific?&lt;/li&gt;
&lt;li&gt;Where are the gaps?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That kind of audit becomes much easier once the design layer and the issue layer are searchable in one place.&lt;/p&gt;
&lt;h3&gt;
  
  
  5. Design system audits
&lt;/h3&gt;

&lt;p&gt;This is another category that gets dramatically better once design becomes text.&lt;/p&gt;

&lt;p&gt;Instead of clicking through files manually, you can search for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;empty states&lt;/li&gt;
&lt;li&gt;loading states&lt;/li&gt;
&lt;li&gt;modal patterns&lt;/li&gt;
&lt;li&gt;permission flows&lt;/li&gt;
&lt;li&gt;error states&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That makes inconsistency much easier to spot.&lt;/p&gt;


&lt;h2&gt;
  
  
  The most interesting capability: drift detection
&lt;/h2&gt;

&lt;p&gt;The biggest surprise was that the highest-value use case was not search.&lt;/p&gt;

&lt;p&gt;It was &lt;strong&gt;drift detection&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Once specs, issues, and designs were all searchable together, it became much easier to spot cases where they disagreed.&lt;/p&gt;

&lt;p&gt;At Gigaverse, this surfaced real examples of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;contradictory toggle defaults across issues, product docs, and QA expectations&lt;/li&gt;
&lt;li&gt;terminology drift between active ticket language and newer design language&lt;/li&gt;
&lt;li&gt;design elements that remained in Figma after they had effectively been removed from the product direction&lt;/li&gt;
&lt;li&gt;exploratory design states with no matching implementation work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where the system stopped feeling like a retrieval convenience and started feeling like infrastructure.&lt;/p&gt;

&lt;p&gt;Search saves time.&lt;/p&gt;

&lt;p&gt;Drift detection saves teams from bugs, confusion, and rework.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why not just use Figma MCP directly?
&lt;/h2&gt;

&lt;p&gt;Because the two approaches are good at different things.&lt;/p&gt;

&lt;p&gt;Figma MCP is best when you already know what you want to inspect. It is excellent for exact visual truth, precise frame inspection, and prototype details.&lt;/p&gt;

&lt;p&gt;The markdown mirror is best when you do not yet know where to look.&lt;/p&gt;

&lt;p&gt;That distinction matters.&lt;/p&gt;

&lt;p&gt;For broad search, inventory, audits, and AI context-building, markdown is dramatically more efficient. It is faster to search, cheaper to process, and much more compact for LLM workflows than raw API payloads or screenshot-heavy browsing.&lt;/p&gt;

&lt;p&gt;The best workflow is not one or the other.&lt;/p&gt;

&lt;p&gt;It is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Use the markdown layer to narrow the search space.&lt;br&gt;
Use Figma MCP only for the specific frames where visual precision matters.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That division of labor turned out to be extremely effective.&lt;/p&gt;


&lt;h2&gt;
  
  
  How &lt;code&gt;figmaclaw&lt;/code&gt; stays current
&lt;/h2&gt;

&lt;p&gt;A one-time export would be mildly useful.&lt;/p&gt;

&lt;p&gt;What makes &lt;code&gt;figmaclaw&lt;/code&gt; interesting is that it is incremental.&lt;/p&gt;

&lt;p&gt;At a high level, it works like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tracked Figma files are checked for changes&lt;/li&gt;
&lt;li&gt;unchanged files or pages are skipped&lt;/li&gt;
&lt;li&gt;changed pages are updated&lt;/li&gt;
&lt;li&gt;changed frames are the ones that get re-enriched&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That means the system does not need to rebuild the entire design workspace every time someone makes a small edit.&lt;/p&gt;

&lt;p&gt;This matters for both speed and cost.&lt;/p&gt;

&lt;p&gt;It also means the mirror can stay reasonably close to the actual design source of truth without turning every sync into a huge batch process.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why sync and enrichment are separate
&lt;/h2&gt;

&lt;p&gt;This is one of the smartest parts of the architecture.&lt;/p&gt;

&lt;p&gt;There are really two different problems here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;keeping the mirror structurally up to date&lt;/li&gt;
&lt;li&gt;making the mirror richly readable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first part is deterministic and cheap.&lt;br&gt;
The second part is interpretive and more expensive.&lt;/p&gt;

&lt;p&gt;So the system treats them separately.&lt;/p&gt;

&lt;p&gt;The sync layer updates the structure.&lt;br&gt;
The enrichment layer turns that structure into useful prose.&lt;/p&gt;

&lt;p&gt;That is a better architecture than trying to do everything in one pass.&lt;/p&gt;

&lt;p&gt;It means the mirror can stay fresh even while enrichment is still running, and it means you can prioritize the highest-value parts of the design corpus first.&lt;/p&gt;

&lt;p&gt;In our case, enrichment is currently in progress and the whole mirrored system should be enriched within a few hours. That matters because it removes the biggest remaining weakness we saw: pages that were structurally present but not yet richly described.&lt;/p&gt;


&lt;h2&gt;
  
  
  What this means for AI workflows
&lt;/h2&gt;

&lt;p&gt;This pattern is especially powerful if you are serious about AI-assisted development.&lt;/p&gt;

&lt;p&gt;LLMs do much better when the input is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;textual&lt;/li&gt;
&lt;li&gt;structured&lt;/li&gt;
&lt;li&gt;scoped&lt;/li&gt;
&lt;li&gt;and semantically meaningful&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A good markdown page description is a far better input than raw design JSON or a folder full of screenshots.&lt;/p&gt;

&lt;p&gt;That means this mirror is useful not just for human search, but for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;implementation assistance&lt;/li&gt;
&lt;li&gt;code review context&lt;/li&gt;
&lt;li&gt;architectural reasoning&lt;/li&gt;
&lt;li&gt;product/design retrieval&lt;/li&gt;
&lt;li&gt;and agent workflows that need to move across systems cleanly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is one of the reasons the approach feels increasingly important. It is not just about convenience for humans. It is about building a better substrate for machine reasoning too.&lt;/p&gt;


&lt;h2&gt;
  
  
  How to use this privately on your own team
&lt;/h2&gt;

&lt;p&gt;You do not need to make any of this public.&lt;/p&gt;

&lt;p&gt;The default setup should be private.&lt;/p&gt;

&lt;p&gt;A simple model is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;keep Linear and Figma as your source systems&lt;/li&gt;
&lt;li&gt;mirror them into a private git repo with &lt;code&gt;aviadr1/issueclaw&lt;/code&gt; and &lt;code&gt;aviadr1/figmaclaw&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;run sync and enrichment in your own environment&lt;/li&gt;
&lt;li&gt;let your team and internal tools search the resulting markdown&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A minimal repo might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;product-memory/
  linear/
  figma/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The practical flow is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;create a private repo&lt;/li&gt;
&lt;li&gt;install &lt;code&gt;aviadr1/issueclaw&lt;/code&gt; and point it at your Linear workspace&lt;/li&gt;
&lt;li&gt;install &lt;code&gt;aviadr1/figmaclaw&lt;/code&gt; and point it at the Figma files you want mirrored&lt;/li&gt;
&lt;li&gt;run the initial syncs&lt;/li&gt;
&lt;li&gt;schedule periodic syncs via cron or CI&lt;/li&gt;
&lt;li&gt;run enrichment privately and commit the results back into the repo&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I have not verified the live README commands for both repositories line by line in this session, so I would use each repository’s current README and CLI help for the exact installation syntax. The important point is architectural: the whole pipeline can remain private end to end.&lt;/p&gt;




&lt;h2&gt;
  
  
  Who should care
&lt;/h2&gt;

&lt;p&gt;This is especially useful for teams that already have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;serious product work in Linear&lt;/li&gt;
&lt;li&gt;serious design work in Figma&lt;/li&gt;
&lt;li&gt;fast-moving engineering&lt;/li&gt;
&lt;li&gt;and growing use of AI in development or internal tooling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words, this is useful anywhere the real answer to a question usually lives across multiple systems rather than inside one.&lt;/p&gt;




&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;The real point of &lt;code&gt;aviadr1/issueclaw&lt;/code&gt; and &lt;code&gt;aviadr1/figmaclaw&lt;/code&gt; is not markdown.&lt;/p&gt;

&lt;p&gt;It is that they turn two important systems of record into a shared, searchable knowledge layer.&lt;/p&gt;

&lt;p&gt;That layer is useful because it helps humans and AI reason across product, design, and engineering as one system rather than three disconnected tools.&lt;/p&gt;

&lt;p&gt;At Gigaverse, that already made search, feature tracing, platform comparison, and design audits much easier.&lt;/p&gt;

&lt;p&gt;The deeper win was drift detection.&lt;/p&gt;

&lt;p&gt;That is what made the whole approach feel important.&lt;/p&gt;

&lt;p&gt;If you are building software with Linear, Figma, git, and AI-assisted workflows, this pattern is worth serious consideration.&lt;/p&gt;

&lt;p&gt;It gives your team something most tool stacks are missing:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;a working memory that spans the actual shape of product development.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>figma</category>
      <category>linear</category>
      <category>mcp</category>
      <category>development</category>
    </item>
    <item>
      <title>Claude Feels Slow. But Is Moving a Team to Open-Weight Models Actually the Fix?</title>
      <dc:creator>Aviad Rozenhek</dc:creator>
      <pubDate>Mon, 30 Mar 2026 22:21:51 +0000</pubDate>
      <link>https://dev.to/aviadr1/claude-feels-slow-but-is-moving-a-team-to-open-weight-models-actually-the-fix-584f</link>
      <guid>https://dev.to/aviadr1/claude-feels-slow-but-is-moving-a-team-to-open-weight-models-actually-the-fix-584f</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Claude has a real speed problem for our team — but mostly in &lt;strong&gt;TTFT&lt;/strong&gt;, not in raw decoding speed.&lt;/p&gt;

&lt;p&gt;I measured our actual usage and found this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TTFT p50:&lt;/strong&gt; 4.2s–6.8s&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TTFT p90:&lt;/strong&gt; 14.5s–28.1s&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude Sonnet decode p50:&lt;/strong&gt; 176 tok/s &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That explains the feeling: Claude often isn’t that slow &lt;strong&gt;once it starts&lt;/strong&gt;, but sometimes it takes so long to begin that the whole thing feels like it’s crawling.&lt;/p&gt;

&lt;p&gt;That naturally raises the next question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Should we move the team to self-hosted open-weight models?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At first glance, that sounds promising. Self-hosted setups can have dramatically better TTFT. In the numbers I looked at, open-weight deployments were often estimated around &lt;strong&gt;150–600ms TTFT&lt;/strong&gt;, versus Claude’s &lt;strong&gt;4–7s median&lt;/strong&gt; in our real usage. &lt;/p&gt;

&lt;p&gt;But once I looked at the actual team setup — &lt;strong&gt;10 engineers sharing one GPU budget&lt;/strong&gt; — the answer stopped looking obvious.&lt;/p&gt;

&lt;p&gt;The best open-weight models need &lt;strong&gt;serious multi-GPU infra&lt;/strong&gt;, and once that infra is shared, the speed case starts looking surprisingly shaky. &lt;/p&gt;

&lt;p&gt;So this post is not “open source bad.”&lt;/p&gt;

&lt;p&gt;It’s a narrower question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If Claude feels slow, is moving a team to open-weight models on shared infra actually the answer?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Right now, I’m not convinced.&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem: Claude feels like it crawls
&lt;/h2&gt;

&lt;p&gt;This started with a very practical complaint:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Claude is slow.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That could mean a lot of things, so I measured it.&lt;/p&gt;

&lt;p&gt;From about &lt;strong&gt;50 session files&lt;/strong&gt; and roughly &lt;strong&gt;3,000 API calls&lt;/strong&gt;, the problem was clear: the main issue was &lt;strong&gt;TTFT&lt;/strong&gt;, especially in the tail. &lt;/p&gt;

&lt;h3&gt;
  
  
  TTFT from our real usage
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Trigger&lt;/th&gt;
&lt;th&gt;p10&lt;/th&gt;
&lt;th&gt;p50&lt;/th&gt;
&lt;th&gt;p90&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;User message&lt;/td&gt;
&lt;td&gt;2.8s&lt;/td&gt;
&lt;td&gt;6.8s&lt;/td&gt;
&lt;td&gt;28.1s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tool result&lt;/td&gt;
&lt;td&gt;2.5s&lt;/td&gt;
&lt;td&gt;4.2s&lt;/td&gt;
&lt;td&gt;14.5s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That &lt;strong&gt;28.1s p90&lt;/strong&gt; is the whole story. &lt;/p&gt;

&lt;p&gt;Claude is not just “a bit laggy” there. It’s slow enough to break flow.&lt;/p&gt;




&lt;h2&gt;
  
  
  The surprising part: decode speed wasn’t the main problem
&lt;/h2&gt;

&lt;p&gt;Here’s the other half of the picture.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generation speed
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;p10&lt;/th&gt;
&lt;th&gt;p50&lt;/th&gt;
&lt;th&gt;p90&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Decode tok/s (excluding TTFT)&lt;/td&gt;
&lt;td&gt;72&lt;/td&gt;
&lt;td&gt;178&lt;/td&gt;
&lt;td&gt;567&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wall tok/s (including TTFT)&lt;/td&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;td&gt;41&lt;/td&gt;
&lt;td&gt;63&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;And per model:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;TTFT p50&lt;/th&gt;
&lt;th&gt;Decode p50&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Haiku 4.5&lt;/td&gt;
&lt;td&gt;1.8s&lt;/td&gt;
&lt;td&gt;287 tok/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sonnet 4.6&lt;/td&gt;
&lt;td&gt;4.2s&lt;/td&gt;
&lt;td&gt;176 tok/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Opus 4.6&lt;/td&gt;
&lt;td&gt;4.7s&lt;/td&gt;
&lt;td&gt;130 tok/s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So the core problem wasn’t really:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Claude can’t stream fast enough.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Claude often takes too long to get started. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That distinction matters, because it makes self-hosting sound much more attractive than it might actually be.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why open weights sound like the obvious answer
&lt;/h2&gt;

&lt;p&gt;If TTFT is the problem, then self-hosting sounds like the clean fix.&lt;/p&gt;

&lt;p&gt;The pitch is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no provider-side queue&lt;/li&gt;
&lt;li&gt;no shared API congestion&lt;/li&gt;
&lt;li&gt;your own inference server&lt;/li&gt;
&lt;li&gt;much lower TTFT&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the numbers I collected from the self-hosting side were definitely seductive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Best-case self-hosted framing
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Claude now&lt;/th&gt;
&lt;th&gt;Best self-hosted&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tool-triggered TTFT p50&lt;/td&gt;
&lt;td&gt;4,200ms&lt;/td&gt;
&lt;td&gt;~160ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User-triggered TTFT p50&lt;/td&gt;
&lt;td&gt;6,800ms&lt;/td&gt;
&lt;td&gt;~160ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bad-day p90&lt;/td&gt;
&lt;td&gt;14,500ms+&lt;/td&gt;
&lt;td&gt;&amp;lt;400ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If TTFT were the only thing that mattered, I think this would already be enough to move seriously toward GPUs. &lt;/p&gt;

&lt;p&gt;But TTFT is not the whole developer experience.&lt;/p&gt;




&lt;h2&gt;
  
  
  The models we’d actually consider
&lt;/h2&gt;

&lt;p&gt;We’re not talking about toy models here. We’re talking about the real open-weight candidates people would actually put on the table.&lt;/p&gt;

&lt;h3&gt;
  
  
  Models considered
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Why consider it?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Qwen3-Coder-Next&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fast MoE coding model, 80B total / 3B active&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MiniMax M2.5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stronger quality candidate, 230B total / 10B active&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DeepSeek V3.2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Very large MoE option&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Qwen3.5-27B&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dense, simpler, slower but cheaper&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;And the inference engines are the standard ones you’d expect:&lt;/p&gt;

&lt;h3&gt;
  
  
  Inference engines
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model family&lt;/th&gt;
&lt;th&gt;Realistic inference engine&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Qwen / DeepSeek&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;vLLM&lt;/strong&gt; or &lt;strong&gt;SGLang&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MiniMax M2.5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;vLLM&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dense smaller models&lt;/td&gt;
&lt;td&gt;usually &lt;strong&gt;vLLM&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That means this isn’t some hypothetical future stack. It’s the standard modern self-hosted inference path. &lt;/p&gt;




&lt;h2&gt;
  
  
  The part that makes this much less exciting: GPU budgets are shared
&lt;/h2&gt;

&lt;p&gt;This is the piece I think gets hand-waved away too often.&lt;/p&gt;

&lt;p&gt;Our current setup is:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Engineers&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude subscription per engineer&lt;/td&gt;
&lt;td&gt;$150/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total Claude cost&lt;/td&gt;
&lt;td&gt;$1,500/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The budget I was willing to entertain for self-hosting was roughly &lt;strong&gt;3× that&lt;/strong&gt;, so about &lt;strong&gt;$4,500/month&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;That sounds like a lot.&lt;/p&gt;

&lt;p&gt;But for top open-weight coding models, it buys you something like this:&lt;/p&gt;

&lt;h3&gt;
  
  
  What the budget can buy
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Config&lt;/th&gt;
&lt;th&gt;Cost/month&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;5× H100 on Vast.ai&lt;/td&gt;
&lt;td&gt;$4,712&lt;/td&gt;
&lt;td&gt;Enough for MiniMax M2.5 / DeepSeek-class INT4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3× H100 on Lambda&lt;/td&gt;
&lt;td&gt;$4,521&lt;/td&gt;
&lt;td&gt;More reliable, lower GPU count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4× H200 on Vast.ai&lt;/td&gt;
&lt;td&gt;$4,153&lt;/td&gt;
&lt;td&gt;Better memory bandwidth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8× A100 on Vast.ai&lt;/td&gt;
&lt;td&gt;$2,580&lt;/td&gt;
&lt;td&gt;Cheapest high-count option&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That’s not “10 engineers each get a fast private model.”&lt;/p&gt;

&lt;p&gt;That’s &lt;strong&gt;one shared cluster&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;And that changes the question completely.&lt;/p&gt;




&lt;h2&gt;
  
  
  The real metric is not TTFT. It’s team step time.
&lt;/h2&gt;

&lt;p&gt;The right equation is not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;lower TTFT = faster experience&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s more like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;team step time = queueing + TTFT + output_tokens / decode_speed&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s the part that made me hesitate.&lt;/p&gt;

&lt;p&gt;Because once you share one cluster across 10 engineers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TTFT might improve&lt;/li&gt;
&lt;li&gt;but per-user decoding might not&lt;/li&gt;
&lt;li&gt;and queueing becomes part of the story&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is a very different situation from “look how fast this benchmark is on one box.”&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I’m not yet sold
&lt;/h2&gt;

&lt;p&gt;The self-hosted numbers I gathered looked like this:&lt;/p&gt;

&lt;h3&gt;
  
  
  Self-hosted decode estimates I considered
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Config&lt;/th&gt;
&lt;th&gt;INT4 decode tok/s&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3-Coder-Next&lt;/td&gt;
&lt;td&gt;2× H100&lt;/td&gt;
&lt;td&gt;~3,400&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MiniMax M2.5&lt;/td&gt;
&lt;td&gt;4× H100&lt;/td&gt;
&lt;td&gt;~2,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MiniMax M2.5&lt;/td&gt;
&lt;td&gt;2× H100&lt;/td&gt;
&lt;td&gt;~1,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek V3.2&lt;/td&gt;
&lt;td&gt;5× H100&lt;/td&gt;
&lt;td&gt;~700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3.5-27B&lt;/td&gt;
&lt;td&gt;2× H100&lt;/td&gt;
&lt;td&gt;~380&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3.5-27B&lt;/td&gt;
&lt;td&gt;1× H100&lt;/td&gt;
&lt;td&gt;~190&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Those numbers are exciting. They make open weights look like a no-brainer. &lt;/p&gt;

&lt;p&gt;But they also raise exactly the question I still don’t think I’ve answered cleanly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Are these the numbers one engineer feels, or the numbers a shared cluster produces in aggregate?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because for a 10-person team, those are not the same thing.&lt;/p&gt;

&lt;p&gt;And once I started looking at the problem through the lens of &lt;strong&gt;shared infra&lt;/strong&gt;, the speed case stopped looking like an obvious slam dunk.&lt;/p&gt;




&lt;h2&gt;
  
  
  So where does that leave me?
&lt;/h2&gt;

&lt;p&gt;I think I’ve convinced myself of a few things:&lt;/p&gt;

&lt;h3&gt;
  
  
  What seems true
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Statement&lt;/th&gt;
&lt;th&gt;My current view&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude has a real speed problem&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The problem is mostly TTFT&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Self-hosting probably improves TTFT a lot&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The best open-weight models are expensive to run well&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shared infra weakens the speed story&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Moving the whole team looks obviously promising&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That’s the interesting part.&lt;/p&gt;

&lt;p&gt;The story I expected was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Claude is slow, open weights are fast, buy GPUs, problem solved.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The story I actually found was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Claude is slow mostly because of TTFT.&lt;br&gt;
Open weights probably help that.&lt;br&gt;
But once the infra is shared across a team, the speed case gets much less clean.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Bottom line
&lt;/h2&gt;

&lt;p&gt;I started with a very simple frustration:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Claude felt slow.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I measured it and found a very specific issue:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TTFT, especially the p90 tail, was bad enough to make the whole experience feel like it was crawling.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;That led to the obvious next idea:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if we just move to open-weight models on our own GPUs?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And right now, my answer is not “definitely no.”&lt;/p&gt;

&lt;p&gt;It’s this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Open-weight models look promising for TTFT.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;They look much less promising as a shared-infra speed fix for a whole team.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s the question I’m left with.&lt;/p&gt;

&lt;p&gt;Not whether open weights are good.&lt;br&gt;
Not whether they’re possible.&lt;br&gt;
But whether they really solve the problem we actually have.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>opensource</category>
      <category>performance</category>
    </item>
    <item>
      <title>I Replaced My LangSmith MCP Server with a 200-Token CLI Skill</title>
      <dc:creator>Aviad Rozenhek</dc:creator>
      <pubDate>Thu, 05 Mar 2026 01:03:14 +0000</pubDate>
      <link>https://dev.to/aviadr1/i-replaced-my-langsmith-mcp-server-with-a-200-token-cli-skill-4p94</link>
      <guid>https://dev.to/aviadr1/i-replaced-my-langsmith-mcp-server-with-a-200-token-cli-skill-4p94</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;How langsmith-cli gives you 100% MCP parity, 96% less context usage, and features the MCP server doesn't have — all in a single pip install."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you're using LangSmith with Claude Code (or any AI coding agent), you're probably running the official MCP server. It works. But every session, it injects &lt;strong&gt;5,000+ tokens&lt;/strong&gt; of tool schemas into your context window — whether you touch LangSmith or not.&lt;/p&gt;

&lt;p&gt;I built &lt;a href="https://github.com/gigaverse-app/langsmith-cli" rel="noopener noreferrer"&gt;langsmith-cli&lt;/a&gt; to fix that. It's a standalone CLI &lt;em&gt;and&lt;/em&gt; a Claude Code plugin that replaces the MCP server with a &lt;strong&gt;&amp;lt;200 token skill definition&lt;/strong&gt;. That's a 95% reduction in context overhead.&lt;/p&gt;

&lt;p&gt;And it does more than the MCP server does.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with MCP Servers
&lt;/h2&gt;

&lt;p&gt;MCP servers are always-on. The moment your agent session starts, every tool definition gets loaded into context. For LangSmith's MCP server, that's 66 parameters across multiple tools — around 5,000 tokens of JSON schema that sits in your context window doing nothing until you actually need to query traces.&lt;/p&gt;

&lt;p&gt;For agents that need to do many things — write code, run tests, debug, &lt;em&gt;and occasionally&lt;/em&gt; check LangSmith — this is wasteful. Context is your agent's working memory. Every token of schema is a token not available for reasoning.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix: On-Demand Skills Instead of Always-On Schemas
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;langsmith-cli&lt;/code&gt; takes a different approach. Instead of an MCP server, it's a CLI tool with a tiny skill file that teaches your agent &lt;em&gt;how&lt;/em&gt; to use it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install the CLI&lt;/span&gt;
uv tool &lt;span class="nb"&gt;install &lt;/span&gt;langsmith-cli

&lt;span class="c"&gt;# Add as Claude Code plugin&lt;/span&gt;
claude plugin marketplace add gigaverse-app/langsmith-cli
claude plugin &lt;span class="nb"&gt;install &lt;/span&gt;langsmith-cli@langsmith-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The skill file is ~200 tokens. It loads on-demand. Your agent learns to run shell commands like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Get the latest failed run with only the fields you need&lt;/span&gt;
langsmith-cli &lt;span class="nt"&gt;--json&lt;/span&gt; runs get-latest &lt;span class="nt"&gt;--project&lt;/span&gt; my-app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--failed&lt;/span&gt; &lt;span class="nt"&gt;--fields&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;,name,error
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No schema bloat. No always-on server. Just a CLI your agent calls when it needs observability data.&lt;/p&gt;

&lt;h2&gt;
  
  
  96% Token Reduction with &lt;code&gt;--fields&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This is the feature that matters most for agents. A typical LangSmith run object is &lt;strong&gt;20KB&lt;/strong&gt; — easily 1,000+ tokens. With &lt;code&gt;--fields&lt;/code&gt;, you get only what you asked for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Full run object: ~1000 tokens&lt;/span&gt;
langsmith-cli &lt;span class="nt"&gt;--json&lt;/span&gt; runs get abc-123

&lt;span class="c"&gt;# Just what you need: ~40 tokens&lt;/span&gt;
langsmith-cli &lt;span class="nt"&gt;--json&lt;/span&gt; runs get abc-123 &lt;span class="nt"&gt;--fields&lt;/span&gt; name,status,error
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--fields&lt;/code&gt; works on every list and get command: runs, projects, datasets, examples, prompts. Your agent stays lean.&lt;/p&gt;

&lt;h2&gt;
  
  
  Built for Two Audiences
&lt;/h2&gt;

&lt;p&gt;Most developer tools pick one audience. &lt;code&gt;langsmith-cli&lt;/code&gt; serves both:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For humans&lt;/strong&gt; — rich terminal tables with color-coded statuses, smart column truncation, syntax highlighting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;langsmith-cli runs list &lt;span class="nt"&gt;--project&lt;/span&gt; my-app &lt;span class="nt"&gt;--status&lt;/span&gt; error &lt;span class="nt"&gt;--last&lt;/span&gt; 24h
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━┓
┃ Name         ┃ Status     ┃ Tokens ┃ Latency  ┃ Error       ┃
┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━┩
│ extractor    │ error      │ 2,340  │ 3.2s     │ Rate limit  │
│ classifier   │ error      │ 1,102  │ 12.4s    │ Timeout     │
└──────────────┴────────────┴────────┴──────────┴─────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;For agents&lt;/strong&gt; — add &lt;code&gt;--json&lt;/code&gt; as the first flag and everything switches: strict JSON to stdout, diagnostics to stderr, zero formatting noise:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;langsmith-cli &lt;span class="nt"&gt;--json&lt;/span&gt; runs list &lt;span class="nt"&gt;--project&lt;/span&gt; my-app &lt;span class="nt"&gt;--status&lt;/span&gt; error &lt;span class="nt"&gt;--limit&lt;/span&gt; 5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One flag. Two completely different UX modes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Features the MCP Server Doesn't Have
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;langsmith-cli&lt;/code&gt; has 100% parity with the official MCP server (all 66 parameters mapped). But it also has features the MCP server can't offer:&lt;/p&gt;

&lt;h3&gt;
  
  
  Live Monitoring with &lt;code&gt;runs watch&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;A real-time streaming dashboard in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;langsmith-cli runs watch &lt;span class="nt"&gt;--project&lt;/span&gt; my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  One-Command Debugging with &lt;code&gt;runs get-latest&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;No more &lt;code&gt;list | jq | get&lt;/code&gt; pipelines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Before: three commands piped together&lt;/span&gt;
langsmith-cli &lt;span class="nt"&gt;--json&lt;/span&gt; runs list &lt;span class="nt"&gt;--project&lt;/span&gt; X &lt;span class="nt"&gt;--limit&lt;/span&gt; 1 &lt;span class="se"&gt;\&lt;/span&gt;
  | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.[0].id'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | xargs langsmith-cli &lt;span class="nt"&gt;--json&lt;/span&gt; runs get

&lt;span class="c"&gt;# After: one command&lt;/span&gt;
langsmith-cli &lt;span class="nt"&gt;--json&lt;/span&gt; runs get-latest &lt;span class="nt"&gt;--project&lt;/span&gt; X &lt;span class="nt"&gt;--fields&lt;/span&gt; inputs,outputs,error
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Stratified Sampling with &lt;code&gt;runs sample&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Build statistically sound eval datasets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;langsmith-cli runs sample &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stratify-by&lt;/span&gt; tag:length,tag:content_type &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dimension-values&lt;/span&gt; &lt;span class="s2"&gt;"short|long,news|gaming"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--samples-per-combination&lt;/span&gt; 5 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; eval_samples.jsonl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Aggregate Analytics with &lt;code&gt;runs analyze&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Group-by metrics without leaving the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;langsmith-cli &lt;span class="nt"&gt;--json&lt;/span&gt; runs analyze &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--group-by&lt;/span&gt; tag:model &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--metrics&lt;/span&gt; count,error_rate,p50_latency,avg_cost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Schema Discovery with &lt;code&gt;runs fields&lt;/code&gt; / &lt;code&gt;runs describe&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Don't know what fields your runs have? Discover them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;langsmith-cli &lt;span class="nt"&gt;--json&lt;/span&gt; runs fields &lt;span class="nt"&gt;--include&lt;/span&gt; inputs,outputs
&lt;span class="c"&gt;# Returns field paths, types, presence rates, even language distribution&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tag &amp;amp; Metadata Discovery
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;langsmith-cli runs tags &lt;span class="nt"&gt;--project&lt;/span&gt; my-app
langsmith-cli runs metadata-keys &lt;span class="nt"&gt;--project&lt;/span&gt; my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Bulk Export with Pattern Filenames
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;langsmith-cli runs &lt;span class="nb"&gt;export&lt;/span&gt; ./traces &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--project&lt;/span&gt; my-app &lt;span class="nt"&gt;--roots&lt;/span&gt; &lt;span class="nt"&gt;--limit&lt;/span&gt; 1000 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--filename-pattern&lt;/span&gt; &lt;span class="s2"&gt;"{name}-{run_id}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Production Run to Eval Example in One Command
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;langsmith-cli &lt;span class="nt"&gt;--json&lt;/span&gt; examples from-run &amp;lt;run-id&amp;gt; &lt;span class="nt"&gt;--dataset&lt;/span&gt; my-eval-set
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Smart Filtering That Translates to FQL
&lt;/h2&gt;

&lt;p&gt;Nobody wants to write raw Filter Query Language. The CLI translates human-friendly flags automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# These flags...&lt;/span&gt;
langsmith-cli runs list &lt;span class="nt"&gt;--tag&lt;/span&gt; summarizer &lt;span class="nt"&gt;--failed&lt;/span&gt; &lt;span class="nt"&gt;--last&lt;/span&gt; 24h &lt;span class="nt"&gt;--slow&lt;/span&gt;

&lt;span class="c"&gt;# ...become this FQL:&lt;/span&gt;
&lt;span class="c"&gt;# and(has(tags, "summarizer"), eq(error, true),&lt;/span&gt;
&lt;span class="c"&gt;#     gt(start_time, "2026-03-03T..."), gt(latency, "5s"))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Time presets like &lt;code&gt;--recent&lt;/code&gt; (last hour), &lt;code&gt;--today&lt;/code&gt;, &lt;code&gt;--last 7d&lt;/code&gt;, and &lt;code&gt;--since 2026-01-01&lt;/code&gt; all work. Content search with &lt;code&gt;--grep&lt;/code&gt; supports regex and field-specific matching. Everything composes.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's New in v0.4.0
&lt;/h2&gt;

&lt;p&gt;The v0.4.0 release focused on type safety and code quality:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero pyright errors&lt;/strong&gt; — every function has proper type annotations. &lt;code&gt;client: langsmith.Client&lt;/code&gt;, not &lt;code&gt;client: Any&lt;/code&gt;. Return types are real SDK Pydantic models, not &lt;code&gt;object&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;datasets delete&lt;/code&gt;&lt;/strong&gt; command with confirmation prompts and JSON mode support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved error handling&lt;/strong&gt; across prompts and runs commands using specific SDK exception types (&lt;code&gt;LangSmithNotFoundError&lt;/code&gt;, &lt;code&gt;LangSmithConflictError&lt;/code&gt;) instead of broad &lt;code&gt;except Exception&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;702 unit tests&lt;/strong&gt; passing with real Pydantic model instances (no MagicMock for test data)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install&lt;/span&gt;
uv tool &lt;span class="nb"&gt;install &lt;/span&gt;langsmith-cli
&lt;span class="c"&gt;# or: pip install langsmith-cli&lt;/span&gt;

&lt;span class="c"&gt;# Authenticate&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;LANGSMITH_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"lsv2_..."&lt;/span&gt;
&lt;span class="c"&gt;# or: langsmith-cli auth login&lt;/span&gt;

&lt;span class="c"&gt;# Start exploring&lt;/span&gt;
langsmith-cli runs list &lt;span class="nt"&gt;--project&lt;/span&gt; my-app &lt;span class="nt"&gt;--last&lt;/span&gt; 24h
langsmith-cli &lt;span class="nt"&gt;--json&lt;/span&gt; runs get-latest &lt;span class="nt"&gt;--failed&lt;/span&gt; &lt;span class="nt"&gt;--fields&lt;/span&gt; name,error
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're using Claude Code, add the plugin for the best agent experience:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude plugin marketplace add gigaverse-app/langsmith-cli
claude plugin &lt;span class="nb"&gt;install &lt;/span&gt;langsmith-cli@langsmith-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;The code is MIT licensed and on GitHub: &lt;a href="https://github.com/gigaverse-app/langsmith-cli" rel="noopener noreferrer"&gt;gigaverse-app/langsmith-cli&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're building with LangSmith and tired of context-heavy MCP servers, give it a try. Happy to hear feedback in the issues.&lt;/p&gt;

</description>
      <category>langchain</category>
      <category>mcp</category>
      <category>claude</category>
      <category>ai</category>
    </item>
    <item>
      <title>Using AI to Audit Engineering Team Output: An Experiment with Claude</title>
      <dc:creator>Aviad Rozenhek</dc:creator>
      <pubDate>Mon, 19 Jan 2026 22:47:46 +0000</pubDate>
      <link>https://dev.to/aviadr1/using-ai-to-audit-engineering-team-output-an-experiment-with-claude-51h7</link>
      <guid>https://dev.to/aviadr1/using-ai-to-audit-engineering-team-output-an-experiment-with-claude-51h7</guid>
      <description>&lt;p&gt;In engineering management, few challenges are as persistent and contentious as objectively measuring team performance. How do we assess velocity, output quality, and even seniority in a way that is fair, insightful, and resistant to gaming? Traditional metrics—such as story points, commit counts, or lines of code—often fall short. They can incentivize quantity over quality, reward activity rather than impact, and fail to capture the nuanced reality of production software development.&lt;/p&gt;

&lt;p&gt;Recently, I conducted an experiment to explore a novel approach: using a large language model (Claude) as an impartial "auditor" to evaluate a codebase over several months. The goal was to answer fundamental questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What was actually delivered?&lt;/li&gt;
&lt;li&gt;How complex was the work in reality?&lt;/li&gt;
&lt;li&gt;How long did it take compared to expectations for a senior engineer?&lt;/li&gt;
&lt;li&gt;How stable was the resulting output?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The results were thought-provoking—and somewhat unflattering—prompting deeper reflection on whether AI can (or should) play a role in performance assessment.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Idea: An AI-Powered Engineering Audit
&lt;/h2&gt;

&lt;p&gt;The experiment centered on a detailed system prompt designed to guide Claude through a structured audit process. Rather than relying on superficial signals like commit volume, the prompt instructs the model to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identify distinct deliverables by analyzing code changes and timelines.&lt;/li&gt;
&lt;li&gt;Evaluate true complexity by examining architecture, dependencies, novel logic, and integration challenges.&lt;/li&gt;
&lt;li&gt;Compare actual development time against benchmarks for senior engineers.&lt;/li&gt;
&lt;li&gt;Assess stability through regression patterns, hotfixes, and rework.&lt;/li&gt;
&lt;li&gt;Quantify overall efficiency and highlight red flags.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Importantly, the prompt emphasizes reading the actual code, not just commit messages, to avoid bias from optimistic descriptions.&lt;/p&gt;

&lt;p&gt;Here is the full prompt I used: &lt;a href="https://gist.github.com/aviadr1/57bb7a7d80d415571174e1fed7acacba" rel="noopener noreferrer"&gt;GitHub Gist&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The prompt is divided into six phases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Deliverables Inventory&lt;/strong&gt; – Catalog what was built.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;True Complexity Assessment&lt;/strong&gt; – Classify each deliverable (TRIVIAL to HIGHLY COMPLEX) with evidence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time Efficiency Analysis&lt;/strong&gt; – Expected vs. actual timelines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quality &amp;amp; Regression Assessment&lt;/strong&gt; – Stability and post-ship issues.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Breadth vs. Depth Analysis&lt;/strong&gt; – Distribution of new features, improvements, maintenance, and rework.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Honest Assessment&lt;/strong&gt; – Overall efficiency, rework rate, and verdict.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Running the Experiment
&lt;/h2&gt;

&lt;p&gt;I applied this prompt to a real production codebase spanning approximately four months of work by a small team. The audit produced a comprehensive report with tables, evidence excerpts, and quantitative summaries.&lt;/p&gt;

&lt;p&gt;Key high-level findings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Most deliverables were rated as MODERATE or lower complexity, even those that felt substantial during development.&lt;/li&gt;
&lt;li&gt;Several features showed timelines 2–10x longer than the prompt's senior-engineer benchmarks.&lt;/li&gt;
&lt;li&gt;Rework (fixes, hotfixes, reverts) consumed a significant portion of total effort.&lt;/li&gt;
&lt;li&gt;The overall efficiency calculation suggested room for substantial improvement compared to a "well-functioning" baseline.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notably, the assessment appeared conservative—often downgrading apparent complexity by emphasizing reusable patterns, existing infrastructure, and boilerplate. For instance, systems involving real-time processing and multiple external integrations were classified as COMPLEX but with expected senior timelines of 1–2 weeks, despite months of iteration in practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reflections: Does AI Underestimate—or Cut Through Bias?
&lt;/h2&gt;

&lt;p&gt;The results raised immediate questions about the reliability of AI-driven audits.&lt;/p&gt;

&lt;p&gt;On one hand, LLMs may systematically underestimate complexity in real-world systems. Production engineering involves hidden challenges that models struggle to appreciate fully:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Iterative policy refinement and edge-case handling.&lt;/li&gt;
&lt;li&gt;Integration debt with legacy systems or third-party APIs.&lt;/li&gt;
&lt;li&gt;Coordination overhead in distributed teams.&lt;/li&gt;
&lt;li&gt;The intangible cost of context-switching and debugging in complex environments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the other hand, the audit's critical tone might reflect a valuable strength: cutting through human bias. Engineers (myself included) naturally overestimate the novelty and difficulty of our work. An impartial observer—especially one trained on vast codebases—can identify patterns, leverage points, and simplifications that feel groundbreaking internally but are standard externally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Broader Implications and Open Questions
&lt;/h2&gt;

&lt;p&gt;This experiment touches on several important topics in engineering leadership:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Measurement Practices&lt;/strong&gt;: How do most organizations currently evaluate velocity and quality? Many rely on proxies (velocity in story points, DORA metrics, cycle time), but few attempt deep code-level audits. Could structured AI analysis complement these?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seniority Calibration&lt;/strong&gt;: The prompt uses senior-engineer time estimates as anchors. Is this fair? Seniority varies widely—what one engineer completes in days, another might require weeks due to domain knowledge gaps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ethical Considerations&lt;/strong&gt;: Automating performance assessment raises concerns around fairness, transparency, and morale. If an AI labels output as "inefficient," how do we ensure the evaluation is accurate and contextualized? Should such tools ever influence compensation or promotion?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Limitations and Improvements&lt;/strong&gt;: Current models excel at pattern matching but may miss subtle production realities. Future iterations could incorporate more signals (e.g., test coverage depth, production incident data, user impact metrics).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Have you experimented with LLMs for code review, retrospective analysis, or performance measurement? What pros and cons have you encountered?&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Using Claude to audit engineering output proved to be a revealing—if humbling—exercise. While the assessment seemed to underestimate certain complexities, it highlighted areas for process improvement and forced a more honest reckoning with deliverables.&lt;/p&gt;

&lt;p&gt;I share this not to prescribe AI audits as the solution, but to spark discussion. In an era where AI is transforming how we write code, perhaps it can also help us better understand the code we write—and the teams that write it.&lt;/p&gt;

&lt;p&gt;What are your thoughts? How do you measure engineering effectiveness today, and where do you see AI fitting in (or not)?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks for reading. Feedback welcome in the comments.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  engineering #productivity #management #ai
&lt;/h1&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>codereview</category>
      <category>claude</category>
    </item>
    <item>
      <title>LangSmith CLI: Not Just Efficient — Actually Better Than MCP</title>
      <dc:creator>Aviad Rozenhek</dc:creator>
      <pubDate>Thu, 15 Jan 2026 00:13:07 +0000</pubDate>
      <link>https://dev.to/aviadr1/langsmith-cli-not-just-efficient-actually-better-than-mcp-4o71</link>
      <guid>https://dev.to/aviadr1/langsmith-cli-not-just-efficient-actually-better-than-mcp-4o71</guid>
      <description>&lt;p&gt;LangSmith gives us incredible visibility into LLM applications: full traces, datasets, prompt versioning, evaluations — everything we need to build reliable AI systems.&lt;/p&gt;

&lt;p&gt;But actually &lt;strong&gt;using&lt;/strong&gt; LangSmith day-to-day has always felt clunky:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Constantly refreshing the web UI
&lt;/li&gt;
&lt;li&gt;Writing custom API scripts
&lt;/li&gt;
&lt;li&gt;Or using MCP servers that quietly eat &lt;strong&gt;16,100 tokens&lt;/strong&gt; of context — permanently.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I built &lt;strong&gt;&lt;a href="https://github.com/gigaverse-app/langsmith-cli" rel="noopener noreferrer"&gt;langsmith-cli&lt;/a&gt;&lt;/strong&gt; to solve this properly.&lt;/p&gt;

&lt;p&gt;It's not only dramatically more efficient (177× less context overhead).&lt;br&gt;&lt;br&gt;
It is &lt;strong&gt;fundamentally better&lt;/strong&gt; for real debugging, analysis, and production monitoring workflows.&lt;/p&gt;

&lt;p&gt;Here’s why — with real measurements and concrete examples.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Context Is Precious — 177× Less Waste
&lt;/h3&gt;

&lt;p&gt;Straight from &lt;code&gt;/context&lt;/code&gt; in Claude Code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MCP LangSmith tools&lt;/strong&gt; → &lt;strong&gt;16,100 tokens&lt;/strong&gt; always loaded (~8% of 200k context)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;langsmith-cli as Skill&lt;/strong&gt; → &lt;strong&gt;91 tokens&lt;/strong&gt; only when activated, &lt;strong&gt;0&lt;/strong&gt; when idle&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;→ &lt;strong&gt;177× difference&lt;/strong&gt; in context overhead.&lt;/p&gt;

&lt;p&gt;This is not theoretical.&lt;br&gt;&lt;br&gt;
Every extra 10–20k tokens of tool definitions means less room for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;conversation history
&lt;/li&gt;
&lt;li&gt;source code
&lt;/li&gt;
&lt;li&gt;documentation
&lt;/li&gt;
&lt;li&gt;actual reasoning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add 2–3 more MCP servers → 20–30% of your context disappears before you start working.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Real-time Production Monitoring — &lt;code&gt;runs watch&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The single feature that made me never want to go back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;langsmith-cli runs watch &lt;span class="nt"&gt;--project&lt;/span&gt; production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get an auto-refreshing, color-coded terminal dashboard:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Live status (🟢 / 🔴)
&lt;/li&gt;
&lt;li&gt;Latency, token usage, relative time
&lt;/li&gt;
&lt;li&gt;Instant visibility into error rate and average performance
&lt;/li&gt;
&lt;li&gt;Filter on the fly: &lt;code&gt;--failed&lt;/code&gt;, &lt;code&gt;--slow&lt;/code&gt;, &lt;code&gt;--model gpt-4&lt;/code&gt;, &lt;code&gt;--tag customer-facing&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No browser refresh. No delay.&lt;br&gt;&lt;br&gt;
You literally see production break (or recover) in real time.&lt;/p&gt;

&lt;p&gt;MCP + web UI simply cannot match this immediacy.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Powerful, Developer-first Filtering
&lt;/h3&gt;

&lt;p&gt;Finding the right runs should not require writing custom code every time.&lt;/p&gt;

&lt;p&gt;Examples that MCP/web simply cannot do easily:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Regex on run names&lt;/span&gt;
langsmith-cli runs list &lt;span class="nt"&gt;--name-regex&lt;/span&gt; &lt;span class="s2"&gt;"^api-v[0-9]+&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="s2"&gt;[0-9]+"&lt;/span&gt;

&lt;span class="c"&gt;# Wildcard + smart presets&lt;/span&gt;
langsmith-cli runs list &lt;span class="nt"&gt;--name-pattern&lt;/span&gt; &lt;span class="s2"&gt;"*auth*"&lt;/span&gt; &lt;span class="nt"&gt;--failed&lt;/span&gt; &lt;span class="nt"&gt;--today&lt;/span&gt;

&lt;span class="c"&gt;# Time ranges (very natural syntax)&lt;/span&gt;
langsmith-cli runs list &lt;span class="nt"&gt;--since&lt;/span&gt; &lt;span class="s2"&gt;"1 hour ago"&lt;/span&gt;
langsmith-cli runs list &lt;span class="nt"&gt;--last&lt;/span&gt; 24h
langsmith-cli runs list &lt;span class="nt"&gt;--since&lt;/span&gt; &lt;span class="s2"&gt;"2025-12-01"&lt;/span&gt; &lt;span class="nt"&gt;--until&lt;/span&gt; &lt;span class="s2"&gt;"2025-12-02"&lt;/span&gt;

&lt;span class="c"&gt;# Expensive / slow runs&lt;/span&gt;
langsmith-cli runs list &lt;span class="nt"&gt;--min-tokens&lt;/span&gt; 8000 &lt;span class="nt"&gt;--slow&lt;/span&gt; &lt;span class="nt"&gt;--today&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These filters are fast, composable, and — most importantly — &lt;strong&gt;stay in your terminal flow&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Field Pruning: 95% Token Reduction on Responses
&lt;/h3&gt;

&lt;p&gt;A complex multi-agent trace can easily be &lt;strong&gt;~4,200 tokens&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Fetching 10 failed runs full → &lt;strong&gt;~42k tokens&lt;/strong&gt; just for data.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;--fields&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;langsmith-cli &lt;span class="nt"&gt;--json&lt;/span&gt; runs list &lt;span class="nt"&gt;--failed&lt;/span&gt; &lt;span class="nt"&gt;--limit&lt;/span&gt; 10 &lt;span class="nt"&gt;--fields&lt;/span&gt; name,error,latency,status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;→ ~214 tokens per run instead of 4,210&lt;br&gt;&lt;br&gt;
→ &lt;strong&gt;~95% reduction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You only pay for the information you actually need.&lt;/p&gt;

&lt;p&gt;MCP always returns the complete object. Every time.&lt;/p&gt;
&lt;h3&gt;
  
  
  5. Dual Excellent UX — Humans + Agents
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Human mode (beautiful rich table)&lt;/span&gt;
langsmith-cli runs list &lt;span class="nt"&gt;--project&lt;/span&gt; production &lt;span class="nt"&gt;--limit&lt;/span&gt; 8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;→ Color-coded, aggregates, relative times, clean formatting&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Agent / script mode (strict, minimal JSON)&lt;/span&gt;
langsmith-cli &lt;span class="nt"&gt;--json&lt;/span&gt; runs list &lt;span class="nt"&gt;--failed&lt;/span&gt; &lt;span class="nt"&gt;--fields&lt;/span&gt; name,error,latency &lt;span class="nt"&gt;--limit&lt;/span&gt; 20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One tool. Two perfect interfaces.&lt;br&gt;&lt;br&gt;
No compromises.&lt;/p&gt;
&lt;h3&gt;
  
  
  6. Export Formats That Actually Help Teams
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--format csv&lt;/code&gt; → Excel, pivot tables, stakeholder reports
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--format yaml&lt;/code&gt; → configs, reproducible environments
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--json&lt;/code&gt; → agents, automation, monitoring pipelines
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;langsmith-cli runs list &lt;span class="nt"&gt;--failed&lt;/span&gt; &lt;span class="nt"&gt;--today&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; csv &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; failed-runs-today.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Open → analyze → share. Done.&lt;/p&gt;
&lt;h3&gt;
  
  
  7. Unix Philosophy — Full Composability
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# How many timeout errors today?&lt;/span&gt;
langsmith-cli &lt;span class="nt"&gt;--json&lt;/span&gt; runs list &lt;span class="nt"&gt;--failed&lt;/span&gt; &lt;span class="nt"&gt;--today&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | jq &lt;span class="s1"&gt;'.[] | select(.error | contains("timeout"))'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;

&lt;span class="c"&gt;# Top 5 most common errors&lt;/span&gt;
langsmith-cli &lt;span class="nt"&gt;--json&lt;/span&gt; runs list &lt;span class="nt"&gt;--failed&lt;/span&gt; &lt;span class="nt"&gt;--limit&lt;/span&gt; 200 &lt;span class="se"&gt;\&lt;/span&gt;
  | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.[] | .error'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This is where CLI completely outclasses MCP + web.&lt;/p&gt;

&lt;p&gt;You already know these tools.&lt;br&gt;&lt;br&gt;
You already have the scripts.&lt;br&gt;&lt;br&gt;
Now they work with LangSmith too.&lt;/p&gt;
&lt;h3&gt;
  
  
  Quick Start (Really 30–60 Seconds)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install (isolated, safe, works everywhere)&lt;/span&gt;
curl &lt;span class="nt"&gt;-sSL&lt;/span&gt; https://raw.githubusercontent.com/langchain-ai/langsmith-cli/main/scripts/install.sh | sh

&lt;span class="c"&gt;# Or faster with uv:&lt;/span&gt;
uv tool &lt;span class="nb"&gt;install &lt;/span&gt;langsmith-cli

&lt;span class="c"&gt;# Add as skill in Claude Code&lt;/span&gt;
/plugin marketplace add gigaverse-app/langsmith-cli

&lt;span class="c"&gt;# First login&lt;/span&gt;
langsmith-cli auth login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then try:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;langsmith-cli runs watch &lt;span class="nt"&gt;--project&lt;/span&gt; production
&lt;span class="c"&gt;# or&lt;/span&gt;
langsmith-cli runs list &lt;span class="nt"&gt;--failed&lt;/span&gt; &lt;span class="nt"&gt;--today&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Final Verdict
&lt;/h3&gt;

&lt;p&gt;langsmith-cli is &lt;strong&gt;not&lt;/strong&gt; just "lighter" than MCP.&lt;/p&gt;

&lt;p&gt;It is &lt;strong&gt;objectively better&lt;/strong&gt; at the things that matter most when debugging and operating LLM systems in production:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real-time visibility
&lt;/li&gt;
&lt;li&gt;Powerful filtering without code
&lt;/li&gt;
&lt;li&gt;Massive context &amp;amp; token savings
&lt;/li&gt;
&lt;li&gt;Beautiful human UX + perfect machine UX
&lt;/li&gt;
&lt;li&gt;Export formats teams actually use
&lt;/li&gt;
&lt;li&gt;Full Unix-style composability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;177× less context overhead is nice.&lt;br&gt;&lt;br&gt;
But being able to &lt;strong&gt;watch production live&lt;/strong&gt;, &lt;strong&gt;find problems in seconds&lt;/strong&gt;, and &lt;strong&gt;export meaningful data instantly&lt;/strong&gt; — that's why I built it, and why I never want to go back.&lt;/p&gt;

&lt;p&gt;Give it 60 seconds.&lt;br&gt;&lt;br&gt;
Run &lt;code&gt;/context&lt;/code&gt; before and after.&lt;br&gt;&lt;br&gt;
The numbers don't lie.&lt;/p&gt;

&lt;p&gt;Repo → &lt;a href="https://github.com/gigaverse-app/langsmith-cli" rel="noopener noreferrer"&gt;https://github.com/gigaverse-app/langsmith-cli&lt;/a&gt; (MIT)&lt;/p&gt;

&lt;p&gt;Happy (much faster) debugging!&lt;br&gt;&lt;br&gt;
Aviad&lt;/p&gt;

&lt;h1&gt;
  
  
  LangSmith #LLM #Observability #AIDevTools #ClaudeCode
&lt;/h1&gt;

</description>
      <category>claude</category>
      <category>mcp</category>
      <category>langsmith</category>
      <category>ai</category>
    </item>
    <item>
      <title>LangSmith CLI Why Lightweight Skills Crush Heavy MCP Servers (Context Is All You Need)</title>
      <dc:creator>Aviad Rozenhek</dc:creator>
      <pubDate>Wed, 14 Jan 2026 23:47:47 +0000</pubDate>
      <link>https://dev.to/aviadr1/langsmith-cli-why-lightweight-skills-crush-heavy-mcp-servers-context-is-all-you-need-1aeb</link>
      <guid>https://dev.to/aviadr1/langsmith-cli-why-lightweight-skills-crush-heavy-mcp-servers-context-is-all-you-need-1aeb</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Measured reality in Claude Code sessions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MCP LangSmith tools&lt;/strong&gt; → &lt;strong&gt;16,100 tokens&lt;/strong&gt; always loaded (≈8% of 200k context)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;langsmith-cli as Skill&lt;/strong&gt; → &lt;strong&gt;91 tokens&lt;/strong&gt; when activated, &lt;strong&gt;0 tokens&lt;/strong&gt; when idle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Difference&lt;/strong&gt;: &lt;strong&gt;×177&lt;/strong&gt; less context overhead&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Installation: 30 seconds vs typical 15+ minutes&lt;br&gt;&lt;br&gt;
Field pruning: up to &lt;strong&gt;95%&lt;/strong&gt; token reduction on responses&lt;br&gt;&lt;br&gt;
Startup: 43–87 ms cold/warm&lt;/p&gt;

&lt;p&gt;Skills win for the majority of stateless AI tooling operations.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Context Tax – Measured Reality
&lt;/h2&gt;

&lt;p&gt;Right now, in my Claude Code session, the LangSmith MCP tools are consuming:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MCP tools · /mcp
├ mcp__langsmith__run_experiment     3.2k tokens
├ mcp__langsmith__push_prompt         2.8k tokens
├ mcp__langsmith__fetch_runs          2.2k tokens
...
└ mcp__langsmith__get_prompt_by_name    146 tokens
TOTAL: 16,100 tokens   (≈8% of 200k context window)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These definitions are &lt;strong&gt;permanently loaded&lt;/strong&gt; — even if I never touch LangSmith during the entire conversation.&lt;/p&gt;

&lt;p&gt;The same functionality implemented as a &lt;strong&gt;Skill&lt;/strong&gt; (subprocess-based CLI):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Skills · /skills
├ commit-commands:clean_gone          46 tokens
├ agent-sdk-dev:new-sdk-app           19 tokens
...
TOTAL when activated: 91 tokens   (0.045% of context)
Inactive: 0 tokens
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;177× difference.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Not an estimate — actual numbers from &lt;code&gt;/context&lt;/code&gt; command.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why Does This Matter? Context Economics
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Price (Claude Opus 4.5)&lt;/th&gt;
&lt;th&gt;Impact of losing 16k tokens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Input tokens&lt;/td&gt;
&lt;td&gt;$15 / million&lt;/td&gt;
&lt;td&gt;~$0.24 per query just overhead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;200k context window&lt;/td&gt;
&lt;td&gt;shared resource&lt;/td&gt;
&lt;td&gt;8% permanently occupied&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3 typical MCP servers&lt;/td&gt;
&lt;td&gt;~36–48k tokens&lt;/td&gt;
&lt;td&gt;18–24% of context gone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Freed context (35k+ tokens)&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;≈30 pages docs / 500+ LOC / long conversation history&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The more MCP servers you add, the faster your effective context window shrinks — &lt;strong&gt;before any real work begins&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  Architectural Comparison: Persistent vs On-demand
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;MCP Servers&lt;/th&gt;
&lt;th&gt;Skills (subprocess CLI)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Loading moment&lt;/td&gt;
&lt;td&gt;At application start&lt;/td&gt;
&lt;td&gt;Only when explicitly activated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context occupation&lt;/td&gt;
&lt;td&gt;Permanent&lt;/td&gt;
&lt;td&gt;Temporary + very small&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Startup time (measured)&lt;/td&gt;
&lt;td&gt;Usually 1–3+ seconds&lt;/td&gt;
&lt;td&gt;43–87 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resource consumption&lt;/td&gt;
&lt;td&gt;Persistent process&lt;/td&gt;
&lt;td&gt;Starts → works → exits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lifecycle management&lt;/td&gt;
&lt;td&gt;Required (start/stop/restart/debug)&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Installation complexity&lt;/td&gt;
&lt;td&gt;Medium–high (config, env vars, debugging)&lt;/td&gt;
&lt;td&gt;Very low (curl / uv tool)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Composability&lt;/td&gt;
&lt;td&gt;Limited (JSON only)&lt;/td&gt;
&lt;td&gt;Excellent (Unix pipes friendly)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output control&lt;/td&gt;
&lt;td&gt;Full objects always&lt;/td&gt;
&lt;td&gt;Field pruning + multiple formats&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Most AI tooling operations are stateless queries&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
→ list, get, create, update, export&lt;br&gt;&lt;br&gt;
→ They don't need persistent connections, pools, watchers, or bidirectional streaming.&lt;/p&gt;


&lt;h2&gt;
  
  
  Added Value of langsmith-cli (Beyond Context Efficiency)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Aggressive field pruning&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Full &lt;code&gt;Run&lt;/code&gt; object ≈ 4.2k tokens&lt;br&gt;&lt;br&gt;
Pruned (name, error, latency, etc.) ≈ 200–300 tokens&lt;br&gt;&lt;br&gt;
→ &lt;strong&gt;~90–95%&lt;/strong&gt; reduction&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Multiple output formats&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;--json&lt;/code&gt;, &lt;code&gt;--format csv&lt;/code&gt;, &lt;code&gt;--format yaml&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Human-friendly + agent-friendly dual UX&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Rich tables when interactive, clean JSON when piped&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Advanced filtering presets&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;--failed&lt;/code&gt;, &lt;code&gt;--slow&lt;/code&gt;, &lt;code&gt;--today&lt;/code&gt;, regex/wildcard on names, etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Live watching TUI&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;langsmith-cli runs watch --project production&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  Real Numbers from Real Session (Debug Example)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Task:&lt;/strong&gt; Find failed runs from last hour + show error messages&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Skills version&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Context cost: 91 tokens (skill definition)&lt;br&gt;&lt;br&gt;
Response: ≈500 tokens (pruned fields, 5 runs)&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Total ≈ 591 tokens&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MCP version&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Context cost: 16,100 tokens (always)&lt;br&gt;&lt;br&gt;
Response: ≈2,000 tokens (full objects)&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Total ≈ 18,100 tokens&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ &lt;strong&gt;×30.6 more context&lt;/strong&gt; for the same information&lt;/p&gt;


&lt;h2&gt;
  
  
  Installation – 30 Seconds vs 15+ Minutes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Recommended (Skills):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# One-liner (creates isolated venv, adds to PATH)&lt;/span&gt;
curl &lt;span class="nt"&gt;-sSL&lt;/span&gt; https://raw.githubusercontent.com/gigaverse-app/langsmith-cli/main/scripts/install.sh | sh

&lt;span class="c"&gt;# Then in Claude Code&lt;/span&gt;
/plugin marketplace add gigaverse-app/langsmith-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Typical MCP path:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pip install langsmith-mcp-server&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;manual editing of &lt;code&gt;config.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;setting env variables&lt;/li&gt;
&lt;li&gt;debugging python path / permissions / port conflicts&lt;/li&gt;
&lt;li&gt;restart client&lt;/li&gt;
&lt;li&gt;check logs...
→ frequently 15–40 minutes&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  When MCP Still Makes Sense (Fair Comparison)
&lt;/h2&gt;

&lt;p&gt;Use &lt;strong&gt;MCP servers&lt;/strong&gt; when you really need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;persistent expensive state (connection pools, large in-memory caches)&lt;/li&gt;
&lt;li&gt;background processing (file watchers, long-polling)&lt;/li&gt;
&lt;li&gt;bidirectional streaming&lt;/li&gt;
&lt;li&gt;very heavy initialization (5GB+ ML models)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For 90–95%&lt;/strong&gt; of current LangSmith / tracing / evaluation use-cases → &lt;strong&gt;skills are superior&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Start – Measure It Yourself
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install CLI&lt;/span&gt;
curl &lt;span class="nt"&gt;-sSL&lt;/span&gt; https://raw.githubusercontent.com/gigaverse-app/langsmith-cli/main/scripts/install.sh | sh

&lt;span class="c"&gt;# Add as skill in Claude Code&lt;/span&gt;
/plugin marketplace add gigaverse-app/langsmith-cli

&lt;span class="c"&gt;# See the dramatic difference&lt;/span&gt;
/context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repo: &lt;a href="https://github.com/gigaverse-app/langsmith-cli" rel="noopener noreferrer"&gt;https://github.com/gigaverse-app/langsmith-cli&lt;/a&gt;&lt;br&gt;&lt;br&gt;
( MIT license – contributions welcome )&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Context is the most precious resource in long-context LLMs.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Don't waste it on infrastructure that can be replaced with an 80-millisecond subprocess call.&lt;/p&gt;

&lt;p&gt;Try the skills approach.&lt;br&gt;&lt;br&gt;
The numbers don't lie.&lt;/p&gt;

&lt;p&gt;Happy (much lighter) hacking!&lt;br&gt;&lt;br&gt;
Aviad&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>langsmith</category>
      <category>mcp</category>
    </item>
    <item>
      <title>Enhancing Claude Code: Turning a Brilliant Showcase into a Modular Plugin Marketplace</title>
      <dc:creator>Aviad Rozenhek</dc:creator>
      <pubDate>Sun, 11 Jan 2026 17:31:16 +0000</pubDate>
      <link>https://dev.to/aviadr1/enhancing-claude-code-turning-a-brilliant-showcase-into-a-modular-plugin-marketplace-25c0</link>
      <guid>https://dev.to/aviadr1/enhancing-claude-code-turning-a-brilliant-showcase-into-a-modular-plugin-marketplace-25c0</guid>
      <description>&lt;p&gt;I'm really excited about &lt;strong&gt;Claude Code&lt;/strong&gt; from Anthropic—it's an impressive way to bring AI assistance deeply into your development workflow. Recently, thanks to a recommendation from &lt;a href="https://x.com/0x0SojalSec" rel="noopener noreferrer"&gt;@0x0SojalSec&lt;/a&gt;, I discovered &lt;a href="https://github.com/ChrisWiles" rel="noopener noreferrer"&gt;@ChrisWiles&lt;/a&gt;' excellent repository: &lt;a href="https://github.com/ChrisWiles/claude-code-showcase" rel="noopener noreferrer"&gt;claude-code-showcase&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With nearly 4,000 stars, the original repo is a wonderful resource. It clearly demonstrates how to configure Claude Code with hooks for automation, skills for project conventions, agents for tasks like code reviews, slash commands for workflows, and GitHub Actions for scheduled maintenance. It's a fantastic guide for anyone looking to make Claude a true team member.&lt;/p&gt;

&lt;p&gt;That said, while it's perfect for learning, adopting it often means copying large portions into your own projects. To make it even more accessible, I forked it and added a modular structure—a &lt;strong&gt;plugin marketplace&lt;/strong&gt; that lets you install only the components you need.&lt;/p&gt;

&lt;p&gt;My fork: &lt;a href="https://github.com/aviadr1/claude-code-showcase" rel="noopener noreferrer"&gt;aviadr1/claude-code-showcase&lt;/a&gt;&lt;br&gt;&lt;br&gt;
(It's brand new as of January 11, 2026—feel free to star it if you find it useful! 🚀)&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Claude Code Is So Exciting
&lt;/h2&gt;

&lt;p&gt;For those new to it: Claude Code lets you configure an AI agent with persistent project memory (via CLAUDE.md), specialized skills, automated hooks, dedicated agents, and integrations with external tools. It goes beyond simple autocompletion to help enforce standards, review code, update dependencies, sync documentation, and manage tickets.&lt;/p&gt;

&lt;p&gt;The original showcase illustrates these capabilities beautifully. My goal was to reduce the barriers to real-world use.&lt;/p&gt;
&lt;h2&gt;
  
  
  Key Improvements in the Fork
&lt;/h2&gt;

&lt;p&gt;I preserved all the great features from the original and added several enhancements:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Plugin Marketplace Architecture&lt;/strong&gt;
The repository now functions as a marketplace for installable components. Symlinks ensure a single source of truth—updates in one place automatically apply everywhere, with no need for build steps or sync scripts.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Installation is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   /plugin marketplace add aviadr1/claude-code-showcase

   &lt;span class="c"&gt;# Then install what you need&lt;/span&gt;
   /plugin &lt;span class="nb"&gt;install &lt;/span&gt;testing-patterns@claude-code-showcase
   /plugin &lt;span class="nb"&gt;install &lt;/span&gt;code-review-suite@claude-code-showcase
   /plugin &lt;span class="nb"&gt;install &lt;/span&gt;github-actions@claude-code-showcase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;13 Modular Plugins&lt;/strong&gt;
Choose from individual skills, bundled sets, commands, and hooks.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;| Plugin                | Type    | Description                                       |&lt;br&gt;
   |-----------------------|---------|---------------------------------------------------|&lt;br&gt;
   | testing-patterns      | Skill   | Jest, TDD, factory functions, and mocking         |&lt;br&gt;
   | systematic-debugging  | Skill   | Structured 4-phase debugging approach             |&lt;br&gt;
   | react-ui-patterns     | Skill   | Handling loading, error, and empty states         |&lt;br&gt;
   | formik-patterns       | Skill   | Form validation best practices                    |&lt;br&gt;
   | graphql-schema        | Skill   | Queries, mutations, and codegen patterns          |&lt;br&gt;
   | core-components       | Skill   | Design system and component library usage         |&lt;br&gt;
   | pr-toolkit            | Bundle  | PR review, summary, and GitHub workflow agent     |&lt;br&gt;
   | code-review-suite     | Bundle  | Dedicated reviewer agent + code quality command   |&lt;br&gt;
   | ticket-workflow       | Bundle  | JIRA/Linear integration with ticket commands      |&lt;br&gt;
   | docs-sync             | Command | Automated documentation consistency checks        |&lt;br&gt;
   | skill-activation      | Hook    | Suggests relevant skills based on your prompt     |&lt;br&gt;
   | plugin-marketplace    | Skill   | Guides you on building your own marketplace       |&lt;br&gt;
   | github-actions        | Bundle  | Reusable CI/CD workflows (detailed below)         |&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reusable GitHub Actions Workflows&lt;/strong&gt;
A major addition: workflows that use GitHub's &lt;code&gt;workflow_call&lt;/code&gt; feature, so any repository can reference them directly with just a few lines of YAML.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example caller workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;   &lt;span class="c1"&gt;# .github/workflows/claude-pr-review.yml&lt;/span&gt;
   &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Claude PR Review&lt;/span&gt;
   &lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;opened&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;reopened&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
     &lt;span class="na"&gt;issue_comment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;created&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
   &lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;review&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aviadr1/claude-code-showcase/.github/workflows/showcase-pr-review.yml@main&lt;/span&gt;
       &lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
         &lt;span class="na"&gt;ANTHROPIC_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.ANTHROPIC_API_KEY }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Available workflows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;showcase-pr-review.yml&lt;/code&gt; → Automated PR reviews and &lt;a class="mentioned-user" href="https://dev.to/claude"&gt;@claude&lt;/a&gt; responses&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;showcase-docs-sync.yml&lt;/code&gt; → Monthly documentation vs. code checks&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;showcase-nodejs-code-quality.yml&lt;/code&gt; → Weekly quality sweeps with auto-fix PRs&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;showcase-nodejs-dependency-audit.yml&lt;/code&gt; → Bi-weekly dependency updates with testing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are fully configurable and integrate with &lt;code&gt;anthropics/claude-code-action@beta&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Additional Polish&lt;/strong&gt;
Intelligent prompt-based skill suggestions, deeper MCP integrations (JIRA, Slack, GitHub), LSP for real-time code insights, and more—all while keeping the modular design.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why These Changes Matter
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Easier Adoption&lt;/strong&gt;: Install just one skill or the full suite—no need to copy everything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Always Up-to-Date&lt;/strong&gt;: Symlinks and reusable references keep your projects current.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility&lt;/strong&gt;: Mix and match components to fit your team's needs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sharing the Approach&lt;/strong&gt;: The marketplace plugin itself documents how others can apply this pattern.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This evolution makes powerful AI-assisted workflows more approachable for everyday projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Get Started
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Add the marketplace:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   /plugin marketplace add aviadr1/claude-code-showcase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Explore and install:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   /plugin
   /plugin install &amp;lt;name&amp;gt;@claude-code-showcase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;For GitHub Actions: Use the example YAML above and add your Anthropic API key as a secret.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Full fork: &lt;a href="https://github.com/aviadr1/claude-code-showcase" rel="noopener noreferrer"&gt;https://github.com/aviadr1/claude-code-showcase&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Original (definitely worth starring!): &lt;a href="https://github.com/ChrisWiles/claude-code-showcase" rel="noopener noreferrer"&gt;https://github.com/ChrisWiles/claude-code-showcase&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A big thank you to &lt;a class="mentioned-user" href="https://dev.to/chriswiles"&gt;@chriswiles&lt;/a&gt; for the inspiring foundation and to @0x0SojalSec for introducing me to it. I'm enthusiastic about where AI tools like this are taking development—let's keep building great things together! 🚀&lt;/p&gt;




&lt;p&gt;Tags: #claude #anthropic #ai #productivity #githubactions #devtools #coding&lt;/p&gt;

</description>
      <category>claude</category>
      <category>skills</category>
      <category>agents</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Modding Games: The Masochist's Magnum Opus, Where AI Meets Its Match</title>
      <dc:creator>Aviad Rozenhek</dc:creator>
      <pubDate>Fri, 19 Dec 2025 22:00:16 +0000</pubDate>
      <link>https://dev.to/aviadr1/modding-games-the-masochists-magnum-opus-where-ai-meets-its-match-3odj</link>
      <guid>https://dev.to/aviadr1/modding-games-the-masochists-magnum-opus-where-ai-meets-its-match-3odj</guid>
      <description>&lt;p&gt;In PC gaming, modding isn't just a hobby—it's a rite of passage, a digital Sisyphus pushing boulders of code up endless hills of incompatibility. Picture legions of enthusiasts for Skyrim, Fallout, Minecraft, or any moddable title: they spend weeks curating load orders, only for a single misplaced patch to summon crashes from the void. It's not frustration; it's &lt;em&gt;culture&lt;/em&gt;. A proud masochism where tweaking trumps triumphing, and victory is measured not in playtime, but in stable saves. Forums pulse with arcane scrolls—Reddit's r/skyrimmods decoding conflict tabs like hieroglyphs, Nexus guides whispering "load UI last" as gospel. Modders aren't playing the game; the game plays &lt;em&gt;them&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The gauntlet is legendary. &lt;strong&gt;Load orders&lt;/strong&gt;: A top-to-bottom tyranny where later mods overwrite earlier ones, turning your launcher into a gladiatorial arena. One texture pack too high? Your dragon's scales turn into glitchy soup. &lt;strong&gt;Conflicts&lt;/strong&gt;: Invisible file wars, revealed only by tools like xEdit or LOOT, demanding hours of manual arbitration. &lt;strong&gt;Patches&lt;/strong&gt;: Brittle ceasefires between rivals, often outdated after a dev patch, vanishing into "removed for guidelines" purgatory. And the research? A Sisyphean odyssey through Steam's dynamic hellscape—flaky update dates, regional locks, "incompatible" flags on freshly-maintained mods. One wrong click, and your 100GB setup bricks; delete and rebuild, or perish. Yet modders thrive here, trading war stories on X: "One wrong mod and the game freaks out," or "Mod loaders make it &lt;em&gt;harder&lt;/em&gt;."&lt;/p&gt;

&lt;p&gt;Enter the AI savior—or so we hoped. Desperate for a merged modlist, I unleashed ChatGPT 5.2 in PRO mode on two sprawling setups. Equipped with browsing tools, it dove in: "Researching compatibility... verifying 4.2.4... Steam rate-limited... server disconnect... ACOT removed? UIOD patched?" Forty-one minutes of "Pro thinking" later—three retries, version whiplash (3.13 to 4.2.4), phantom removals, load order flip-flops—it surrendered. Never merged. Never stable. Even Reddit mocks such attempts: "Let AI handle load order? Issues incoming." X devs lament users blindly following AI "optimizations" that shatter games.&lt;/p&gt;

&lt;p&gt;This isn't mere failure; it's revelation. Modding embodies unstructured chaos: tribal docs scattered across wikis/forums, dependency graphs implied not explicit, platforms mutating (Steam's CSS-hidden "removals," dynamic JS walls). AI excels at patterns, but mod hell is folklore—unwritten rules from "load patches last" etched in comment sections. GPT looped because it couldn't grok the human element: modders' intuition from 50 crashes, the "just works" heuristics no dataset captures.&lt;/p&gt;

&lt;p&gt;Here's the thoughtful crux: Modding is the &lt;em&gt;true&lt;/em&gt; intelligence test. AGI arrives not with eloquent essays, but when it ingests a Workshop page, parses changelogs via regex sorcery, simulates overrides in a virtual engine, predicts conflicts from file hashes, and spits a flawless load order—complete with "new game required" flags. Until then, AI remains a noob in the arena, while modders reign as wizards.&lt;/p&gt;

&lt;p&gt;Embrace it. Modding's joy &lt;em&gt;is&lt;/em&gt; the struggle—the god-complex of resurrection after CTDs. We mod for hours, play for minutes, crash, repeat. It's harder than the game, infinitely more rewarding. AI? Keep dreaming. The culture endures. 🚀💀&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
