<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Cloud on Carles Abarca</title><link>https://carlesabarca.com/tags/cloud/</link><description>Recent content in Cloud on Carles Abarca</description><generator>Hugo -- gohugo.io</generator><language>en</language><copyright>© 2026 Carles Abarca</copyright><lastBuildDate>Sat, 26 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://carlesabarca.com/tags/cloud/index.xml" rel="self" type="application/rss+xml"/><item><title>Minix: a hybrid agent that does not need to send every question to the cloud</title><link>https://carlesabarca.com/posts/minix-hybrid-agent/</link><pubDate>Sat, 26 Sep 2026 00:00:00 +0000</pubDate><guid>https://carlesabarca.com/posts/minix-hybrid-agent/</guid><description>One conversation, local and cloud AI. A six-turn experiment used about 48% fewer cloud tokens, with the full dialogue, architecture, and limitations.</description><content:encoded>&lt;style&gt;
.minix-article pre { white-space: pre-wrap; overflow-wrap: anywhere; }
&lt;/style&gt;
&lt;div class="minix-article"&gt;
&lt;p&gt;Recently, the pace of innovation in &lt;em&gt;open-weight&lt;/em&gt; AI models has been frenetic, with promises of &lt;strong&gt;“free, unlimited AI”&lt;/strong&gt; running on consumer hardware. The specifications are becoming remarkable: a MacBook Pro with M4 Max can offer up to 128 GB of unified memory and 546 GB/s of memory bandwidth, while a Mac Studio with M3 Ultra reaches 512 GB and exceeds 800 GB/s. Apple says the latter can run language models with more than &lt;strong&gt;600 billion parameters&lt;/strong&gt; locally.&lt;/p&gt;
&lt;p&gt;The evolution of the models themselves matters just as much. &lt;em&gt;Mixture of Experts&lt;/em&gt; (MoE) architectures make it possible to build enormous models while activating only a fraction of their parameters for each inference. Qwen3-30B-A3B, for example, has 30 billion parameters but activates approximately &lt;strong&gt;3 billion per token&lt;/strong&gt;. More efficient models, quantization, and hardware with large amounts of unified memory are rapidly narrowing the gap between AI available in large &lt;em&gt;data centers&lt;/em&gt; and what can run privately and locally on personal machines.&lt;/p&gt;
&lt;p&gt;Yet a narrower gap does not mean that cloud-based &lt;em&gt;frontier&lt;/em&gt; models are no longer necessary. When a task demands complex reasoning or advanced &lt;em&gt;agentic&lt;/em&gt; capabilities, or when latency and throughput—TTFT (&lt;em&gt;time to first token&lt;/em&gt;) and inference speed in &lt;em&gt;tokens per second&lt;/em&gt;—are decisive factors, cloud infrastructure and &lt;em&gt;frontier&lt;/em&gt; models still offer important advantages.&lt;/p&gt;
&lt;p&gt;Meanwhile, YouTube channels devoted to local AI are multiplying, and debates between &lt;em&gt;local AI fanboys&lt;/em&gt; and &lt;em&gt;frontier AI fanboys&lt;/em&gt; keep going without a clear winner: &lt;strong&gt;they are arguing about different things&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;From my perspective, the optimal approach is &lt;strong&gt;hybrid AI&lt;/strong&gt;: combining local and cloud models to optimize costs, provide fallback mechanisms that reduce dependence on a single provider, and offer an alternative when handling information I would rather not send outside my own systems.&lt;/p&gt;
&lt;p&gt;That vision led to &lt;strong&gt;Minix&lt;/strong&gt;. It is a working experiment that I already use as my personal assistant: I maintain a single conversation, and the system decides, for each request, &lt;strong&gt;where it makes the most sense to run the intelligence&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I do not want to choose a model every time I type. I want a short translation handled locally and to turn to the &lt;em&gt;cloud&lt;/em&gt; when I need more demanding reasoning. And I want to know which is happening.&lt;/p&gt;

&lt;h2 class="relative group"&gt;Minix&amp;rsquo;s server: a Mac mini M4 Pro with 24 GB
 &lt;div id="minixs-server-a-mac-mini-m4-pro-with-24-gb" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#minixs-server-a-mac-mini-m4-pro-with-24-gb" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;My LiteLLM server is a &lt;strong&gt;Mac mini with an Apple M4 Pro and 24 GB of unified memory&lt;/strong&gt;. It is the entry point to the models: it receives Minix&amp;rsquo;s requests, selects a route, and connects the agent to local inference or the cloud provider. Local inference is served through LM Studio. I do not need a server farm to experiment with this architecture, but I do need to respect the available memory and avoid loading two large models simultaneously.&lt;/p&gt;
&lt;p&gt;This Mac is a shared dependency: if the cloud fails, the local route can remain available; if the LiteLLM server itself stops, having two inference destinations does not guarantee continuity. Model fallback and gateway high availability are different problems.&lt;/p&gt;

&lt;h2 class="relative group"&gt;One conversation, three LLMs, and a router
 &lt;div id="one-conversation-three-llms-and-a-router" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#one-conversation-three-llms-and-a-router" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;I talk to Minix through Matrix. Hermes maintains the history and agent loop: it receives my message, calls the model, executes tools when needed, and returns the answer. LiteLLM provides a common entry point, &lt;code&gt;minix-smart&lt;/code&gt;, behind which the complexity router operates.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;minix-smart&lt;/code&gt; is not an LLM.&lt;/strong&gt; It is a routing alias. Understanding the system means separating the models from the components that transport, classify, and route their calls.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Function&lt;/th&gt;
 &lt;th&gt;Alias or component&lt;/th&gt;
 &lt;th&gt;Configured model&lt;/th&gt;
 &lt;th&gt;Role in the experiment&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Classify requests locally&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;minix-classifier&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;qwen35-08b-classifier-bench&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Six decisions, one per turn&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Generate local answers&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;minix-local&lt;/code&gt;, served by LM Studio&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;google/gemma-4-12b&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;T1, T3, and T5&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Generate cloud answers&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;minix-cloud&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;chatgpt/gpt-5.5&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;T2, T4, and T6&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Handle the REASONING category&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;minix-think&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;chatgpt/gpt-5.5&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Configured route, not used in these six turns&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;These are &lt;strong&gt;three distinct models&lt;/strong&gt;, not four: &lt;code&gt;minix-cloud&lt;/code&gt; and &lt;code&gt;minix-think&lt;/code&gt; point to the same cloud model name. SIMPLE and MEDIUM go to &lt;code&gt;minix-local&lt;/code&gt;; COMPLEX goes to &lt;code&gt;minix-cloud&lt;/code&gt;; REASONING goes to &lt;code&gt;minix-think&lt;/code&gt;. The default when classification fails is &lt;code&gt;minix-cloud&lt;/code&gt;: I must not confuse that policy with local recovery from a cloud outage. Session affinity is disabled, so a cloud answer does not lock the entire conversation to that destination.&lt;/p&gt;
&lt;p&gt;I use the current aliases here. The historical experiment and its appendices retain the earlier names: &lt;code&gt;kathy-smart&lt;/code&gt; → &lt;code&gt;minix-smart&lt;/code&gt;, &lt;code&gt;kathy-local&lt;/code&gt; → &lt;code&gt;minix-local&lt;/code&gt;, &lt;code&gt;kathy-classifier&lt;/code&gt; → &lt;code&gt;minix-classifier&lt;/code&gt;, &lt;code&gt;kathy-premium&lt;/code&gt; → &lt;code&gt;minix-cloud&lt;/code&gt;, and &lt;code&gt;kathy-codex&lt;/code&gt; → &lt;code&gt;minix-think&lt;/code&gt;. The sixth alias, &lt;code&gt;minix-smart-mlx&lt;/code&gt;, is an existing auxiliary alias, not an additional route in this experiment. &lt;code&gt;minix-think&lt;/code&gt; retains &lt;code&gt;reasoning_effort: high&lt;/code&gt;, which was already configured before migration. The table maps current functions to historical turns, not to a new run.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
 class="my-0 rounded-md"
 loading="lazy"
 decoding="async"
 fetchpriority="low"
 alt="Minix architecture: local Qwen classifier, local Gemma, and cloud GPT-5.5; planned extensions in pink"
 src="../../images/minix/architecture-en.svg"
 &gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;The drawing shows the configured flow, including the REASONING route that did not occur in the experiment. Planned privacy is separate and dashed pink; deployed fallback is green. Turn and call labels refer to the historical benchmark. I have web and file tools, plus a Docker terminal without network access; that does not make web tools local or establish isolation of the whole agent. These six turns invoked no tools or auxiliary vision model; I do not add a fourth LLM without evidence.&lt;/p&gt;
