You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 1d32a5d
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: docs/architecture/rfcs/typescript-control-plane-migration-v0.md
+34-6Lines changed: 34 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -159,6 +159,7 @@ choice is now implemented rather than hypothetical.
159
159
| Scheduler durable state ([#3440](https://github.com/huangruiteng/loopx/pull/3440)) | State normalization, persistence, replay, and one coarse transition are TS-owned | The Python compatibility path still pays a cross-runtime transport tax |
160
160
| Scheduler heartbeat/state transaction | TypeScript owns receipt freshness, ACK and host-failure validation, state construction, failure-cache transitions, replay/CAS fencing, atomic writes, and the public JSON/Markdown projection | Generated, receipt-bound host follow-up runs through the native TS CLI; Python remains only for unbound/manual compatibility calls and external host mutation |
161
161
| Quota spend commit transaction | TypeScript owns final spend-transition validation, typed event construction, effect replay/CAS fencing, crash repair, and the JSON/Markdown/index write set | Python still projects `should-run` and settlement readback facts, and holds the legacy cross-writer index lock until the CLI/index writers move in-process |
162
+
| Quota void commit transaction | TypeScript owns spend-target resolution, before/after reduction, canonical correction construction, effect replay/index CAS, prepared-receipt repair, and the JSON/Markdown/index write set | Python retains `should-run` facts, clock/effect identity, the legacy cross-writer index lock, one transport call, and compatibility entry points |
162
163
| Quota monitor-poll commit transaction | TypeScript owns monitor admission revalidation, target/event/result construction, effect replay/index CAS, provider intent, and repairable JSON/Markdown/index persistence | Python projects compact `should-run` facts, invokes the real Todo provider between at most two reductions, reloads legacy status, and holds the cross-writer index lock |
163
164
| Runtime decoders ([#3443](https://github.com/huangruiteng/loopx/pull/3443)) | Stable primitive decoding has one small shared module; domain decoders remain local | No larger schema framework is justified |
164
165
| Transaction payoff ([#3464](https://github.com/huangruiteng/loopx/pull/3464), [#3481](https://github.com/huangruiteng/loopx/pull/3481), and Todo completion) | Turn settlement, quota delivery routing, and Todo completion each cross one coarse TS boundary; the Todo transaction owns identity, replay fencing, validation planning/result reduction, continuation/recovery, and completion metadata | Python still executes explicitly external providers and materializes legacy Markdown/event results; other domains still need their own bounded cutovers |
@@ -242,7 +243,7 @@ domains would now increase total complexity.
242
243
243
244
Select by deletion leverage and runtime traffic, not by ease of translation.
244
245
The shipped Turn settlement, quota delivery-routing, Todo-completion,
245
-
scheduler-heartbeat, quota-spend commit, and task-lease acquire cutovers
246
+
scheduler-heartbeat, quota-spend commit, quota-void commit, and task-lease acquire cutovers
246
247
establish the pattern.
247
248
Subsequent candidates must name a remaining transaction and its deletion
248
249
leverage; remaining quota settlement readback is eligible only when it can
@@ -294,6 +295,18 @@ shipped Stage 2B cutovers are in place:
294
295
Python retains `should-run`/settlement fact projection plus one coarse
295
296
transport call and the legacy kernel index lock; it no longer constructs or
296
297
writes the spend event.
298
+
- Quota void commit: TypeScript finds the referenced spend under the mutation
299
+
lock, reduces the before/after accounting decision, constructs the canonical
300
+
correction, and commits its JSON, Markdown, index row, and prepared receipt
301
+
through the closed spend/void accounting-artifact kernel. Same-effect retry
302
+
replays or repairs one transaction; a fresh CLI invocation remains a fresh
303
+
effect and therefore preserves the existing ability to append another
304
+
correction for the same spend target. Malformed index rows now fail closed
305
+
instead of being skipped. Void artifact names include an effect digest and
306
+
JSONL rows use compact JSON; public payload semantics remain stable. The
307
+
shared kernel also validates persisted receipt/path identity for spend
308
+
recovery. Python retains `should-run` facts, UUID/clock ownership, one coarse
309
+
transport call, and the legacy cross-writer index lock.
297
310
- Local task-lease lifecycle: native TypeScript transactions now own acquire,
298
311
renew, transfer, release, terminal verification, holder verification, and
299
312
fence close. They own boundary decode, handoff and owner/Todo eligibility,
@@ -328,11 +341,13 @@ shipped Stage 2B cutovers are in place:
328
341
durability checks. Invalid identities stop before the provider, while a
329
342
crash/retry after the provider re-enters its same-key idempotent path.
330
343
331
-
The quota-spend cutover removes the Python spend-event builder and three-file
332
-
writer. Its bounded facade exits when the quota CLI and remaining run-index
333
-
writers execute the transaction in-process; until then it supplies compact
334
-
projection facts and shares the legacy Python index lock with unmigrated
335
-
writers. The Todo cutover removes the Python state-evaluation dataclass, local identity
344
+
The quota-accounting cutovers remove the Python spend and void event builders
345
+
and their three-file writers. Their bounded facades exit when quota decision
346
+
and the top-level CLI execute in-process TypeScript, all run-index writers use
347
+
the native lock, and the legacy Python void API compatibility window closes.
348
+
Until then Python supplies compact projection facts, clock/effect identity,
349
+
result validation, and the shared legacy index lock. The Todo cutover removes
350
+
the Python state-evaluation dataclass, local identity
336
351
projection, replay helper, and public runtime handlers for those implementation
337
352
leaves. The remaining Python Todo facade owns transport, external command
338
353
execution, source compare-and-swap, legacy response projection, and the actual
@@ -359,6 +374,19 @@ retiring a lock. This is not an exactly-once guarantee for a timed-out handler
359
374
that is still executing concurrently inside the same Node process; callers must
360
375
not start a second independent operation while that handler may still be live.
361
376
377
+
#### Quota void commit migration economics
378
+
379
+
| Field | Receipt |
380
+
| --- | --- |
381
+
| Canonical owner | Before: Python `slot_accounting.py` owned spend-target lookup, correction reduction, event/result construction, artifact allocation, and JSON/Markdown/index persistence. After: versioned TypeScript `quota.void.commit` owns those semantics plus effect fencing, index CAS, receipts, replay, and repair through the closed spend/void accounting kernel. |
382
+
| Legacy semantic code deleted | 212 Python product LOC covering the prior void lookup, transition, event/projection, path-allocation, and JSON/Markdown/index writer path. |
383
+
| Bridge code added | 263 Python diff LOC: the 243-line bounded `void_commit.py` transport/compatibility facade plus 20 import, re-export, normalization, and route-wiring lines in `loopx/quota.py` and the legacy `slot_accounting.py` surface. |
384
+
| Cross-runtime calls | The public execute and dry-run paths move from zero crossings to one coarse request/response. Exact-effect replay or repair also uses one request/response. Distinct CLI invocations remain distinct effects; the legacy two-step preview-plus-record compatibility surface uses one call per entry point. |
385
+
| Product-code net change | Product code is +2,210/−898 LOC, net +1,312. Tests/examples are +1,416/−3, net +1,413; build configuration is +3 and docs are excluded. The production shared kernel is already used by spend and void, replacing 671 lines in `spend_commit.ts` rather than creating a speculative framework. |
386
+
| Migration scaffolding | No migration-only worker, parity corpus, or temporary schema framework is added. Native boundary/invariant/replay/CAS/repair tests remain as shipped and persisted contracts; Python bridge tests exit with the compatibility facade. |
387
+
| Facade exit | Delete the Python void facade when quota decision and the top-level CLI run in-process TypeScript, all run-index writers use the native lock, and the legacy `build_*void*`/`record_*void*` Python API compatibility window closes. |
388
+
| Correctness and performance | Typed-decoder negatives, legacy target compatibility, effect isolation, index CAS, malformed receipts and paths, exact index-row identity, supported duplicate-index repair, concurrent mutation, truncated-tail repair, public CLI behavior, and clean wheel/sdist semantic probes pass. Across 16 cold starts, p50/p95 is 230.88/260.92 ms; 128 warm typed pings are 1.07/1.29 ms and warm void previews are 1.93/2.34 ms. Across 64 durable facade transactions, commit is 30.64/37.49 ms and exact-effect replay is 8.05/9.86 ms. Daemon RSS is 108.38 MiB idle and 109.80 MiB after 256 requests. In 64 interleaved full-CLI pairs, baseline/candidate p50/p95 is 736.51/828.68 versus 779.52/856.49 ms: p95 +27.81 ms (+3.36%). The absolute delta is the measured cost of one new managed-runtime fingerprint/request plus prepared-receipt durability; the percentage stays below the 5% material-regression gate, and Stage 3 removes that crossing. |
0 commit comments