Skip to content

Commit 92ba6b1

Browse files
committed
docs(authority): reconcile event capture and default-cutover path
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
1 parent b53edec commit 92ba6b1

7 files changed

Lines changed: 355 additions & 0 deletions
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Event-owned completion: one publication before capture integration
2+
3+
Baseline: `90f21a5299188d54f984a5313e774c9ac48d6595`. This advances overall
4+
roadmap R5/G2, shared-authority L2/L7 and TS T1/T2. It closes an existing event
5+
writer correctness gap; it does not qualify that writer for shadow capture.
6+
7+
## Reconcile already-delivered work
8+
9+
Transaction-bound Markdown/lease shadow outboxes already exist (#3870).
10+
Complete source assembly moved to TS in #4967; prepared-entry source resolution
11+
and delivery moved to TS in #4968. Those owners must be reused. “Event source
12+
capture is missing” was too broad: the remaining gap is binding the event-log
13+
writer's actual commit to the existing capture lifecycle, then qualifying mixed
14+
writers and whole-Goal recovery. Source assembly is not another remaining PR.
15+
The `event_log_writer_not_bound` hold stays in bootstrap, capture and delivery.
16+
17+
## Behavior and ownership
18+
19+
Previously event-owned Todo completion appended successor add/claim events
20+
before it finished encoding the parent completion. A later error left runnable
21+
successors without a completed parent. Its context check also did not compare
22+
the actual event log under the append lock. Both failures reproduce on the
23+
baseline through the public completion function.
24+
25+
- The event adapter now encodes the existing TS successor proposals, then
26+
submits successors and completion as one eager batch. Duplicate normalization
27+
and default/ownership decisions are removed from the Python successor helper.
28+
- `goals/state_event_append.ts` owns whole-batch identity conflicts, replay,
29+
sequence allocation and source-checksum admission. Python holds the existing
30+
sibling lock, supplies compact identity/hash facts and retains legacy codecs
31+
and IO. There is one planning RPC per batch, not per historical event.
32+
- Exact `list`/`tuple` batches validate fully before publication. Atomic replace
33+
plus file/directory fsync makes the event stream visible as all old or all new
34+
bytes. Prior bytes and event schemas remain unchanged. Lazy iterables and
35+
subclasses keep their per-item visibility/reentrancy contract; callers must
36+
materialize them before requesting an atomic source-bound batch.
37+
- Source drift returns the existing completion validation failure, without a
38+
successor prefix. A lost publication acknowledgement reports an uncertain
39+
outcome; read back the original Todo and retry completion. Terminal replay
40+
re-establishes log durability without generating more successors.
41+
42+
This deliberately changes eager-batch failure/visibility semantics. The log is
43+
logically append-only, but the physical inode is replaced. Consumers must reopen
44+
it; an indefinitely open file descriptor is not a live-tail contract. No event
45+
schema version, capture gate, migration permission or provider default changes.
46+
No frontend setting changes: the public completion result and existing CLI/API
47+
route remain the entrypoints; only failure atomicity and replay are corrected.
48+
49+
## Evidence and cost
50+
51+
Public-entrypoint counterexamples fail on baseline and pass on this change.
52+
Validation also covers late duplicate/invalid events, source drift, concurrent
53+
process batches, pre-replace failure, post-replace fsync failure, exact retry,
54+
historical CRLF/no-final-newline preservation, both successor roles and dry-run.
55+
Existing event-only capture holds and non-Todo supervisor/read consumers remain
56+
covered. A source-projection return-value narrowing fixes an existing mypy
57+
failure without changing its runtime acceptance rules.
58+
59+
The read-only source-copy rehearsal consumed 6,111,476 Markdown bytes and
60+
backfilled 874 events. A disposable registry's real CLI completed a synthetic
61+
Todo with three appended events in 1,531 ms; replay left bytes unchanged and
62+
source digest readback matched. It uses real record variety/volume, not live
63+
Goal configuration, execution or promotion; it does not assert complete archive
64+
capture. `examples/control_plane/event-completion-rehearsal.py` reproduces it.
65+
66+
On the same 707,414-byte detached log, seven warm three-event batches had median
67+
11.33 ms on baseline and 28.94 ms on this change. This pays for admission and
68+
crash durability; it is not a speedup. Existing whole-log reads remain, and
69+
atomic publication adds a whole-file copy. Do not use this legacy adapter as the
70+
future high-throughput provider; retire it with its final caller after migration.
71+
No RPC budget was raised. File/SQLite/PostgreSQL stores are not changed here.
72+
73+
## Remaining local-default delivery program
74+
75+
The conditional estimate remains **5–8 cohesive delivery PRs**, subject to
76+
integration findings and existing open prerequisites. This transaction repair
77+
is a prerequisite within public-writer/capture closure, not grounds to subtract
78+
one complete package. Older 7–9 estimates describe earlier checkpoints.
79+
80+
| Package | PRs | Concrete exit |
81+
| --- | --- | --- |
82+
| Public callers and executor boundaries | 1–2 | Reconcile real CLI/Turn/Chat writers and external-effect consumers; close current-proof/fence gaps and delete replaced Python rules. Reuse current leased handoff/selection/receipt work. |
83+
| Consumer and display closure (D1) | 1 | Integrate merged #4961 projection recovery, #4964 complete-source summary and #4922 snapshot paging; prove missing/stale display recovery through actual clients. Do not reimplement these owners. |
84+
| Selected SQLite profile (D2) | 1–2 | Continue #4224 and coordinate #4931: capacity, crash/restore/upgrade, lag, supported runtime/OS and applicable elapsed soak on the selected profile. Test count is not elapsed soak. |
85+
| Capture continuity and whole-Goal rehearsal (L7/L8, D3) | 1–2 | Bind actual event transactions to prepared/committed outbox identity; prove mixed writers, interruption, drain, old-writer fencing, canonical readback and fenced rollback on File/SQLite. Keep unbound holds until this passes. |
86+
| Default selection and bounded retirement (L9/T4) | 1 | New-Goal creation/settings/install select the qualified profile; migrate approved existing cohorts and delete old business writers only after final callers and compatibility windows close. Markdown import/export/rendering are not obsolete business writers. |
87+
88+
File remains the explicit reference profile and SQLite the long-lived local
89+
candidate. PostgreSQL already has a provider and scoped service factory; real
90+
service authentication, deployment, restore/failover and capacity qualification
91+
remain a separate medium-term package. They are not prerequisites for local
92+
default selection and this PR does not qualify them.
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# 事件源完成事务:先建立完整提交,再接入捕获
2+
3+
基线:`90f21a5299188d54f984a5313e774c9ac48d6595`。对应总路线 R5/G2、
4+
shared-authority L2/L7 与 TS T1/T2。本批修复已有事件写入者的正确性,
5+
不授予该写入者 shadow capture 资格。
6+
7+
## 核对已经交付的工作
8+
9+
绑定事务的 Markdown/lease shadow outbox 早已存在(#3870)。#4967 已把完整
10+
来源组装移到 TS,#4968 已把 prepared-entry 的来源判定与交付移到 TS,必须复用。
11+
笼统地说“事件源捕获还没做”不准确:剩下的是把事件日志写入者的实际提交绑定到
12+
既有捕获生命周期,再验证混合写入者和整 Goal 恢复。来源组装不应重复列成待做 PR。
13+
Bootstrap、capture 和 delivery 中的 `event_log_writer_not_bound` 阻塞保持。
14+
15+
## 行为与所有权
16+
17+
之前,事件源 Todo 完成会先写入后继 add/claim 事件,再编码父任务完成事件。
18+
后续出错会留下可运行后继,却没有完成父任务。原来的上下文检查也没有在追加锁内
19+
比较真实事件日志。这两个反例均能通过公开完成函数在基线复现。
20+
21+
- 事件适配器现在只编码既有 TS 后继提案,把后继和完成事件作为一个完整批次提交。
22+
删除 Python 后继 helper 中重复的归一化、默认值和所有权决定。
23+
- `goals/state_event_append.ts` 负责整批身份冲突、回放、序号分配和来源校验准入。
24+
Python 持有既有 sibling lock,传输紧凑身份/hash 事实,保留旧格式 codec 和 IO。
25+
每批一次规划 RPC,不逐条历史事件调用。
26+
- 精确的 `list`/`tuple` 批次先完整验证,再原子替换并 fsync 文件和目录。读者看到
27+
全部旧字节或全部新字节;已有字节与事件 schema 不变。惰性迭代器和子类保留逐条
28+
可见与可重入合同;请求原子且绑定来源的批次前,调用者须先物化集合。
29+
- 来源变化返回既有完成校验失败,不留下部分后继。发布确认丢失时报告结果不确定;
30+
应回读原 Todo 再重试完成。终态重放重新确认日志持久性,不生成更多后继。
31+
32+
这是有意改变 eager batch 的失败/可见性语义。日志在逻辑上只追加,但物理 inode
33+
会替换,读者需重新打开;长期持有文件描述符不构成实时尾读合同。不修改事件 schema
34+
版本、capture gate、迁移权限或 provider 默认值。没有前端设置变化:入口仍是既有
35+
CLI/API 和完成结果,修复的是失败原子性与重放。
36+
37+
## 证据与代价
38+
39+
公开入口反例在基线失败、在本批通过。覆盖批末重复/非法事件、来源变化、多个进程
40+
整批竞争、替换前失败、替换后 fsync 失败、精确重试、历史 CRLF/无末尾换行保留、
41+
两种角色的后继及 dry-run。保留事件独有来源捕获阻塞与非 Todo 的 supervisor/read
42+
消费验证。另将来源投影返回值绑定到经校验的局部变量,修复已有 mypy 失败,运行时
43+
准入语义不变。
44+
45+
只读源副本演练读取 6,111,476 字节 Markdown、回填 874 条事件。在临时 registry
46+
中通过真实 CLI 完成合成 Todo,追加三个事件,耗时 1,531 ms;重试字节不变,真实
47+
来源 digest 回读一致。使用真实记录多样性/规模,不激活真实 Goal 配置、不执行或
48+
晋升真实 Goal,也不宣称完整 archive capture。可通过
49+
`examples/control_plane/event-completion-rehearsal.py` 复现。
50+
51+
同一个 707,414 字节隔离日志,七次预热后三事件批次,中位耗时基线 11.33 ms、
52+
本批 28.94 ms。这是准入与崩溃持久性的成本,不是性能提升。既有整日志读取仍在,
53+
原子发布还增加整文件复制。该旧适配器不是未来高吞吐 provider,应在迁移后随最后
54+
调用者退役。不提高 RPC 预算,本批不修改 File/SQLite/PostgreSQL store。
55+
56+
## 到本地默认切换的剩余交付
57+
58+
条件估算仍为 **5–8 个完整交付 PR**,取决于集成发现和已有开放前置 PR。
59+
本次事务修复属于真实写入者/捕获收口的前置项,不能据此机械减去一个完整包。
60+
旧的 7–9 批估算对应更早的检查点。
61+
62+
| 交付包 | PR 数 | 具体出口 |
63+
| --- | --- | --- |
64+
| 公开 caller 与执行边界 | 1–2 | 核对 CLI/Turn/Chat 真实写入者及外部效果消费者,收口当前证明/fence 缺口,删除被替换的 Python 规则;复用进行中的 leased handoff/selection/receipt 工作。 |
65+
| 消费与显示闭合(D1) | 1 | 集成已合入的 #4961 投影恢复、#4964 完整来源摘要、#4922 快照分页,沿真实客户端证明缺失/过期显示恢复,不重写这些 owner。 |
66+
| 选定 SQLite profile(D2) | 1–2 | 继续 #4224、协调 #4931,完成容量、崩溃/恢复/升级、lag、支持的 runtime/OS 与适用的真实持续运行验证;测试次数不等于持续时间。 |
67+
| 捕获连续性与整 Goal 演练(L7/L8、D3) | 1–2 | 将实际事件事务绑定 prepared/committed outbox 身份;在 File/SQLite 验证混合写入者、中断、排空、旧写入者 fencing、canonical 回读和受 fencing 保护的回滚。通过前保持 unbound 阻塞。 |
68+
| 默认选择与有限退役(L9/T4) | 1 | 新 Goal 创建/设置/安装采用已资格化 profile;迁移获准旧 cohort,最后调用者与兼容窗口关闭后删除旧业务 writer。Markdown import/export/rendering 不属于过时业务 writer。 |
69+
70+
File 保持显式参考 profile,SQLite 是长期本地候选。PostgreSQL 已有 provider 和
71+
受作用域约束的 service factory;真实服务认证、部署、恢复/故障切换与容量资格是
72+
独立中期工作,不应让本地默认切换等它完成,本批也不授予这些资格。

‎docs/architecture/rfcs/shared-goal-authority-state-provider-v0.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ are not a guaranteed total PR count. Use the [reconciled inventory and exits](le
3838

3939
## Current implementation checkpoint
4040

41+
The current [event transaction and default-cutover plan](ledger/shared-goal-authority-state-provider-v0/2026-09-24-event-completion-transaction.md) estimates 5–8 complete packages conditionally. #4967 source assembly and #4968 capture delivery are already delivered; event-writer binding remains open, with atomic completion repaired here as a prerequisite. Earlier counts below describe historical checkpoints, not additional current work.
42+
4143
Handoff-mode changes now share one TS ownership-fact classifier before and
4244
after promotion. Legacy event-only claims reject rather than disappear at a
4345
Markdown boundary; event append locks protect the observation through writeback.

‎docs/architecture/rfcs/shared-goal-authority-state-provider-v0.zh-CN.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434

3535
## 当前实现检查点
3636

37+
当前剩余交付以[事件事务与默认切换计划](ledger/shared-goal-authority-state-provider-v0/2026-09-24-event-completion-transaction.zh-CN.md)为准:条件估算 5–8 个完整包。#4967 来源组装、#4968 捕获交付已经完成;事件写入者绑定仍未完成,本批先修复完整完成事务。下文更早的包数属于历史检查点,不能作为当前待办重复计算。
38+
3739
终结 caller 现将审核与验证绑定 canonical 来源,历史回执恢复不再依赖私有 argv。
3840
Agent 完成和 Monitor 停止复用普通编辑的当前 head 显示确认。
3941
[调用与恢复合同](../../reference/canonical-terminal-review.zh-CN.md)。此批推进 L2/L5,

‎docs/architecture/rfcs/typescript-control-plane-migration-v0.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ Retain T0 caller/parity inventory, T1/T2 transaction/effect convergence, T3 comp
2929

3030
## Current implementation checkpoint
3131

32+
The current [event transaction and default-cutover plan](ledger/shared-goal-authority-state-provider-v0/2026-09-24-event-completion-transaction.md) estimates 5–8 complete packages conditionally. #4967 source assembly and #4968 capture delivery are already delivered; event-writer binding remains open, with atomic completion repaired here as a prerequisite. Earlier counts below describe historical checkpoints, not additional current work.
33+
3234
Canonical collection transport now uses snapshot-bound, byte-bounded TS pages.
3335
The same `canonicalTodoCollection` owner validates both the retained direct list
3436
and paged reads; Python assembles complete pages and preserves the caller shape.

‎docs/architecture/rfcs/typescript-control-plane-migration-v0.zh-CN.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ shared-authority 的当前核对表区分已合入实现、在途 PR、新代码
3636

3737
## 当前实现检查点
3838

39+
当前剩余交付以[事件事务与默认切换计划](ledger/shared-goal-authority-state-provider-v0/2026-09-24-event-completion-transaction.zh-CN.md)为准:条件估算 5–8 个完整包。#4967 来源组装、#4968 捕获交付已经完成;事件写入者绑定仍未完成,本批先修复完整完成事务。下文更早的包数属于历史检查点,不能作为当前待办重复计算。
40+
3941
Canonical command 的 receipt/head 观察顺序统一归属 TS:团队规划、Todo 创建/
4042
修改/领取/终态/归档、Monitor、lease 维护和 Goal acceptance 在读 head 后复查原
4143
receipt,再执行新准入。这修复同 operation 并发竞争,不扩展 provider API、不

0 commit comments

Comments
 (0)