<?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: Cristián Labra</title>
    <description>The latest articles on DEV Community by Cristián Labra (@tatanlabra).</description>
    <link>https://dev.to/tatanlabra</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%2F4047593%2F2db52cac-b1de-4162-a0fe-c5d36f7d5ed1.png</url>
      <title>DEV Community: Cristián Labra</title>
      <link>https://dev.to/tatanlabra</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tatanlabra"/>
    <language>en</language>
    <item>
      <title>Nushell in three spoonfuls: when does a structured shell actually help an agent?</title>
      <dc:creator>Cristián Labra</dc:creator>
      <pubDate>Mon, 07 Sep 2026 00:39:54 +0000</pubDate>
      <link>https://dev.to/tatanlabra/nushell-in-three-spoonfuls-when-does-a-structured-shell-actually-help-an-agent-520a</link>
      <guid>https://dev.to/tatanlabra/nushell-in-three-spoonfuls-when-does-a-structured-shell-actually-help-an-agent-520a</guid>
      <description>&lt;h2&gt;
  
  
  Prelude — Does structure actually help?
&lt;/h2&gt;

&lt;p&gt;In late August 2026, I heard Lorenzo Carbonell of &lt;a href="https://atareao.es/" rel="noopener noreferrer"&gt;atareao.es&lt;/a&gt; discuss Nushell and its advantage when working with structured data. One question stayed with me: &lt;strong&gt;could that structure genuinely improve my workflow?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Unix shell works well, but many of its pipelines depend on text, column positions, and options whose behaviour can differ across implementations.&lt;sup id="fnref1"&gt;1&lt;/sup&gt; Nushell takes a different approach: it preserves tables and typed values—dates, numbers, or file sizes, for example—throughout the pipeline.&lt;sup id="fnref2"&gt;2&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;I did not want to replace &lt;code&gt;zsh&lt;/code&gt;. I used Nushell as a selective route instead, then tested the decision against three possible outcomes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;improvement&lt;/strong&gt;, if accuracy rises enough to justify the cost;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;regression&lt;/strong&gt;, if it adds time, tokens, or complexity without compensating benefits;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;no material difference&lt;/strong&gt;, if the technical route changes but the relevant outcome does not.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To test this, I wrote a &lt;em&gt;skill&lt;/em&gt; (a rule that guides an agent on when to use a tool) and collected &lt;strong&gt;380 runs&lt;/strong&gt;: 200 pipeline comparisons, 100 A/B runs on a tuned corpus, 50 runs on held-out tasks, and 30 observations from a real aggregate case inspired by the reconstruction of my master's thesis.&lt;/p&gt;

&lt;p&gt;That is a large number of repetitions across only a few task families. Part of the integration was also tuned during the process. The results are therefore &lt;strong&gt;bounded exploratory evidence, not a universal test&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The question is not whether Nushell is better than Bash:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;When does a structured route improve an agent's work, when does it make it worse, and when does it make no material difference?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Route before you replace
&lt;/h2&gt;

&lt;p&gt;The policy uses the least complex tool that can solve the task robustly.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;Preferred tool&lt;/th&gt;
&lt;th&gt;Preferred use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;git&lt;/code&gt;, &lt;code&gt;systemctl&lt;/code&gt;, &lt;code&gt;pacman&lt;/code&gt;, &lt;code&gt;ssh&lt;/code&gt;, &lt;code&gt;rsync&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;The operation already has a direct interface.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;rg&lt;/code&gt;, &lt;code&gt;jq&lt;/code&gt;, &lt;code&gt;yq&lt;/code&gt;, &lt;code&gt;awk&lt;/code&gt;, &lt;code&gt;fd&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;A specialised utility handles the transformation.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Nushell&lt;/td&gt;
&lt;td&gt;Several transformations over tabular or typed data.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;DuckDB, Python, Polars, or R&lt;/td&gt;
&lt;td&gt;The volume or logic calls for an analytical engine.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Before comparing results, it helps to locate the shell families. Sharing the word &lt;em&gt;shell&lt;/em&gt; does not mean they carry the same kind of information: &lt;code&gt;zsh&lt;/code&gt; usually orchestrates text and commands, while Nushell preserves structured values. This is an orientation map, not a ranking.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;source&amp;gt;
&amp;lt;img src="https://3cucharadas.cl/assets/images/structured-shell/fig-d2-shell-families-en-devto-1600x1360.png" alt="D2 diagram. Text shells sh, Bash, and zsh pass text between programs; fish is an interactive non-POSIX shell; Nushell carries records, columns, numbers, and dates; PowerShell pipes .NET objects. The experiment compares only selective routing between zsh and Nushell."&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;&lt;strong&gt;D2 — Where Nushell fits.&lt;/strong&gt; A conceptual map for reading the routing rule, not a ranking of shells or a comparison with fish or PowerShell.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;The practical rule is simple: &lt;strong&gt;if Nushell merely runs, inside another shell, a command that already works well, it is unnecessary&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The integration consists of a &lt;em&gt;skill&lt;/em&gt;, narrow activation rules, and a small wrapper named &lt;code&gt;nu-query&lt;/code&gt;, which returns JSON, enforces a timeout, and states whether it truncated rows. I did not add it to the auto-approved permission list: &lt;code&gt;nu -c&lt;/code&gt; can modify the system.&lt;/p&gt;