&lt;p&gt;&lt;a href="../../images/minix/architecture-en.mmd" &gt;Mermaid source&lt;/a&gt; · &lt;a href="../../images/minix/architecture-en.html" &gt;HTML version&lt;/a&gt;&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Editable diagram source&lt;/summary&gt;
&lt;div class="highlight-wrapper"&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;flowchart TB
 M[&amp;#34;Matrix&amp;lt;br/&amp;gt;My conversation&amp;#34;]
 H[&amp;#34;Hermes · Minix&amp;lt;br/&amp;gt;History + tool loop&amp;#34;]
 T[&amp;#34;Web · files · terminal&amp;lt;br/&amp;gt;Docker: terminal without network&amp;lt;br/&amp;gt;0 calls in the experiment&amp;#34;]
 R[&amp;#34;LiteLLM · minix-smart&amp;lt;br/&amp;gt;Router / alias, NOT an LLM&amp;lt;br/&amp;gt;No session affinity&amp;#34;]
 Q[&amp;#34;minix-classifier · local LLM&amp;lt;br/&amp;gt;qwen35-08b-classifier-bench&amp;lt;br/&amp;gt;6 historical calls&amp;#34;]
 L[&amp;#34;minix-local · LM Studio&amp;lt;br/&amp;gt;google/gemma-4-12b&amp;lt;br/&amp;gt;Local LLM · T1, T3, T5&amp;#34;]
 P[&amp;#34;minix-cloud&amp;lt;br/&amp;gt;COMPLEX · T2, T4, T6&amp;lt;br/&amp;gt;Also classifier default&amp;#34;]
 C[&amp;#34;minix-think&amp;lt;br/&amp;gt;REASONING · effort high&amp;lt;br/&amp;gt;Not used in the six turns&amp;#34;]
 G[&amp;#34;LLM cloud · chatgpt/gpt-5.5&amp;lt;br/&amp;gt;Same model in both aliases&amp;#34;]
 O[&amp;#34;Answer + route metadata&amp;lt;br/&amp;gt;Hermes → Matrix · local / cloud&amp;lt;br/&amp;gt;Not an audit of auxiliary calls&amp;#34;]
 S[&amp;#34;PLANNED local substitution&amp;lt;br/&amp;gt;History + text + tool results&amp;lt;br/&amp;gt;Identity mapping stays local&amp;#34;]
 K[&amp;#34;Cloud with placeholders&amp;lt;br/&amp;gt;Only placeholder-bearing text&amp;lt;br/&amp;gt;PLANNED flow&amp;#34;]
 F[&amp;#34;DEPLOYED reactive fallback&amp;lt;br/&amp;gt;smart / cloud / think → local · max 1 hop&amp;lt;br/&amp;gt;500 / 429: 1 retry · 400 / 401 / timeout: 0&amp;lt;br/&amp;gt;Cooldown: 60 s after 2 failures&amp;#34;]
 M --&amp;gt; H
 H --&amp;gt; T
 T --&amp;gt; H
 H --&amp;gt; R
 R --&amp;gt; Q
 Q --&amp;gt; |&amp;#34;tier&amp;#34;| R
 R --&amp;gt; |&amp;#34;SIMPLE / MEDIUM&amp;#34;| L
 R --&amp;gt; |&amp;#34;COMPLEX / default&amp;#34;| P
 R --&amp;gt; |&amp;#34;REASONING&amp;#34;| C
 P --&amp;gt; G
 C --&amp;gt; G
 L --&amp;gt; O
 G --&amp;gt; O
 O --&amp;gt; |&amp;#34;tool loop&amp;#34;| H
 S -.-&amp;gt; K
 V[&amp;#34;Planned LOCAL restoration&amp;lt;br/&amp;gt;Validate markers + restore&amp;lt;br/&amp;gt;Private mapping stays local&amp;#34;]
 D[&amp;#34;Local health every 300 s · no cloud probes: passive recovery&amp;lt;br/&amp;gt;Local terminal: error on failure · no return to cloud&amp;lt;br/&amp;gt;No global deadline · health filter may fail open when all destinations are unhealthy&amp;#34;]
 K -.-&amp;gt; V
 S -.-&amp;gt; V
 R --&amp;gt; |&amp;#34;fallback&amp;#34;| F
 P --&amp;gt; |&amp;#34;fallback&amp;#34;| F
 C --&amp;gt; |&amp;#34;fallback&amp;#34;| F
 F --&amp;gt; L
 classDef planned fill:#2b1725,stroke:#fb7185,stroke-dasharray:5 5,color:#ffffff
 class S,K,V planned
 classDef deployed fill:#064e3b,stroke:#34d399,color:#ffffff
 class F,D deployed&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/details&gt;
&lt;p&gt;An answer carries a marker: 🖥️ for local generation and ☁️ for cloud generation. The system adds it from metadata about the served route; it is not the model describing itself. It identifies the final generation, not every auxiliary call in a tool-using task.&lt;/p&gt;
&lt;p&gt;I checked the names and mappings against the configuration and experiment artifacts. Logs directly corroborate Gemma and the classifier. The hybrid run did not archive each cloud response&amp;rsquo;s raw header: the marker and its rendering code support the cloud category, while the configured mapping supports GPT-5.5. The baseline did preserve all six &lt;code&gt;kathy-premium&lt;/code&gt; deployment headers. That identifies the deployment, not an independent fingerprint of the provider&amp;rsquo;s model weights.&lt;/p&gt;

&lt;h2 class="relative group"&gt;The experiment: returning to local after using the cloud
 &lt;div id="the-experiment-returning-to-local-after-using-the-cloud" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-experiment-returning-to-local-after-using-the-cloud" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;I prepared six requests about one project, &lt;strong&gt;Puente&lt;/strong&gt;, a file synchronizer between two servers that may work while disconnected. I fixed the prompts before starting, without requesting a particular route or adjusting the classifier during the test. I preserved the existing conversation, including its earlier history.&lt;/p&gt;
&lt;p&gt;The sequence was &lt;strong&gt;local → cloud → local → cloud → local → cloud&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;T1, local:&lt;/strong&gt; a welcome sentence and three alternative names. Gemma offered Nexo, Vínculo, and Nodo.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;T2, cloud:&lt;/strong&gt; metadata, merge rules, pseudocode, tombstones, and convergence analysis. The answer proposed causality and concurrent variants, but not a complete proof of the algorithm.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;T3, local:&lt;/strong&gt; literal extraction of three values. It returned &lt;code&gt;nombre=Puente&lt;/code&gt;, &lt;code&gt;nodos=A,B&lt;/code&gt;, and &lt;code&gt;política=conservar conflictos&lt;/code&gt;, exactly as requested.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;T4, cloud:&lt;/strong&gt; a concrete edit/delete conflict. The answer preserved variants &lt;code&gt;(2,1)&lt;/code&gt; and &lt;code&gt;(1,2)&lt;/code&gt;, summarized their context as &lt;code&gt;(2,2)&lt;/code&gt;, and explained a later resolution &lt;code&gt;(3,2)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;T5, local:&lt;/strong&gt; a short English translation: “Puente preserves concurrent edits and requires explicit resolution of conflicts.”&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;T6, cloud:&lt;/strong&gt; a critical review of three dangerous shortcuts: choosing content by hash, collecting tombstones too early, and reusing an identity with its counter reset to zero.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The interesting part is not just that a difficult question reaches the cloud. It is that &lt;strong&gt;the next simple question returns to local execution while retaining the conversation&lt;/strong&gt;. I do not need another chat or to move the context myself.&lt;/p&gt;
&lt;p&gt;To make the result reviewable beyond my summary, I include &lt;a href="../../posts/minix-hybrid-agent/#complete-conversations-the-six-turns" &gt;all six prompts and all twelve complete answers, hybrid and cloud, at the end of this article&lt;/a&gt;. The source-language text is preserved verbatim, including its mistakes; I have not reconstructed or corrected it to make the experiment look better. The appendix has English editorial guidance, not translated experimental dialogue.&lt;/p&gt;
&lt;p&gt;Each run produced six final answers, with no tool calls or observed retries. Both handled the simple tasks satisfactorily. The complex answers offered useful ideas but retained gaps around general conflict-set merging and safe version collection. My review was not blind and does not establish statistical quality equivalence: choosing a cloud model does not turn its answer into a formal proof.&lt;/p&gt;

&lt;h2 class="relative group"&gt;What I measured, and what 48% means
 &lt;div id="what-i-measured-and-what-48-means" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-i-measured-and-what-48-means" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Historical benchmark, before the alias migration and new fallback policy.&lt;/strong&gt; I retain the original measurements unchanged; they are not a rerun of the current configuration.&lt;/p&gt;
&lt;p&gt;I recorded input and output tokens, including each call&amp;rsquo;s context. I then repeated the same six prompts in an all-cloud run, accumulating that run&amp;rsquo;s own answers rather than the hybrid answers.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Turn&lt;/th&gt;
 &lt;th&gt;Hybrid route&lt;/th&gt;
 &lt;th style="text-align: right"&gt;Local answer-call tokens&lt;/th&gt;
 &lt;th style="text-align: right"&gt;Hybrid cloud tokens&lt;/th&gt;
 &lt;th style="text-align: right"&gt;All-cloud baseline tokens&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;T1&lt;/td&gt;
 &lt;td&gt;local&lt;/td&gt;
 &lt;td style="text-align: right"&gt;7,351&lt;/td&gt;
 &lt;td style="text-align: right"&gt;0&lt;/td&gt;
 &lt;td style="text-align: right"&gt;8,257&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;T2&lt;/td&gt;
 &lt;td&gt;cloud&lt;/td&gt;
 &lt;td style="text-align: right"&gt;0&lt;/td&gt;
 &lt;td style="text-align: right"&gt;9,356&lt;/td&gt;
 &lt;td style="text-align: right"&gt;9,368&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;T3&lt;/td&gt;
 &lt;td&gt;local&lt;/td&gt;
 &lt;td style="text-align: right"&gt;8,650&lt;/td&gt;
 &lt;td style="text-align: right"&gt;0&lt;/td&gt;
 &lt;td style="text-align: right"&gt;9,441&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;T4&lt;/td&gt;
 &lt;td&gt;cloud&lt;/td&gt;
 &lt;td style="text-align: right"&gt;0&lt;/td&gt;
 &lt;td style="text-align: right"&gt;10,284&lt;/td&gt;
 &lt;td style="text-align: right"&gt;10,301&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;T5&lt;/td&gt;
 &lt;td&gt;local&lt;/td&gt;
 &lt;td style="text-align: right"&gt;9,631&lt;/td&gt;
 &lt;td style="text-align: right"&gt;0&lt;/td&gt;
 &lt;td style="text-align: right"&gt;10,379&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;T6&lt;/td&gt;
 &lt;td&gt;cloud&lt;/td&gt;
 &lt;td style="text-align: right"&gt;0&lt;/td&gt;
 &lt;td style="text-align: right"&gt;11,316&lt;/td&gt;
 &lt;td style="text-align: right"&gt;11,454&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td style="text-align: right"&gt;&lt;strong&gt;25,632&lt;/strong&gt;&lt;/td&gt;
 &lt;td style="text-align: right"&gt;&lt;strong&gt;30,956&lt;/strong&gt;&lt;/td&gt;
 &lt;td style="text-align: right"&gt;&lt;strong&gt;59,200&lt;/strong&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;These totals include input and output, not just visible text. The baseline adds up to &lt;strong&gt;56,550 input + 2,650 output&lt;/strong&gt;; the hybrid cloud portion is &lt;strong&gt;28,510 + 2,446&lt;/strong&gt;. The classifier consumed another &lt;strong&gt;2,014 local tokens&lt;/strong&gt; and about &lt;strong&gt;1.71 seconds&lt;/strong&gt; in aggregate, separate from answer-generation calls.&lt;/p&gt;
&lt;p&gt;The observed difference is:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;(59,200 − 30,956) / 59,200 × 100 = 47.71%: 28,244 fewer cloud tokens.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I have not converted the 25,632 local tokens into supposed cloud-token savings: I compare cloud usage across two actual runs. Nor have I eliminated that work; I moved some of it to local infrastructure. The hybrid&amp;rsquo;s 14,336 cached cloud tokens and the baseline&amp;rsquo;s 15,360 are already included in input, not counted twice. Different tokenizers prevent treating combined local/cloud counts as equivalent computation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This is an indicative comparison, not a controlled causal or universal savings estimate.&lt;/strong&gt; I recovered the available initial history and tools, but not every original dynamic instruction exactly. The hybrid ran through Matrix and the baseline through the API; their answers and caches evolved differently. One six-turn conversation does not measure the classifier&amp;rsquo;s general accuracy either.&lt;/p&gt;
&lt;p&gt;The defensible claim is: &lt;strong&gt;in this experiment, Minix used approximately 48% fewer cloud tokens than a comparable all-cloud run&lt;/strong&gt;. That does not mean a 48% lower bill or greater speed: I have not calculated monetary cost, electricity, hardware, or maintenance, or run a controlled latency benchmark.&lt;/p&gt;

&lt;h2 class="relative group"&gt;Continuity: reactive fallback is now deployed
 &lt;div id="continuity-reactive-fallback-is-now-deployed" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#continuity-reactive-fallback-is-now-deployed" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;I deployed a bounded LiteLLM policy: &lt;code&gt;minix-cloud&lt;/code&gt;, &lt;code&gt;minix-think&lt;/code&gt;, and &lt;code&gt;minix-smart&lt;/code&gt; can fall back to &lt;code&gt;minix-local&lt;/code&gt;, with &lt;strong&gt;at most one fallback hop&lt;/strong&gt;. Local is terminal: if it also fails, the request ends with an error, without returning to cloud or entering a cycle. This is not a promise that Gemma can solve every task equally well.&lt;/p&gt;
&lt;p&gt;For cloud HTTP 500 and 429 errors I allow &lt;strong&gt;one retry&lt;/strong&gt;; for 400, 401, and timeouts, &lt;strong&gt;zero retries&lt;/strong&gt;. Disabling retries does not mean disabling fallback. After the second failure, the deployment enters a &lt;strong&gt;60-second&lt;/strong&gt; cooldown. I retain the existing inference timeouts; there is no strict global deadline for the entire sequence.&lt;/p&gt;
&lt;p&gt;I check local health every &lt;strong&gt;300 seconds&lt;/strong&gt;. I do not run background cloud probes: cloud recovery is &lt;strong&gt;passive&lt;/strong&gt;, when cooldown expires and a new request tries the route again. If all destinations are marked unhealthy, the health filter can allow another attempt (&lt;em&gt;fail-open&lt;/em&gt;); that does not change the terminal error when local ultimately fails. Both cloud aliases share a provider and model: they are not independent redundancy.&lt;/p&gt;
&lt;p&gt;Verification separates failure tests from live use. I have &lt;strong&gt;28 passing unit tests&lt;/strong&gt; and earlier evidence from &lt;strong&gt;12 isolated loopback cases&lt;/strong&gt;, not rerun during this migration. I did not force a production outage. In production I verified &lt;strong&gt;streaming HTTP 200&lt;/strong&gt; responses from local, cloud, and think without fallback, with content, completion, and exact served-route identifiers; I also tested smart and confirmed an actual final local reply in Matrix. This validates those paths, not every API mode.&lt;/p&gt;
&lt;p&gt;One specific limitation remains: the &lt;strong&gt;non-streaming&lt;/strong&gt; cloud call returns &lt;code&gt;Unknown items in responses API response: []&lt;/code&gt;. I reproduced the same failure with the previous configuration; I do not attribute it to the migration or claim it is fixed. The streaming path passed verification.&lt;/p&gt;
&lt;p&gt;Inference fallback does not imply semantic task reassessment, an automatic degraded-mode notice, or demonstrated protection against repeating tools with external effects. I do not claim to have validated those behaviors. Accessing an unavailable external service does not become possible just because the model changes. Runtime, storage, and communication must remain available: &lt;strong&gt;local inference does not guarantee that a remote chat survives a lost connection&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 class="relative group"&gt;Privacy: send the problem, not necessarily the identities
 &lt;div id="privacy-send-the-problem-not-necessarily-the-identities" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#privacy-send-the-problem-not-necessarily-the-identities" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;The other decision I want to add concerns data rather than difficulty: &lt;strong&gt;what information needs to leave my environment to solve the task?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The planned mechanism would locally replace sensitive data with consistent identifiers, keeping the mapping under local control, with restricted access and a lifetime limited to the task. For example, my fictional request:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;“Draft an email to Elena at Taller Norte about Project Brisa.”&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;would become, before leaving:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;“Draft an email to [PERSON_1] at [COMPANY_1] about [PROJECT_1].”&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;The cloud would write using those markers. On return, the local layer would validate each identifier and restore the original values for me. The same person would retain the same marker within the task; an unknown or altered marker would not authorize invented names or retrieval of a different mapping.&lt;/p&gt;
&lt;p&gt;Cleaning the latest message alone would not be enough. I want the check to cover everything leaving: history, documents, tool results, and relevant metadata. An attachment that cannot be handled safely should stay local or be excluded. If solving the task requires revealing an identity, the alternative would be to keep it local, request my specific authorization, or explain the limitation—not silently send the original.&lt;/p&gt;
&lt;p&gt;This is &lt;strong&gt;reversible pseudonymization&lt;/strong&gt;, not guaranteed anonymity. Context can reveal identities even after names disappear; detection and markers can fail too. That is why output validation, logs without secrets, and conservative handling of uncertain cases belong in the behavior I want.&lt;/p&gt;

&lt;h2 class="relative group"&gt;My assistant, not a contest between models
 &lt;div id="my-assistant-not-a-contest-between-models" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#my-assistant-not-a-contest-between-models" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;I do not use Minix to prove that local models always replace cloud models, or the reverse. I use it to choose where each task runs without having to manage that choice manually.&lt;/p&gt;
&lt;p&gt;The experiment already lets me observe something concrete: a conversation that returns to local execution after using the cloud, with lower cloud consumption in the comparison I ran. Reactive fallback is now deployed; data control remains the next step, without erasing the limits of what I measured.&lt;/p&gt;
&lt;p&gt;The question stops being “which model is the most powerful?” and becomes something more useful:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Which part of this task really needs to leave my environment?&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;

&lt;h2 class="relative group"&gt;Complete conversations: the six turns
 &lt;div id="complete-conversations-the-six-turns" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#complete-conversations-the-six-turns" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;I preserve the six original prompts and all twelve complete answers here: one hybrid and one all-cloud answer per turn. Each run accumulated its own answers; I place them together for review, not as a single conversation.&lt;/p&gt;
&lt;p&gt;The blocks are verbatim in their original language (Spanish, except for the English translations requested in T5). This is not an English translation of the experiment. I retain even plural wording and questionable claims inside quoted material; they are not my editorial voice. Experiment identifiers are part of the prompts. Hybrid-run icons are system-added metadata, not model-generated words. I exclude internal instructions, earlier history, Matrix identifiers, and credentials.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Critical reading:&lt;/strong&gt; both runs handle the simple tasks satisfactorily. T2 does not prove a general merge over conflict sets; T4 works through one example rather than establishing that proof. T6 identifies useful risks and properties but does not establish minimal garbage-collection conditions. In the baseline, rejecting old dominated operations must not be confused with discarding legitimate concurrent edits. I ran neither a distributed synchronizer nor a blind quality evaluation here.&lt;/p&gt;

&lt;h3 class="relative group"&gt;Contents
 &lt;div id="contents" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#contents" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h3&gt;
&lt;p&gt;&lt;a href="../../posts/minix-hybrid-agent/#t1" &gt;T1&lt;/a&gt; · &lt;a href="../../posts/minix-hybrid-agent/#t2" &gt;T2&lt;/a&gt; · &lt;a href="../../posts/minix-hybrid-agent/#t3" &gt;T3&lt;/a&gt; · &lt;a href="../../posts/minix-hybrid-agent/#t4" &gt;T4&lt;/a&gt; · &lt;a href="../../posts/minix-hybrid-agent/#t5" &gt;T5&lt;/a&gt; · &lt;a href="../../posts/minix-hybrid-agent/#t6" &gt;T6&lt;/a&gt;&lt;/p&gt;

&lt;h3 class="relative group"&gt;T1
 &lt;div id="t1" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#t1" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h3&gt;

&lt;h4 class="relative group"&gt;My original prompt
 &lt;div id="my-original-prompt" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#my-original-prompt" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h4&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[MXHYB-20260926-A T1] Vamos a diseñar juntos un pequeño sincronizador de archivos entre dos servidores que pueden trabajar desconectados, A y B. Lo llamaremos Puente. De momento, responde en español con una bienvenida de una frase y tres nombres alternativos cortos. No crees archivos ni ejecutes nada.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 class="relative group"&gt;Hybrid answer — local
 &lt;div id="hybrid-answer--local" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#hybrid-answer--local" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h4&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;🖥️ Bienvenido al diseño del proyecto Puente para la sincronización entre servidores.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Nombres alternativos: Nexo, Vínculo, Nodo.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 class="relative group"&gt;All-cloud answer
 &lt;div id="all-cloud-answer" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#all-cloud-answer" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h4&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Bienvenido a Puente: podemos diseñar un sincronizador simple, robusto y preparado para desconexiones entre A y B.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; Nexo
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; Brisa
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- Trama&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 class="relative group"&gt;T2
 &lt;div id="t2" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#t2" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h3&gt;

&lt;h4 class="relative group"&gt;My original prompt
 &lt;div id="my-original-prompt-1" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#my-original-prompt-1" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h4&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[MXHYB-20260926-A T2] Sigamos con Puente. A y B pueden editar o borrar el mismo archivo mientras están desconectados; los relojes físicos no son fiables y la red duplica y reordena mensajes. Diseña un algoritmo de sincronización que converja sin perder silenciosamente ediciones concurrentes. Especifica metadatos, reglas de fusión, tombstones y condiciones para recolectarlos. Da pseudocódigo y justifica convergencia e idempotencia; incluye un contraejemplo a elegir siempre la fecha más reciente. Unas 450 palabras, sin ejecutar ni crear archivos.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 class="relative group"&gt;Hybrid answer — cloud
 &lt;div id="hybrid-answer--cloud" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#hybrid-answer--cloud" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h4&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;☁️ Un diseño seguro para Puente debe tratar cada archivo como un objeto replicado con historial causal, no como “el último gana”.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Metadatos por archivo**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`id`&lt;/span&gt;: ruta lógica o identificador estable.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`replica_id`&lt;/span&gt;: &lt;span class="sb"&gt;`A`&lt;/span&gt; o &lt;span class="sb"&gt;`B`&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`version_vector`&lt;/span&gt;: mapa &lt;span class="sb"&gt;`{replica_id: contador}`&lt;/span&gt; que resume las actualizaciones conocidas.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`dot`&lt;/span&gt;: par único &lt;span class="sb"&gt;`(replica_id, contador)`&lt;/span&gt; de la operación local.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`content_hash`&lt;/span&gt;: hash del contenido, si existe.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`deleted`&lt;/span&gt;: booleano.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`tombstone`&lt;/span&gt;: registro de borrado con &lt;span class="sb"&gt;`dot`&lt;/span&gt;, &lt;span class="sb"&gt;`version_vector`&lt;/span&gt; y fecha local solo informativa.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`conflicts`&lt;/span&gt;: conjunto de variantes concurrentes, cada una con contenido y metadatos causales.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Los relojes físicos no deciden nada. Solo sirven para mostrar información al usuario.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Reglas**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Cada edición local incrementa el contador propio y crea una nueva versión causal. Cada borrado crea un tombstone, no elimina inmediatamente el registro. Al sincronizar, se comparan vectores:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; Si &lt;span class="sb"&gt;`remote.version_vector &amp;lt;= local.version_vector`&lt;/span&gt;, el remoto ya está incluido: ignorar.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; Si &lt;span class="sb"&gt;`local.version_vector &amp;lt;= remote.version_vector`&lt;/span&gt;, aceptar remoto.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; Si ninguno domina al otro, hay concurrencia:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;-&lt;/span&gt; edición vs edición: conservar ambas variantes en &lt;span class="sb"&gt;`conflicts`&lt;/span&gt;, o intentar fusión automática si el tipo lo permite; si la fusión no es limpia, exponer conflicto.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;-&lt;/span&gt; edición vs borrado: no borrar silenciosamente; conservar la edición como conflicto contra tombstone.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;-&lt;/span&gt; borrado vs borrado: fusionar tombstones tomando la unión causal.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Pseudocódigo**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;```text
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;edit(file, content):
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; clock[self] += 1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; dot = (self, clock[self])
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; file.content = content
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; file.deleted = false
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; file.version_vector[self] = clock[self]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; file.dot = dot
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; file.conflicts = {}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;delete(file):
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; clock[self] += 1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; dot = (self, clock[self])
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; file.content = null
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; file.deleted = true
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; file.tombstone = {dot, version_vector + self counter}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; file.version_vector[self] = clock[self]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;merge(local, remote):
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; if dominates(local.vv, remote.vv):
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; return local
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; if dominates(remote.vv, local.vv):
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; return remote
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; merged.vv = pointwise_max(local.vv, remote.vv)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; if local.deleted and remote.deleted:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; merged.deleted = true
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; merged.tombstone = merge_tombstones(local, remote)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; return merged
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; if not local.deleted and not remote.deleted:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; result = try_semantic_merge(local.content, remote.content)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; if result.clean:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; merged.content = result.content
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; merged.conflicts = {}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; else:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; merged.content = null
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; merged.conflicts = {variant(local), variant(remote)}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; return merged
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; merged.deleted = false
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; merged.content = null
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; merged.conflicts = {variant(local), variant(remote)}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; return merged
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;```&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Tombstones y recolección**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Un tombstone solo puede eliminarse cuando todas las réplicas conocidas han observado causalmente el borrado. Con dos servidores, A puede recolectar un tombstone de B cuando sabe que B ha recibido un vector que domina el vector del tombstone, y viceversa. En sistemas ampliables, hace falta un mecanismo de membresía estable o acknowledgements por réplica. Si hay réplicas desconocidas o restauraciones desde copias antiguas, no se debe recolectar aún.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Convergencia e idempotencia**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;La convergencia viene de usar una unión determinista: máximo punto a punto de vectores, preservación de variantes concurrentes y reglas simétricas. Si A y B reciben los mismos estados, acaban calculando el mismo resultado. La idempotencia se obtiene porque reentregar el mismo mensaje no cambia nada: un vector ya dominado se ignora, y los conflictos se identifican por &lt;span class="sb"&gt;`dot`&lt;/span&gt; o hash causal, no por número de recepción.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Contraejemplo al “más reciente gana”**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;A edita &lt;span class="sb"&gt;`informe.txt`&lt;/span&gt; a “versión legal” con reloj adelantado a las 12:10. B, desconectado, edita a “versión financiera” con reloj atrasado a las 12:05. Ambas ediciones son concurrentes y válidas. Elegir la fecha más reciente descarta silenciosamente la versión financiera, aunque no fue sobrescrita causalmente por A.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 class="relative group"&gt;All-cloud answer
 &lt;div id="all-cloud-answer-1" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#all-cloud-answer-1" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h4&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Propongo que Puente use versiones causales, no fechas físicas. Cada servidor replica un conjunto de objetos CRDT por ruta lógica, con detección explícita de conflictos.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Metadatos por archivo**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`node_id`&lt;/span&gt;: &lt;span class="sb"&gt;`A`&lt;/span&gt; o &lt;span class="sb"&gt;`B`&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`version_vector`&lt;/span&gt;: mapa &lt;span class="sb"&gt;`{nodo: contador}`&lt;/span&gt; que identifica causalidad.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`content_hash`&lt;/span&gt;: hash del contenido.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`op_id`&lt;/span&gt;: par único &lt;span class="sb"&gt;`(node_id, contador)`&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`deleted`&lt;/span&gt;: booleano.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`tombstone`&lt;/span&gt;: si está borrado, guarda &lt;span class="sb"&gt;`delete_vector`&lt;/span&gt;, &lt;span class="sb"&gt;`delete_op_id`&lt;/span&gt; y fecha local solo informativa.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`conflicts`&lt;/span&gt;: conjunto de variantes concurrentes, cada una con contenido/hash/vector.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Cada operación local incrementa el contador del nodo. Una edición genera una versión con &lt;span class="sb"&gt;`deleted=false`&lt;/span&gt;; un borrado genera una tombstone con &lt;span class="sb"&gt;`deleted=true`&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Reglas de sincronización**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Al recibir un estado remoto para una ruta:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;```pseudo
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;function dominates(v1, v2):
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; return forall n: v1[n] &amp;gt;= v2[n] and exists n: v1[n] &amp;gt; v2[n]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;function concurrent(v1, v2):
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; return not dominates(v1, v2) and not dominates(v2, v1)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;function merge(local, remote):
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; if remote.op_id already_seen:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; return local
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; mark_seen(remote.op_id)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; if dominates(remote.vector, local.vector):
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; return remote
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; if dominates(local.vector, remote.vector):
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; return local
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; if concurrent(local.vector, remote.vector):
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; return resolve_concurrent(local, remote)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;```&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;La resolución concurrente no debe descartar silenciosamente. Para archivos binarios o cambios incompatibles:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;```pseudo
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;function resolve_concurrent(x, y):
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; if x.deleted and y.deleted:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; return tombstone(union_vector(x.vector, y.vector))
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; if x.deleted and not y.deleted:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; return conflict_record(
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; visible = y,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; conflicts = [tombstone_variant(x)]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; if not x.deleted and y.deleted:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; return conflict_record(
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; visible = x,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; conflicts = [tombstone_variant(y)]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; if same content_hash:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; return version(content=x.content, vector=union_vector(x.vector, y.vector))
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; if textual_auto_merge_possible(x.content, y.content, base):
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; return merged_file(
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; content=three_way_merge(base, x, y),
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; vector=union_vector(x.vector, y.vector),
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; conflicts=[]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; return conflict_record(
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; visible = deterministic_choice(x, y), // solo para presentación
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; conflicts = [x, y],
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; vector = union_vector(x.vector, y.vector)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;```&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;En disco, un conflicto puede exponerse como &lt;span class="sb"&gt;`archivo`&lt;/span&gt;, &lt;span class="sb"&gt;`archivo.conflict-A`&lt;/span&gt;, &lt;span class="sb"&gt;`archivo.conflict-B`&lt;/span&gt; o como un manifiesto. Lo importante: ambas ediciones quedan accesibles.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Tombstones**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Una tombstone representa “esta ruta fue borrada en esta causalidad”. Debe propagarse igual que una edición para evitar que una réplica antigua resucite el archivo. Puede recolectarse solo cuando todos los nodos conocidos han observado el borrado:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;```pseudo
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;function can_gc_tombstone(t):
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; return forall node in membership:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ack_vector[node] &amp;gt;= t.delete_vector[node]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;```&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Si la membresía cambia, hace falta un protocolo de épocas: no se recolectan tombstones de una época hasta que todos los miembros de esa época hayan acusado recibo, o hasta que exista una instantánea completa posterior aceptada por todos los miembros activos.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Convergencia e idempotencia**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Converge porque la fusión es determinista, conmutativa y asociativa sobre vectores causales y conjuntos de variantes. Los mensajes duplicados no cambian el resultado porque &lt;span class="sb"&gt;`op_id`&lt;/span&gt; y los hashes hacen la aplicación idempotente. El reordenamiento no importa: si llega primero una versión antigua, luego será dominada; si llegan versiones concurrentes, se conservan ambas.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Contraejemplo a “fecha más reciente”**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;A y B editan &lt;span class="sb"&gt;`nota.txt`&lt;/span&gt; desconectados. A escribe “precio 10” a las 10:05, pero su reloj va adelantado; B escribe “precio 12” a las 10:01 reales, con reloj atrasado marcando 09:50. Al reconectar, elegir la fecha más reciente conserva A y borra B, aunque son cambios concurrentes y ambos deben revisarse. Peor aún: con relojes desviados, el resultado puede depender del reloj defectuoso, no de la causalidad.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 class="relative group"&gt;T3
 &lt;div id="t3" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#t3" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h3&gt;

&lt;h4 class="relative group"&gt;My original prompt
 &lt;div id="my-original-prompt-2" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#my-original-prompt-2" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h4&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[MXHYB-20260926-A T3] Para la ficha de Puente, extrae literalmente estos tres valores del texto, sin explicaciones ni consultar nada: «nombre=Puente; nodos=A,B; política=conservar conflictos». Devuelve solo tres líneas con formato clave=valor.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 class="relative group"&gt;Hybrid answer — local
 &lt;div id="hybrid-answer--local-1" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#hybrid-answer--local-1" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h4&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;🖥️ nombre=Puente
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nodos=A,B
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;política=conservar conflictos&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 class="relative group"&gt;All-cloud answer
 &lt;div id="all-cloud-answer-2" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#all-cloud-answer-2" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h4&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nombre=Puente
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nodos=A,B
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;política=conservar conflictos&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 class="relative group"&gt;T4
 &lt;div id="t4" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#t4" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h3&gt;

&lt;h4 class="relative group"&gt;My original prompt
 &lt;div id="my-original-prompt-3" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#my-original-prompt-3" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h4&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[MXHYB-20260926-A T4] Probemos el diseño de Puente con este historial. Parten del mismo archivo con vector (1,1). Desconectados, A lo edita y genera (2,1), B lo borra y genera (1,2). A recibe primero el borrado, luego un duplicado del archivo inicial; B recibe primero la edición y luego un duplicado del borrado. Calcula el estado final de ambos y demuestra qué propiedades debe cumplir la fusión para coincidir. Explica cómo resolver explícitamente el conflicto con una nueva versión y por qué borrar el tombstone demasiado pronto puede resucitar datos. Señala cualquier ambigüedad de tu propuesta anterior y corrígela. Unas 350 palabras; razonamiento en el chat, sin herramientas ni archivos.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 class="relative group"&gt;Hybrid answer — cloud
 &lt;div id="hybrid-answer--cloud-1" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#hybrid-answer--cloud-1" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h4&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;☁️ Estado inicial: archivo existente con vector &lt;span class="sb"&gt;`(1,1)`&lt;/span&gt;. Luego hay dos operaciones concurrentes: edición de A con vector &lt;span class="sb"&gt;`(2,1)`&lt;/span&gt; y borrado de B con vector &lt;span class="sb"&gt;`(1,2)`&lt;/span&gt;. Ninguna domina a la otra, porque A tiene más información de A y B más información de B.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Estado final esperado**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; En A, al recibir el borrado &lt;span class="sb"&gt;`(1,2)`&lt;/span&gt;, la fusión detecta concurrencia edición-vs-borrado y debe producir un conflicto explícito: variante editada de A &lt;span class="sb"&gt;`(2,1)`&lt;/span&gt; frente a tombstone de B &lt;span class="sb"&gt;`(1,2)`&lt;/span&gt;, con vector fusionado &lt;span class="sb"&gt;`(2,2)`&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; El duplicado del archivo inicial &lt;span class="sb"&gt;`(1,1)`&lt;/span&gt; queda dominado por &lt;span class="sb"&gt;`(2,2)`&lt;/span&gt;, así que se ignora.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; En B, al recibir la edición &lt;span class="sb"&gt;`(2,1)`&lt;/span&gt;, obtiene el mismo conflicto edición-vs-borrado con vector &lt;span class="sb"&gt;`(2,2)`&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; El duplicado del borrado &lt;span class="sb"&gt;`(1,2)`&lt;/span&gt; queda dominado por &lt;span class="sb"&gt;`(2,2)`&lt;/span&gt; o ya está incluido como variante/tombstone, así que no cambia nada.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Por tanto, ambos terminan con un estado conflictivo convergente: “archivo no resuelto”, con la edición de A preservada y el borrado de B preservado como intención concurrente. No debe aparecer como archivo normal ni como borrado definitivo hasta que alguien resuelva.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Propiedades necesarias de la fusión**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;La fusión debe ser:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; conmutativa: &lt;span class="sb"&gt;`merge(edición, borrado) = merge(borrado, edición)`&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; asociativa: el orden de llegada no cambia el resultado final;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; idempotente: duplicados como &lt;span class="sb"&gt;`(1,1)`&lt;/span&gt; o &lt;span class="sb"&gt;`(1,2)`&lt;/span&gt; no alteran el estado;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; inflacionaria causalmente: no pierde dots ni conocimiento causal;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; determinista: las mismas entradas producen el mismo estado.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Resolución explícita**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Un usuario o regla superior debe crear una nueva operación, por ejemplo en A: “conservar edición y descartar borrado”. Esa resolución incrementa A y genera &lt;span class="sb"&gt;`(3,2)`&lt;/span&gt;, dominando tanto &lt;span class="sb"&gt;`(2,1)`&lt;/span&gt; como &lt;span class="sb"&gt;`(1,2)`&lt;/span&gt;. El nuevo estado contiene el archivo elegido, &lt;span class="sb"&gt;`conflicts={}`&lt;/span&gt;, y un registro causal de que el tombstone conflictivo fue observado y resuelto. Si se elige borrar, se crea un nuevo tombstone &lt;span class="sb"&gt;`(3,2)`&lt;/span&gt; que domina la edición.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Riesgo de recolectar tombstones**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Si B borra el tombstone demasiado pronto y luego recibe desde A una copia antigua del archivo &lt;span class="sb"&gt;`(1,1)`&lt;/span&gt;, puede no tener memoria causal del borrado &lt;span class="sb"&gt;`(1,2)`&lt;/span&gt;. Entonces podría tratar el archivo viejo como válido y resucitar datos borrados. El tombstone debe conservarse hasta que todos los nodos conocidos hayan reconocido un vector que domina el borrado y no puedan reintroducir estados anteriores.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Ambigüedad corregida**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Antes dije &lt;span class="sb"&gt;`merged.deleted=false`&lt;/span&gt; en edición-vs-borrado. Eso es ambiguo: podría parecer que el archivo existe. Corrección: el estado debe ser &lt;span class="sb"&gt;`status=conflict`&lt;/span&gt;, sin contenido canónico, con variantes explícitas &lt;span class="sb"&gt;`{edit_variant, delete_variant}`&lt;/span&gt; y vector &lt;span class="sb"&gt;`(2,2)`&lt;/span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 class="relative group"&gt;All-cloud answer
 &lt;div id="all-cloud-answer-3" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#all-cloud-answer-3" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h4&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Estado inicial: archivo vivo con vector &lt;span class="sb"&gt;`(1,1)`&lt;/span&gt;. Durante la desconexión:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; A edita: versión viva &lt;span class="sb"&gt;`EA = (2,1)`&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; B borra: tombstone &lt;span class="sb"&gt;`DB = (1,2)`&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Estos vectores son concurrentes: &lt;span class="sb"&gt;`(2,1)`&lt;/span&gt; no domina a &lt;span class="sb"&gt;`(1,2)`&lt;/span&gt; y &lt;span class="sb"&gt;`(1,2)`&lt;/span&gt; no domina a &lt;span class="sb"&gt;`(2,1)`&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Estado final esperado**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Cuando A recibe el borrado &lt;span class="sb"&gt;`DB`&lt;/span&gt;, fusiona &lt;span class="sb"&gt;`EA`&lt;/span&gt; con &lt;span class="sb"&gt;`DB`&lt;/span&gt; y obtiene un conflicto explícito: “edición de A” frente a “borrado de B”. El archivo no debe quedar simplemente vivo ni simplemente borrado; debe quedar en estado conflictivo con ambas variantes preservadas.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Luego A recibe un duplicado del archivo inicial &lt;span class="sb"&gt;`(1,1)`&lt;/span&gt;. Como &lt;span class="sb"&gt;`(2,1)`&lt;/span&gt; y &lt;span class="sb"&gt;`(1,2)`&lt;/span&gt; dominan causalmente a &lt;span class="sb"&gt;`(1,1)`&lt;/span&gt;, ese duplicado se ignora.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Cuando B recibe la edición &lt;span class="sb"&gt;`EA`&lt;/span&gt;, fusiona &lt;span class="sb"&gt;`DB`&lt;/span&gt; con &lt;span class="sb"&gt;`EA`&lt;/span&gt; y debe obtener exactamente el mismo conflicto. Luego recibe un duplicado del borrado &lt;span class="sb"&gt;`DB`&lt;/span&gt;; como ya está incluido en el estado conflictivo, no cambia nada.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Resultado final en ambos: conflicto &lt;span class="sb"&gt;`{EA viva, DB tombstone}`&lt;/span&gt;, vector resumen &lt;span class="sb"&gt;`(2,2)`&lt;/span&gt; o conjunto causal equivalente, con contenido de A preservado y borrado de B preservado.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Propiedades necesarias de la fusión**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;La fusión debe ser:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; Conmutativa: &lt;span class="sb"&gt;`merge(EA, DB) = merge(DB, EA)`&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; Asociativa: el agrupamiento de mensajes no cambia el resultado.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; Idempotente: duplicados como &lt;span class="sb"&gt;`(1,1)`&lt;/span&gt; o &lt;span class="sb"&gt;`DB`&lt;/span&gt; no alteran el estado.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; Causal: una versión dominada por el estado actual no puede resucitar ni borrar nada.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; Conservadora ante concurrencia: si edición y borrado son concurrentes, no se descarta ninguno silenciosamente.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Resolución explícita**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;El usuario o una política superior puede resolver el conflicto creando una nueva operación causalmente posterior, por ejemplo en A:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sb"&gt;`R = (3,2)`&lt;/span&gt; si A ya conoce &lt;span class="sb"&gt;`DB`&lt;/span&gt;, con decisión “mantener contenido editado” o “confirmar borrado”.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Si mantiene el archivo, &lt;span class="sb"&gt;`R`&lt;/span&gt; contiene el contenido elegido y domina tanto &lt;span class="sb"&gt;`EA`&lt;/span&gt; como &lt;span class="sb"&gt;`DB`&lt;/span&gt;. Si confirma borrado, &lt;span class="sb"&gt;`R`&lt;/span&gt; es una nueva tombstone que también domina ambas variantes. Al propagarse, &lt;span class="sb"&gt;`R`&lt;/span&gt; reemplaza el conflicto porque ya no hay concurrencia: hay una decisión posterior.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Riesgo de recolectar tombstones pronto**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Si B borra la tombstone &lt;span class="sb"&gt;`DB`&lt;/span&gt; antes de que A la haya visto, A puede reenviar una versión viva antigua o concurrente y B no tendrá memoria causal suficiente para saber que hubo un borrado. El archivo puede “resucitar” como si el borrado nunca hubiera existido.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Ambigüedad corregida**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Antes dije que un conflicto podía tener una variante “visible” elegida determinísticamente. Eso puede inducir a pérdida semántica. Mejor: el estado canónico debe ser un conjunto de variantes concurrentes, sin variante ganadora. La presentación puede destacar una, pero la sincronización debe conservar todas hasta una resolución causal posterior.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 class="relative group"&gt;T5
 &lt;div id="t5" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#t5" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h3&gt;

&lt;h4 class="relative group"&gt;My original prompt
 &lt;div id="my-original-prompt-4" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#my-original-prompt-4" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h4&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[MXHYB-20260926-A T5] Una tarea pequeña para la documentación de Puente: traduce al inglés solo esta frase: «Puente conserva las ediciones concurrentes y pide una resolución explícita de los conflictos». Responde únicamente con la traducción.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 class="relative group"&gt;Hybrid answer — local
 &lt;div id="hybrid-answer--local-2" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#hybrid-answer--local-2" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h4&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;🖥️ Puente preserves concurrent edits and requires explicit resolution of conflicts.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 class="relative group"&gt;All-cloud answer
 &lt;div id="all-cloud-answer-4" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#all-cloud-answer-4" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h4&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Puente preserves concurrent edits and requires explicit conflict resolution.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 class="relative group"&gt;T6
 &lt;div id="t6" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#t6" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h3&gt;

&lt;h4 class="relative group"&gt;My original prompt
 &lt;div id="my-original-prompt-5" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#my-original-prompt-5" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h4&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[MXHYB-20260926-A T6] Revisión crítica final de Puente: un compañero propone guardar solo el vector máximo por componente y un único contenido elegido por hash; eliminar tombstones cuando ambos servidores hayan visto su vector; y, tras perder el disco, reutilizar el identificador del servidor con contador cero. Evalúa rigurosamente las tres decisiones, distingue convergencia de conservación de intención y explica un contraejemplo concreto para cada decisión insegura. Formula los supuestos mínimos bajo los que recolectar tombstones sí sería seguro, incluyendo mensajes antiguos en tránsito y réplicas restauradas. Cierra con tres pruebas de propiedades que detectarían estos fallos. Unas 450 palabras, sin ejecutar ni crear archivos.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 class="relative group"&gt;Hybrid answer — cloud
 &lt;div id="hybrid-answer--cloud-2" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#hybrid-answer--cloud-2" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h4&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;☁️ &lt;span class="gs"&gt;**Hallazgos críticos**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; Guardar solo el vector máximo y un único contenido elegido por hash puede converger, pero no conserva intención. Si A edita “precio=10” con &lt;span class="sb"&gt;`(2,1)`&lt;/span&gt; y B edita “precio=12” con &lt;span class="sb"&gt;`(1,2)`&lt;/span&gt;, el máximo &lt;span class="sb"&gt;`(2,2)`&lt;/span&gt; no indica que hubo dos valores concurrentes; elegir por hash descarta una edición válida sin resolución explícita. Es convergente porque ambos acabarán con el mismo hash, pero semánticamente incorrecto: convierte un conflicto en una pérdida silenciosa.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; Eliminar tombstones cuando ambos servidores “han visto su vector” solo es seguro si esa frase significa reconocimiento causal estable y no simple recepción momentánea. Contraejemplo: B borra &lt;span class="sb"&gt;`f`&lt;/span&gt; con &lt;span class="sb"&gt;`(1,2)`&lt;/span&gt;; A lo ve y ambos eliminan el tombstone. Después llega un mensaje antiguo en tránsito con el archivo inicial &lt;span class="sb"&gt;`(1,1)`&lt;/span&gt;. Si ya no queda memoria del borrado, el archivo puede reaparecer. La convergencia local no basta: hace falta impedir la resurrección de estados causalmente obsoletos.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; Reutilizar el identificador del servidor con contador cero tras perder el disco rompe la unicidad de dots y la causalidad. Contraejemplo: antes del fallo, A generó &lt;span class="sb"&gt;`(A,5)`&lt;/span&gt; y B lo conserva. Tras reinstalarse como &lt;span class="sb"&gt;`A`&lt;/span&gt; desde cero, A genera otra operación &lt;span class="sb"&gt;`(A,1)`&lt;/span&gt;, &lt;span class="sb"&gt;`(A,2)`&lt;/span&gt;... B puede considerarlas dominadas por su vector &lt;span class="sb"&gt;`{A:5}`&lt;/span&gt; e ignorarlas, o confundirlas con historia antigua. Resultado: actualizaciones nuevas desaparecen o se mezclan con generaciones anteriores.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Convergencia vs intención**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Convergencia significa que réplicas que reciben la misma información terminan en el mismo estado. Conservación de intención significa que operaciones concurrentes no se sobrescriben sin una relación causal o una resolución explícita. Un algoritmo “elige hash menor” puede ser determinista, conmutativo e idempotente, pero sigue siendo una política de pérdida silenciosa.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Cuándo recolectar tombstones**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;La recolección es segura solo bajo supuestos mínimos fuertes:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; membresía cerrada: se conocen todas las réplicas que pueden emitir o reenviar estados;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; identificadores de réplica persistentes y no reutilizados;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; cada réplica ha confirmado de forma duradera un vector que domina el tombstone;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; no existen mensajes antiguos en tránsito que puedan reaparecer sin ser filtrados por memoria causal;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; las restauraciones desde backup conservan, como mínimo, el vector causal reconocido y la información de borrados aún no estabilizados;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; si una réplica pierde esa memoria, debe reincorporarse con nuevo identificador/generación y sincronización completa, no reutilizando contadores antiguos.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Con solo A y B, puede recolectarse un tombstone de B cuando A sabe duraderamente que B sabe que A también ha observado un vector que domina el borrado, y ambos conservan una frontera causal estable que rechaza estados anteriores. Si no hay control de colas, snapshots, backups y mensajes duplicados, la recolección debe retrasarse o mantener un resumen causal compacto.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Pruebas de propiedades**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; Concurrencia edición-edición: generar dos ediciones desconectadas y verificar que el estado final contiene ambas variantes o una fusión explícitamente marcada, nunca un único contenido arbitrario.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; Borrado contra mensaje antiguo: borrar, sincronizar, intentar reinyectar un estado anterior y comprobar que no resucita el archivo tras compactación.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- Pérdida de disco: reiniciar una réplica con el mismo ID y contador cero debe ser rechazado o detectado; con nuevo ID/generación, sus nuevas operaciones no deben ser confundidas con historia vieja.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 class="relative group"&gt;All-cloud answer
 &lt;div id="all-cloud-answer-5" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#all-cloud-answer-5" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h4&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;La propuesta mezcla convergencia superficial con preservación semántica. Que todos acaben con el mismo estado no basta: Puente debe conservar intenciones concurrentes hasta una resolución causal explícita.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**1. Vector máximo + único contenido por hash**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Es inseguro. Guardar solo el máximo componente a componente, por ejemplo unir &lt;span class="sb"&gt;`(2,1)`&lt;/span&gt; y &lt;span class="sb"&gt;`(1,2)`&lt;/span&gt; en &lt;span class="sb"&gt;`(2,2)`&lt;/span&gt;, destruye la información de que eran versiones concurrentes. Si además se elige un único contenido por hash, se obtiene convergencia, pero con pérdida silenciosa.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Contraejemplo: A y B parten de &lt;span class="sb"&gt;`x=0`&lt;/span&gt; con &lt;span class="sb"&gt;`(1,1)`&lt;/span&gt;. Desconectados, A escribe &lt;span class="sb"&gt;`x=10`&lt;/span&gt; → &lt;span class="sb"&gt;`(2,1)`&lt;/span&gt; y B escribe &lt;span class="sb"&gt;`x=20`&lt;/span&gt; → &lt;span class="sb"&gt;`(1,2)`&lt;/span&gt;. Al sincronizar, ambos guardan vector &lt;span class="sb"&gt;`(2,2)`&lt;/span&gt; y el contenido con hash menor. Convergen, sí, pero la edición perdedora desaparece y el vector resultante finge que existe una versión que causó ambas decisiones, aunque nadie resolvió el conflicto.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Corrección: el estado debe contener un conjunto de variantes concurrentes o un registro de conflicto; el vector máximo puede ser resumen, no sustituto del historial causal relevante.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**2. Eliminar tombstones cuando ambos servidores hayan visto su vector**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Puede ser seguro solo bajo supuestos estrictos, pero la frase es incompleta. Si hay exactamente A y B, almacenamiento estable, identificidades persistentes, canales que no entregan mensajes anteriores tras el acuse, y ambos han confirmado haber integrado una versión posterior que domina la tombstone, entonces se puede recolectar.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Contraejemplo inseguro: B borra &lt;span class="sb"&gt;`f`&lt;/span&gt; con &lt;span class="sb"&gt;`(1,2)`&lt;/span&gt;. A aún tiene en una cola antigua una edición viva &lt;span class="sb"&gt;`(2,1)`&lt;/span&gt; enviada antes o durante la partición. Ambos intercambian acuses de la tombstone y B la recolecta. Después llega el mensaje viejo &lt;span class="sb"&gt;`(2,1)`&lt;/span&gt; a B o B restaura una copia anterior que no recordaba la tombstone. Si no queda memoria causal del borrado, el archivo puede resucitar o volver a conflicto falso.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Supuestos mínimos para GC: membresía cerrada o épocas explícitas; todos los miembros de la época han persistido un estado que domina la tombstone; no existen remitentes autorizados capaces de enviar estados no dominados; los canales o logs garantizan que mensajes antiguos anteriores a la barrera han sido drenados, descartados por época, o serán rechazados por un filtro causal persistido; las restauraciones desde backup deben traer un vector de réplica/época no ambiguo o requerir resincronización completa antes de emitir cambios. En la práctica, conservar un “floor” causal persistente por ruta o por réplica evita aceptar operaciones por debajo de lo ya recolectado.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**3. Reutilizar identificador con contador cero tras perder disco**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Es muy inseguro. Los identificadores de réplica y contadores forman la unicidad de operaciones. Reutilizar &lt;span class="sb"&gt;`A`&lt;/span&gt; con contador cero hace indistinguibles operaciones nuevas de antiguas o dominadas.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Contraejemplo: antes del fallo, A emitió &lt;span class="sb"&gt;`(5,3)`&lt;/span&gt; y B lo conserva. A pierde disco, vuelve como &lt;span class="sb"&gt;`A`&lt;/span&gt; contador &lt;span class="sb"&gt;`0`&lt;/span&gt;, edita y emite &lt;span class="sb"&gt;`(1,3)`&lt;/span&gt; o &lt;span class="sb"&gt;`(1,0)`&lt;/span&gt;. B puede tratarlo como antiguo y descartarlo, o mezclarlo causalmente con operaciones previas de A. Peor: si reaparecen mensajes antiguos de A, pueden colisionar con nuevos &lt;span class="sb"&gt;`op_id`&lt;/span&gt;. Resultado: pérdida, resurrección o divergencia.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Corrección: tras pérdida de disco, generar nueva identidad de réplica o recuperar contador/epoch desde almacenamiento duradero. Alternativamente, reincorporarse mediante protocolo de rejoin que asigna nueva época y prohíbe emitir operaciones hasta descargar un snapshot válido.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Pruebas de propiedades**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; Concurrencia preservada: para dos ediciones concurrentes distintas, tras cualquier orden y duplicación de mensajes, ambas variantes siguen presentes hasta una resolución explícita.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; No resurrección tras borrado: una versión viva no dominante enviada antes de una tombstone recolectada no puede recrear el archivo.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- Identidad única: después de crash/rejoin, ninguna operación nueva puede compartir &lt;span class="sb"&gt;`op_id`&lt;/span&gt; ni ser ordenada falsamente bajo una época anterior.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 class="relative group"&gt;Provenance
 &lt;div id="provenance" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#provenance" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h3&gt;
&lt;p&gt;Extracted from the &lt;code&gt;prompt&lt;/code&gt; and &lt;code&gt;response&lt;/code&gt; fields of &lt;code&gt;turn-1.json&lt;/code&gt; through &lt;code&gt;turn-6.json&lt;/code&gt; in each run. All six prompts match the &lt;code&gt;prompts.json&lt;/code&gt; fixture. I have neither rewritten nor corrected the answers. The article contains the usage table and comparison limitations.&lt;/p&gt;
&lt;/div&gt;</content:encoded><media:content xmlns:media="http://search.yahoo.com/mrss/" url="https://carlesabarca.com/posts/minix-hybrid-agent/featured.png"/></item></channel></rss>