Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<body class="ts-migration">
<a class="skip-link" href="#main">Skip to content</a>
<header class="site-header"><a class="brand" href="../../"><span class="product-mark" aria-hidden="true"><i></i><i></i></span>LoopX</a><nav aria-label="Primary navigation"><a href="../">Blog</a><a href="../../docs/book/en/">Docs</a></nav><div class="header-actions"><a class="language" href="../../blog/zh/typescript-control-plane-refactor/" lang="zh-CN" hreflang="zh-CN">中文</a><a class="github" href="https://github.com/loopx-project/loopx">GitHub ↗</a></div></header>
<main id="main"><div class="article-heading"><a class="back" href="../">← All articles</a><p class="eyebrow">LoopX 2.0 · Engineering</p><h1>LoopX 2.0: Two Months of TypeScript Control-Plane Engineering</h1><p class="lead">From complete transactions and shared authority to model-assisted semantic repairs: real callers, counterexamples and complete costs behind LoopX’s TypeScript control plane.</p><div class="byline"><a href="https://github.com/huangruiteng">Ruiteng Huang</a><span>·</span><time datetime="2026-10-16">October 16, 2026</time><span>·</span><span>LoopX creator</span><span>·</span><time datetime="2026-10-28">Updated October 28, 2026</time></div></div>
<main id="main"><div class="article-heading"><a class="back" href="../">← All articles</a><p class="eyebrow">LoopX 2.0 · Engineering</p><h1>LoopX 2.0: Two Months of TypeScript Control-Plane Engineering</h1><p class="lead">An effect succeeds without a checkpoint. An expired executor returns with a valid receipt. An evidence correction restarts finished work. Two months of refactoring brought these problems under complete transactions and shared semantics.</p><div class="byline"><a href="https://github.com/huangruiteng">Ruiteng Huang</a><span>·</span><time datetime="2026-10-16">October 16, 2026</time><span>·</span><span>LoopX creator</span><span>·</span><time datetime="2026-10-29">Updated October 29, 2026</time></div></div>
<div class="article-layout"><aside class="toc"><div class="desktop-contents"><p class="toc-caption">Contents</p><nav aria-label="Article contents"><a href="#section-1">1. Reconstruct the history: the migration plan changed too</a><a href="#section-2">2. Core control-plane statistics across the full period</a><a href="#section-3">3. Start with the Effect Program: agree on what happened</a><a href="#section-4">4. Migrate a complete transaction: two calls can be right</a><a href="#section-5">5. Shared authority: separate domain rules from storage primitives</a><a href="#section-6">6. After SQLite: retained history needs a cost model</a><a href="#section-7">7. Song&#39;s Semantics: connect definition, production, and interpretation</a><a href="#model-assisted-semantics">8. How stronger models repair semantics</a><a href="#section-8">9. Callers and defaults determine whether migration reaches users</a><a href="#section-9">10. Recover the original result while protecting current authority</a><a href="#section-10">11. CLI output budgets: protect decision semantics before compression</a><a href="#section-11">12. Final retirement: preserve credit and account for the deletions</a><a href="#section-12">13. Community contributions and the next step</a></nav></div><details class="mobile-contents"><summary>Contents</summary><nav aria-label="Article contents"><a href="#section-1">1. Reconstruct the history: the migration plan changed too</a><a href="#section-2">2. Core control-plane statistics across the full period</a><a href="#section-3">3. Start with the Effect Program: agree on what happened</a><a href="#section-4">4. Migrate a complete transaction: two calls can be right</a><a href="#section-5">5. Shared authority: separate domain rules from storage primitives</a><a href="#section-6">6. After SQLite: retained history needs a cost model</a><a href="#section-7">7. Song&#39;s Semantics: connect definition, production, and interpretation</a><a href="#model-assisted-semantics">8. How stronger models repair semantics</a><a href="#section-8">9. Callers and defaults determine whether migration reaches users</a><a href="#section-9">10. Recover the original result while protecting current authority</a><a href="#section-10">11. CLI output budgets: protect decision semantics before compression</a><a href="#section-11">12. Final retirement: preserve credit and account for the deletions</a><a href="#section-12">13. Community contributions and the next step</a></nav></details></aside><article class="prose">
<p>LoopX 2.0 is out. This is an account of the history and technical details behind its TypeScript refactor.</p>
<p>The formal migration RFC merged on August 15. By the concentrated retirement of ordinary legacy writers in mid-October, the program had spanned roughly two months. Several tracks overlapped: transaction ownership, caller integration, shared authority, storage and history costs, Semantics, compatibility, and retirement.</p>
Expand Down Expand Up @@ -324,18 +324,27 @@ <h3>Counterexamples for the checker itself</h3>
<p>False alarms and missed values have different consequences. A conservative candidate set may report an extra problem. Missing a real produced value can let an unregistered output through. Unsupported dynamic forms must preserve an unknown.</p>
<p>The checks also need scan-coverage floors, so scanning fewer files cannot make the result green. Retiring an expanding committed inventory in favor of on-demand full-tree computation did not remove that obligation. Gaps outside supported forms remain explicit, and the <a href="https://github.com/loopx-project/loopx/issues/4447">tracking issue</a> remains part of the work.</p>
<p>Generation and checking reduce synchronization mistakes. Their assumptions, coverage, and error direction still require review.</p>
<h2 id="model-assisted-semantics">8. Using stronger models to repair control-plane semantics</h2>
<p>After migration, we continue using stronger models for design, implementation and review, tracing what a state means across producers, rules, callers and recovery. Models help uncover assumptions spanning files and runtimes; their explanations still face real entrypoints and counterexamples. Both later repairs below fall after October 16 and outside the two-month historical statistics.</p>
<h3>Correct arguments can still recover through the wrong program</h3>
<p>The wrong assumption in <a href="https://github.com/loopx-project/loopx/pull/7145">#7145</a> was that preserving Goal, Agent and Turn arguments preserved the operation’s recovery route. After entry through an explicit runtime, a failed selection could return a command invoking the global <code>loopx</code> on PATH. Work identity survived, but the executable interpreting its rules changed, potentially loading another version or runtime route.</p>
<p>The repair reuses the existing CLI entrypoint builder to carry runtime routing alongside work identity. The regression puts a deliberately failing global CLI first on PATH, then <strong>executes the returned argv unchanged</strong>. Real File / SQLite paths check original routing, rejection before business side effects and settlement of the original result once. Assertions that a few argument strings appear would miss this defect.</p>
<h3>An obligation to act is not permission for arbitrary work</h3>
<p><a href="https://github.com/loopx-project/loopx/pull/7269">#7269</a> further separates the obligation to advance work, ordinary execution permission and original-Turn closeout. A required replan can set <code>must_attempt</code> while ordinary <code>delivery_allowed</code> stays closed. Creating a successor records a new route; it cannot move the old review’s settlement to the new task. Shared TS replan and settlement owners retain the original binding, return its closeout steps on a same-Turn reread, and leave the successor for fresh admission.</p>
<p><a href="https://github.com/loopx-project/loopx/blob/10e5fa57a4e7dad399d59bfa04d5b28dc04919f3/tests/control_plane/test_replan_successor_guard_reentry.py#L237">Native CLI regressions</a> execute the actual returned writeback, settlement and replay. <a href="https://github.com/loopx-project/loopx/blob/10e5fa57a4e7dad399d59bfa04d5b28dc04919f3/tests/control_plane/test_replan_successor_guard_reentry.py#L334">Invalidated-successor cases</a> check that changed ownership, deferral or a removed claim cannot use an old receipt to settle. Whether less identity negotiation improves later work still depends on actual outcomes and complete cost. The <a href="../edgebench-feedback-and-memory/#repair-cases">kernel efficiency and effectiveness article</a> follows that chain and retains negative context-cost measurements.</p>
<p>This extends the earlier <a href="https://github.com/loopx-project/loopx/pull/3464">#3464</a> lesson: fewer calls still require preflight before side effects. We constrain model-assisted work through <strong>a wrong assumption → the shared semantic owner → an old-version counterexample → real calls, refusal and recovery</strong>. The repaired meaning is what matters. PR counts, generated TypeScript and test counts do not independently establish model benefit. We have no per-PR model comparison attributing these repairs to a particular model’s independent causal effect.</p>
<h2 id="model-assisted-semantics">8. The semantic questions we put stronger models to work on</h2>
<p>We use stronger models in design, implementation and review, especially to trace assumptions across files and runtimes. The difficult cases often combine individually reasonable checks into an unsafe execution order. Core PRs in August and September already dealt with these problems; they were part of the migration itself.</p>
<h3>The effect happened, but its checkpoint did not. Is retry safe?</h3>
<p>A provider succeeds, then the process crashes before writing the checkpoint. The journal says unfinished; a blind retry may execute twice. Treating every uncertain result as success would instead lose work that never happened. A try/catch cannot settle that distinction.</p>
<p>On August 22, <a href="https://github.com/loopx-project/loopx/pull/3485">#3485</a> introduced a durable prepared attempt and stable effect identity before execution. Recovery asks the provider: <strong>confirmed committed means checkpoint the original result; confirmed absent permits execution with the same identity; unknown remains pending recovery.</strong> Effect order, receipt prefixes and identity checks remain part of the contract. This is not arbitrary external exactly-once execution: confirming the result depends on provider recovery support.</p>
<p>This complements the preflight in <a href="https://github.com/loopx-project/loopx/pull/3464">#3464</a>. Before execution, determine whether the effect is allowed; after interruption, determine whether it already happened. Moving Python functions to TS one at a time does not protect both boundaries.</p>
<p>On September 13, <a href="https://github.com/loopx-project/loopx/pull/4286">#4286</a> converged receipt recovery for seven Todo operations on one owner while retaining a finer distinction. Suppose two agents update a task: A commits first; B receives a definite version-conflict rejection, then loses its connection while reading the receipt. <strong>The later read failure must not erase the earlier rejection.</strong> If the connection drops before the commit response arrives, the result remains uncertain and recovery must use the original operation identity.</p>
<p>Both paths can encounter a network error, but they require different next steps. The <a href="https://github.com/loopx-project/loopx/blob/c0b572d2d508bb10c32701057dd71ff4f8eb663c/loopx/control_plane/coordination/command_receipt.ts">shared implementation</a> attempts the write once, then reads the original receipt. Its <a href="https://github.com/loopx-project/loopx/blob/c0b572d2d508bb10c32701057dd71ff4f8eb663c/tests/control_plane_ts/command_receipt.test.ts">regressions</a> distinguish conclusive rejection, commit uncertainty and exact-receipt recovery. Shared error handling must retain the phase of failure and facts already established.</p>
<h3>Claiming work and acquiring execution rights must commit together</h3>
<p>Two agents see an unclaimed task. If the claim is written before the lease is acquired, who owns the intermediate state? If an earlier claimant returns with a receipt after its lease expires, may it keep writing?</p>
<p>From the coordination foundations in <a href="https://github.com/loopx-project/loopx/pull/3529">#3529</a> and <a href="https://github.com/loopx-project/loopx/pull/3669">#3669</a> to the September 6 transaction in <a href="https://github.com/loopx-project/loopx/pull/4032">#4032</a>, claim, lease and operation receipt converge on one conditional commit. The <a href="https://github.com/loopx-project/loopx/pull/3798">#3798</a> store owns atomic persistence; the domain owner checks identity, scope and legal transitions. <strong>A receipt proves a past claim succeeded. The current lease and epoch determine permission now.</strong> Replay must recover the original result without reviving expired execution rights. Storage revision, domain revision and lease epoch remain different identities.</p>
<p>Idempotency therefore requires more than returning an old success. Recovery must remember history and respect present authority. Foundation, shadow and production qualification arrived in stages; the first interface merge did not establish deployment of every backend.</p>
<h3>Why could correcting evidence restart finished work?</h3>
<p>A planning update corrects evidence without changing monitor state. Rebuilding the object from defaults can accidentally rearm an already satisfied monitor. Conversely, an explicitly empty list or <code>false</code> cannot be interpreted as an omitted input.</p>
<p>On September 10, <a href="https://github.com/loopx-project/loopx/pull/4178">#4178</a> converged planning updates on one authority snapshot and a complete TS transaction: omission retains intent, explicit clearing follows field and authority rules, and evidence correction does not rearm a satisfied monitor. It also removed Python’s serialize-to-Markdown-and-parse-back route. This repaired behavior as well as consolidating ownership; describing it as a mechanically equivalent language translation would miss the point.</p>
<p>Song’s <a href="https://github.com/loopx-project/loopx/pull/4433">#4433</a> and <a href="https://github.com/loopx-project/loopx/pull/4494">#4494</a> extended this discipline to vocabulary definitions, actual producers and consumers. A compiling enum does not establish that all callers mean the same thing. The earlier <code>skip</code> / <code>quota_skip</code> example and separation of action from decision belong to this same thread.</p>
<h3>Making a plausible model answer face a counterexample</h3>
<p>Ask for the mistaken assumption, then an execution sequence that refutes it: disconnect after commit, replay an expired lease, correct only evidence, explicitly clear a value. Check the real owner, provider and caller.</p>
<p>Stronger models help connect scattered conditions; counterexamples, real storage and refusal paths constrain their answers. There is no per-PR model comparison here, so these examples cannot quantify a particular model’s independent contribution. They show the mechanisms and reasons for changing them. Later runtime routing in <a href="https://github.com/loopx-project/loopx/pull/7145">#7145</a> and original-Turn closeout in <a href="https://github.com/loopx-project/loopx/pull/7269">#7269</a> continue this work rather than defining its whole history.</p>
<h2 id="section-8">9. Callers and defaults determine whether migration reaches users</h2>
<p>Complete transactions need real producers. In September, <a href="https://github.com/loopx-project/loopx/pull/4178">#4178</a> converged nonterminal planning updates around one authority snapshot and a TS transaction. It removed a Python route that synthesized Markdown, parsed it again, and performed target reads before the transaction.</p>
<p>Omission, <code>null</code>, an empty value, and <code>false</code> each carry intent here. Dry-run must model that intent accurately. An unavailable selected provider must not trigger fallback elsewhere. <a href="https://github.com/loopx-project/loopx/pull/4279">#4279</a> later converged read classification and projection-delivery semantics. Some Python read ownership was deliberately retained. Clear ownership matters more than creating a second interpretation to pursue language purity.</p>
<p>Complete transactions also need callers to express intent accurately. In <a href="https://github.com/loopx-project/loopx/pull/4178">#4178</a>, a blank note means no update; an explicit empty list or <code>false</code> is interpreted by its field’s contract. These values cannot all be discarded as absent, or all treated as clearing. Dry-run must model that intent accurately. An unavailable selected provider must not trigger fallback elsewhere. <a href="https://github.com/loopx-project/loopx/pull/4279">#4279</a> later converged read classification and projection-delivery semantics. Some Python read ownership was deliberately retained. Clear ownership matters more than creating a second interpretation to pursue language purity.</p>
<p>Defaults expose similar distinctions. Current priority regressions protect these cases:</p>
<div class="table-scroll" role="region" tabindex="0" aria-label="Scrollable technical comparison table"><table>
<thead>
Expand Down
Loading
Loading