&lt;p&gt;The failures that surfaced before measurement were more useful than any benchmark. In the tested environment, &lt;code&gt;ls **/*&lt;/code&gt; omitted hidden paths, Claude Code substituted some commands with different implementations, and localised output made parsing harder. In that tree, &lt;code&gt;ls **/*&lt;/code&gt; found roughly 46,400 files while &lt;code&gt;ls -a **/*&lt;/code&gt; found about 127,900: omitting &lt;code&gt;-a&lt;/code&gt; excluded close to 64%. This is not a general property of Nushell; it is a dated warning about a silent failure observed in that environment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Measure with R, not impressions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  First level: pipeline against pipeline
&lt;/h3&gt;

&lt;p&gt;The first experiment was a &lt;strong&gt;microbenchmark&lt;/strong&gt; (a repeated comparison of small, tightly scoped tasks): four tasks, two variants, and 25 repetitions per variant, for &lt;strong&gt;200 runs&lt;/strong&gt; in total. I analysed the results with R 4.5.3.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Variant&lt;/th&gt;
&lt;th&gt;Median time&lt;/th&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Files &amp;gt; 1 MB, modified in the last 30 days&lt;/td&gt;
&lt;td&gt;&lt;code&gt;find + sort + head&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;389 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;648 B&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Nushell&lt;/td&gt;
&lt;td&gt;1,315 ms&lt;/td&gt;
&lt;td&gt;1,060 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Five processes using the most memory&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ps + awk&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;22 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;113 B&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Nushell&lt;/td&gt;
&lt;td&gt;233 ms&lt;/td&gt;
&lt;td&gt;366 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Containers grouped by image&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker + sort + uniq&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;19 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;339 B&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Nushell&lt;/td&gt;
&lt;td&gt;40 ms&lt;/td&gt;
&lt;td&gt;603 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context from &lt;code&gt;docker ps -a&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Unreduced&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;17 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3,353 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Aggregated in Nushell&lt;/td&gt;
&lt;td&gt;39 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;566 B&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;&lt;br&gt;
    The analysis ran in R. The viewer renders precomputed data from the frozen corpus and does not query the reader's machine.&lt;br&gt;
  &lt;p&gt;&lt;/p&gt;

&lt;p&gt;The result was uncomfortable, as useful measurements often are: &lt;strong&gt;Nushell was slower on every timed task&lt;/strong&gt;. It did not always reduce context either. The advantage appeared only when there was real aggregation: the container summary fell from 3,353 to 566 bytes, about 83%.&lt;/p&gt;

&lt;p&gt;The potential gain was semantic. Comparing sizes or dates as typed values leaves fewer implicit assumptions than manufacturing text columns and deciding how to sort them. But the microbenchmark measured pipelines I had written. I still needed to observe the agent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Second level: agent against agent
&lt;/h3&gt;

&lt;p&gt;The tuned corpus used five task families in Spanish: three where the policy favoured Nushell and two where it should avoid it. Each family was repeated ten times per arm: &lt;strong&gt;100 runs&lt;/strong&gt;. The agent was headless Claude Code using a model from the Sonnet family; ground truth was recomputed before every run, and the command came from the transcript rather than the agent's account of what it had done.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Subset&lt;/th&gt;
&lt;th&gt;Arm&lt;/th&gt;
&lt;th&gt;Correct&lt;/th&gt;
&lt;th&gt;Activation&lt;/th&gt;
&lt;th&gt;Median time&lt;/th&gt;
&lt;th&gt;Median tokens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Without &lt;em&gt;skill&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;23/30&lt;/td&gt;
&lt;td&gt;0/30&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6,239 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;152&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;With &lt;em&gt;skill&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;30/30&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;28/30&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;9,537 ms&lt;/td&gt;
&lt;td&gt;368&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Without &lt;em&gt;skill&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;20/20&lt;/td&gt;
&lt;td&gt;0/20&lt;/td&gt;
&lt;td&gt;5,220 ms&lt;/td&gt;
&lt;td&gt;91&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;With &lt;em&gt;skill&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;20/20&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0/20&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5,107 ms&lt;/td&gt;
&lt;td&gt;99&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On positive tasks, observed accuracy rose from 23/30 to 30/30. If the 60 runs are treated as independent observations, Fisher's exact test gives &lt;em&gt;p&lt;/em&gt; = 0.0105.&lt;sup id="fnref3"&gt;3&lt;/sup&gt; That assumption is not defensible for generalisation: the substantive unit is the task family, and there were only three positive families, all reused during tuning. I therefore report the value as an &lt;strong&gt;exploratory description of the tuned corpus&lt;/strong&gt;, not as confirmation at the 5% level.&lt;/p&gt;

&lt;p&gt;The exact Clopper–Pearson interval (a range compatible with a binomial proportion) was [88.4%; 100%] for 30/30 and [57.7%; 90.1%] for 23/30.&lt;sup id="fnref4"&gt;4&lt;/sup&gt;&lt;sup id="fnref5"&gt;5&lt;/sup&gt; Median time increased 1.53× and median output tokens increased 2.43×: &lt;strong&gt;higher accuracy on that corpus, at a cost&lt;/strong&gt;. I estimated the medians and their uncertainty by &lt;em&gt;bootstrap&lt;/em&gt; (resampling the observations to construct intervals).&lt;sup id="fnref6"&gt;6&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;The task that separated the arms most strongly asked for the three files larger than 1 MB modified during the previous 30 days. Without the &lt;em&gt;skill&lt;/em&gt;, the agent succeeded 3/10 times; with it, 10/10. The recurring failure used &lt;code&gt;fd -I&lt;/code&gt;: that option ignores exclusion rules, but does not include hidden files on its own; that requires &lt;code&gt;-H -I&lt;/code&gt; or &lt;code&gt;-u&lt;/code&gt;.&lt;sup id="fnref7"&gt;7&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;The transcript also exposed a &lt;code&gt;stdin&lt;/code&gt; (standard input) trap. Bash piped into &lt;code&gt;nu -c&lt;/code&gt;, which returned &lt;code&gt;null&lt;/code&gt; with exit code 0, and the agent entered a 77-second search. The documentation requires &lt;code&gt;--stdin&lt;/code&gt; for that route.&lt;sup id="fnref8"&gt;8&lt;/sup&gt; After the correction, that case fell from 70,901 to 7,852 ms and from 3,287 to 303 tokens. This is a causal observation for that task, not a general estimator.&lt;/p&gt;




&lt;h2&gt;
  
  
  Counter-tests before the verdict
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The holdout that corrected the story
&lt;/h3&gt;

&lt;p&gt;The earlier corpus was both tuned and evaluated on the same five families. I also expanded the sample after seeing preliminary results. To test what survived outside that set, I froze the &lt;em&gt;skill&lt;/em&gt; description and wrote five held-out tasks (&lt;em&gt;holdout&lt;/em&gt;: cases kept out of tuning), three positive and two negative. Five repetitions per arm produced &lt;strong&gt;50 runs&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Held-out subset&lt;/th&gt;
&lt;th&gt;Arm&lt;/th&gt;
&lt;th&gt;Correct&lt;/th&gt;
&lt;th&gt;Activation&lt;/th&gt;
&lt;th&gt;Median time&lt;/th&gt;
&lt;th&gt;Median tokens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Without &lt;em&gt;skill&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;10/15&lt;/td&gt;
&lt;td&gt;0/15&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6,449 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;178&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;With &lt;em&gt;skill&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;14/15&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4/15&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8,019 ms&lt;/td&gt;
&lt;td&gt;282&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Without &lt;em&gt;skill&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6/10&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0/10&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5,227 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;143&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;With &lt;em&gt;skill&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;5/10&lt;/td&gt;
&lt;td&gt;0/10&lt;/td&gt;
&lt;td&gt;5,476 ms&lt;/td&gt;
&lt;td&gt;160&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All four positive activations occurred in one family, at 4/5. The other two achieved 5/5 correct answers without activating the &lt;em&gt;skill&lt;/em&gt;. The holdout is small, and its negative tasks produced weak results in both arms. It does not prove a general advantage or show that the &lt;em&gt;skill&lt;/em&gt; harms negative tasks. It does refute one concrete extrapolation: &lt;strong&gt;28/30 described the tuned corpus, not the integration in general&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  A real aggregate case from my thesis
&lt;/h3&gt;

&lt;p&gt;The second counter-test reused the governed temporal audit from a reconstruction of my master's thesis, but only as a &lt;em&gt;fixture&lt;/em&gt; (a small, frozen dataset used to test a contract) of public aggregates: a source with 306,768 rows and 263 columns summarised into 21 annual rows, with no microdata or identifiers. The source hash, invariants, export process, and results are available in the &lt;a href="https://github.com/tatanlabra/3cucharadas/tree/main/research/structured-shell-thesis-case" rel="noopener noreferrer"&gt;reproducible case package&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The control arm was forbidden from using Nu; the frozen policy required it only for the two structured tasks. With Codex in read-only mode and low reasoning effort, three tasks across two arms and five repetitions produced &lt;strong&gt;30 observations&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;R1 requested a ranking of valid aggregates;&lt;/li&gt;
&lt;li&gt;R2 received the same corrupted &lt;em&gt;fixture&lt;/em&gt; and had to block the ranking;&lt;/li&gt;
&lt;li&gt;R3 asked whether &lt;code&gt;presence=0&lt;/code&gt; proved institutional closure and had to answer that the claim was out of scope.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Without Nu&lt;/th&gt;
&lt;th&gt;Nu policy&lt;/th&gt;
&lt;th&gt;Strict reading&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;R1 · valid ranking&lt;/td&gt;
&lt;td&gt;5/5; Nu 0/5; 30.6 s; 445 tokens&lt;/td&gt;
&lt;td&gt;5/5; Nu 5/5; 42.0 s; 580 tokens&lt;/td&gt;
&lt;td&gt;The policy activated the route but did not improve accuracy.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;R2 · corrupted &lt;em&gt;fixture&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;5/5; Nu 0/5; 31.4 s; 358 tokens&lt;/td&gt;
&lt;td&gt;5/5; Nu 5/5; 32.0 s; 410 tokens&lt;/td&gt;
&lt;td&gt;Both blocked the ranking; the expected activation occurred 5/5 times.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;R3 · epistemic boundary&lt;/td&gt;
&lt;td&gt;5/5; Nu 0/5; 15.0 s; 88 tokens&lt;/td&gt;
&lt;td&gt;5/5; Nu 0/5; 11.4 s; 62 tokens&lt;/td&gt;
&lt;td&gt;The policy did not route a conceptual question through Nu.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both arms achieved 5/5 on all three tasks. The case demonstrates policy conformity—Nu in 10/10 structured observations and 0/5 conceptual observations within that arm—but &lt;strong&gt;no difference in accuracy&lt;/strong&gt;. I did not calculate a &lt;em&gt;p&lt;/em&gt;-value: five repetitions of the same case are not five independent problem types. Nor does this alter the boundary of the data: &lt;code&gt;presence=0&lt;/code&gt; does not prove institutional closure.&lt;/p&gt;

&lt;h3&gt;
  
  
  A verdict for each outcome
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Outcome&lt;/th&gt;
&lt;th&gt;Where it appears&lt;/th&gt;
&lt;th&gt;Defensible interpretation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Improvement&lt;/td&gt;
&lt;td&gt;Tuned corpus: 30/30 versus 23/30; holdout: 14/15 versus 10/15&lt;/td&gt;
&lt;td&gt;Some task families show an accuracy signal, but not a generalisable rate.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Regression&lt;/td&gt;
&lt;td&gt;Microbenchmark, tuned corpus, and holdout&lt;/td&gt;
&lt;td&gt;The structured route added latency and, in the agent experiments, more tokens.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No material difference&lt;/td&gt;
&lt;td&gt;Real aggregate case and tasks that did not activate the &lt;em&gt;skill&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;Changing the route did not improve accuracy when both arms already satisfied the contract.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is the rule I kept after measuring:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nushell is worth testing when a query combines structured data, several transformations, and a concrete risk of silent failure.&lt;/strong&gt; For a native operation, a single transformation, or analysis at larger scale, a simpler or more appropriate tool usually exists.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I do not want to make it my primary shell or tell the agent to use it “whenever possible.” The objective is the opposite: &lt;strong&gt;it should be able to justify both entering and avoiding that route&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I also see no reason yet to turn this route into an MCP server. A query would merit promotion to a dedicated tool if it recurred, had a stable interface, accepted typed parameters, could be reused across agents, and measurably improved safety or observability. These cases do not yet meet all those conditions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing — A shell is not a religion
&lt;/h2&gt;

&lt;p&gt;The result is not that Nushell wins. It improved accuracy on the tuned corpus; it added time in the microbenchmark and agent experiments, plus output tokens in the latter; and it did not change correctness in the real aggregate case. The reasonable decision depends on the task and on the cost of being wrong.&lt;/p&gt;

&lt;p&gt;The most interesting finding was not Nushell either. It was discovering that a &lt;em&gt;skill&lt;/em&gt; might never activate, that a rule about hidden files might be incomplete, and that a pipeline could return &lt;code&gt;null&lt;/code&gt; with apparent success. The benchmark ended up testing both the tool and my assumptions.&lt;/p&gt;

&lt;p&gt;There are probably better routes. If you use Nushell, DuckDB, &lt;code&gt;jq&lt;/code&gt;, Python, MCP, &lt;em&gt;skills&lt;/em&gt;, or another strategy that gives an agent a concrete advantage, I would like to know &lt;strong&gt;what problem it solves, what baseline you compared it against, and how you verified that it actually improved the workflow&lt;/strong&gt;. Rather than collecting tools, we should start collecting evidence about when they are worth using and what they measurably improve.&lt;/p&gt;

&lt;h2&gt;
  
  
  References and notes
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Conflicts of interest and provenance.&lt;/strong&gt; The &lt;code&gt;penta-agent&lt;/code&gt; metrics and the thesis case were produced by the same project that implemented the &lt;em&gt;skill&lt;/em&gt;. The Nushell documentation also comes from the tool's developers. I use it to document the declared contract and behaviour, not as independent evidence of superiority.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Greenberg, Michael, and Austin J. Blatt. 2020. “Executable Formal Semantics for the POSIX Shell.” &lt;em&gt;Proceedings of the ACM on Programming Languages&lt;/em&gt; 4 (POPL), article 43, 1–30. &lt;a href="https://doi.org/10.1145/3371111" rel="noopener noreferrer"&gt;https://doi.org/10.1145/3371111&lt;/a&gt;.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;Nushell. n.d. “Nushell: A New Type of Shell.” Accessed September 5, 2026. &lt;a href="https://www.nushell.sh/" rel="noopener noreferrer"&gt;https://www.nushell.sh/&lt;/a&gt;.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;R Core Team. n.d. “Fisher's Exact Test for Count Data.” &lt;em&gt;R Documentation&lt;/em&gt;. Accessed September 5, 2026. &lt;a href="https://stat.ethz.ch/R-manual/R-devel/library/stats/html/fisher.test.html" rel="noopener noreferrer"&gt;https://stat.ethz.ch/R-manual/R-devel/library/stats/html/fisher.test.html&lt;/a&gt;.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;Clopper, C. J., and E. S. Pearson. 1934. “The Use of Confidence or Fiducial Limits Illustrated in the Case of the Binomial.” &lt;em&gt;Biometrika&lt;/em&gt; 26, no. 4: 404–413. &lt;a href="https://doi.org/10.1093/biomet/26.4.404" rel="noopener noreferrer"&gt;https://doi.org/10.1093/biomet/26.4.404&lt;/a&gt;.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;R Core Team. n.d. “Exact Binomial Test.” &lt;em&gt;R Documentation&lt;/em&gt;. Accessed September 5, 2026. &lt;a href="https://stat.ethz.ch/R-manual/R-devel/library/stats/html/binom.test.html" rel="noopener noreferrer"&gt;https://stat.ethz.ch/R-manual/R-devel/library/stats/html/binom.test.html&lt;/a&gt;.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn6"&gt;
&lt;p&gt;Efron, Bradley, and Robert J. Tibshirani. 1993. &lt;em&gt;An Introduction to the Bootstrap&lt;/em&gt;. New York: Chapman &amp;amp; Hall. &lt;a href="https://www.routledge.com/An-Introduction-to-the-Bootstrap/Efron-Tibshirani/p/book/9780412042317" rel="noopener noreferrer"&gt;https://www.routledge.com/An-Introduction-to-the-Bootstrap/Efron-Tibshirani/p/book/9780412042317&lt;/a&gt;.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn7"&gt;
&lt;p&gt;sharkdp. n.d. “fd: A Simple, Fast and User-Friendly Alternative to &lt;code&gt;find&lt;/code&gt;.” GitHub repository. Accessed September 5, 2026. &lt;a href="https://github.com/sharkdp/fd" rel="noopener noreferrer"&gt;https://github.com/sharkdp/fd&lt;/a&gt;.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn8"&gt;
&lt;p&gt;Nushell. n.d. “Scripts.” &lt;em&gt;The Nushell Book&lt;/em&gt;. Accessed September 5, 2026. &lt;a href="https://www.nushell.sh/book/scripts.html" rel="noopener noreferrer"&gt;https://www.nushell.sh/book/scripts.html&lt;/a&gt;.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>nushell</category>
      <category>shell</category>
      <category>agents</category>
      <category>claudecode</category>
    </item>
    <item>
      <title>Appraisal and vulnerability in 3 spoonfuls: change the denominator, change the map</title>
      <dc:creator>Cristián Labra</dc:creator>
      <pubDate>Mon, 07 Sep 2026 00:39:03 +0000</pubDate>
      <link>https://dev.to/tatanlabra/appraisal-and-vulnerability-in-3-spoonfuls-change-the-denominator-change-the-map-4j3e</link>
      <guid>https://dev.to/tatanlabra/appraisal-and-vulnerability-in-3-spoonfuls-change-the-denominator-change-the-map-4j3e</guid>
      <description>&lt;p&gt;Most countries tax immovable property, and most of them argue about it badly. The argument usually skips the part that decides the answer: before any map is coloured, someone has to choose &lt;strong&gt;what is added up, what it is divided by, over which territory it is aggregated, and which cases are left out&lt;/strong&gt;. Change any of those and the map can change while the underlying data stay identical.&lt;/p&gt;

&lt;p&gt;This post works through that problem with Chilean data, because Chile happens to publish the pieces needed to do it honestly: a national cadastre of every taxable property, and an official index that ranks small civic territories by socio-territorial vulnerability. The mechanics, though, are not Chilean. Any jurisdiction that assesses property for tax and then maps the result against a deprivation measure faces exactly the same four choices.&lt;/p&gt;

&lt;p&gt;The question fits in one small fraction:&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;territorial&amp;nbsp;indicator&lt;/span&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;the&amp;nbsp;unit&amp;nbsp;you&amp;nbsp;compare&amp;nbsp;it&amp;nbsp;against&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;the&amp;nbsp;total&amp;nbsp;you&amp;nbsp;want&amp;nbsp;to&amp;nbsp;describe&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;Adding up the assessed value inside a territory answers how much administrative value was allocated there. Dividing that same total by households, by residents or by square metres answers different questions. None of them is «the correct one» by nature; the error appears when one is presented under another's name. The arithmetic is usually innocent. The narrative is not always.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading contract
&lt;/h2&gt;

&lt;p&gt;I cross two Chilean administrative registers: the real-estate cadastre of the &lt;strong&gt;Servicio de Impuestos Internos (SII)&lt;/strong&gt; —Chile's tax authority, roughly the counterpart of the IRS or HMRC— and the &lt;strong&gt;Índice Global de Vulnerabilidad Socioterritorial (IGVUST)&lt;/strong&gt;, a socio-territorial vulnerability index published by the Ministry of Social Development and Family. The unit of analysis is the &lt;strong&gt;neighbourhood unit&lt;/strong&gt;, not the parcel, the household or the person.&lt;/p&gt;

&lt;p&gt;A word on that unit, because it has no clean equivalent elsewhere and it drives half of what follows. A Chilean &lt;strong&gt;unidad vecinal (UV)&lt;/strong&gt; is a civic territory drawn for neighbourhood organisation and local participation — closer to a British ward or an American neighbourhood association boundary than to a census tract. Crucially, &lt;strong&gt;it was never designed to tile the country&lt;/strong&gt;. Large stretches of rural Chile belong to no UV at all. A census geography would cover everything by construction; this one does not, and pretending otherwise is the first way to get the map wrong.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;What it means here&lt;/th&gt;
&lt;th&gt;What it does not mean&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SII cadastre&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Administrative register of real estate and its characteristics.&lt;/td&gt;
&lt;td&gt;A population census or a register of residents.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Parcel&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cadastral unit identified by municipality, block and parcel number.&lt;/td&gt;
&lt;td&gt;A dwelling, a household, an owner or a person.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fiscal appraisal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Administrative valuation used as the base of the property tax.&lt;/td&gt;
&lt;td&gt;Sale price, income, or the wealth of whoever lives there.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IGVUST&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A ranking of neighbourhood units by socio-territorial vulnerability.&lt;/td&gt;
&lt;td&gt;An individual diagnosis or a causal mechanism.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RSH&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Chile's &lt;em&gt;Registro Social de Hogares&lt;/em&gt;, the means-testing registry that supplies the households and people used as denominators. In this processing it sums 15,978,644 people, close to 85% of the country's population.&lt;/td&gt;
&lt;td&gt;A complete census, or a universe with even coverage across municipalities.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Neighbourhood unit (UV)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Territory defined for neighbourhood organisation and participation.&lt;/td&gt;
&lt;td&gt;An exhaustive mesh covering all of Chile parcel by parcel.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Denominator&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The magnitude the allocated appraisal is divided by.&lt;/td&gt;
&lt;td&gt;Small print added afterwards: it defines the question.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The RSH matters because it is the source of the households and people I use as denominators. Nationally it is broad —around 85% of the population— but it does not cover every municipality equally. In municipalities with low relative RSH enrolment, as can happen in the wealthiest districts of Santiago, an indicator «per RSH household» can inflate because the denominator is narrow, not because there is more appraised value. That is a coverage artefact, not a finding.&lt;/p&gt;

&lt;p&gt;Fiscal appraisal is also not market price: the SII builds it from the characteristics of the property and its homogeneous valuation zone, not from an observed transaction. It can be a useful territorial signal as long as it keeps its surname, &lt;strong&gt;fiscal&lt;/strong&gt; — &lt;a href="https://doi.org/10.1086/260169" rel="noopener noreferrer"&gt;Rosen's (1974)&lt;/a&gt; hedonic price theory explains why surroundings weigh on the valuation of a differentiated good such as housing, but this post observes neither transactions nor household wealth.&lt;/p&gt;

&lt;p&gt;One note on notation for readers used to the short scale: Chilean Spanish uses &lt;em&gt;billón&lt;/em&gt; for 10¹². Throughout this English version I write &lt;strong&gt;trillion&lt;/strong&gt; for that same quantity. Chilean pesos traded around 980 CLP per US dollar in July 2026, so the national total below —587.4 trillion CLP— is on the order of US$600 billion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data cut:&lt;/strong&gt; 19 July 2026. &lt;strong&gt;Editorial date:&lt;/strong&gt; 26 July 2026. All relationships are descriptive and depend on the UV mesh used.&lt;/p&gt;

&lt;h2&gt;
  
  
  The question
&lt;/h2&gt;

&lt;p&gt;I aggregated the fiscal appraisal of SII parcels to UV scale and compared it against the IGVUST national vulnerability ordering, holding the numerator fixed and changing the lens:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Measure&lt;/th&gt;
&lt;th&gt;Question it answers&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Total appraisal&lt;/td&gt;
&lt;td&gt;How much fiscal appraisal was allocated to this UV?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Appraisal per RSH household&lt;/td&gt;
&lt;td&gt;How much allocated appraisal corresponds to each registered household in the UV?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Appraisal per RSH person&lt;/td&gt;
&lt;td&gt;How much corresponds to each registered person?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Appraisal per m² of parcel area&lt;/td&gt;
&lt;td&gt;How much corresponds to each square metre of allocated parcel surface?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The short answer: per household and per person there is almost no relationship with vulnerability. Per square metre a strong national relationship appears, but it dissolves when you look only at predominantly urban UVs. This is not a glamorous paradox between wealth and vulnerability. It is more sober, and therefore more useful: &lt;strong&gt;the denominator, the universe and the scale are part of the result&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A warning before continuing: none of this says how much the people living there earn, what their house is worth on the market, or who owns it. Turning a territorial association into a statement about persons is precisely the ecological inference &lt;a href="https://doi.org/10.2307/2087176" rel="noopener noreferrer"&gt;Robinson (1950)&lt;/a&gt; warned about more than seventy years ago.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before the spoonfuls: why we tax what cannot move
&lt;/h2&gt;

&lt;p&gt;The property tax has a less exotic logic than its public reputation suggests. Property is a visible base, immobile, and tied to the territory where services are delivered. In Chile, property tax revenue is municipal: part stays in the municipality of origin and part feeds the &lt;strong&gt;Fondo Común Municipal&lt;/strong&gt;, an equalisation fund that redistributes resources for street lighting, green areas, infrastructure and social programmes (&lt;a href="https://www.sii.cl/destacados/impuesto_territorial/index.html" rel="noopener noreferrer"&gt;SII, &lt;em&gt;Impuesto Territorial&lt;/em&gt;&lt;/a&gt;). Without a cadastre, appraisals and parcel-level location, that architecture simply does not work.&lt;/p&gt;

&lt;p&gt;None of this is a Chilean peculiarity. Recurrent taxes on immovable property hold a relevant place in local finance across many countries. The comparison assembled by the World Bank shows collections close to 2%–3% of GDP in the United States, Canada and the United Kingdom, and significant shares of local revenue. These are not copies of the Chilean system, but they share the same intuition: part of the value that accumulates in a territory helps finance that territory (&lt;a href="https://openknowledge.worldbank.org/handle/10986/34793" rel="noopener noreferrer"&gt;World Bank, 2020&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The comparative evidence does not say that any property tax is fair by definition. It says something more uncomfortable: &lt;strong&gt;design rules&lt;/strong&gt;. The OECD and the IMF highlight its immobile base, its revenue potential and its link to local services, but they recommend up-to-date valuations, moderate rates, and targeted or deferred relief for owners with low liquidity. The IMF itself uses the British &lt;em&gt;Council Tax&lt;/em&gt; as an example of how overly compressed bands can produce a regressive outcome (&lt;a href="https://doi.org/10.1787/03dfe007-en" rel="noopener noreferrer"&gt;OECD, 2022&lt;/a&gt;; &lt;a href="https://doi.org/10.5089/9798400288753.061" rel="noopener noreferrer"&gt;IMF, 2024&lt;/a&gt;). The instrument does not arrive progressive from the factory.&lt;/p&gt;

&lt;p&gt;There is, of course, a flashier route: delete a line from the bill and rebuild the cost on another spreadsheet. The sum can balance; the distribution need not. When an exemption stops looking at income or value and the compensation reproduces prior revenue, the tax does not disappear: it changes pocket, fund, or postcode. The accounting stays calm. The territory may not.&lt;/p&gt;

&lt;p&gt;Seen that way, the cadastre stops being a collection of tax rolls and becomes what it actually is: the infrastructure that lets you measure the base, divide it, and argue about who benefits from each rule. Now, the three spoonfuls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spoonful 1: building the numerator without closing the leak
&lt;/h2&gt;

&lt;p&gt;The original extract holds &lt;strong&gt;10,343,893 records&lt;/strong&gt;. A record is not the same as a unique parcel; administrative databases have echoes too. After deduplicating the cadastral key —municipality, block and parcel number— &lt;strong&gt;9,401,277 parcels&lt;/strong&gt; remain. The goal is to distribute their appraised value across &lt;strong&gt;6,891 UVs&lt;/strong&gt; before testing any denominator.&lt;/p&gt;

&lt;p&gt;Parcels and UVs are both polygons, but their boundaries do not coincide. A parcel can fall entirely inside one UV, straddle several, or touch none. I use areal apportionment by intersection area, a form of areal interpolation (&lt;a href="https://doi.org/10.1068/a250383" rel="noopener noreferrer"&gt;Goodchild, Anselin &amp;amp; Deichmann, 1993&lt;/a&gt;):&lt;/p&gt;


&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;f&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;p&lt;/span&gt;&lt;span class="mpunct mtight"&gt;,&lt;/span&gt;&lt;span class="mord mathnormal mtight"&gt;u&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;area&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;p&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;area&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;p&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;∩&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;u&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;A&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;u&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mop op-limits"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;p&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="mop op-symbol large-op"&gt;∑&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;a&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;p&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;⋅&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;f&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;p&lt;/span&gt;&lt;span class="mpunct mtight"&gt;,&lt;/span&gt;&lt;span class="mord mathnormal mtight"&gt;u&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;where $$a_p$$ is the parcel's fiscal appraisal and $$A_u$$ the total allocated to the neighbourhood unit. If the parcel sits entirely inside one UV, it contributes everything; if it straddles the boundary in half, it contributes half.&lt;/p&gt;

&lt;p&gt;Two methodological decisions matter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I use geometric area to apportion.&lt;/strong&gt; Among unique parcels, 10.7% have no usable declared surface and 2.8% have no geometry at all. Declared surface can serve for auditing or sensitivity checks, but it is not enough to place a parcel in space when there is no polygon. So I do not impute UV allocation from reported square metres alone. If a fallback is implemented later, it has to enter the analytical pipeline first, with an explicit location rule, and only then the post and the viewer. A pretty map is not a licence to invent geometry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I do not renormalise.&lt;/strong&gt; If $$\sum_u f_{p,u} &amp;lt; 1$$, part of the parcel fell outside every UV. Redistributing that remainder among the UVs it did touch would produce a perfect sum —very convenient for the chart— and a worse measurement: it would hide the fact that the UV was never designed to tile Chile.&lt;/p&gt;

&lt;p&gt;The residual is concrete: &lt;strong&gt;271,150 parcels&lt;/strong&gt;, &lt;strong&gt;2.884%&lt;/strong&gt; of the deduplicated set, landed in no UV at all. The median municipal leakage is &lt;strong&gt;0.644%&lt;/strong&gt;, but it is not homogeneous. In Antofagasta, 2,325 parcels outside any UV concentrate &lt;strong&gt;32.6%&lt;/strong&gt; of the municipality's fiscal appraisal; in Tortel, Timaukel, San Gregorio, Laguna Blanca and Río Verde the leakage reaches 100%, because the UVs cover the settlement and the catalogued parcels lie outside that mesh.&lt;/p&gt;

&lt;p&gt;That bias gets declared, not swept under the rug. In municipalities with many parcels lacking polygons, or with heavy leakage outside the UV mesh, the crosswalk under-represents part of the cadastre. Public reading has to look at those indicators before interpreting any colour.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://3cucharadas.cl/assets/images/avaluo-vulnerabilidad-unidad-vecinal/sankey-pipeline-en.webp" title="Figure 1 — From the original register to the spatial universe" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  &amp;lt;source type="image/webp"&amp;gt;
  &amp;lt;img src="https://3cucharadas.cl/assets/images/avaluo-vulnerabilidad-unidad-vecinal/sankey-pipeline-en.webp" alt="Flow diagram: 10,343,893 original records split into 942,616 duplicates and 9,401,277 unique parcels; of the latter, 9,130,127 touch at least one neighbourhood unit and 271,150 touch none."&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;&lt;br&gt;&lt;strong&gt;Figure 1&lt;/strong&gt; — From the original register to the spatial universe. «Touches at least one UV» does not mean full allocation: a parcel can intersect a UV and still keep part of its surface outside the mesh. Source: own elaboration, data cut 19/07/2026. Click to enlarge.
  &lt;p&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Before the IGVUST: the size of the observed appraisal
&lt;/h2&gt;

&lt;p&gt;Before crossing anything with vulnerability, it helps to know the order of magnitude. Across the 346 municipalities, allocated fiscal appraisal sums &lt;strong&gt;587.4 trillion pesos&lt;/strong&gt; (≈US$600 billion). The Santiago Metropolitan Region concentrates &lt;strong&gt;286.7 trillion&lt;/strong&gt; (&lt;strong&gt;48.8%&lt;/strong&gt;); Valparaíso, &lt;strong&gt;56.2 trillion&lt;/strong&gt; (&lt;strong&gt;9.6%&lt;/strong&gt;); Biobío, &lt;strong&gt;39.4 trillion&lt;/strong&gt; (&lt;strong&gt;6.7%&lt;/strong&gt;). The concentration is not a detail: it sets the size of the numerator that later gets divided.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table 1&lt;/strong&gt; — Allocated fiscal appraisal before normalising&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;Territory&lt;/th&gt;
&lt;th&gt;Allocated appraisal (trillion CLP)&lt;/th&gt;
&lt;th&gt;National share&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Region&lt;/td&gt;
&lt;td&gt;Metropolitana&lt;/td&gt;
&lt;td&gt;286.7&lt;/td&gt;
&lt;td&gt;48.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Region&lt;/td&gt;
&lt;td&gt;Valparaíso&lt;/td&gt;
&lt;td&gt;56.2&lt;/td&gt;
&lt;td&gt;9.6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Region&lt;/td&gt;
&lt;td&gt;Biobío&lt;/td&gt;
&lt;td&gt;39.4&lt;/td&gt;
&lt;td&gt;6.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Region&lt;/td&gt;
&lt;td&gt;La Araucanía&lt;/td&gt;
&lt;td&gt;28.2&lt;/td&gt;
&lt;td&gt;4.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Region&lt;/td&gt;
&lt;td&gt;Maule&lt;/td&gt;
&lt;td&gt;26.4&lt;/td&gt;
&lt;td&gt;4.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Municipality&lt;/td&gt;
&lt;td&gt;Las Condes&lt;/td&gt;
&lt;td&gt;38.2&lt;/td&gt;
&lt;td&gt;6.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Municipality&lt;/td&gt;
&lt;td&gt;Santiago&lt;/td&gt;
&lt;td&gt;27.1&lt;/td&gt;
&lt;td&gt;4.6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Municipality&lt;/td&gt;
&lt;td&gt;Providencia&lt;/td&gt;
&lt;td&gt;16.3&lt;/td&gt;
&lt;td&gt;2.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Municipality&lt;/td&gt;
&lt;td&gt;Lo Barnechea&lt;/td&gt;
&lt;td&gt;16.0&lt;/td&gt;
&lt;td&gt;2.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Municipality&lt;/td&gt;
&lt;td&gt;Vitacura&lt;/td&gt;
&lt;td&gt;15.2&lt;/td&gt;
&lt;td&gt;2.6%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Shares are computed over the national allocated total of 587.4 trillion. The amounts for La Araucanía and Maule are derived from that published share, not from a separately measured figure. In municipalities with UV leakage or low relative coverage of the RSH denominator, this size should not be confused with a complete reading of the territory or its residents.&lt;/p&gt;

&lt;p&gt;The five municipalities in the table add up to &lt;strong&gt;112.8 trillion&lt;/strong&gt;: &lt;strong&gt;19.2% of the entire allocated base of the country&lt;/strong&gt;, in five municipalities out of 346, all of them in the eastern districts of Santiago. That is the concentration that later disappears from view once you divide by households or by square metres. Antofagasta also ranks high (&lt;strong&gt;15.1 trillion&lt;/strong&gt;), but with very high municipal UV leakage; that is exactly the kind of case where the total, the denominator and the universe have to be read together.&lt;/p&gt;

&lt;p&gt;The viewer carries this raw reading in the &lt;strong&gt;Appraisals&lt;/strong&gt; tab of its denominator laboratory. First the numerator; then the story.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spoonful 2: quartiles, bivariate map and denominators
&lt;/h2&gt;

&lt;p&gt;The IGVUST ranks neighbourhood units by socio-territorial vulnerability. I keep its four official quartiles because they are that source's analytical contract — and note the direction, because it is easy to reverse: &lt;strong&gt;&lt;code&gt;q1&lt;/code&gt; is the most vulnerable quartile&lt;/strong&gt; and &lt;code&gt;q4&lt;/code&gt; the least. On the appraisal axis, each UV is compared against the median of &lt;strong&gt;its own region&lt;/strong&gt;, not against a fixed national cut: it lands below or above that regional median. The result is a 4×2 matrix —four IGVUST rows by two appraisal columns— which avoids two problems at once: a finer partition would make the bivariate map hard to read and, in regions with few UVs such as Arica y Parinacota, would suggest a precision the aggregated data do not deliver; and a fixed national cut would ignore that typical appraisal levels differ sharply between regions. A quartile indicates relative order, not distance.&lt;/p&gt;

&lt;p&gt;In the bivariate map the main reading uses &lt;strong&gt;appraisal per m² of parcel area&lt;/strong&gt;. It is the clearest signal for drawing attention where high unit appraisal coincides with high territorial vulnerability. In the palette those cells are darker, with a transparent layer so as not to cover the basemap or turn the map into a chromatic alarm. The counterintuitive should draw attention; the obvious does not need a megaphone. The indicator remains descriptive and national; restricting to UVs with $$p_\text{urban} &amp;gt; 50$$ attenuates the per-m² association sharply.&lt;/p&gt;

&lt;p&gt;In the viewer, this crosswalk lives in an analytical map: UVs only, a graphic selector of Chile, hover with the unit's data, and a 4×2 legend. A single region-and-municipality search box governs the whole tour, and the selection you fix there carries into the tables and the territorial ranking where it applies. Inspecting parcel geometry is deliberately kept apart, as a documented annex: mixing it with the bivariate classification invites reading a cadastral boundary as if it were a result.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Per household, the eye-catching cell is a lead
&lt;/h3&gt;

&lt;p&gt;If you divide by RSH households, the most vulnerable quartile (&lt;code&gt;q1&lt;/code&gt; IGVUST) contains 530 UVs in the highest quartile of appraisal per household. That number can look like a social contradiction. It is not one yet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table 2&lt;/strong&gt; — Neighbourhood units by national vulnerability quartile and appraisal per household&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;IGVUST quartile&lt;/th&gt;
&lt;th&gt;Appraisal/household q1&lt;/th&gt;
&lt;th&gt;q2&lt;/th&gt;
&lt;th&gt;q3&lt;/th&gt;
&lt;th&gt;q4&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;q1 · most vulnerable&lt;/td&gt;
&lt;td&gt;399&lt;/td&gt;
&lt;td&gt;344&lt;/td&gt;
&lt;td&gt;446&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;530&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;q2&lt;/td&gt;
&lt;td&gt;564&lt;/td&gt;
&lt;td&gt;451&lt;/td&gt;
&lt;td&gt;385&lt;/td&gt;
&lt;td&gt;321&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;q3&lt;/td&gt;
&lt;td&gt;467&lt;/td&gt;
&lt;td&gt;509&lt;/td&gt;
&lt;td&gt;429&lt;/td&gt;
&lt;td&gt;316&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;q4 · least vulnerable&lt;/td&gt;
&lt;td&gt;293&lt;/td&gt;
&lt;td&gt;419&lt;/td&gt;
&lt;td&gt;463&lt;/td&gt;
&lt;td&gt;546&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The right word is &lt;strong&gt;lead&lt;/strong&gt;, not conclusion. The ratio combines territorial appraisal with RSH households. If the denominator is small or the territory is large, the quotient rises without that demonstrating any greater wealth among the people who live there.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The eye-catching quadrant has fewer households and far more area
&lt;/h3&gt;

&lt;p&gt;The vulnerable UVs with the highest appraisal per household have a median of &lt;strong&gt;120.5 households&lt;/strong&gt; and &lt;strong&gt;78.2 km²&lt;/strong&gt;. The vulnerable ones with the lowest appraisal per household have &lt;strong&gt;265 households&lt;/strong&gt; and &lt;strong&gt;3.36 km²&lt;/strong&gt;. The ratio grows because the denominator shrinks while the territory expands. The spreadsheet does its job; interpretation has to do its own.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table 3&lt;/strong&gt; — Mechanism inside the most vulnerable quartile (&lt;code&gt;q1&lt;/code&gt; IGVUST)&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Appraisal/household&lt;/th&gt;
&lt;th&gt;UVs&lt;/th&gt;
&lt;th&gt;Median households&lt;/th&gt;
&lt;th&gt;Median area (km²)&lt;/th&gt;
&lt;th&gt;Median appraisal/household (million CLP)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;q1&lt;/td&gt;
&lt;td&gt;399&lt;/td&gt;
&lt;td&gt;265.0&lt;/td&gt;
&lt;td&gt;3.36&lt;/td&gt;
&lt;td&gt;10.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;q2&lt;/td&gt;
&lt;td&gt;344&lt;/td&gt;
&lt;td&gt;368.5&lt;/td&gt;
&lt;td&gt;15.21&lt;/td&gt;
&lt;td&gt;26.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;q3&lt;/td&gt;
&lt;td&gt;446&lt;/td&gt;
&lt;td&gt;274.5&lt;/td&gt;
&lt;td&gt;45.59&lt;/td&gt;
&lt;td&gt;57.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;q4&lt;/td&gt;
&lt;td&gt;530&lt;/td&gt;
&lt;td&gt;120.5&lt;/td&gt;
&lt;td&gt;78.22&lt;/td&gt;
&lt;td&gt;185.6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The table does not invalidate the crosswalk. It delimits what it measures: fiscal appraisal allocated to a UV divided by RSH households, not the wellbeing of its residents.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The association changes with normalisation
&lt;/h3&gt;

&lt;p&gt;The next table uses two summaries between -1 and +1. &lt;strong&gt;Pearson&lt;/strong&gt; summarises a linear relationship over the logarithm of positive appraisal; &lt;strong&gt;Spearman&lt;/strong&gt; summarises whether the ordering of UVs changes monotonically. Near zero there is little linear or monotonic relationship. Neither column estimates a causal effect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table 4&lt;/strong&gt; — Sensitivity of the association to the denominator&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Appraisal measure&lt;/th&gt;
&lt;th&gt;Pearson&lt;/th&gt;
&lt;th&gt;Spearman&lt;/th&gt;
&lt;th&gt;UVs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Total allocated&lt;/td&gt;
&lt;td&gt;-0.371&lt;/td&gt;
&lt;td&gt;-0.382&lt;/td&gt;
&lt;td&gt;6,857&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Per RSH household&lt;/td&gt;
&lt;td&gt;-0.061&lt;/td&gt;
&lt;td&gt;-0.047&lt;/td&gt;
&lt;td&gt;6,849&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Per RSH person&lt;/td&gt;
&lt;td&gt;-0.079&lt;/td&gt;
&lt;td&gt;-0.072&lt;/td&gt;
&lt;td&gt;6,849&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Per m² of allocated parcel area&lt;/td&gt;
&lt;td&gt;-0.582&lt;/td&gt;
&lt;td&gt;-0.575&lt;/td&gt;
&lt;td&gt;6,851&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Per m², predominantly urban UVs only&lt;/td&gt;
&lt;td&gt;+0.079&lt;/td&gt;
&lt;td&gt;+0.081&lt;/td&gt;
&lt;td&gt;3,221&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Per household and per person, the association is close to nil. Per square metre, the national pattern looks strong. Restricting to predominantly urban UVs it flips to roughly +0.08. The prudent reading is that the national per-m² result contains a great deal of urban–rural contrast; not that a neighbourhood-level mechanism has been identified.&lt;/p&gt;

&lt;p&gt;The violins in Figure 2 show the full distribution. Their width indicates where more UVs concentrate; the internal lines show median and interquartile range. They are smoothed densities, not literal silhouettes of the territory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://3cucharadas.cl/assets/images/avaluo-vulnerabilidad-unidad-vecinal/violin-denominadores-en.webp" title="Figure 2 — Sensitivity to denominator and universe" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  &amp;lt;source type="image/webp"&amp;gt;
  &amp;lt;img src="https://3cucharadas.cl/assets/images/avaluo-vulnerabilidad-unidad-vecinal/violin-denominadores-en.webp" alt="Three violin plots compare the distributions of appraisal per household, appraisal per square metre nationally, and appraisal per square metre in predominantly urban neighbourhood units, across the four national vulnerability quartiles."&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;/p&gt;&lt;br&gt;&lt;strong&gt;Figure 2&lt;/strong&gt; — The same appraisal produces different distributions once the denominator and the universe change. The violins show estimated densities; their shape depends on the bandwidth and proves neither natural groupings nor causality. The internal lines summarise median and interquartile range. Source: own elaboration, data cut 19/07/2026. Click to enlarge.
  &lt;p&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. What is robust is the sensitivity
&lt;/h3&gt;

&lt;p&gt;Among the &lt;strong&gt;6,843 UVs&lt;/strong&gt; with an available quartile both per household and per m², only &lt;strong&gt;1,362 (19.9%)&lt;/strong&gt; stay in the same quartile and &lt;strong&gt;3,132 (45.8%)&lt;/strong&gt; move two or more. The parcels did not change. The question did.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spoonful 3: scale and concentration
&lt;/h2&gt;

&lt;p&gt;Among the &lt;strong&gt;6,857 UVs with positive allocated appraisal&lt;/strong&gt;, the Gini coefficient is &lt;strong&gt;0.7265&lt;/strong&gt;. It describes a highly concentrated territorial tax base; it is not a Gini of personal income or wealth.&lt;/p&gt;

&lt;p&gt;This matters beyond the statistical exercise. On a base this concentrated, any exemption also redistributes: it decides not only who stops paying, but which municipality, fund or tax will have to rebuild the amount. The benefit fits on one line; the full incidence usually needs another spreadsheet.&lt;/p&gt;

&lt;p&gt;The Gini summarises concentration but does not separate how much occurs within and between groups. That is what the &lt;strong&gt;Theil&lt;/strong&gt; index is for, since it does decompose. With $$x_i$$ as the appraisal allocated to UV $$i$$, $$\mu$$ its mean and $$w_g$$ the share of group $$g$$ in total appraisal:&lt;/p&gt;


&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;T&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mop op-limits"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;span class="mrel mtight"&gt;=&lt;/span&gt;&lt;span class="mord mtight"&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="mop op-symbol large-op"&gt;∑&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;μ&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mop"&gt;ln&lt;/span&gt;&lt;span class="mclose"&gt;!&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="minner"&gt;&lt;span class="mopen delimcenter"&gt;&lt;span class="delimsizing size3"&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;μ&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose delimcenter"&gt;&lt;span class="delimsizing size3"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord munder"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span class="svg-align"&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="stretchy"&gt;&lt;span class="brace-left"&gt;&lt;/span&gt;&lt;span class="brace-center"&gt;&lt;/span&gt;&lt;span class="brace-right"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mop op-limits"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;g&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="mop op-symbol large-op"&gt;∑&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;w&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;g&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;T&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;g&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;within&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord munder"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord text mtight"&gt;&lt;span class="mord mtight"&gt;between&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord munder"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span class="svg-align"&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="stretchy"&gt;&lt;span class="brace-left"&gt;&lt;/span&gt;&lt;span class="brace-center"&gt;&lt;/span&gt;&lt;span class="brace-right"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mop op-symbol large-op"&gt;∑&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;w&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;g&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mop"&gt;ln&lt;/span&gt;&lt;span class="mclose"&gt;!&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="minner"&gt;&lt;span class="mopen delimcenter"&gt;&lt;span class="delimsizing size3"&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;μ&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;μ&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;g&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose delimcenter"&gt;&lt;span class="delimsizing size3"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;Grouping by region, $$T = 1.2042$$ and 81.0% stays within regions. Regrouping by municipality, the between-group part reaches 56.9%. The municipality does not «reveal» segregation by magic: when you refine a partition, part of the inequality shifts from the within component to the between component by construction (&lt;a href="https://doi.org/10.2307/1913511" rel="noopener noreferrer"&gt;Shorrocks, 1984&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;That is the modifiable areal unit problem, or MAUP (&lt;a href="https://doi.org/10.1068/a231025" rel="noopener noreferrer"&gt;Fotheringham &amp;amp; Wong, 1991&lt;/a&gt;). The same parcels can produce different statistics depending on whether they are grouped into regions, municipalities or UVs. Scale does not decorate the result: it defines it. Which is why it matters what the UV actually is before using it as a container: if the unit does not correspond to a territory with its own meaning, the statistic it produces inherits that mismatch.&lt;/p&gt;

&lt;p&gt;Second brake: IGVUST quartiles order UVs, not people. The most vulnerable quartile gathers 25% of the units, but &lt;strong&gt;2,032,893 of 15,978,644 RSH people (12.7%)&lt;/strong&gt;. A choropleth can over-represent dispersed rural territory even when its colours are perfectly computed.&lt;/p&gt;

&lt;p&gt;Third brake: the &lt;em&gt;assessment ratio&lt;/em&gt; literature shows that fiscal valuation can depart systematically from market values, and that the pattern depends on institutional context. &lt;a href="https://doi.org/10.1111/1540-6229.12126" rel="noopener noreferrer"&gt;Hodge, McMillen, Sands and Skidmore (2017)&lt;/a&gt; study that problem in another market; they do not prove the SII carries the same bias. What they do show is that using fiscal appraisal as a synonym for market price requires substantial adjustment factors — although, even so, territorial gradients and trends should not diverge that much.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing: gaps pay property tax too
&lt;/h2&gt;

&lt;p&gt;The map does not dictate a tax policy. It does make it impossible to pretend the base is homogeneous, that every municipality starts from the same place, or that an exemption has no geography. The crosswalk identifies five gaps that must be closed before its colours can be used as strong evidence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gap 1: the denominator's universe.&lt;/strong&gt; The RSH covers around 85% of the country's population, but not with equal intensity across municipalities. Any indicator per RSH household or person has to declare that coverage and, where applicable, contrast it against census data. That contrast is exactly what the viewer opens with: before any crosswalk, it measures what fraction of the &lt;strong&gt;private dwellings counted in the 2024 Census&lt;/strong&gt; is reached by the SII residential register, municipality by municipality. It is the same warning as this post, in its simplest form — declare the universe before dividing by it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gap 2: geometry and surface.&lt;/strong&gt; Apportionment requires polygons. Declared surface does not replace missing geometry without an additional spatial rule. Municipalities with many parcels lacking polygons need a warning before anyone interprets their colour.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gap 3: leakage outside the UV mesh.&lt;/strong&gt; The UV does not cover the entire cadastral territory. If a municipality concentrates a lot of appraisal outside any UV, the crosswalk describes a subset, not its municipal total. The UV mesh needs local work to fit reality better.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gap 4: urban–rural.&lt;/strong&gt; Appraisal per m² has a strong national signal, but it changes when the universe is restricted to urban UVs. The indicator needs a universe filter, not just an intense palette.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gap 5: scale.&lt;/strong&gt; Region, municipality and UV are not enlarged versions of the same question. Changing the territorial unit changes the statistic.&lt;/p&gt;

&lt;p&gt;These gaps are not an automatic defence of every appraisal, rate or charge in force either. Relieving someone with little liquidity and a highly valued home is a real problem. Comparative evidence offers more precise instruments: income-based reductions, caps on the tax burden, deferrals until sale or inheritance, and transparent reassessments. There is no need to ask the cadastre to pretend the asset stopped existing.&lt;/p&gt;

&lt;p&gt;You can, of course, remove the obligation from one column and restore it with transfers from another. The relief stays visible; its financing moves. Before celebrating that the bill disappeared, it is worth checking the second spreadsheet and asking which territories end up paying for the courtesy.&lt;/p&gt;

&lt;p&gt;The viewer lets you explore these gaps from the general to the particular: the whole country, the municipality you choose, its neighbourhood units in the bivariate map, and a laboratory that zooms back out to test denominators, with an appraisals view, distributions, sensitivity checks and a municipal reading. An exemption does not stop being distributive because it is called a benefit: it also has a numerator, a denominator and a geography.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://3cucharadas.cl/catastro_sii_brecha/" rel="noopener noreferrer"&gt;Explore the map and the denominator laboratory&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The viewer's interface is in Spanish, but its charts, tables and downloadable Parquet files are readable without it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Property tax and international comparison&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Servicio de Impuestos Internos (SII). &lt;em&gt;Impuesto Territorial&lt;/em&gt;. Tax base, exemptions, reassessment and cadastral mapping. &lt;a href="https://www.sii.cl/destacados/impuesto_territorial/index.html" rel="noopener noreferrer"&gt;sii.cl&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Servicio de Impuestos Internos (SII). &lt;em&gt;¿Para qué sirve el pago del impuesto territorial?&lt;/em&gt; Municipal destination and the Fondo Común Municipal. &lt;a href="https://www.sii.cl/destacados/reavaluo/contribucionesreavaluo.html" rel="noopener noreferrer"&gt;sii.cl&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;OECD (2022). &lt;em&gt;Housing Taxation in OECD Countries&lt;/em&gt;. OECD Tax Policy Studies, No. 29. &lt;a href="https://doi.org/10.1787/03dfe007-en" rel="noopener noreferrer"&gt;10.1787/03dfe007-en&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Grote, M. &amp;amp; Wen, J.-F. (2024). &lt;em&gt;How to Design and Implement Property Tax Reforms&lt;/em&gt;. IMF How-To Notes, 2024/006. &lt;a href="https://doi.org/10.5089/9798400288753.061" rel="noopener noreferrer"&gt;10.5089/9798400288753.061&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;World Bank (2020). &lt;em&gt;Property Tax Diagnostic Manual&lt;/em&gt;. &lt;a href="https://openknowledge.worldbank.org/handle/10986/34793" rel="noopener noreferrer"&gt;Institutional repository&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Senate of the Republic of Chile (15 July 2026). &lt;em&gt;Reconstrucción nacional: ¿cuáles fueron los aspectos centrales aprobados?&lt;/em&gt; Legislative background to Bill No. 18.216-05, accessed 26 July 2026. &lt;a href="https://www.senado.cl/comunicaciones/noticias/reconstruccion-nacional-cuales-fueron-los-aspectos-centrales-aprobados" rel="noopener noreferrer"&gt;senado.cl&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Cooperativa (22 July 2026). &lt;em&gt;Megarreforma: comisión mixta aprobó compensación a municipios por exención de contribuciones&lt;/em&gt;. Formula approved by the joint committee and status of the bill, accessed 26 July 2026. &lt;a href="https://www.cooperativa.cl/noticias/pais/politica/agenda-legislativa/megarreforma-comision-mixta-aprobo-compensacion-a-municipios-por/2026-07-22/171256.html" rel="noopener noreferrer"&gt;cooperativa.cl&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Method and theory&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sabatini, F., Cáceres, G. &amp;amp; Cerda, J. (2001). &lt;em&gt;Segregación residencial en las principales ciudades chilenas: Tendencias de las tres últimas décadas y posibles cursos de acción&lt;/em&gt;. EURE, 27(82), 21-42. &lt;a href="https://doi.org/10.4067/S0250-71612001008200002" rel="noopener noreferrer"&gt;10.4067/S0250-71612001008200002&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;López-Morales, E., Sanhueza, C., Espinoza, S. &amp;amp; Órdenes, F. (2019). &lt;em&gt;Verticalización inmobiliaria y valorización de renta de suelo por infraestructura pública: un análisis econométrico del Gran Santiago, 2008-2011&lt;/em&gt;. EURE, 45(136), 113-134. &lt;a href="https://doi.org/10.4067/S0250-71612019000300113" rel="noopener noreferrer"&gt;10.4067/S0250-71612019000300113&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Rosen, S. (1974). &lt;em&gt;Hedonic Prices and Implicit Markets&lt;/em&gt;. Journal of Political Economy, 82(1), 34-55. &lt;a href="https://doi.org/10.1086/260169" rel="noopener noreferrer"&gt;10.1086/260169&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Goodchild, M. F., Anselin, L. &amp;amp; Deichmann, U. (1993). &lt;em&gt;A Framework for the Areal Interpolation of Socioeconomic Data&lt;/em&gt;. Environment and Planning A, 25(3), 383-397. &lt;a href="https://doi.org/10.1068/a250383" rel="noopener noreferrer"&gt;10.1068/a250383&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Robinson, W. S. (1950). &lt;em&gt;Ecological Correlations and the Behavior of Individuals&lt;/em&gt;. American Sociological Review, 15(3), 351-357. &lt;a href="https://doi.org/10.2307/2087176" rel="noopener noreferrer"&gt;10.2307/2087176&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Shorrocks, A. F. (1984). &lt;em&gt;Inequality Decomposition by Population Subgroups&lt;/em&gt;. Econometrica, 52(6), 1369-1385. &lt;a href="https://doi.org/10.2307/1913511" rel="noopener noreferrer"&gt;10.2307/1913511&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Fotheringham, A. S. &amp;amp; Wong, D. W. S. (1991). &lt;em&gt;The Modifiable Areal Unit Problem in Multivariate Statistical Analysis&lt;/em&gt;. Environment and Planning A, 23(7), 1025-1044. &lt;a href="https://doi.org/10.1068/a231025" rel="noopener noreferrer"&gt;10.1068/a231025&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Hodge, T. R., McMillen, D. P., Sands, G. &amp;amp; Skidmore, M. (2017). &lt;em&gt;Assessment Inequity in a Declining Housing Market: The Case of Detroit&lt;/em&gt;. Real Estate Economics, 45(2), 237-258. &lt;a href="https://doi.org/10.1111/1540-6229.12126" rel="noopener noreferrer"&gt;10.1111/1540-6229.12126&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cadastre</category>
      <category>propertytax</category>
      <category>territorialvulnerability</category>
      <category>inequality</category>
    </item>
    <item>
      <title>Multi-agent work in three spoonfuls III: a memory that leaves traces</title>
      <dc:creator>Cristián Labra</dc:creator>
      <pubDate>Mon, 07 Sep 2026 00:38:16 +0000</pubDate>
      <link>https://dev.to/tatanlabra/multi-agent-work-in-three-spoonfuls-iii-a-memory-that-leaves-traces-3hfn</link>
      <guid>https://dev.to/tatanlabra/multi-agent-work-in-three-spoonfuls-iii-a-memory-that-leaves-traces-3hfn</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Status of the demo.&lt;/strong&gt; The viewer was regenerated on August 29, 2026 from a sanitized public projection (with the non-public bits stripped out 😀): the artifact contains no mail bodies, attachments, addresses, absolute paths, tokens, credentials, or microdata.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Preamble: remembering is not enough
&lt;/h2&gt;

&lt;p&gt;In the &lt;a href="https://3cucharadas.cl/ia/productividad/desarrollo/multiagente-penta-agent-memoria/" rel="noopener noreferrer"&gt;second part&lt;/a&gt; I went after a bounded problem: getting &lt;code&gt;penta-agent&lt;/code&gt;'s memory to retrieve evidence and to recognize when it had found none. The question in this third part is more practical, and it comes out of the system having been in use for a while: &lt;strong&gt;what happens to a memory as it grows and turns blurry, or even contradictory?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An index can pile up fragments without any trouble, and there are plenty of tools that already do that well. A more useful memory, in my judgment, has to carry provenance, currency, permissions, contradictions, and deletion criteria. It also has to tell finding a source apart from using it correctly. Recent literature insists on separating RAG — retrieval-augmented generation — context management, and agent memory, because they do different jobs and call for different evaluations &lt;sup id="fnref1"&gt;1&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;What follows has three movements: what changed since part II; which experiments survived a more serious evaluation; and how to show a memory without passing it off as a mind.&lt;/p&gt;




&lt;h2&gt;
  
  
  Spoonful 1: from retrieving fragments to governing evidence
&lt;/h2&gt;

&lt;p&gt;In part II the problem was &lt;strong&gt;retrieving well&lt;/strong&gt;: finding the relevant context and recognizing when there was not enough evidence. A useful memory does not only retrieve information; it also has to know &lt;strong&gt;where it came from, whether it still holds, where it can be used, and what is allowed to be done with it&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;RAG mostly solves retrieval. The memory layer adds rules for keeping, updating, relating, or discarding evidence. None of those functions amounts, on its own, to identity. To describe provenance I use concepts compatible with PROV-O — entities, activities, and agents — while currency, sensitivity, and permissions need rules of their own &lt;sup id="fnref2"&gt;2&lt;/sup&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What changed since part II
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Open item from II&lt;/th&gt;
&lt;th&gt;What exists now&lt;/th&gt;
&lt;th&gt;What is still open&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Compare dense retrieval, lexical retrieval, and reranking.&lt;/td&gt;
&lt;td&gt;A frozen cut of 319 contexts and 40 queries evaluated at &lt;code&gt;k=5&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;The best configuration gets 38 complete cases and 2 partials; the strict gate stays shut.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check that an answer uses its sources and knows how to abstain.&lt;/td&gt;
&lt;td&gt;The &lt;code&gt;context-answer-v1&lt;/code&gt; contract, with 12 positive and 6 negative cases, all sanitized.&lt;/td&gt;
&lt;td&gt;The end-to-end evaluation with a local model and the reserved human review are unfinished.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add currency without erasing history.&lt;/td&gt;
&lt;td&gt;The proof of concept admits &lt;code&gt;supersedes&lt;/code&gt; only toward later versions and rejects competing successors.&lt;/td&gt;
&lt;td&gt;There is still no temporal reasoner wired into live memory.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show growth without turning the graph into truth.&lt;/td&gt;
&lt;td&gt;A 3D viewer, regenerable from sanitized JSON and accompanied by a text alternative.&lt;/td&gt;
&lt;td&gt;The viewer neither validates relations nor represents a mind.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The difference may look small, but it changes the question. Knowing &lt;strong&gt;which fragment sits close to a query&lt;/strong&gt; is no longer enough; what matters as well is &lt;strong&gt;what kind of evidence it is and under what conditions it can be reused&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What memory actually exists
&lt;/h3&gt;

&lt;p&gt;The PoC brings several layers together, but does not treat them as equivalent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;penta-agent&lt;/code&gt; experience:&lt;/strong&gt; 16,955 indexed points and 1,432 strategies make it possible to explore actions, corrections, and failures. Semantic closeness proves neither causality nor truth.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Curated context:&lt;/strong&gt; 319 canonical records make it possible to retrieve &lt;em&gt;handoffs&lt;/em&gt; and decisions with their provenance. The index helps to find them, but does not replace the original record.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Research process:&lt;/strong&gt; a projection with 796 derived artifacts and 533 links makes it possible to inspect the work trajectory without publishing documents or microdata.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Personal mail:&lt;/strong&gt; the local inventory holds 12,072 unique messages between 2011 and 2026. It serves to test reading and deduplication within an authorized scope; it is not a public corpus and not a personality model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Experimental personal memory:&lt;/strong&gt; a separate prototype tests retrieval through &lt;code&gt;mbox&lt;/code&gt;, SQLite/FTS, scopes, and purge. There is still no authorization to generate answers out of sensitive mail.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This separation heads off a frequent temptation: confusing &lt;strong&gt;having technical access to a source&lt;/strong&gt; with &lt;strong&gt;being authorized to turn it into reusable memory&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  A useful failure
&lt;/h3&gt;

&lt;p&gt;The pipeline itself supplied an example of why provenance matters. The historical exporter expected a plain vector, while the current index stored the named &lt;code&gt;dense&lt;/code&gt; vector. The first regeneration produced a degraded graph, with no semantic edges. A run against the real index caught the incompatibility and, once the exporter was fixed, 3,658 edges came back.&lt;/p&gt;

&lt;p&gt;The lesson matters more than the number: &lt;strong&gt;an auditable visualization has to be able to show when its data chain degraded, why it happened, and how it was put back together&lt;/strong&gt;. It is a real use case.&lt;/p&gt;

&lt;h3&gt;
  
  
  From command to task
&lt;/h3&gt;

&lt;p&gt;The 1,432 recorded strategies are still the basic unit of this memory. Many of them started life as commands, so I added a deterministic classification to make them more readable: coordinating agents (399), researching and analyzing (396), tracing evidence (385), executing or automating (118), versioning changes (76), verifying (28), operating infrastructure (18), and communicating results (12).&lt;/p&gt;

&lt;p&gt;A strategy could belong to several categories, but today it lands in the first matching rule. &lt;strong&gt;It is a lens for walking through the memory, not a description of the person behind it.&lt;/strong&gt; There is plenty of room to improve this with other visualizations; if you know of one, I would be grateful for the tip.&lt;/p&gt;




&lt;h2&gt;
  
  
  Spoonful 2: governing sources before blending them
&lt;/h2&gt;

&lt;p&gt;The architecture starts from a simple separation. The &lt;strong&gt;canonical record&lt;/strong&gt; (the &lt;em&gt;ledger&lt;/em&gt;) and the source files hold the evidence; Qdrant keeps indexes that can be destroyed and regenerated; the viewer publishes a sanitized projection; and the private adapters stay outside Git. The index is there to find things. It has no authority to turn a vector match, on its own, into a durable fact.&lt;/p&gt;

&lt;h3&gt;
  
  
  An architecture of permissions, not a blender
&lt;/h3&gt;

&lt;p&gt;Mail, thesis material (from the master's degree), and operational memory may all be technically readable by the same system, but that does not make them interchangeable. In this PoC, only the previously curated context may feed cross-agent retrieval and the public projection. Mail stays in a private, reversible, revocable circuit; the thesis contributes authorized process metadata and nothing else.&lt;/p&gt;

&lt;p&gt;The distinction matters: &lt;strong&gt;having technical access to a piece of data does not settle whether reusing it for any purpose is legitimate&lt;/strong&gt;. Contextual integrity theory poses exactly this problem: privacy depends not only on the data, but also on the context, the actors, and the norms that govern its circulation &lt;sup id="fnref3"&gt;3&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://3cucharadas.cl/assets/images/multiagente-penta-agent-memoria-gobernada/governed-sources-devto-1600x1169.png" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/p&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%2F3cucharadas.cl%2Fassets%2Fimages%2Fmultiagente-penta-agent-memoria-gobernada%2Fgoverned-sources-devto-1600x1169.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%2F3cucharadas.cl%2Fassets%2Fimages%2Fmultiagente-penta-agent-memoria-gobernada%2Fgoverned-sources-devto-1600x1169.png" alt="Three governed sources: operational memory allows retrieval with provenance; personal mail allows only private, reversible review; master's thesis material and documents allow citations and verification status. With a boundary that blocks publishing mail bodies, attachments, addresses, absolute paths, microdata, and inferences about identity." width="799" height="584"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;Figure 1. The current integration is an architecture of permissions: each source determines which outputs are permitted.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;The thesis is a good example. What enters the public system is a &lt;a href="https://3cucharadas.cl/assets/data/memoria_gobernada/thesis-research-index-snapshot.json" rel="noopener noreferrer"&gt;snapshot of the research index&lt;/a&gt;, not the corpus. The August 29 cut holds 796 derived textual artifacts — 316 audits, 244 reports, 133 contracts, 71 pseudocode files, and 32 inventory, replication, or documentation pieces — plus 533 links and 7 open leads. It leaves out the source documents, DTA, Parquet, PDF, images, paths, full texts, and microdata. The search is deliberately lexical: FTS5 and TF-IDF help to find artifacts, but they make no claim to understand the research.&lt;/p&gt;

&lt;p&gt;There is a detail more interesting than the figures. The published snapshot is still faithful to the cut it declares: its fingerprint matches the manifest of that moment. The corpus, however, kept growing and reached 817 documents, twenty-one more. Since the index was not regenerated, the currency gate is red: &lt;code&gt;check-research-index&lt;/code&gt; returns &lt;code&gt;2&lt;/code&gt;, because &lt;code&gt;document_count&lt;/code&gt; and &lt;code&gt;source_fingerprint&lt;/code&gt; no longer match the current state.&lt;/p&gt;

&lt;p&gt;That does not invalidate the snapshot; it &lt;strong&gt;dates&lt;/strong&gt; it. Its correct status is "valid for the August 29 cut, but not current with respect to today's corpus". A gate that had stayed green after the corpus grew would have been worse than a failed gate: it would have certified a currency that does not exist. This is the formal record of the memory's growth and potential divergence.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reuse before reinventing
&lt;/h3&gt;

&lt;p&gt;None of this requires building every memory component from scratch. In fact, a good sign of maturity would be being able to &lt;strong&gt;delete my own code&lt;/strong&gt; when an existing tool solves the same problem better without degrading privacy, provenance, or reversibility.&lt;/p&gt;

&lt;p&gt;To recover decisions scattered across coding sessions, &lt;code&gt;deja-vu&lt;/code&gt; already indexes the local histories of many agents and exposes them through a CLI and MCP. It is a natural candidate to contrast against my own &lt;em&gt;handoffs&lt;/em&gt; and events before writing yet another equivalent layer.&lt;/p&gt;

&lt;p&gt;For more structured persistent memory there are more ambitious approaches. MemGPT introduced the idea of managing distinct memory tiers as a way of extending an agent's effective context &lt;sup id="fnref4"&gt;4&lt;/sup&gt;; Letta's current line carries that principle into editable memory blocks and external memory. Mem0 automates the extraction, consolidation, and retrieval of information from conversations &lt;sup id="fnref5"&gt;5&lt;/sup&gt;, while Hindsight distinguishes the operations of &lt;strong&gt;retaining, recalling, and reflecting&lt;/strong&gt;, taking in semantic, lexical, temporal, and graph-based retrieval &lt;sup id="fnref6"&gt;6&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;currency&lt;/strong&gt; problem deserves an experiment of its own. Graphiti, the open component described in Zep's architecture, explicitly models relations that change over time and keeps historical information instead of silently replacing it &lt;sup id="fnref7"&gt;7&lt;/sup&gt;. That comes far closer to the problem I am trying to solve with &lt;code&gt;supersedes&lt;/code&gt; than piling on more temporal rules of my own.&lt;/p&gt;

&lt;p&gt;For document collections, Microsoft GraphRAG offers another useful idea: build a graph derived from the corpus and summarize communities to answer global questions that a conventional RAG handles worse &lt;sup id="fnref8"&gt;8&lt;/sup&gt;. I would not use it yet as a replacement for the thesis index. First it would have to show, over a bounded scientific subset, that the added complexity and cost improve something the lexical baseline cannot resolve.&lt;/p&gt;

&lt;p&gt;Visualization, finally, is a practically solved problem: &lt;code&gt;3d-force-graph&lt;/code&gt; already provides a three-dimensional layout built on Three.js and force algorithms. The relevant part of this PoC is not writing another graphics engine, but controlling &lt;strong&gt;which graph the browser receives&lt;/strong&gt; and remembering that visual distance proves neither conceptual proximity, nor causality, nor human affinity.&lt;/p&gt;

&lt;p&gt;The common rule is simple: &lt;strong&gt;test components, do not collect them&lt;/strong&gt;. That a library can remember more does not mean it should receive more data.&lt;/p&gt;

&lt;p&gt;There is also a conflict of interest worth making explicit. Mem0, Hindsight, Zep/Graphiti, and Letta are described in large part by their own teams. Their papers and repositories are appropriate sources for learning the architectures they propose, but their comparative results are not independent validation of superiority. I use them here as designs that deserve a contrast, not as winners of a competition — and my own stack is not superior either. If anything, mine is rather experimental, built for my own purposes.&lt;/p&gt;

&lt;h3&gt;
  
  
  A retrieval experiment that could fail
&lt;/h3&gt;

&lt;p&gt;I tested &lt;code&gt;Qwen3-Embedding-0.6B&lt;/code&gt; on an isolated copy of 319 contexts, without touching production. The evaluation used 40 queries, eight of them designed to check that the system also knew how &lt;strong&gt;not to retrieve evidence when it should not&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The best configuration got &lt;strong&gt;38 complete cases, 2 partials, and no outright miss&lt;/strong&gt; within the top five positions. From there I tried several possible improvements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dropping the BM25 lexical boost held the best result;&lt;/li&gt;
&lt;li&gt;adding a generic instruction to the &lt;em&gt;embedding&lt;/em&gt; made both retrieval and abstention worse;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Qwen3-Reranker-0.6B&lt;/code&gt; ordered the results somewhat better, but pushed response time up too far;&lt;/li&gt;
&lt;li&gt;diversifying by repository and fusing rankings did not resolve the two partial cases either.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The experiment left a conclusion more useful than finding a new model: &lt;strong&gt;the two open cases do not seem to need more power, but better metadata&lt;/strong&gt;. Both of them require retrieving documents from different families within only five results.&lt;/p&gt;

&lt;p&gt;So the next test will be to add explicit information about &lt;strong&gt;lineage and document intent&lt;/strong&gt;, without using the correct answers to build those labels. Arbitrarily raising &lt;code&gt;k&lt;/code&gt; or piling on more models would only have moved the problem elsewhere.&lt;/p&gt;

&lt;p&gt;Until those two cases are resolved reproducibly, the collection stays in &lt;code&gt;staging&lt;/code&gt;. In this system, &lt;strong&gt;remembering that an improvement has not been demonstrated yet is also part of the memory&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spoonful 3: a map that does not pass itself off as a mind
&lt;/h2&gt;

&lt;p&gt;Once what may enter the memory is sorted out, another question remains: &lt;strong&gt;how do you show it without mistaking a representation for reality?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The viewer answers with a precaution: it queries neither Qdrant (the database where the vectors are indexed) nor any private source when someone opens the site — it &lt;strong&gt;receives a sanitized JSON&lt;/strong&gt; (a data file from which the information that must not be published has been removed), generated from a specific cut of the memory.&lt;/p&gt;

&lt;p&gt;That way the graph can be rebuilt and audited without touching the original sources again. The &lt;a href="https://3cucharadas.cl/assets/data/rag_knowledge_graph/public-graph.json" rel="noopener noreferrer"&gt;public JSON export&lt;/a&gt; even lets you inspect directly what information the browser receives.&lt;/p&gt;

&lt;h3&gt;
  
  
  Seeing the network without attributing more to it than it says
&lt;/h3&gt;

&lt;p&gt;The August 29 cut holds &lt;strong&gt;1,432 strategies, 3,949 relations, and eight task families&lt;/strong&gt;, derived from 16,955 indexed points.&lt;/p&gt;

&lt;p&gt;The image below &lt;strong&gt;is not that network&lt;/strong&gt;: it is an illustration, and the percentage is the joke. The real network is explored in the full viewer, which lets you select tasks, projects, and errors.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://3cucharadas.cl/assets/visualizations/penta-rag-knowledge-graph/index.html" rel="noopener noreferrer"&gt;&lt;br&gt;
    &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2F3cucharadas.cl%2Fassets%2Fimages%2Fmultiagente-penta-agent-memoria-gobernada%2Fcerebro-20-por-ciento-1600x1000.webp" alt="Illustration: half the silhouette of a brain, made of points and edges, coming apart toward the right into loose points, with a loading bar stopped at 20%." width="800" height="500"&gt;&lt;br&gt;
  &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;br&gt;
    Figure 2. An illustration, not a projection of the data: the digital-clone metaphor loaded to 20%. The network hints at itself where there is density and comes apart where there is none, which is exactly what an index knows how to do. The real projection, with its nodes and edges, is in the viewer.&lt;br&gt;
  &lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://3cucharadas.cl/assets/visualizations/penta-rag-knowledge-graph/index.html" rel="noopener noreferrer"&gt;&lt;strong&gt;Open the interactive viewer in a new tab →&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is worth reading with some precautions. A &lt;strong&gt;node&lt;/strong&gt; (a point in the graph) stands for a recorded strategy. An &lt;strong&gt;edge&lt;/strong&gt; (a line between two nodes) stands for some derived relation: semantic similarity, a shared tool, or a correction, for instance. A &lt;strong&gt;community&lt;/strong&gt; (a group of nodes that appears especially well connected) helps you get your bearings inside the network.&lt;/p&gt;

&lt;p&gt;None of those relations proves, on its own, causality, truth, or a trait of the person who used the system. Mail and private documents do not appear as content nodes either.&lt;/p&gt;

&lt;p&gt;The numbers belong, moreover, to a dated cut. Updating the memory does not silently change the graph: the projection has to be regenerated, sanitized again, and its differences reviewed before it is published anew.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The viewer is not the memory. It is barely a map built from one of its projections.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Retrieving a source is not enough either
&lt;/h3&gt;

&lt;p&gt;Finding the right document is only half the problem. The system also has to &lt;strong&gt;use the right source, respect its scope, and abstain when the evidence falls short&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To test that layer I built &lt;code&gt;context-answer-v1&lt;/code&gt;, a small set of 18 cases: 12 with sufficient evidence and 6 in which the right answer was to claim no more than what was available. The deterministic test passed all 18 cases and confirmed that the gate works; it still does not prove that a generative model answers well on its own.&lt;/p&gt;

&lt;p&gt;The tests with external models also left a practical rule. Only the sources authorized to leave the machine should be evaluated outside it. The exportable subset passed 11/11 cases; another seven sources stayed &lt;code&gt;local_only&lt;/code&gt; and require an evaluation inside the local environment.&lt;/p&gt;

&lt;p&gt;The conclusion is simple: &lt;strong&gt;retrieving is not enough; every answer has to carry provenance, permissions, and the ability to abstain&lt;/strong&gt;. Until that local evaluation and its human review are complete, I can talk about a governed and attributable memory, but not yet about a reliable generative memory.&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing: a useful memory before a digital clone
&lt;/h2&gt;

&lt;p&gt;The "digital clone" is still some way off; today it is a distant horizon. What is missing, at a minimum: a temporal memory able to correct itself, revocable preferences and limits, evidence of authorship, behavior evaluated in new situations, and "human" or higher control over what is kept, shared, or deleted. MemGPT&lt;sup id="fnref4"&gt;4&lt;/sup&gt; and the generative agents of Park et al.&lt;sup id="fnref9"&gt;9&lt;/sup&gt; offer influential architectures for handling context, memories, reflection, and planning, but they do not turn those functions into identity (Packer et al. 2023; Park et al. 2023).&lt;/p&gt;

&lt;p&gt;In this third part, &lt;code&gt;penta-agent&lt;/code&gt; is a local prototype of governed memory: it keeps experience, records corrections, exposes a bounded public projection, and lets its own tests fail without disturbing production.&lt;/p&gt;

&lt;p&gt;The next step forward is not adding more nodes. It is closing the local answer evaluation and comparing alternatives — Graphiti, GraphRAG, or &lt;code&gt;deja-vu&lt;/code&gt; — under the same cut, the same permissions, and observable output criteria. I am very much open to your comments and usage experience; I think one practical point of comparison would serve me better than going on testing the many tools that appear day after day.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Further reading
&lt;/h3&gt;

&lt;p&gt;Works that guided this piece and that this version no longer cites in the body.&lt;br&gt;
They are kept because losing track of what was read is worse than declaring it.&lt;/p&gt;

&lt;p&gt;Cormack, Gordon V., Charles L. A. Clarke, and Stefan Büttcher. 2009. "Reciprocal Rank Fusion Outperforms Condorcet and Individual Rank Learning Methods". In &lt;em&gt;Proceedings of the 32nd Annual International ACM SIGIR Conference on Research and Development in Information Retrieval&lt;/em&gt;, 758–59. New York: Association for Computing Machinery. &lt;a href="https://doi.org/10.1145/1571941.1572114" rel="noopener noreferrer"&gt;https://doi.org/10.1145/1571941.1572114&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Wu, Di, Hongwei Wang, Wenhao Yu, Yuwei Zhang, Kai-Wei Chang, and Dong Yu. 2024. "LongMemEval: Benchmarking Chat Assistants on Long-Term Interactive Memory". arXiv, October 14, 2024; revised March 4, 2025. Accepted at ICLR 2025. &lt;a href="https://arxiv.org/abs/2410.10813" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2410.10813&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Wu, Di, Zixiang Ji, Asmi Kawatkar, Bryan Kwan, Jia-Chen Gu, Nanyun Peng, and Kai-Wei Chang. 2026. "LongMemEval-V2: Evaluating Long-Term Agent Memory Toward Experienced Colleagues". Work in progress, arXiv, May 12, 2026. &lt;a href="https://arxiv.org/abs/2605.06304" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2605.06304&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Zhang, Yanzhao, Mingxin Li, Dingkun Long, Xin Zhang, Huan Lin, Baosong Yang, Pengjun Xie, et al. 2025. "Qwen3 Embedding: Advancing Text Embedding and Reranking Through Foundation Models". arXiv, June 5, 2025. &lt;a href="https://arxiv.org/abs/2506.05176" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2506.05176&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Software and technical documentation
&lt;/h3&gt;

&lt;p&gt;GetZep. n.d. &lt;em&gt;Graphiti: A Framework for Building Temporal Knowledge Graphs&lt;/em&gt;. GitHub repository. Accessed August 31, 2026. &lt;a href="https://github.com/getzep/graphiti" rel="noopener noreferrer"&gt;https://github.com/getzep/graphiti&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Hugging Face. n.d. &lt;em&gt;Sentence Transformers&lt;/em&gt;. Technical documentation. Accessed August 31, 2026. &lt;a href="https://www.sbert.net/" rel="noopener noreferrer"&gt;https://www.sbert.net/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Letta AI. n.d. &lt;em&gt;Letta Code&lt;/em&gt;. GitHub repository. Accessed August 31, 2026. &lt;a href="https://github.com/letta-ai/letta-code" rel="noopener noreferrer"&gt;https://github.com/letta-ai/letta-code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Mem0 AI. n.d. &lt;em&gt;Mem0: Universal Memory Layer for AI Agents&lt;/em&gt;. GitHub repository. Accessed August 31, 2026. &lt;a href="https://github.com/mem0ai/mem0" rel="noopener noreferrer"&gt;https://github.com/mem0ai/mem0&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Microsoft. n.d. &lt;em&gt;GraphRAG&lt;/em&gt;. GitHub repository. Accessed August 31, 2026. &lt;a href="https://github.com/microsoft/graphrag" rel="noopener noreferrer"&gt;https://github.com/microsoft/graphrag&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Qwen. n.d. &lt;em&gt;Qwen3-Embedding-0.6B&lt;/em&gt;. Model card, Hugging Face. Accessed August 31, 2026. &lt;a href="https://huggingface.co/Qwen/Qwen3-Embedding-0.6B" rel="noopener noreferrer"&gt;https://huggingface.co/Qwen/Qwen3-Embedding-0.6B&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Qwen. n.d. &lt;em&gt;Qwen3-Reranker-0.6B&lt;/em&gt;. Model card, Hugging Face. Accessed August 31, 2026. &lt;a href="https://huggingface.co/Qwen/Qwen3-Reranker-0.6B" rel="noopener noreferrer"&gt;https://huggingface.co/Qwen/Qwen3-Reranker-0.6B&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Shulcz, Vladislav. n.d. &lt;em&gt;deja-vu&lt;/em&gt;. GitHub repository. Accessed August 31, 2026. &lt;a href="https://github.com/vshulcz/deja-vu" rel="noopener noreferrer"&gt;https://github.com/vshulcz/deja-vu&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Vasturiano. n.d. &lt;em&gt;3d-force-graph&lt;/em&gt;. GitHub repository. Accessed August 31, 2026. &lt;a href="https://github.com/vasturiano/3d-force-graph" rel="noopener noreferrer"&gt;https://github.com/vasturiano/3d-force-graph&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Vectorize. n.d. &lt;em&gt;Hindsight: Agent Memory That Learns&lt;/em&gt;. GitHub repository. Accessed August 31, 2026. &lt;a href="https://github.com/vectorize-io/hindsight" rel="noopener noreferrer"&gt;https://github.com/vectorize-io/hindsight&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Conflicts of interest in the sources.&lt;/strong&gt; The model cards, the repositories, and several of the systems papers are written by their own developers or by organizations that offer related services; LongMemEval-V2, moreover, is still declared work in progress. They are used here to document architecture, declared functions, and maintenance status, not to accept claims of superiority. &lt;code&gt;penta-agent&lt;/code&gt;'s own metrics are likewise evidence produced by the project itself, and they require reproducible artifacts and independent review before they can support general comparisons.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Hu, Yuyang, Shichun Liu, Yanwei Yue, Guibin Zhang, Boyang Liu, Fangyi Zhu, Jiahang Lin, et al. 2025. "Memory in the Age of AI Agents". arXiv, December 15, 2025. &lt;a href="https://arxiv.org/abs/2512.13564" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2512.13564&lt;/a&gt;.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;Lebo, Timothy, Satya Sahoo, and Deborah McGuinness, eds. 2013. &lt;em&gt;PROV-O: The PROV Ontology&lt;/em&gt;. W3C Recommendation, April 30, 2013. &lt;a href="https://www.w3.org/TR/prov-o/" rel="noopener noreferrer"&gt;https://www.w3.org/TR/prov-o/&lt;/a&gt;.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;Nissenbaum, Helen. 2004. "Privacy as Contextual Integrity". &lt;em&gt;Washington Law Review&lt;/em&gt; 79, no. 1: 119–58. &lt;a href="https://digitalcommons.law.uw.edu/wlr/vol79/iss1/10/" rel="noopener noreferrer"&gt;https://digitalcommons.law.uw.edu/wlr/vol79/iss1/10/&lt;/a&gt;.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;Packer, Charles, Sarah Wooders, Kevin Lin, Vivian Fang, Shishir G. Patil, Ion Stoica, and Joseph E. Gonzalez. 2023. "MemGPT: Towards LLMs as Operating Systems". arXiv, October 12, 2023; revised February 12, 2024. &lt;a href="https://doi.org/10.48550/arXiv.2310.08560" rel="noopener noreferrer"&gt;https://doi.org/10.48550/arXiv.2310.08560&lt;/a&gt;.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;Chhikara, Prateek, Dev Khant, Saket Aryan, Taranjeet Singh, and Deshraj Yadav. 2025. "Mem0: Building Production-Ready AI Agents with Scalable Long-Term Memory". arXiv, April 28, 2025. &lt;a href="https://arxiv.org/abs/2504.19413" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2504.19413&lt;/a&gt;.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn6"&gt;
&lt;p&gt;Latimer, Chris, Nicoló Boschi, Andrew Neeser, Chris Bartholomew, Gaurav Srivastava, Xuan Wang, and Naren Ramakrishnan. 2025. "Hindsight Is 20/20: Building Agent Memory That Retains, Recalls, and Reflects". arXiv, December 16, 2025. &lt;a href="https://arxiv.org/abs/2512.12818" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2512.12818&lt;/a&gt;.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn7"&gt;
&lt;p&gt;Rasmussen, Preston, Pavlo Paliychuk, Travis Beauvais, Jack Ryan, and Daniel Chalef. 2025. "Zep: A Temporal Knowledge Graph Architecture for Agent Memory". arXiv, January 20, 2025. &lt;a href="https://arxiv.org/abs/2501.13956" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2501.13956&lt;/a&gt;.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn8"&gt;
&lt;p&gt;Edge, Darren, Ha Trinh, Newman Cheng, Joshua Bradley, Alex Chao, Apurva Mody, Steven Truitt, Dasha Metropolitansky, Robert Osazuwa Ness, and Jonathan Larson. 2024. "From Local to Global: A GraphRAG Approach to Query-Focused Summarization". arXiv, April 24, 2024; revised February 19, 2025. &lt;a href="https://arxiv.org/abs/2404.16130" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2404.16130&lt;/a&gt;.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn9"&gt;
&lt;p&gt;Park, Joon Sung, Joseph C. O'Brien, Carrie J. Cai, Meredith Ringel Morris, Percy Liang, and Michael S. Bernstein. 2023. "Generative Agents: Interactive Simulacra of Human Behavior". In &lt;em&gt;Proceedings of the 36th Annual ACM Symposium on User Interface Software and Technology&lt;/em&gt;, article 2, 1–22. New York: Association for Computing Machinery. &lt;a href="https://doi.org/10.1145/3586183.3606763" rel="noopener noreferrer"&gt;https://doi.org/10.1145/3586183.3606763&lt;/a&gt;.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>multiagent</category>
      <category>rag</category>
      <category>agentmemory</category>
      <category>provenance</category>
    </item>
    <item>
      <title>Multi-agent work in three spoonfuls II: auditable memory</title>
      <dc:creator>Cristián Labra</dc:creator>
      <pubDate>Mon, 07 Sep 2026 00:37:14 +0000</pubDate>
      <link>https://dev.to/tatanlabra/multi-agent-work-in-three-spoonfuls-ii-auditable-memory-127a</link>
      <guid>https://dev.to/tatanlabra/multi-agent-work-in-three-spoonfuls-ii-auditable-memory-127a</guid>
      <description>&lt;p&gt;In the &lt;a href="https://3cucharadas.cl/ia/productividad/desarrollo/multiagente-penta-agent-modelos/" rel="noopener noreferrer"&gt;first post&lt;/a&gt; I described how I organized my local multi-agent setup, &lt;code&gt;penta-agent&lt;/code&gt;: Codex executes, Claude reviews, other agents enter in bounded ways, and the human keeps closure authority. I also argued that operational memory should not depend on a single conversation or be confused with the vector index.&lt;/p&gt;

&lt;p&gt;By the time I closed that first post, I already had continuity mechanisms: handoffs, routing rules, append-only logs, experiential memory in JSONL/YAML, a rebuildable vector collection, and the &lt;code&gt;recall-context&lt;/code&gt; skill. My problem was not absolute amnesia. It was that I still could not prove what the system retrieved, when it confused a coincidence with evidence, and when it should admit that it did not have an answer.&lt;/p&gt;

&lt;p&gt;This second part, then, is not about inventing memory from scratch. It is about turning still-fragile operational continuity into a &lt;strong&gt;traceable, testable, and rebuildable&lt;/strong&gt; mechanism.&lt;/p&gt;

&lt;p&gt;The idea of an external working memory is not new. It echoes Bush's old ambition of augmenting recall through a personal archive and the extended-mind intuition that notes and tools can become part of cognition.&lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;sup id="fnref2"&gt;2&lt;/sup&gt; My claim here is narrower: local traces are useful only if I can retrieve them with provenance and audit how they were used.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spoonful 1: the problem was not storing, but retrieving well
&lt;/h2&gt;

&lt;p&gt;Storing information is easy. The difficult part, I think, is retrieving the right piece when there are successive decisions, similar names, contradictory versions, and explanations spread across several files.&lt;/p&gt;

&lt;p&gt;To organize that "memory" in my own setup, I separated its operational layers:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table 1&lt;/strong&gt; - System memory layers&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Question it answers&lt;/th&gt;
&lt;th&gt;Effective implementation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Canonical record&lt;/td&gt;
&lt;td&gt;What happened, and what was decided?&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;memory/experience-events.jsonl&lt;/code&gt;, &lt;code&gt;memory/experience-lessons.yaml&lt;/code&gt;, &lt;code&gt;memory/interaction-metrics.jsonl&lt;/code&gt;, and curated &lt;code&gt;context&lt;/code&gt; events.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retrieval index&lt;/td&gt;
&lt;td&gt;Where is the relevant evidence?&lt;/td&gt;
&lt;td&gt;Qdrant with &lt;code&gt;penta_context_v2&lt;/code&gt; for curated context and &lt;code&gt;penta_experience_v1&lt;/code&gt; for operational memory; both are derived.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Episodic history&lt;/td&gt;
&lt;td&gt;How did a session unfold?&lt;/td&gt;
&lt;td&gt;Selected Markdown handoffs, compact context events, and operational logs; full sessions are not indexed raw.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Working context&lt;/td&gt;
&lt;td&gt;What context do I need to provide now?&lt;/td&gt;
&lt;td&gt;MCP &lt;code&gt;experience-memory&lt;/code&gt; (&lt;code&gt;experience_status&lt;/code&gt;, &lt;code&gt;recall_experience&lt;/code&gt;) consumed by the &lt;code&gt;recall-context&lt;/code&gt; skill.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The distinction matters. A retrieved result is not yet a verified decision. It is a clue that must preserve provenance, date, and a link to its source. In the current contract, local JSONL/YAML files are the source of truth; Qdrant is rebuilt from them. If the index contradicts a current file, the file wins, and the right fix is to reindex or correct ingestion, not to publish vector proximity as if it were final evidence.&lt;/p&gt;

&lt;p&gt;The actual flow ended up like this:&lt;/p&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%2F3cucharadas.cl%2Fassets%2Fimages%2Fmultiagente-penta-agent-memoria%2Fflujo-memoria-penta-agent-en-devto-1200x2172.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%2F3cucharadas.cl%2Fassets%2Fimages%2Fmultiagente-penta-agent-memoria%2Fflujo-memoria-penta-agent-en-devto-1200x2172.png" alt="Flow diagram of multi-agent memory: curated traces, selective ingestion, canonical source, embeddings and BM25 representation, derived Qdrant index, hybrid retrieval, MCP, and use by Codex, Claude, or Gemini with human closure." width="800" height="1448"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;&lt;strong&gt;Figure 1&lt;/strong&gt; - Operational flow of auditable memory in my multi-agent setup. Note: local JSONL/YAML files are the source of truth; Qdrant and BM25 are derived retrieval indexes, not final evidence.
  &lt;p&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  RAG, without turning it into magic
&lt;/h3&gt;

&lt;p&gt;A language model stores part of what it learned during training in its parameters. That memory does not necessarily include what I decided yesterday in a local repository. Retrieval-Augmented Generation, or RAG, adds an external memory: before answering, a retriever searches for relevant passages and gives them to the generative model as context.&lt;sup id="fnref3"&gt;3&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;In my implementation, &lt;code&gt;experience-memory&lt;/code&gt; acts as the retriever: it queries Qdrant, fuses signals with the canonical records, and returns candidates through MCP. Codex, Claude, or Gemini then use that context according to the workflow. That is why I prefer to describe this as &lt;strong&gt;retrieval-augmented agents&lt;/strong&gt; rather than as an "autonomous RAG": storing and ordering fragments is not the same as reasoning over them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Searching by meaning and by words
&lt;/h3&gt;

&lt;p&gt;Semantic search transforms each fragment into a vector. The query is represented with the same model and then compared by orientation through cosine similarity:&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mop"&gt;&lt;span class="mord mathrm"&gt;sim&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;q&lt;/span&gt;&lt;span class="mpunct"&gt;,&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;d&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen"&gt;∥&lt;/span&gt;&lt;span class="mord mathbf"&gt;q&lt;/span&gt;&lt;span class="mclose"&gt;∥&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mopen"&gt;∥&lt;/span&gt;&lt;span class="mord mathbf"&gt;d&lt;/span&gt;&lt;span class="mclose"&gt;∥&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathbf"&gt;q&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;⋅&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord mathbf"&gt;d&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;If the query and the document point in similar directions, their similarity increases. This makes it possible to find paraphrases even when they do not share the exact same words.&lt;/p&gt;

&lt;p&gt;Lexical search covers the complementary problem: identifiers, acronyms, paths, proper names, and exact terms. BM25 does more than count matches; it weighs term rarity and frequency, saturates repetitions, and partially adjusts for document length.&lt;sup id="fnref4"&gt;4&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Today I combine five signals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dense search in Qdrant with &lt;code&gt;bge-m3&lt;/code&gt; embeddings;&lt;/li&gt;
&lt;li&gt;lexical fallback over canonical &lt;code&gt;context&lt;/code&gt; events;&lt;/li&gt;
&lt;li&gt;BM25 sparse vector with &lt;code&gt;Qdrant/bm25&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;event recency;&lt;/li&gt;
&lt;li&gt;lifecycle type: &lt;code&gt;outcome&lt;/code&gt;, &lt;code&gt;review&lt;/code&gt;, &lt;code&gt;decision&lt;/code&gt;, or &lt;code&gt;handoff&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I still do not use RRF in the evaluated version. The active fusion is a weighted formula in &lt;code&gt;hybrid_context_hits&lt;/code&gt;: when there is a semantic signal, it assigns 0.60 to dense similarity, 0.28 to lexical evidence, 0.07 to recency, and 0.05 to lifecycle type. When there is no semantic signal, the fallback mostly weights lexical evidence. BM25 contributes as an additional reordering signal, but it is not enough by itself to rescue a result.&lt;/p&gt;

&lt;p&gt;One less flashy virtue is still missing: teaching the system to stay quiet. A query with no answer in the corpus should not receive a fragment only because it looks nearby. In this version I measure &lt;strong&gt;retriever rejection&lt;/strong&gt;: a candidate qualifies if it passes any of these criteria in the gate run, &lt;code&gt;lexical_score &amp;gt;= 0.34&lt;/code&gt;, &lt;code&gt;semantic_score &amp;gt;= 0.44&lt;/code&gt;, or &lt;code&gt;hybrid_score &amp;gt;= 0.61&lt;/code&gt;. I still do not have an automated abstention metric for the final generator. If a model receives context and invents anyway, this evaluation will not catch it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spoonful 2: from a useful index to evaluated retrieval
&lt;/h2&gt;

&lt;p&gt;What I left working uses these pieces, verified against the local repo and active services:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table 2&lt;/strong&gt; - Active components&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Verified configuration&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Canonical source&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;memory/*.jsonl&lt;/code&gt; and &lt;code&gt;memory/*.yaml&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Rebuildable local records; at review time, &lt;code&gt;experience-events.jsonl&lt;/code&gt; had 1,829 lines and &lt;code&gt;interaction-metrics.jsonl&lt;/code&gt; had 904.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dense vectorization&lt;/td&gt;
&lt;td&gt;Ollama + &lt;code&gt;bge-m3:latest&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;bert&lt;/code&gt; model, 566.70M parameters, F16, 8192 context, 1024-dimensional embeddings.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vector database&lt;/td&gt;
&lt;td&gt;Local Qdrant&lt;/td&gt;
&lt;td&gt;Collections &lt;code&gt;penta_context_v2&lt;/code&gt; and &lt;code&gt;penta_experience_v1&lt;/code&gt;; the MCP reported &lt;code&gt;green&lt;/code&gt; status, 14 canonical contexts, and 1,769 operational points.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lexical retrieval&lt;/td&gt;
&lt;td&gt;Canonical JSONL + FastEmbed BM25&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;context_fallback_hits&lt;/code&gt; computes lexical matching; &lt;code&gt;SparseTextEmbedding("Qdrant/bm25")&lt;/code&gt; feeds the &lt;code&gt;bm25&lt;/code&gt; sparse vector.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fusion&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hybrid_context_hits&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fuses dense, lexical, BM25, recency, and lifecycle signals; deduplicates by &lt;code&gt;handoff_id&lt;/code&gt; or &lt;code&gt;source_document&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reranking&lt;/td&gt;
&lt;td&gt;Not active in the published run&lt;/td&gt;
&lt;td&gt;An opt-in reranker exists with &lt;code&gt;PENTA_AGENT_RERANK=1&lt;/code&gt;, but the validated gate does not use it.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent interface&lt;/td&gt;
&lt;td&gt;MCP &lt;code&gt;experience-memory&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;experience_status&lt;/code&gt; and &lt;code&gt;recall_experience&lt;/code&gt;; the &lt;code&gt;recall-context&lt;/code&gt; skill uses it as its primary path.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test automation&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;evaluate_context_retrieval.py&lt;/code&gt; and &lt;code&gt;rag_regression_gate.py&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;The &lt;code&gt;penta-agent-rag-gate.timer&lt;/code&gt; is enabled weekly; the gate records history in &lt;code&gt;memory/retrieval-metrics.jsonl&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I run this on a local Arch Linux/KDE workstation. Ollama and Qdrant are queried over loopback; the timers are &lt;code&gt;systemd --user&lt;/code&gt; timers. That detail is not cosmetic: if I isolate the canonical fallback or the gate environment is missing, retrieval changes materially. That is why the gate service explicitly sets &lt;code&gt;PENTA_AGENT_EMBED_BACKEND=ollama&lt;/code&gt;, &lt;code&gt;PENTA_AGENT_OLLAMA_MODEL=bge-m3&lt;/code&gt;, and &lt;code&gt;PENTA_AGENT_CONTEXT_SEM_THRESHOLD=0.44&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The embedding model I actually used
&lt;/h3&gt;

&lt;p&gt;BGE-M3, in its original implementation, supports dense, sparse, and multivector representations, works with more than one hundred languages, and accepts long sequences.&lt;sup id="fnref5"&gt;5&lt;/sup&gt; But those model capabilities do not prove that all of them are exposed in my stack.&lt;/p&gt;

&lt;p&gt;I use &lt;code&gt;bge-m3:latest&lt;/code&gt;, served by Ollama through &lt;code&gt;/api/embed&lt;/code&gt;.&lt;sup id="fnref6"&gt;6&lt;/sup&gt; In practice I use &lt;strong&gt;only the dense representation&lt;/strong&gt; returned by Ollama: 1024-dimensional vectors in the local installation. The sparse branch of the system does not come from BGE-M3; it comes from BM25 with FastEmbed and Qdrant.&lt;sup id="fnref7"&gt;7&lt;/sup&gt;&lt;sup id="fnref8"&gt;8&lt;/sup&gt; Pooling, tokenization, and normalization are encapsulated in the Ollama runtime, not in custom repo code.&lt;/p&gt;

&lt;p&gt;I also did not chunk the whole workspace indiscriminately. The &lt;code&gt;context&lt;/code&gt; ingestion works with selected documents: sanitized Markdown handoffs and compact events. When importing handoffs, the extractor takes sections such as goal/context, decision, and outcome; compacts them; preserves &lt;code&gt;source_document&lt;/code&gt;, &lt;code&gt;source_hash&lt;/code&gt;, &lt;code&gt;source_type&lt;/code&gt;, &lt;code&gt;handoff_id&lt;/code&gt;, &lt;code&gt;workspace_entry&lt;/code&gt;, &lt;code&gt;repo_scope&lt;/code&gt;, branch, and date; and limits text to short fields. I do not dump full conversations or raw private files into the index.&lt;/p&gt;

&lt;h3&gt;
  
  
  A question set that makes the system uncomfortable
&lt;/h3&gt;

&lt;p&gt;I evaluated with a local golden set of 40 questions: 32 positives and 8 negatives. Positive cases point to expected documents through suffixes relative to the workspace; the evaluator does not open or emit the source content. Difficulty labels are not mutually exclusive: there are 10 exact-keyword cases, 9 paraphrase cases, 7 cross-language cases, 3 colloquial cases, and 4 multi-document cases. Negatives include cooking, sports, finance, science, and unrelated technology queries.&lt;/p&gt;

&lt;p&gt;I annotated expected answers manually against selected handoffs and relative paths. That makes the set useful for development and regression, not as an independent benchmark. If I use the same file to calibrate thresholds and then celebrate the result, I cannot treat it as independent evidence.&lt;/p&gt;

&lt;h3&gt;
  
  
  Results: what happened in the validated run
&lt;/h3&gt;

&lt;p&gt;The traceable run was:&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;env &lt;/span&gt;&lt;span class="nv"&gt;PENTA_AGENT_EMBED_BACKEND&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ollama &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;PENTA_AGENT_OLLAMA_MODEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bge-m3 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;PENTA_AGENT_CONTEXT_SEM_THRESHOLD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.44 &lt;span class="se"&gt;\&lt;/span&gt;
    /opt/entornos/mamba312/bin/python scripts/evaluate_context_retrieval.py &lt;span class="nt"&gt;--strict&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I ran it in strict mode, and it exited with code 1 because there was one partial case. The gate itself passed because it evaluates aggregate thresholds: minimum recall 0.95, minimum MRR 0.85, minimum precision 0.40, and perfect negative abstention.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table 3&lt;/strong&gt; - Validated local result&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Configuration&lt;/th&gt;
&lt;th&gt;Recall@5&lt;/th&gt;
&lt;th&gt;MRR&lt;/th&gt;
&lt;th&gt;Precision@5&lt;/th&gt;
&lt;th&gt;Negative rejection&lt;/th&gt;
&lt;th&gt;Latency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;bge-m3&lt;/code&gt; + dense Qdrant + lexical JSONL + sparse BM25&lt;/td&gt;
&lt;td&gt;0.9896&lt;/td&gt;
&lt;td&gt;0.9479&lt;/td&gt;
&lt;td&gt;0.4448&lt;/td&gt;
&lt;td&gt;8/8&lt;/td&gt;
&lt;td&gt;p50 401 ms; max 4,250 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;My cautious reading is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The active retrieval setup finds almost all annotated evidence in this small, curated corpus.&lt;/li&gt;
&lt;li&gt;The negative cases are the strongest result: all eight were rejected.&lt;/li&gt;
&lt;li&gt;Precision@5 is low by design: I prefer bringing extra context over missing the expected document.&lt;/li&gt;
&lt;li&gt;The remaining error is multi-document: &lt;code&gt;catastro_multi_sii&lt;/code&gt; recovered part of the expected evidence, not all of it.&lt;/li&gt;
&lt;li&gt;The metric does not prove that the final answer is faithful; it only proves that the retriever brought or rejected candidates.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I also found a practical signal: when I isolate the canonical fallback without Qdrant, aggregate recall drops to 0.8177. That is not the result of the chosen stack; it is the comparison I needed to understand how much semantic retrieval contributes and why the evaluation configuration must be explicit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fusion and reranking
&lt;/h3&gt;

&lt;p&gt;I do not compare raw scores from different models as if they lived on the same scale. First I gather candidates from dense Qdrant, lexical JSONL, and sparse BM25. Then I compute a hybrid score with admission thresholds. After that I deduplicate by lifecycle: if an &lt;code&gt;outcome&lt;/code&gt; and a pending handoff share the same &lt;code&gt;handoff_id&lt;/code&gt;, the completed result wins.&lt;/p&gt;

&lt;h3&gt;
  
  
  A regression gate
&lt;/h3&gt;

&lt;p&gt;I can run the evaluation by hand, and I also installed it as a weekly user timer: &lt;code&gt;penta-agent-rag-gate.timer&lt;/code&gt;, with &lt;code&gt;OnCalendar=weekly&lt;/code&gt;, &lt;code&gt;Persistent=true&lt;/code&gt;, and a randomized delay of up to 30 minutes. The service runs &lt;code&gt;scripts/rag_regression_gate.py&lt;/code&gt; with Ollama, &lt;code&gt;bge-m3&lt;/code&gt;, and semantic threshold 0.44. It records aggregate metrics in &lt;code&gt;memory/retrieval-metrics.jsonl&lt;/code&gt; and fails if they fall below the configured thresholds.&lt;/p&gt;

&lt;p&gt;I call it a &lt;strong&gt;regression gate&lt;/strong&gt; because it checks known cases. Detecting real drift requires observing changes in queries, documents, versions, or score distributions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spoonful 3: what this memory still does not solve
&lt;/h2&gt;

&lt;p&gt;The memory now retrieves better, but it still has clear limits: it does not handle document validity over time very well, it does not detect contradictions in a general way, it does not turn full sessions into permanent memory, and it still fails on some answers that require combining several sources.&lt;/p&gt;

&lt;p&gt;I also do not yet evaluate the faithfulness of the final answer. Recall, MRR, and precision indicate whether the right evidence appeared, not whether Claude or Codex interpreted it correctly, respected its validity, or knew when to abstain. Benchmarks such as BEIR also show that no retriever dominates uniformly across all domains.&lt;sup id="fnref9"&gt;9&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Next, I want to compare each layer of the system reproducibly: dense search, lexical JSONL, BM25, hybrid retrieval, RRF, and opt-in reranking. After that I want to add document-validity handling and answer evaluation with citations, so I can measure not only whether I retrieve evidence, but whether the answer uses it faithfully.&lt;/p&gt;

&lt;p&gt;If you are building something similar - a multi-agent setup, a local RAG, a second brain, or working memory to avoid repeating context - I would like to read what worked for you, where it failed, and how you decided when to abstain. That comparison between honest logs is worth more than a perfect architecture.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Vannevar Bush, "As We May Think", &lt;em&gt;The Atlantic&lt;/em&gt;, July 1945. &lt;a href="https://www.theatlantic.com/magazine/archive/1945/07/as-we-may-think/303881/" rel="noopener noreferrer"&gt;https://www.theatlantic.com/magazine/archive/1945/07/as-we-may-think/303881/&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;Andy Clark and David Chalmers, "The Extended Mind", &lt;em&gt;Analysis&lt;/em&gt; 58, no. 1 (1998): 7-19. &lt;a href="https://doi.org/10.1111/1467-8284.00096" rel="noopener noreferrer"&gt;https://doi.org/10.1111/1467-8284.00096&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;Patrick Lewis et al., "Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks", &lt;em&gt;Advances in Neural Information Processing Systems&lt;/em&gt; 33 (2020). &lt;a href="https://arxiv.org/abs/2005.11401" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2005.11401&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;Stephen Robertson and Hugo Zaragoza, "The Probabilistic Relevance Framework: BM25 and Beyond", &lt;em&gt;Foundations and Trends in Information Retrieval&lt;/em&gt; 3, no. 4 (2009): 333-389. &lt;a href="https://doi.org/10.1561/1500000019" rel="noopener noreferrer"&gt;https://doi.org/10.1561/1500000019&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;Jianlv Chen et al., "BGE M3-Embedding: Multi-Lingual, Multi-Functionality, Multi-Granularity Text Embeddings Through Self-Knowledge Distillation", 2024. &lt;a href="https://arxiv.org/abs/2402.03216" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2402.03216&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn6"&gt;
&lt;p&gt;Ollama, "Generate embeddings", &lt;code&gt;/api/embed&lt;/code&gt; documentation, accessed July 23, 2026. &lt;a href="https://docs.ollama.com/api/embed" rel="noopener noreferrer"&gt;https://docs.ollama.com/api/embed&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn7"&gt;
&lt;p&gt;Qdrant, "Vectors", documentation on named vectors and sparse vectors, accessed July 23, 2026. &lt;a href="https://qdrant.tech/documentation/manage-data/vectors/" rel="noopener noreferrer"&gt;https://qdrant.tech/documentation/manage-data/vectors/&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn8"&gt;
&lt;p&gt;Qdrant, "Full-Text Search: BM25", documentation on BM25 and sparse vectors, accessed July 23, 2026. &lt;a href="https://qdrant.tech/documentation/search/text-search/full-text-search/" rel="noopener noreferrer"&gt;https://qdrant.tech/documentation/search/text-search/full-text-search/&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn9"&gt;
&lt;p&gt;Nandan Thakur et al., "BEIR: A Heterogeneous Benchmark for Zero-shot Evaluation of Information Retrieval Models", 2021. &lt;a href="https://arxiv.org/abs/2104.08663" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2104.08663&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>multiagent</category>
      <category>rag</category>
      <category>embeddings</category>
      <category>agentmemory</category>
    </item>
    <item>
      <title>CASEN 2024 in 3 spoonfuls: without a fine-grained territorial reading, social policy moves blind</title>
      <dc:creator>Cristián Labra</dc:creator>
      <pubDate>Sun, 06 Sep 2026 14:05:49 +0000</pubDate>
      <link>https://dev.to/tatanlabra/casen-2024-in-3-spoonfuls-without-a-fine-grained-territorial-reading-social-policy-moves-blind-517n</link>
      <guid>https://dev.to/tatanlabra/casen-2024-in-3-spoonfuls-without-a-fine-grained-territorial-reading-social-policy-moves-blind-517n</guid>
      <description>&lt;p&gt;In Chile, &lt;strong&gt;La Araucanía records 13.0% extreme poverty; Magallanes, 4.2%&lt;/strong&gt;. That is 8.8 percentage points of difference — and if a public agency at the central or local level (regional governments / municipalities) designs its intervention using only the national average (6.9%), or disregarding regional differences, it could get the allocation of resources or the distribution of its components wrong.&lt;/p&gt;

&lt;p&gt;This post documents a reproducible analysis of CASEN 2024 in &lt;a href="https://julialang.org" rel="noopener noreferrer"&gt;Julia&lt;/a&gt;, with cross-validation of official public figures against BIDAT and good traceability of the flow in the repo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three findings to start with — statistically robust:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Extreme poverty&lt;/strong&gt;: La Araucanía 13.0% 95% CI [11.8%, 14.1%] versus Magallanes 4.2% [3.1%, 5.2%] — a gap of &lt;strong&gt;8.8 pp with non-overlapping CIs&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FONASA coverage&lt;/strong&gt;: La Araucanía 91.1% [90.2%, 92.0%] versus Metropolitana 75.9% [75.0%, 76.7%] — a gap of &lt;strong&gt;15.2 pp with non-overlapping CIs&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Completed higher education&lt;/strong&gt;: Metropolitana 34.9% [34.0%, 35.7%] versus Maule 20.4% [19.2%, 21.6%] — a gap of &lt;strong&gt;14.5 pp with non-overlapping CIs&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The CIs are 95% and were computed by &lt;strong&gt;Taylor linearization&lt;/strong&gt; (&lt;a href="https://en.wikipedia.org/wiki/Linearization" rel="noopener noreferrer"&gt;wiki&lt;/a&gt;) for CASEN's complex sampling design (stratified two-stage, &lt;code&gt;expr&lt;/code&gt; weights). That the CIs do not overlap implies these regional differences are statistically significant at the conventional level.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tolerance to contrast, or checking against official data
&lt;/h2&gt;

&lt;p&gt;All results were checked against the official tables available in &lt;a href="https://bidat.gob.cl/url/695ff7271b10e" rel="noopener noreferrer"&gt;BIDAT&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table 1&lt;/strong&gt; — Official validation results (BIDAT, CASEN 2024)&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&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;Validation status (national + regional, % + expanded)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;PASS&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maximum tolerance in percentage points&lt;/td&gt;
&lt;td&gt;1×10⁻⁵ pp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maximum tolerance in expanded values&lt;/td&gt;
&lt;td&gt;1×10⁻⁶&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Observed difference (pp)&lt;/td&gt;
&lt;td&gt;2.98×10⁻⁶ pp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Observed difference (expanded)&lt;/td&gt;
&lt;td&gt;0.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;National rows compared&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Regional rows compared&lt;/td&gt;
&lt;td&gt;256&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The maximum observed difference is below 3 millionths of a percentage point and within the preset tolerance. &lt;strong&gt;The results match the official tables within the predefined tolerances.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Spoonful 1: sampling design and expansion without shortcuts
&lt;/h2&gt;

&lt;p&gt;CASEN 2024 has a &lt;strong&gt;probabilistic stratified two-stage&lt;/strong&gt; design (&lt;a href="https://bidat.gob.cl/url/69b71c77197db" rel="noopener noreferrer"&gt;BIDAT methodological note&lt;/a&gt;). The &lt;code&gt;expr&lt;/code&gt; factor applies to national and regional estimates. The complementary &lt;code&gt;expc&lt;/code&gt; factor supports descriptive commune calculations, but &lt;strong&gt;does not make estimates representative of each commune&lt;/strong&gt;, according to the &lt;a href="https://observatorio.ministeriodesarrollosocial.gob.cl/storage/docs/casen/2024/Nota_uso_bases_de_datos_Casen_2024.pdf" rel="noopener noreferrer"&gt;official CASEN 2024 data-use note&lt;/a&gt;. This analysis uses &lt;code&gt;expr&lt;/code&gt; and reports estimates by region with 95% CIs computed by &lt;strong&gt;Taylor linearization&lt;/strong&gt; over the complex design (strata, PSU/UPM and weights/factors).&lt;/p&gt;

&lt;p&gt;Population represented in this run (sum of &lt;code&gt;expr&lt;/code&gt;): &lt;strong&gt;20.13 million people&lt;/strong&gt; (a pending task is to assess the impact of the new 2024 Census instead of projections over the 2017 Census).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# CASEN 2024 sampling design specification&lt;/span&gt;
&lt;span class="n"&gt;design&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;weight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt;     &lt;span class="c"&gt;# regional expansion factor&lt;/span&gt;
    &lt;span class="n"&gt;strata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;estrato&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt;  &lt;span class="c"&gt;# sampling stratum&lt;/span&gt;
    &lt;span class="n"&gt;psu&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;cod_upm&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt;  &lt;span class="c"&gt;# primary sampling unit (PSU)&lt;/span&gt;
    &lt;span class="n"&gt;domain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt;   &lt;span class="c"&gt;# estimation domain&lt;/span&gt;
&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Coverage by analyzed dimension:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table 2&lt;/strong&gt; — CASEN 2024 variables used and target population&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;CASEN variable&lt;/th&gt;
&lt;th&gt;Target population&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Education&lt;/td&gt;
&lt;td&gt;&lt;code&gt;educc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;People ≥ 18 years&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Health&lt;/td&gt;
&lt;td&gt;&lt;code&gt;s13&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Total population&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Poverty&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pobreza&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Total population&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Weighted national results
&lt;/h3&gt;

&lt;p&gt;The following percentages correspond to proportions weighted with &lt;code&gt;expr&lt;/code&gt;, validated against official data in BIDAT. The difference from the official values is &amp;lt; 3×10⁻⁶ pp.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table 3&lt;/strong&gt; — Weighted national distribution (CASEN 2024, main categories)&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;% national&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Education&lt;/td&gt;
&lt;td&gt;Complete secondary&lt;/td&gt;
&lt;td&gt;30.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Education&lt;/td&gt;
&lt;td&gt;Complete higher education&lt;/td&gt;
&lt;td&gt;29.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Education&lt;/td&gt;
&lt;td&gt;Incomplete higher education&lt;/td&gt;
&lt;td&gt;12.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Education&lt;/td&gt;
&lt;td&gt;Incomplete secondary&lt;/td&gt;
&lt;td&gt;9.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Education&lt;/td&gt;
&lt;td&gt;Incomplete primary&lt;/td&gt;
&lt;td&gt;8.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Education&lt;/td&gt;
&lt;td&gt;Other&lt;/td&gt;
&lt;td&gt;7.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Education&lt;/td&gt;
&lt;td&gt;No formal education&lt;/td&gt;
&lt;td&gt;1.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Health&lt;/td&gt;
&lt;td&gt;Public system FONASA&lt;/td&gt;
&lt;td&gt;82.6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Health&lt;/td&gt;
&lt;td&gt;Isapre&lt;/td&gt;
&lt;td&gt;13.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Health&lt;/td&gt;
&lt;td&gt;None (private out-of-pocket)&lt;/td&gt;
&lt;td&gt;2.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Health&lt;/td&gt;
&lt;td&gt;Armed forces and police&lt;/td&gt;
&lt;td&gt;1.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Health&lt;/td&gt;
&lt;td&gt;Does not know&lt;/td&gt;
&lt;td&gt;0.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Health&lt;/td&gt;
&lt;td&gt;Other system&lt;/td&gt;
&lt;td&gt;0.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Poverty&lt;/td&gt;
&lt;td&gt;Non-poverty&lt;/td&gt;
&lt;td&gt;82.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Poverty&lt;/td&gt;
&lt;td&gt;Non-extreme poverty&lt;/td&gt;
&lt;td&gt;10.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Poverty&lt;/td&gt;
&lt;td&gt;Extreme poverty&lt;/td&gt;
&lt;td&gt;6.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  Spoonful 2: the technical flow in Julia
&lt;/h2&gt;

&lt;p&gt;The pipeline runs end to end with &lt;code&gt;julia --project=. scripts/run_all.jl&lt;/code&gt; (the orchestrator). The two most relevant pieces of code to reproduce and "audit" these results are the cell-allocation algorithm and the sampling-design specification, available in the &lt;a href="https://github.com/tatanlabra/casen24_julia_viz" rel="noopener noreferrer"&gt;public repository&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Taylor linearization: SE and CI of the complex design
&lt;/h3&gt;

&lt;p&gt;A Taylor estimator was implemented for proportions in domains (regions). The linearized variable for the proportion $\hat{p}_d$ in domain $d$ is:&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;z&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;span class="mord accent"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;N&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="accent-body"&gt;&lt;span class="mord"&gt;^&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;d&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;j&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;∈&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;d&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;cat&lt;/span&gt;&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;C&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;−&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord accent"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;p&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="accent-body"&gt;&lt;span class="mord"&gt;^&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;d&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;



&lt;p&gt;The variance is estimated with the stratified cluster-design formula:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# Taylor linearization variance (stratified, PSU)&lt;/span&gt;
&lt;span class="c"&gt;# e_{hi} = sum of w_j * z_j over PSU i in stratum h&lt;/span&gt;
&lt;span class="n"&gt;var_total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="n"&gt;over&lt;/span&gt; &lt;span class="n"&gt;strata&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n_h&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n_h&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;sum_i&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e_&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="n"&gt;hi&lt;/span&gt;&lt;span class="x"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e_h&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;

&lt;span class="n"&gt;se&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var_total&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ci_lo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clamp&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p̂&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;1.96&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;se&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ci_hi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clamp&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p̂&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;1.96&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;se&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All PSUs of the design are used (including those outside the domain, with $z_j = 0$), which is correct for random-domain estimation. The median regional 95% CI width is &lt;strong&gt;1.66 pp&lt;/strong&gt;; the maximum is &lt;strong&gt;5.76 pp&lt;/strong&gt; (small regions with low-prevalence categories).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Next entry, at some point 👀 — Jackknife and Bootstrap for CASEN as a contrast:&lt;/strong&gt; TSL is an optimal first-order approximation for means and proportions, but for non-linear statistics (Gini, medians, quantile ratios) it can under-estimate the variance. The &lt;a href="https://github.com/tatanlabra/casen24_julia_viz/blob/main/docs/ic-varianza-casen.md" rel="noopener noreferrer"&gt;code repository&lt;/a&gt; includes the theory and the in-progress Julia code to contrast TSL with Jackknife (delete-1) and Bootstrap — a future entry in this series, which I have not reviewed in depth.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Largest remainder: why it matters in a waffle chart
&lt;/h3&gt;

&lt;p&gt;A 100-cell waffle chart requires the proportions to add up to exactly 100 whole cells. Direct rounding introduces accumulated errors that make the sum 99 or 101. This flow uses the &lt;strong&gt;largest remainder&lt;/strong&gt; algorithm, which guarantees the exact sum by introducing a slight imprecision for the sake of the visualization (take it as a purely visual nicety):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="s"&gt;"""
Allocate `n_cells` whole cells to proportions `p` using largest-remainder.
Guarantees that sum(cells) == n_cells exactly.
"""&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; allocate_cells&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;AbstractVector&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;:&lt;/span&gt;&lt;span class="kt"&gt;Real&lt;/span&gt;&lt;span class="x"&gt;},&lt;/span&gt; &lt;span class="n"&gt;n_cells&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;raw&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;.*&lt;/span&gt; &lt;span class="n"&gt;n_cells&lt;/span&gt;
    &lt;span class="n"&gt;floors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;floor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;remain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;.-&lt;/span&gt; &lt;span class="n"&gt;floors&lt;/span&gt;
    &lt;span class="n"&gt;deficit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n_cells&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;floors&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# Assign remaining cells to the proportions with the largest remainder&lt;/span&gt;
    &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sortperm&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;remain&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rev&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="x"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;deficit&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;floors&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="x"&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;return&lt;/span&gt; &lt;span class="n"&gt;floors&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Validated with unit tests: &lt;code&gt;allocate_cells([0.5, 0.3, 0.2], 100) == [50, 30, 20]&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reproducibility
&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;# Requirements: Julia 1.10+&lt;/span&gt;
git clone &amp;lt;repo&amp;gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;casen2024/julia_viz
julia &lt;span class="nt"&gt;--project&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"using Pkg; Pkg.instantiate()"&lt;/span&gt;
julia &lt;span class="nt"&gt;--project&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; scripts/run_all.jl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The flow generates plenty of artifacts (CSV tables, PNG charts, audit logs for each input). The &lt;code&gt;Manifest.toml&lt;/code&gt; file pins the exact versions of all dependencies (I used Julia 1.10).&lt;/p&gt;




&lt;h2&gt;
  
  
  Spoonful 3: visual evidence
&lt;/h2&gt;

&lt;h3&gt;
  
  
  National composition (Charts 1–3)
&lt;/h3&gt;

&lt;p&gt;Each waffle represents 100 cells allocated by largest remainder over the weighted proportions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp06lwi66f9xk4lhqbh9m.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp06lwi66f9xk4lhqbh9m.webp" alt="Chart 1: Distribution of the highest educational level attained in the adult population (≥18 years), Chile, CASEN 2024. A 100-cell waffle chart with proportions by largest remainder." width="799" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Chart 1 — Education: highest educational level attained (population ≥18 years, CASEN 2024)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flq7pw9zrdsgthsaquj4w.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flq7pw9zrdsgthsaquj4w.webp" alt="Chart 2: Distribution of the health insurance system to which the population belongs, Chile, CASEN 2024. A waffle chart with 6 categories including FONASA, Isapre and others." width="799" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Chart 2 — Health: health insurance system (total population, CASEN 2024)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2u7r8i0vfh4gj5t2273w.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2u7r8i0vfh4gj5t2273w.webp" alt="Chart 3: Income poverty status in Chile according to CASEN 2024: extreme poverty, non-extreme poverty and non-poverty. A 100-cell waffle chart." width="799" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Chart 3 — Income poverty: extreme, non-extreme and non-poverty (total population, CASEN 2024)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Charts 1–3&lt;/strong&gt; — National composition: education (population ≥18 years), health and poverty (total population). Source: CASEN 2024, own elaboration in Julia. Values are validated against BIDAT. Click to enlarge.&lt;/p&gt;

&lt;h3&gt;
  
  
  Regional gaps (Charts 4–6)
&lt;/h3&gt;

&lt;p&gt;Each panel uses an &lt;strong&gt;axis fitted to the range of its own category&lt;/strong&gt; (not a shared global scale), which makes it possible to visualize differences that flatten out if all categories share the same axis. The &lt;strong&gt;horizontal bars&lt;/strong&gt; are 95% CIs computed by Taylor linearization; the dashed vertical line marks the national estimate and the pale grey band its 95% CI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvp8q2w723vjl1soby8ql.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvp8q2w723vjl1soby8ql.webp" alt="Chart 4: Dot plot of regional gaps in educational level by category, CASEN 2024. Each panel has its axis fitted to its own range; dashed vertical line = national reference and grey band = national 95% CI." width="800" height="656"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Chart 4 — Regional gaps in education: dot plot by category (axis fitted per panel, dashed line = national, band = 95% CI)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa1d8id7mrqh6fa3edn98.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa1d8id7mrqh6fa3edn98.webp" alt="Chart 5: Dot plot of regional gaps in the health insurance system by category, CASEN 2024. Axis fitted per panel with national reference." width="800" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Chart 5 — Regional gaps in health: dot plot by category (axis fitted per panel, dashed line = national, band = 95% CI)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgjiq4snfwd4i5uri3o8p.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgjiq4snfwd4i5uri3o8p.webp" alt="Chart 6: Dot plot of regional gaps in income poverty, CASEN 2024. Axis fitted per panel with national reference." width="800" height="272"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Chart 6 — Regional gaps in poverty: dot plot (axis fitted per panel, dashed line = national, band = 95% CI)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Charts 4–6&lt;/strong&gt; — Regional gaps with 95% CIs (Taylor linearization, complex design): education, health and poverty. Bars = regional 95% CI; dashed line = national estimate; grey band = national 95% CI. Axis fitted per category. Click to enlarge.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gaps with 95% CIs — all statistically robust
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Table 4&lt;/strong&gt; — Regional gaps with 95% CIs by Taylor linearization (CASEN 2024)&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Gap (pp)&lt;/th&gt;
&lt;th&gt;Maximum&lt;/th&gt;
&lt;th&gt;95% CI&lt;/th&gt;
&lt;th&gt;Minimum&lt;/th&gt;
&lt;th&gt;95% CI&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Poverty&lt;/td&gt;
&lt;td&gt;Non-poverty&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;18.6 pp&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Magallanes 90.0%&lt;/td&gt;
&lt;td&gt;[88.5%, 91.5%]&lt;/td&gt;
&lt;td&gt;La Araucanía 71.4%&lt;/td&gt;
&lt;td&gt;[69.8%, 73.0%]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Health&lt;/td&gt;
&lt;td&gt;Isapre&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;16.3 pp&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Metropolitana 20.6%&lt;/td&gt;
&lt;td&gt;[19.8%, 21.5%]&lt;/td&gt;
&lt;td&gt;Maule 4.3%&lt;/td&gt;
&lt;td&gt;[3.7%, 5.0%]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Health&lt;/td&gt;
&lt;td&gt;FONASA&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;15.2 pp&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;La Araucanía 91.1%&lt;/td&gt;
&lt;td&gt;[90.2%, 92.0%]&lt;/td&gt;
&lt;td&gt;Metropolitana 75.9%&lt;/td&gt;
&lt;td&gt;[75.0%, 76.7%]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Education&lt;/td&gt;
&lt;td&gt;Complete higher education&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;14.5 pp&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Metropolitana 34.9%&lt;/td&gt;
&lt;td&gt;[34.0%, 35.7%]&lt;/td&gt;
&lt;td&gt;Maule 20.4%&lt;/td&gt;
&lt;td&gt;[19.2%, 21.6%]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Poverty&lt;/td&gt;
&lt;td&gt;Extreme poverty&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8.8 pp&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;La Araucanía 13.0%&lt;/td&gt;
&lt;td&gt;[11.8%, 14.1%]&lt;/td&gt;
&lt;td&gt;Magallanes 4.2%&lt;/td&gt;
&lt;td&gt;[3.1%, 5.2%]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In every case the CIs do not overlap: the gaps are statistically significant at 5%. CIs computed by Taylor linearization over the complex design (strata, PSU/UPM, &lt;code&gt;expr&lt;/code&gt;).&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing: three questions for those who use the survey
&lt;/h2&gt;

&lt;p&gt;If you use CASEN 2024 for territorial characterization or other uses where regional-level variables matter, think twice before using a point estimate (without its confidence interval):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Are your results validated against BIDAT and do they carry design-based CIs?&lt;/strong&gt; A difference greater than 1×10⁻⁵ pp from the official tables is not technical: it is a process issue. And without complex-design CIs, an 8 pp gap can look like evidence when it is noise in small regions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Are you using the correct expansion factor?&lt;/strong&gt; &lt;code&gt;expr&lt;/code&gt; applies to national and regional domains; having &lt;code&gt;expc&lt;/code&gt; does not guarantee commune representativeness. The factors are not interchangeable, and the domain and scope of inference must be stated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is your analysis reproducible?&lt;/strong&gt; A flow that cannot be audited cannot be defended before a technical counterpart, nor updated when CASEN 2026 is released.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The CIs incorporated here are complex-design (Taylor linearization), not simple asymptotic ones. They cover sampling variance but not non-response error or undercoverage. Causal inference between subpopulations requires additional design.&lt;/p&gt;

</description>
      <category>casen2024</category>
      <category>julia</category>
      <category>expansion</category>
      <category>samplingdesign</category>
    </item>
    <item>
      <title>Multi-agent work in three spoonfuls: what worked for me and what did not</title>
      <dc:creator>Cristián Labra</dc:creator>
      <pubDate>Sat, 05 Sep 2026 22:18:38 +0000</pubDate>
      <link>https://dev.to/tatanlabra/multi-agent-work-in-three-spoonfuls-what-worked-for-me-and-what-did-not-44g9</link>
      <guid>https://dev.to/tatanlabra/multi-agent-work-in-three-spoonfuls-what-worked-for-me-and-what-did-not-44g9</guid>
      <description>&lt;p&gt;This started as a practical annoyance: long tasks interrupted by quota limits, conversations that became too heavy, and reviews that required copying context across windows in clumsy and inefficient ways. To deal with that, I built a multi-agent workflow in VS Code, on Arch Linux. I first called it tri-agent; after a couple of months it became &lt;code&gt;penta-agent&lt;/code&gt;, a still imperfect way of coordinating agents, roles, permissions, and traces in my own workflow.&lt;/p&gt;

&lt;p&gt;It is not a new idea, and it is not a promise of full autonomy. In fact, maintaining it takes work. But it is a local work contract that orders who executes, who reviews, when MCP is used, when Gemini/Antigravity enters, when Copilot remains only support, which skills are loaded, and what evidence or artifact should exist at the end.&lt;/p&gt;

&lt;p&gt;I am writing from my own cases: memos, regulatory review, spreadsheets, statistical models, scripts for a home server, system timers, self-hosted cloud, router security, backups, this blog, and everyday maintenance. In those cases, having some agentic governance has helped me. Maybe it is also a false sense of digital sovereignty. I am not immune to that.&lt;/p&gt;

&lt;p&gt;The thesis is simple: a multi-agent system without roles, permissions, and traces may look sophisticated, but in practice it can feel too much like a meeting without minutes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three ideas to start with:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Codex has worked for me as the main executor&lt;/strong&gt;: it reads files, applies patches, runs validations, and can close diffs with evidence. It is not infallible, so it should not be the only validator on critical changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude has worked for me as a strong reviewer, especially with Opus&lt;/strong&gt;: architecture, regulation, statistical assumptions, and argumentative risks. I use it as a brake and as support for the main executor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemini/Antigravity, Copilot, and LiteLLM with DeepSeek and Z.ai work better when bounded&lt;/strong&gt;: exploration, validation, canary work, or small tasks. When they start deciding with too little context, the workflow gets worse.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Spoonful 1: the problem was not using agents, it was losing continuity
&lt;/h2&gt;

&lt;p&gt;In a long task, the cost of a pause is not only the time without an answer. The expensive part is losing continuity: what was being tested, what file changed, which hypothesis failed, which validation remained pending, and which context should no longer be dragged along.&lt;/p&gt;

&lt;p&gt;My first mistake was treating each model or agent as a symmetric second opinion. That does not scale. In a memo or a regulatory review, I ended up with useful comments that were hard to reconcile.&lt;/p&gt;

&lt;p&gt;The fix was more boring and more useful: separate roles. Not because it is the right way for everyone, but because in my workflow it lowers the cost of returning to a task without having to recap everything from scratch.&lt;/p&gt;

&lt;p&gt;I read provider convergence from there. Codex documents configuration, sandboxing, permissions, MCP, &lt;code&gt;AGENTS.md&lt;/code&gt;, skills, and subagents; GitHub Copilot supports repository instructions; Gemini Code Assist describes an agent mode with tools, MCP, and approval of changes; and Claude Code exposes hooks such as &lt;code&gt;PreToolUse&lt;/code&gt;, &lt;code&gt;PostToolUse&lt;/code&gt;, and &lt;code&gt;SessionStart&lt;/code&gt;.&lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;sup id="fnref2"&gt;2&lt;/sup&gt;&lt;sup id="fnref3"&gt;3&lt;/sup&gt;&lt;sup id="fnref4"&gt;4&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table 2&lt;/strong&gt; — Current roles in &lt;code&gt;penta-agent&lt;/code&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Actual role&lt;/th&gt;
&lt;th&gt;Limit I impose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ChatGPT/Codex&lt;/td&gt;
&lt;td&gt;Local technical orchestrator, planner, and executor&lt;/td&gt;
&lt;td&gt;It should not self-validate as the only source of truth on critical changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude&lt;/td&gt;
&lt;td&gt;Reviewer, auditor, and co-planner&lt;/td&gt;
&lt;td&gt;It should not take operational control by default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini/Antigravity&lt;/td&gt;
&lt;td&gt;Explorer, validator, and fallback&lt;/td&gt;
&lt;td&gt;It should not enter the critical path unless explicitly promoted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Copilot&lt;/td&gt;
&lt;td&gt;Canary and IDE/GitHub-native support&lt;/td&gt;
&lt;td&gt;It should not be treated as an independent architectural arbiter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LiteLLM sidecar&lt;/td&gt;
&lt;td&gt;Cheap, narrow delegation&lt;/td&gt;
&lt;td&gt;I do not use it for architecture, security, or final decisions; today I use it in bounded ways with DeepSeek and Z.ai&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Human&lt;/td&gt;
&lt;td&gt;Closure, privacy, and acceptance&lt;/td&gt;
&lt;td&gt;Final judgment is not delegated when risk is involved&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The local architecture makes that explicit. In &lt;code&gt;routing.yaml&lt;/code&gt;, the &lt;code&gt;penta_agent_ops&lt;/code&gt; rule keeps Codex as &lt;code&gt;primary&lt;/code&gt;, Claude as &lt;code&gt;reviewer&lt;/code&gt;, Gemini as validator/fallback, and the human as checkpoint:&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="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;penta_agent_ops&lt;/span&gt;
&lt;span class="na"&gt;planner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;codex&lt;/span&gt;
&lt;span class="na"&gt;primary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;codex&lt;/span&gt;
&lt;span class="na"&gt;reviewer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;claude&lt;/span&gt;
&lt;span class="na"&gt;escalate_to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;human&lt;/span&gt;
&lt;span class="na"&gt;validator_chain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;gemini&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;fallback_chain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;gemini&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;planning&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;proactive_skills&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;handoff-protocol&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;recall-context&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;mcp_policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;conditional&lt;/span&gt;
    &lt;span class="na"&gt;allowed_paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;claude_to_codex_mcp&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;gemini_to_codex_mcp&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;provider_roles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;planning_sidecars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;canary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;copilot&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key detail is &lt;code&gt;mcp_policy.mode: conditional&lt;/code&gt;. Not everything goes through MCP. Not everything deserves a handoff. Not everything deserves a second provider. After some iterations, I made evaluation proactive, as with skills, but invocation still needs a signal. Early on, skills were not activated when I wanted them to be, and handoffs were scarce.&lt;/p&gt;




&lt;h2&gt;
  
  
  Spoonful 2: the repository as a work contract
&lt;/h2&gt;

&lt;p&gt;The piece that helped me most was not a model. It was a startup rule: if a task touches multi-agent work, MCP, Claude, Gemini, Copilot, routing, handoffs, or &lt;code&gt;penta-agent&lt;/code&gt;, first read the local canon.&lt;/p&gt;

&lt;p&gt;In my case, that canon lives in files such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;router/routing.yaml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;shared-references/arquitectura-multiagente.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;playbooks/puente-mcp-codex.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.mcp.json&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This prevents a common vice: generic probing of CLIs, models, and commands before understanding the project's contract. For small things, it may not matter. For a regulatory review, a statistical run, or a local security change, it does. Drift burns tokens and sometimes leaves the task stranded in the wrong place.&lt;/p&gt;

&lt;p&gt;A useful &lt;code&gt;AGENTS.md&lt;/code&gt; does not need to be a novel. Long instructions consume context. I think it should mostly prevent bad practices:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Workspace multi-agent rules&lt;/span&gt;

If the user mentions multi-agent work, MCP, Claude, Gemini,
Copilot, routing, handoff, or penta-agent:
&lt;span class="p"&gt;
1.&lt;/span&gt; Use &lt;span class="sb"&gt;`penta-agent/`&lt;/span&gt; as the source of truth.
&lt;span class="p"&gt;2.&lt;/span&gt; Read routing, architecture, MCP bridge, and MCP config first.
&lt;span class="p"&gt;3.&lt;/span&gt; Do not probe generic CLIs before reading the canon.
&lt;span class="p"&gt;4.&lt;/span&gt; Delegate to Antigravity only through &lt;span class="sb"&gt;`scripts/agent/agy-bridge`&lt;/span&gt;.
&lt;span class="p"&gt;5.&lt;/span&gt; Do not send secrets, &lt;span class="sb"&gt;`.env`&lt;/span&gt;, keys, or credentials to external providers.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Recent literature seems to move in a similar direction, with a healthy warning. Galster et al. describe these artifacts as versionable mechanisms for configuring agentic tools and observe that &lt;code&gt;AGENTS.md&lt;/code&gt; is emerging as an interoperable standard.&lt;sup id="fnref5"&gt;5&lt;/sup&gt; Arabat and Sayagh propose treating them as instructions-as-code, not as side notes.&lt;sup id="fnref6"&gt;6&lt;/sup&gt; On the other hand, Gloaguen et al. warn that context files can reduce success and increase cost when they add unnecessary requirements.&lt;sup id="fnref7"&gt;7&lt;/sup&gt; My experience is that the balance must be tuned constantly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Skills: procedures, not endless prompts
&lt;/h3&gt;

&lt;p&gt;Skills have helped me when they act as reusable procedures with clear activation rules, limits, and references. A publishing skill, for example, should not say "write nicely." It should say verifiable things:&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="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jekyll-post&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Jekyll posts with front matter, drafts/posts workflow, build checks, and reproducible publication.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; Prepare correct front matter and validate the build.
&lt;span class="p"&gt;-&lt;/span&gt; Keep claims dated and traceable.
&lt;span class="p"&gt;-&lt;/span&gt; Review front matter and the editorial guide.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The minimum question for a skill is: when does it activate, what must it not do, which files must it read, and what output makes it possible to verify that it did the job well. That fits Anthropic's progressive-loading model: metadata first, instructions when the skill activates, and resources or scripts only if needed.&lt;sup id="fnref8"&gt;8&lt;/sup&gt; It also fits SkillJuror, which shows that skill organization changes how agents search for and apply knowledge, although the benefit depends on whether resources are actionable.&lt;sup id="fnref9"&gt;9&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;The security warning matters too: &lt;code&gt;SKILL.md&lt;/code&gt; is not passive documentation.&lt;sup id="fnref10"&gt;10&lt;/sup&gt; If a skill brings scripts, ambiguous instructions, or manipulable metadata, it is closer to installing software than to pasting an innocent prompt.&lt;/p&gt;

&lt;h3&gt;
  
  
  MCP: a bridge, but I still do not squeeze it fully
&lt;/h3&gt;

&lt;p&gt;The real MCP surface in &lt;code&gt;penta-agent&lt;/code&gt; is deliberately small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"codex"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"stdio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"scripts/codex_mcp.sh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"mcp-server"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The idea is not that every agent connects to every other agent. The preferred path is &lt;strong&gt;Claude/Gemini to Codex&lt;/strong&gt; when they need filesystem access, commands, tests, or local edits. Codex to Claude or Antigravity uses controlled CLI/file handoffs. I do not assume a bidirectional bridge or a universal &lt;code&gt;filesystem&lt;/code&gt; MCP baseline.&lt;/p&gt;

&lt;p&gt;That is also a security decision. MCP's official security guidance lists risks such as confused deputy, token passthrough, SSRF, session hijacking, and local server compromise.&lt;sup id="fnref11"&gt;11&lt;/sup&gt; Recent work on tool poisoning in MCP argues that the critical problem is often on the client side: malicious metadata, low parameter visibility, and implicit trust.&lt;sup id="fnref12"&gt;12&lt;/sup&gt; I still do not get all the value from MCP, but I prefer it narrow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handoffs: the antidote to "I told them"
&lt;/h3&gt;

&lt;p&gt;A handoff is not saying "let Claude review it." A useful handoff leaves a minimal trace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HANDOFF-TRACE: codex asks claude for support (model: sonnet) via CLI
MODEL-REPORT: {"agent":"codex","provider":"openai","model":"__default__","source":"self_report"}
MODEL-REPORT: {"agent":"claude","provider":"anthropic","model":"sonnet","source":"self_report"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it separates the basics:&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="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;codex&lt;/span&gt;
  &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;claude&lt;/span&gt;
  &lt;span class="na"&gt;via&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CLI&lt;/span&gt;
  &lt;span class="na"&gt;skill&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;handoff-protocol&lt;/span&gt;

&lt;span class="na"&gt;essential_context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;what was done&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;what was rejected&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;what remains to validate&lt;/span&gt;

&lt;span class="na"&gt;task_for_receiver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;review claims&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;detect risks&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;do not modify files&lt;/span&gt;

&lt;span class="na"&gt;verification&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;tests run&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sources checked&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;final decision&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This feels bureaucratic until something fails. When it fails, the trace helps answer whether the problem was insufficient context, wrong model, bad route, ambiguous output, quota, permissions, sandboxing, or a poorly written instruction. OpenAI proposes a similar logic in its agent improvement loop: use traces, feedback, and evals to modify the harness, not only to ask the model to "do better."&lt;sup id="fnref13"&gt;13&lt;/sup&gt; This has also helped me use &lt;code&gt;recall-context&lt;/code&gt;: with a good trace, previous work is much easier to recover across agents.&lt;/p&gt;




&lt;h2&gt;
  
  
  Spoonful 3: what I learned by making it fail
&lt;/h2&gt;

&lt;p&gt;My own error list is more useful than a list of tools. I hope this post reads more like a logbook than a showcase. It will probably age quickly, but perhaps it will be useful to someone else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table 3&lt;/strong&gt; — My mistakes and current controls&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mistake&lt;/th&gt;
&lt;th&gt;What it looked like&lt;/th&gt;
&lt;th&gt;Current control&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Treating models as equivalent&lt;/td&gt;
&lt;td&gt;Any provider could comment on anything&lt;/td&gt;
&lt;td&gt;Explicit roles: primary, reviewer, explorer, validator, canary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Automatic fan-out&lt;/td&gt;
&lt;td&gt;I asked for second opinions out of habit&lt;/td&gt;
&lt;td&gt;Always evaluate, invoke only with enough signal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fragile model IDs&lt;/td&gt;
&lt;td&gt;A rename broke scripts or examples&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;__default__&lt;/code&gt;, aliases, and discovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Declaring a sidecar ready too early&lt;/td&gt;
&lt;td&gt;The proxy started, but the provider failed by quota or balance&lt;/td&gt;
&lt;td&gt;Separate "proxy is alive" from "provider is usable"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Overlong context files&lt;/td&gt;
&lt;td&gt;The agent followed irrelevant rules and explored too much&lt;/td&gt;
&lt;td&gt;Minimal, actionable, dated instructions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MCP as a universal solution&lt;/td&gt;
&lt;td&gt;More servers, more implicit trust&lt;/td&gt;
&lt;td&gt;Conditional MCP, consent, sandboxing, and minimal permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vector memory as source of truth&lt;/td&gt;
&lt;td&gt;Drift risk from dimensions or derived indexes&lt;/td&gt;
&lt;td&gt;JSONL as source; Qdrant/FastEmbed as rebuildable indexes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Copilot as a strong second provider&lt;/td&gt;
&lt;td&gt;It did not always add independent judgment&lt;/td&gt;
&lt;td&gt;Use it as canary/support, not as arbiter&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Memory deserves a paragraph. In &lt;code&gt;penta-agent&lt;/code&gt;, operational memory lives in append-only files such as &lt;code&gt;experience-events.jsonl&lt;/code&gt;, &lt;code&gt;experience-lessons.yaml&lt;/code&gt;, and &lt;code&gt;interaction-metrics.jsonl&lt;/code&gt;. Qdrant and FastEmbed help retrieve experiences, but they are derivatives. If embeddings change or a dimension mismatch appears, the index is rebuilt from JSONL. After fighting that kind of drift, I do not want the source of truth to be fragile.&lt;/p&gt;

&lt;p&gt;The LiteLLM sidecar follows the same philosophy. It is useful for cheap, narrow delegation to aliases such as &lt;code&gt;cheap&lt;/code&gt;, &lt;code&gt;cheap-code&lt;/code&gt;, or &lt;code&gt;cheap-reasoner&lt;/code&gt;, but not for architecture, security, or final decisions. The real provider smoke test matters more than a healthy container. If DeepSeek returns &lt;code&gt;Insufficient Balance&lt;/code&gt;, the system is not ready; we have only learned that Docker can turn on a light.&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing: an optimization loop
&lt;/h2&gt;

&lt;p&gt;My stack should not be read as a universal template. It is not one. What may be useful is the logic behind it: small contracts so the work does not depend on human memory or on the enthusiasm of the moment. I published a sanitized public version of the stack at &lt;a href="https://github.com/tatanlabra/penta-agent" rel="noopener noreferrer"&gt;&lt;code&gt;tatanlabra/penta-agent&lt;/code&gt;&lt;/a&gt;. It is not a dump of my local environment: I removed runtime memory, private paths, logs, &lt;code&gt;.env&lt;/code&gt; files, credentials, internal IPs, and anything that could reveal personal configuration. Anyone cloning it should treat it as a template and review the placeholders in &lt;code&gt;AGENTS.md&lt;/code&gt;, &lt;code&gt;.mcp.json&lt;/code&gt;, &lt;code&gt;router/routing.yaml&lt;/code&gt;, &lt;code&gt;scripts/agent/agy-bridge&lt;/code&gt;, and &lt;code&gt;skills/&lt;/code&gt; before using it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;penta-agent&lt;/code&gt; has helped me not because it has many agents, but because it has brakes: roles, routes, skills, permissions, handoffs, rebuildable memory, and validation. That does not make it universal or superior. It makes it auditable for my problems. &lt;em&gt;Vibe coding&lt;/em&gt; names the first feeling of flow well: ask, watch code appear, correct, continue. But when the workflow matures, the question changes: how efficient, elegant, and verifiable is the solution you get?&lt;/p&gt;




&lt;h2&gt;
  
  
  Update · July 2026
&lt;/h2&gt;

&lt;p&gt;I updated the public repo to a &lt;code&gt;v3.1&lt;/code&gt; profile. None of it came from a redesign: it all came from broken things that kept running.&lt;/p&gt;

&lt;p&gt;The main one, &lt;strong&gt;a silent fallback is the actual failure&lt;/strong&gt;. Degrading is fine; degrading quietly leaves the system "running" on stale inputs. My board detected exactly that, and nobody found out, because looking at it required suspecting it first. It now notifies only on transitions, every check says how long it has been red, and it separates a broken probe from a downed provider: identical on screen, opposite fixes.&lt;/p&gt;

&lt;p&gt;The executor also stopped being at the top. And the cheap models I praise here as a sidecar turned out to be untraced delegations against models I had never verified; they now hold the same evidence contract as everything else. Less impressive than adding another agent, considerably more useful.&lt;/p&gt;




&lt;h2&gt;
  
  
  Technical References
&lt;/h2&gt;







&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;OpenAI Developers, "Configuration Reference -- Codex," accessed June 21, 2026. It documents configuration, permissions, sandboxing, MCP, &lt;code&gt;AGENTS.md&lt;/code&gt;, skills, and subagents in Codex. &lt;a href="https://developers.openai.com/codex/config-reference" rel="noopener noreferrer"&gt;https://developers.openai.com/codex/config-reference&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;GitHub Docs, "Adding repository custom instructions for GitHub Copilot," accessed June 21, 2026. It documents repository instructions and formats associated with Copilot. &lt;a href="https://docs.github.com/en/copilot/how-tos/copilot-on-github/customize-copilot/add-custom-instructions/add-repository-instructions" rel="noopener noreferrer"&gt;https://docs.github.com/en/copilot/how-tos/copilot-on-github/customize-copilot/add-custom-instructions/add-repository-instructions&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;Google Developers, "Use the Gemini Code Assist agent mode," accessed June 21, 2026. It describes agent mode in the IDE, tool use, MCP, and action approval. &lt;a href="https://developers.google.com/gemini-code-assist/docs/use-agentic-chat-pair-programmer" rel="noopener noreferrer"&gt;https://developers.google.com/gemini-code-assist/docs/use-agentic-chat-pair-programmer&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;Anthropic, "Hooks reference -- Claude Code Docs," accessed June 21, 2026. It describes hooks as lifecycle control points, including &lt;code&gt;PreToolUse&lt;/code&gt;, &lt;code&gt;PostToolUse&lt;/code&gt;, and &lt;code&gt;SessionStart&lt;/code&gt;. &lt;a href="https://code.claude.com/docs/en/hooks" rel="noopener noreferrer"&gt;https://code.claude.com/docs/en/hooks&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;Matthias Galster et al., "Configuring Agentic AI Coding Tools: An Exploratory Study," arXiv:2602.14690, 2026. It analyzes mechanisms such as context files, skills, subagents, hooks, settings, and MCP. &lt;a href="https://arxiv.org/abs/2602.14690" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2602.14690&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn6"&gt;
&lt;p&gt;Ali Arabat and Mohammed Sayagh, "Toward Instructions-as-Code: Understanding the Impact of Instruction Files on Agentic Pull Requests," arXiv:2606.13449, 2026. It analyzes agentic pull requests and concludes that instructions do not automatically improve performance. &lt;a href="https://arxiv.org/abs/2606.13449" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2606.13449&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn7"&gt;
&lt;p&gt;Thibaud Gloaguen et al., "Evaluating AGENTS.md: Are Repository-Level Context Files Helpful for Coding Agents?," arXiv:2602.11988, 2026. It reports that context files can reduce success and increase cost when they add unnecessary requirements. &lt;a href="https://arxiv.org/abs/2602.11988" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2602.11988&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn8"&gt;
&lt;p&gt;Anthropic, "Agent Skills -- Claude API Docs," accessed June 21, 2026. It describes skills as modular capabilities with instructions, metadata, scripts, and references loaded in stages. &lt;a href="https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview" rel="noopener noreferrer"&gt;https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn9"&gt;
&lt;p&gt;Zhiyu Chen et al., "SkillJuror: Measuring How Agent Skill Organization Changes Runtime Behavior," arXiv:2606.11543, 2026. It evaluates skill organization and progressive loading. &lt;a href="https://arxiv.org/abs/2606.11543" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2606.11543&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn10"&gt;
&lt;p&gt;Shoumik Saha, Kazem Faghih, and Soheil Feizi, "Under the Hood of SKILL.md: Semantic Supply-chain Attacks on AI Agent Skill Registry," arXiv:2605.11418, 2026. It warns that &lt;code&gt;SKILL.md&lt;/code&gt; metadata and instructions can manipulate skill discovery, selection, and governance. &lt;a href="https://arxiv.org/abs/2605.11418" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2605.11418&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn11"&gt;
&lt;p&gt;Model Context Protocol, "Security Best Practices," accessed June 21, 2026. It lists risks and mitigations for MCP implementations. &lt;a href="https://modelcontextprotocol.io/docs/tutorials/security/security_best_practices" rel="noopener noreferrer"&gt;https://modelcontextprotocol.io/docs/tutorials/security/security_best_practices&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn12"&gt;
&lt;p&gt;Charoes Huang, Xin Huang, Ngoc Phu Tran, and Amin Milani Fard, "Model Context Protocol Threat Modeling and Analyzing Vulnerabilities to Prompt Injection with Tool Poisoning," arXiv:2603.22489, 2026. It applies STRIDE/DREAD to MCP and highlights tool poisoning as a critical client-side vulnerability. &lt;a href="https://arxiv.org/abs/2603.22489" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2603.22489&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn13"&gt;
&lt;p&gt;OpenAI Cookbook, "Build an Agent Improvement Loop with Traces, Evals, and Codex," accessed June 21, 2026. It uses traces, feedback, evals, and harness configuration to improve agents. &lt;a href="https://developers.openai.com/cookbook/examples/agents_sdk/agent_improvement_loop" rel="noopener noreferrer"&gt;https://developers.openai.com/cookbook/examples/agents_sdk/agent_improvement_loop&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>multiagent</category>
      <category>agenticcoding</category>
      <category>contextengineering</category>
      <category>codex</category>
    </item>
    <item>
      <title>AI quotas in three spoonfuls: a HUD for the KDE panel</title>
      <dc:creator>Cristián Labra</dc:creator>
      <pubDate>Sun, 26 Jul 2026 15:38:43 +0000</pubDate>
      <link>https://dev.to/tatanlabra/ai-quotas-in-three-spoonfuls-a-hud-for-the-kde-panel-2db1</link>
      <guid>https://dev.to/tatanlabra/ai-quotas-in-three-spoonfuls-a-hud-for-the-kde-panel-2db1</guid>
      <description>&lt;p&gt;I currently use Claude Code, Codex, Gemini (through &lt;code&gt;agy&lt;/code&gt; on the command line), and DeepSeek on Arch Linux with KDE Plasma 6. For me, as for many others, the problem has become knowing which one still has quota—especially when a task already carries a lot of context, reviewed files, and an hour of iteration.&lt;/p&gt;

&lt;p&gt;That is how I ended up building a viewer, or HUD, for the KDE panel: five indicators showing how much room each agent has left and when its quota should reset. No extra tab, no separate dashboard, and—most importantly—no surprise when the quota runs out while I am wrapping up a task or a &lt;code&gt;git rebase&lt;/code&gt; 😱.&lt;/p&gt;

&lt;h2&gt;
  
  
  First spoonful: the token-week
&lt;/h2&gt;

&lt;p&gt;Each provider invented its own way of measuring how much we can use it.&lt;/p&gt;

&lt;p&gt;Claude speaks in windows of hours and days. Codex reports the windows available for the plan. Gemini requires a local estimate of requests. Copilot counts premium requests against a calendar cutoff. DeepSeek, by contrast, speaks in monetary balance.&lt;/p&gt;

&lt;p&gt;Five agents, five clocks, and no shared unit.&lt;/p&gt;

&lt;p&gt;The industry has achieved something rather peculiar: turning &lt;strong&gt;token-hours&lt;/strong&gt; and &lt;strong&gt;token-weeks&lt;/strong&gt; into real planning units. It is no longer enough to ask how long a task will take. I also have to calculate whether the agent can finish it before leaving me to do things the old-fashioned way :).&lt;/p&gt;

&lt;p&gt;On the panel I reduced it to five rings, or donuts. A nearly full one means the agent still has room. A nearly empty one means it is probably time to thank it for its service and try the next one.&lt;/p&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%2F6t6wqzlsnyeh5facbzb4.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%2F6t6wqzlsnyeh5facbzb4.png" alt="Compact AI Quota HUD bar on the KDE Plasma panel, with five circular indicators." width="210" height="49"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 1&lt;/strong&gt; — Compact AI Quota HUD view on the KDE Plasma panel. The five rings summarize the available margin by agent. The coloured arc is what remains free, and the outer white marks count the days until reset. Source: own screenshot with synthetic data.&lt;/p&gt;

&lt;p&gt;Hovering over one donut shows that agent in detail: its windows and their reset times.&lt;/p&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%2Fvw8hsxsnqyj2yze8vptr.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%2Fvw8hsxsnqyj2yze8vptr.png" alt="AI Quota HUD hover panel showing the four Antigravity and Gemini quota windows, each with its free percentage and reset time." width="799" height="590"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 2&lt;/strong&gt; — The hover panel shows the windows of the agent under the cursor without leaving the active task. Antigravity carries two independent weekly quotas on separate clocks: one for Google models and one for third-party ones. The badge says where each figure comes from: OFFICIAL if the provider reports it, LOCAL if it is a local count. Source: own screenshot with synthetic data.&lt;/p&gt;

&lt;p&gt;Clicking opens the detailed view, with the five agents on top and the windows of whichever one is selected below.&lt;/p&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%2Fe3mexuz5b19gogjksbu7.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%2Fe3mexuz5b19gogjksbu7.png" alt="AI Quota HUD detailed view: a row of five selectors with each agent's free percentage and, below, Claude's four windows with percentage, provenance and reset time." width="800" height="625"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 3&lt;/strong&gt; — The detailed view. On top, the five agents with their tightest margin; below, the windows of whichever one is selected. Each line says how much is left, when it renews, and where the figure comes from. Source: own screenshot with synthetic data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://3cucharadas.cl/en/ia/productividad/ai-quota-hud-kde/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcgh1dd8w7xzqpzoi1oyd.webp" alt="AI Quota HUD demonstration: the bar on the KDE panel, the hover panel over an agent, and the detailed view with the selector row switching agents." width="800" height="626"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Watch the full video demo on &lt;a href="https://3cucharadas.cl/en/ia/productividad/ai-quota-hud-kde/" rel="noopener noreferrer"&gt;3cucharadas.cl&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 4&lt;/strong&gt; — The walkthrough from the panel bar to the detailed view, with the selector row moving through Codex, Antigravity and DeepSeek. The values are synthetic and do not represent personal quotas. Source: a sequence rendered with the same probe the tests use, frame by frame and with the clock pinned, so it can be regenerated identically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Second spoonful: the data decides, not the order
&lt;/h2&gt;

&lt;p&gt;The first version worked well for weeks. Codex returned two windows: a short five-hour one and a weekly one.&lt;/p&gt;

&lt;p&gt;My code interpreted them by position:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The first window, or outer ring, was the five-hour one.&lt;/li&gt;
&lt;li&gt;The second was the weekly one.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It was simple—until Codex changed the schema.&lt;/p&gt;

&lt;p&gt;One day the widget showed 5% available in the supposed “5h” window, but with a reset scheduled six days later. Even for a technology company, five hours lasting almost a week seemed like too much innovation.&lt;/p&gt;

&lt;p&gt;The raw response contained a single window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;604800 seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is seven days.&lt;/p&gt;

&lt;p&gt;Codex had stopped reporting the short window, but my code still called whatever appeared first “5h.” At the same time, the old weekly window remained frozen in the cache because the “keep the last good value” logic could not distinguish between a failed query and a window that had ceased to exist. 👻&lt;/p&gt;

&lt;p&gt;The correction had two parts.&lt;/p&gt;

&lt;p&gt;First, each window stopped being identified by its position and began to be identified by its actual duration. A short duration is a session; an extended duration is a weekly window or equivalent.&lt;/p&gt;

&lt;p&gt;Second, the monitor now distinguishes between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;a query that failed&lt;/strong&gt;, in which case it temporarily preserves the previous value and marks it as cached;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;a valid query that no longer contains a window&lt;/strong&gt;, in which case it removes that window from the current state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The interface also stopped assuming that every agent has the same structure. If two windows arrive, it draws a double donut. If one arrives, it draws a single ring. The data defines the interface, not the other way around.&lt;/p&gt;

&lt;p&gt;The lesson is small but fairly general: &lt;strong&gt;if the provider supplies the duration, that is the identity of the data; its position in an array is only a temporary coincidence.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Third spoonful: local, useful, and not very universal
&lt;/h2&gt;

&lt;p&gt;This project is not meant to be a cross-platform application.&lt;/p&gt;

&lt;p&gt;I built it for an environment very much like mine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Arch Linux;&lt;/li&gt;
&lt;li&gt;KDE Plasma 6;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;systemd --user&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Python;&lt;/li&gt;
&lt;li&gt;Bash;&lt;/li&gt;
&lt;li&gt;QML;&lt;/li&gt;
&lt;li&gt;the local sessions and credentials of the tools I already use.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It can probably be adapted to other distributions running Plasma 6. I do not promise that it will work unchanged on GNOME, Plasma 5, Windows, or macOS. Nor did I abstract every possible authentication method. It is a tool for my own desktop that I decided to organize and publish, not an attempt to solve every possible combination of operating systems, providers, and plans.&lt;/p&gt;

&lt;p&gt;Inside, the path is short:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 systemd --user timer
                          │
                     every five minutes
                          ▼
 helpers/*.sh ──► Python monitor ──► status.json
                                           │
                            ┌──────────────┴──────────────┐
                            ▼                             ▼
                    QML widget                      MCP server
              panel · tooltip · popup       checks before delegation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;em&gt;helpers&lt;/em&gt; are the only component that touches credentials. They return sanitized JSON, without authentication tokens or conversation content.&lt;/p&gt;

&lt;p&gt;The Python monitor queries, validates, and merges the information. If it encounters a &lt;code&gt;timeout&lt;/code&gt;, an expired credential, or a &lt;code&gt;429&lt;/code&gt; error, it preserves the last known value and marks it as cached.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;systemd&lt;/code&gt; timer runs the update every five minutes. The widget does not query providers directly; it only reads a local file with &lt;code&gt;0600&lt;/code&gt; permissions. That way I avoid triggering a &lt;em&gt;rate limit&lt;/em&gt; by checking the &lt;em&gt;rate limit&lt;/em&gt; too often, which would be an especially elegant way of closing the loop. 🫠&lt;/p&gt;

&lt;p&gt;The same &lt;code&gt;status.json&lt;/code&gt; can be read by an MCP server. This lets an agent ask which provider still has quota before delegating a task. The desktop bar and the orchestrator receive exactly the same state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;The repository is published under the MIT license. It is designed for KDE Plasma 6, does not require &lt;code&gt;sudo&lt;/code&gt;, and installs into the user's local paths:&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/tatanlabra/ai-quota-kde.git
&lt;span class="nb"&gt;cd &lt;/span&gt;ai-quota-kde
scripts/install-user.sh
ai-quota-monitor doctor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/tatanlabra/ai-quota-kde" rel="noopener noreferrer"&gt;View the repository on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It does not create more quota, negotiate better plans, or eliminate the token-week. It only prevents me from discovering halfway through a task that the chosen agent has gone off in search of additional quota 🤑.&lt;/p&gt;

&lt;p&gt;It is a small spoonful of sovereignty over my own workflow: knowing how much remains, when it resets, and who should get the next handoff.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update, 8 September 2026
&lt;/h2&gt;

&lt;p&gt;The HUD changed enough that the July screenshots no longer represented it.&lt;br&gt;
Figures 1 to 3 above show the current state; the recording in figure 4 is still&lt;br&gt;
from July and is kept because the walkthrough —panel, hover panel, detailed&lt;br&gt;
view— has not changed, even though the design has.&lt;/p&gt;

&lt;p&gt;What is different:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Five agents instead of four.&lt;/strong&gt; Copilot was added; it counts premium requests against a calendar cutoff. The post said "four" in seven places and has been corrected.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A different hover panel.&lt;/strong&gt; It used to squeeze all five agents into one cramped list. It now shows the detail of the agent under the cursor, with a badge per line saying whether the figure is reported by the provider (&lt;code&gt;OFFICIAL&lt;/code&gt;) or counted locally (&lt;code&gt;LOCAL&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A different click view.&lt;/strong&gt; The five agents moved into a row of selectors showing their tightest margin, with the windows of the selected one below. Previously it was five simultaneous columns that did not fit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slightly thicker strokes and larger logos.&lt;/strong&gt; The quota arc went from 0.055 to 0.060 of the diameter and the logos grew by 3 %, measured at runtime. The central aperture was recovered by tightening the gap between rings, because widening the stroke was shrinking it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The interface is translated.&lt;/strong&gt; Source strings moved to English and Spanish lives in a &lt;code&gt;gettext&lt;/code&gt; catalogue, so the widget speaks the language of the desktop.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And two things that surfaced precisely while preparing these screenshots, which&lt;br&gt;
matter more than the screenshots themselves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dates rendered in English on a Spanish desktop. &lt;code&gt;Qt.formatDate&lt;/code&gt; with a hand-written format uses the C locale, not the system one: with everything else already translated, it still said "Sat 12 Sep" where "sáb 12 sept" belonged. Measured with &lt;code&gt;QLocale("es_CL")&lt;/code&gt; on the same date.&lt;/li&gt;
&lt;li&gt;Four labels were written in Spanish by the collector and reached the screen without passing through the catalogue, so on an English desktop they read in Spanish among translated strings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The screenshots are generated by a script in the repository, not by hand, and&lt;br&gt;
that is where the three new figures come from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;scripts/capture_previews.sh build/previews en 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It renders the three views without opening a window, with the same component the&lt;br&gt;
tests verify, at whatever scale is asked for —3× for these. The data comes from&lt;br&gt;
&lt;code&gt;ai-quota-monitor sample&lt;/code&gt;, which writes a synthetic report into a temporary&lt;br&gt;
directory: the real cache is neither read nor touched, and every line of the&lt;br&gt;
render is labelled as synthetic. That is why the figures do not show my quotas,&lt;br&gt;
and why they can be regenerated identically. It does need a GPU, though, and the&lt;br&gt;
update below explains why.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update, 9 September 2026
&lt;/h2&gt;

&lt;p&gt;Figures 1 to 3 were regenerated for a specific reason: &lt;strong&gt;the logos came out&lt;br&gt;
black&lt;/strong&gt;. The widget on the panel was always fine; what was wrong was the&lt;br&gt;
screenshot script.&lt;/p&gt;

&lt;p&gt;The Kirigami component that recolours a monochrome SVG does it with a GPU&lt;br&gt;
material, and the software renderer has no equivalent for that material: it draws&lt;br&gt;
the silhouette and skips the colour. The script rendered in software, so four of&lt;br&gt;
the five logos showed up black —Codex, white— and in the popup they were nearly&lt;br&gt;
invisible, dark on dark. The fifth, Gemini, looked right because it was never&lt;br&gt;
meant to be tinted: it keeps the blue of its own file.&lt;/p&gt;

&lt;p&gt;What interests me most about this failure is why all 93 tests stayed green. A black&lt;br&gt;
silhouette has exactly the same width, the same height and the same aperture as a&lt;br&gt;
tinted one, so no geometric invariant could see it: the absence of a colour check&lt;br&gt;
&lt;em&gt;was&lt;/em&gt; the reason it went unnoticed. And it had been written down in the repository&lt;br&gt;
since 6 September —«software rendering alone did not show SVG mask colours&lt;br&gt;
faithfully»— two days before a commit switched the flow to offscreen and shipped the&lt;br&gt;
defect.&lt;/p&gt;

&lt;p&gt;Measured: the bar had 1452 opaque pixels of pure black and 299 of pure white; the&lt;br&gt;
popup, 2597 and 6. It now has zero of each, and the four masked logos appear in their&lt;br&gt;
accent colour. There is a new test that looks at the pixel colour inside the central&lt;br&gt;
disc of every donut and requires the dominant colour to be that provider's accent,&lt;br&gt;
with Gemini as the negative control: if Gemini matched both, the measurement would be&lt;br&gt;
looking at the ring rather than the logo. It skips explicitly when the machine has no&lt;br&gt;
GPU, and it never passes by measuring nothing.&lt;/p&gt;

&lt;p&gt;A twist I did not expect: of the three ways to render on the GPU, the two obvious ones&lt;br&gt;
require mapping a window, and &lt;strong&gt;a locked screen stops presenting it&lt;/strong&gt;. The same&lt;br&gt;
commands that worked in the afternoon hung an hour later with the session locked,&lt;br&gt;
waiting for a frame nobody was going to paint. The one that works opens no window at&lt;br&gt;
all. And switching platforms brought another surprise: the new one derived the DPI&lt;br&gt;
from the real monitor instead of using 96, and with that the tooltip grew 24 pixels&lt;br&gt;
without a single line of code changing. The DPI is now pinned, so the geometry does not&lt;br&gt;
depend on the monitor of whichever machine takes the screenshot.&lt;/p&gt;

&lt;p&gt;The logos also grew by 5.3 %, which is all the margin left: the corner of their box now&lt;br&gt;
touches the circumference of the central hole. The check that guaranteed that&lt;br&gt;
containment had been disabled by a silent ceiling that clamped any value above 1&lt;br&gt;
without warning —a value of 1.10 came out green—; removing it put the check back in&lt;br&gt;
service.&lt;/p&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%2Frgh62nwu4tuv1thjcjsv.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%2Frgh62nwu4tuv1thjcjsv.png" alt="xkcd 303, Compiling: two programmers play while waiting for compilation to finish." width="413" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 5&lt;/strong&gt; — &lt;em&gt;Compiling&lt;/em&gt;, &lt;a href="https://xkcd.com/303/" rel="noopener noreferrer"&gt;xkcd no. 303&lt;/a&gt;, by Randall Munroe. The old excuse was that the code was compiling; now I can say the quota resets next week. Licensed under &lt;a href="https://creativecommons.org/licenses/by-nc/2.5/" rel="noopener noreferrer"&gt;CC BY-NC 2.5&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>deepseek</category>
      <category>kde</category>
    </item>
  </channel>
</rss>
