From 03dee775304077ca627aeb182c74da7dbb9b0f7c Mon Sep 17 00:00:00 2001 From: wchwawa Date: Fri, 4 Sep 2026 09:11:50 +1000 Subject: [PATCH 1/3] docs(rfc): propose the Stage 2C promotion design and record its owner decisions Section 12 gains questions 8 to 13 with proposed answers: the promoted head schema and the closed coordination field set the aggregate owns, hard_lease only promotion in v0, prose persistence through a projection outbox, the registry declaration of a promoted goal and who may write it, the file-profile retention, fast-path, and capacity rules that gate the first real promotion, and the fate of the Python reference executor. Appendix C records the design those questions decide: field-split authority with one TypeScript transaction per transition, Markdown and lease files as projections with watermarks, a single legacy-writer gate with typed error codes, the promote, rollback, and verify commands with their preconditions and crash analysis, why growth is a promotion prerequisite, and the PR sequence. Nothing in it is implemented; the parity half must merge and the design must be approved before any promotion code starts. Documentation only. Signed-off-by: wchwawa --- ...shared-goal-authority-state-provider-v0.md | 159 ++++++++++++++++++ ...-goal-authority-state-provider-v0.zh-CN.md | 120 +++++++++++++ 2 files changed, 279 insertions(+) diff --git a/docs/architecture/rfcs/shared-goal-authority-state-provider-v0.md b/docs/architecture/rfcs/shared-goal-authority-state-provider-v0.md index fb67d0c0bc..0ba7b7603a 100644 --- a/docs/architecture/rfcs/shared-goal-authority-state-provider-v0.md +++ b/docs/architecture/rfcs/shared-goal-authority-state-provider-v0.md @@ -1740,6 +1740,64 @@ it does not promote any provider or complete the Stage 2C promotion. store identity or lineage, and an explicit TEST ONLY canary marker; a Goal without that marker cannot be admitted by a shared-authority guard, and the runtime resolves the provider from the record rather than from argv.* +8. Which head schema does the local promotion commit, and which fields does + the aggregate own? *Proposed answer: a new `loopx_local_authority_head_v1` + that is a superset of the Stage 2C shadow projection (`goal_id`, + `handoff_mode`, the closed todo and lease field sets) plus + `authority_revision`, `store_binding`, and an `authority_source` record, + rather than the Python reference `loopx_coordination_head_v1`; Stage 3 + projects from it. The aggregate is canonical only for the closed + coordination set C (per todo: identity, role, status, claim and binding + fields, gates, task class and action kind, required scopes and + capabilities, continuation and successor links, the completion triple + and evidence pointers, the archived flag, `todo_revision`, and + `last_lease_epoch`; every lease record; the goal `handoff_mode`). Prose + (text, notes, next action, monitor metadata, feedback, `updated_at`) + stays Markdown-canonical. `archive-completed` is a head transition, not a + projection rewrite. Appendix C carries the design.* +9. Does v0 promotion cover only `hard_lease` goals? *Proposed answer: yes. A + `legacy` or `soft_claim` goal first switches mode under the Appendix B + quiescence rule; promotion never changes the mode implicitly.* +10. Once the aggregate is canonical, is prose persisted through a projection + outbox, or does the RFC accept a crash window between the head commit and + the Markdown rewrite? *Proposed answer: a projection outbox that shares + the direction-neutral entry schema of the Stage 2C parity half + (artifact-first: outbox entry, TypeScript commit, Markdown render, entry + retirement). Readers compare the front-matter watermark with the head + revision and replay the outbox when behind. A promoted goal stops writing + `.lifecycle-operations` and `.lifecycle-fences`; the transaction replaces + both.* +11. What declares a promoted goal, and who may write that declaration? + *Proposed answer: a goal-level registry record + `coordination.authority_source` (`legacy_local | file_aggregate`, + provider, store identity and directory, `promoted_at`, the promotion + operation id, the source digest, a TEST ONLY marker, `rolled_back_from`), + duplicated into the state-file front matter so it travels with the file + (Appendix B, rule 2). Only `loopx authority promote|rollback --execute` + may write it; `configure-goal` refuses to edit it; `bootstrap --force` + refuses a promoted goal. In v0 a second endpoint that sees the + front-matter declaration gets `goal_promoted_on_other_endpoint` on + coordination-field writes; an older endpoint can only be detected + (`projection_diverged`), not blocked, until shared mode.* +12. Which file-profile retention, fast-path, and capacity rules gate the + first real promotion, and may `committed[].projection` degrade to a + digest? *Proposed answer: question 5 applied to the file profile before + any real goal is promoted: sealed segments outside the head document + (create-only, chained by path, digest, count, and cursor range; a missing + segment fails closed), a head-only fast path that validates the chain + once per process or through an in-document checkpoint, and a + `store_capacity_exhausted` fail-closed limit (proposed 8 MiB). The + projection of a retained transaction may degrade to a digest only if the + Stage 3 scan keeps every field parity compares. Question 6 couples here: + Host renewals must be transactions, never direct lease-file writes, + because their rate sets the segment window.* +13. What happens to the Python reference executor (`executor.py`, + `file_provider.py`, `head.py`, `goal_state_shadow.py`)? *Proposed answer: + keep it coverage-only until the TypeScript transaction module exists, + port its scenario batteries to TypeScript tests, then delete it in the + promotion PR; two local aggregate formats cannot both be canonical. + Flipping the file profile's `qualification_holds` to `[]` and its `stage` + literal happens only inside that PR.* --- @@ -1890,3 +1948,104 @@ claim overriding an active lease; the completion fence disarming in the conflicted state; authorization running before the lease fence; claim-change entry points not yet gated; the window between the lease acquire's projection read and the state-file lock. + + +## Appendix C: Stage 2C Promotion Design (proposal, 2026-09-03) + +This appendix records the design the second half of Stage 2C promotes toward. +It is a proposal for questions 8 to 13 in section 12; nothing in it is +implemented, and the parity half (transaction-bound outbox, drain, typed +parity verdict) must merge and this design must be approved before any +promotion code starts. + +### Target state + +- Field-split authority. The local `FileAuthorityStore` aggregate becomes + canonical for the closed coordination set C named in question 8; prose + stays canonical in Markdown. Every legal transition of a C field is exactly + one `commitAuthority` (events, next head, receipt); the receipt index of + `FileAuthorityStore.committed[]` is the receipt store, so section 6.2 + atomicity holds without embedding receipts in the head. +- Decisions stay in TypeScript. Lease decisions reuse + `evaluateTaskLeaseAcquireDecision` and `decideTaskLeaseLifecycle`; todo, + terminal, and handoff decisions need the Stage 1 Part 2 TypeScript cutover + first. A new `local_authority_transaction.ts` handler reads the head under + the store lock, runs the section 5 steps, composes the pure decisions, + commits, reconciles conflict or ambiguity through `readReceipt`, and returns + the affected records for rendering. The happy path is two round trips. +- Markdown and lease files become projections. The front matter carries + `authority_source`, `authority_store_identity`, `authority_revision`, and + `authority_projection_digest`; lease projection files carry + `projected_from`. Prose persistence goes through the projection outbox of + question 10; `projection_freshness(goal)` compares the watermark with the + head revision and replays the outbox, and a digest mismatch is + `projection_diverged`, which blocks until `loopx authority reconcile`. + +### Fencing legacy writers + +- One gate, `require_local_authority_write_grant`, runs after every Markdown + lock is taken and before any decision, in every Todo, follow-up, + handoff-mode, refresh-state, bootstrap, and feedback writer; the lease side + receives the `authority_source` fact through the existing native requests + and refuses lease-file writes for a promoted goal + (`legacy_lease_writer_fenced`). Error codes: `legacy_writer_fenced`, + `authority_unavailable` (store missing, corrupt, or lock timeout; always + fail closed, never fall back), `store_lineage_mismatch`, + `projection_diverged`, `goal_promoted_on_other_endpoint`, + `promoted_field_write_without_transition`. +- Prose writers stay allowed on a promoted goal but must pass the gate: the + watermark equals the head revision, the outbox is empty, and the C fields + parse identically before and after the write. +- Section 8's no-automatic-fallback rule is local: the first authority write + is `authority_revision > 0`, the gate never degrades, and only + `loopx authority rollback --execute` returns a goal to `legacy_local`, + retiring the store lineage so a later promotion mints a new identity. + +### Commands + +- `loopx authority promote --goal-id G [--execute]` takes the Markdown + (cross-runtime), lease, and store locks in that order; requires an enabled + shadow whose `verify` is `equal` for the current source digest, + `handoff_mode == hard_lease`, Appendix B quiescence, no prepared + `.lifecycle-operations` or held `.lifecycle-fences`, no event-only todo, and + one runtime root; bootstraps the head from every todo (including done ones, + with lease watermarks against ABA) at revision 0; commits it to a fresh + store directory (`operation_id = "promote:" + sha256(goal, source_digest)`, + event `authority_promoted`); renders and verifies the projection digest; + then flips the registry record and retires `authority_shadow`. A rerun + after a crash refuses a store whose source digest differs unless + `--discard-abandoned-store` is given, and completes idempotently once the + watermark is in place. +- `loopx authority rollback --goal-id G --execute` needs the same quiescence + and an empty outbox, exports the head into the Markdown C fields and the + final lease records, verifies `equal`, removes the watermark, records + `legacy_local` plus `rolled_back_from`, and retires the lineage. At + revision 0 it is section 8's return to an untouched local source; after + that it is the reviewed fenced export of question 3 and never runs during + an active lease. +- `loopx authority verify --goal-id G` is the post-promotion parity check + (`equal | diverged | stale`, plus pending outbox) and joins `loopx doctor`. + +### Growth is a promotion prerequisite + +`retain_all_v0` in one document is quadratic: a 600 s TTL renewed every +300 s with three active todos is roughly 300 transitions a day and 9000 a +month, about 140 MB of document at a 15 KiB head, with every CLI command +parsing and hashing the whole file, past the 5 s effect timeout and the 2 MiB +response cap. Question 12 therefore precedes any real promotion; retaining +everything in one document is acceptable only for the promotion bootstrap and +for test goals. + +### Sequence + +A. todo, terminal, and handoff decisions cut over to TypeScript; +B. `local_authority_transaction.ts` reference implementation, unwired, with +the executor batteries ported; C. file-store retention, fast path, and +capacity (questions 5 and 12); D. registry record, gate, watermark, and +projection outbox, dormant with `authority_source` fixed at `legacy_local`; +E. the promotion PR (promote, rollback, verify, routing to B, projection +rendering, deletion of the four reference modules, flipping the holds and +stage literal, governance rows, and this RFC's status section). E and every +`file_aggregate` return path in D wait for the parity half to merge and for +this design to be approved; D's outbox reuses the parity half's entry schema +so the repository never carries two record formats. diff --git a/docs/architecture/rfcs/shared-goal-authority-state-provider-v0.zh-CN.md b/docs/architecture/rfcs/shared-goal-authority-state-provider-v0.zh-CN.md index 8212d0ed52..5bbedc2187 100644 --- a/docs/architecture/rfcs/shared-goal-authority-state-provider-v0.zh-CN.md +++ b/docs/architecture/rfcs/shared-goal-authority-state-provider-v0.zh-CN.md @@ -1394,6 +1394,48 @@ Live 行按环境门控(`LOOPX_TEST_POSTGRES_URL`;`NOKV_COORDINATION_LIVE=1` 布,记录 provider 种类、store identity 或 lineage,以及显式的 TEST ONLY canary 标记;没有该标记的 Goal 不得被 shared-authority guard 准入,runtime 从该记录而 非 argv 解析 provider。* +8. 本地 promotion 提交哪种 head schema,aggregate 拥有哪些字段?*拟议答案:新的 + `loopx_local_authority_head_v1`,它是 Stage 2C shadow 投影(`goal_id`、 + `handoff_mode`、闭合的 todo 与 lease 字段集)的超集,再加 `authority_revision`、 + `store_binding` 与一条 `authority_source` 记录,而不是 Python 参考实现的 + `loopx_coordination_head_v1`;Stage 3 从它投影。aggregate 只对闭合的协调字段集 + C 具有权威(每个 todo:身份、role、status、claim 与 binding 字段、gate、task + class 与 action kind、required scopes 与 capabilities、continuation 与 + successor 链接、completion 三元组与 evidence 指针、archived 标记、 + `todo_revision`、`last_lease_epoch`;每条 lease 记录;goal 的 + `handoff_mode`)。prose(正文、note、next action、monitor 元数据、feedback、 + `updated_at`)仍以 Markdown 为准。`archive-completed` 是一次 head transition, + 不是投影重写。设计见附录 C。* +9. v0 promotion 是否只覆盖 `hard_lease` goal?*拟议答案:是。`legacy` 或 + `soft_claim` goal 先按附录 B 的静止规则切换模式;promotion 从不隐式改变模式。* +10. aggregate 成为权威后,prose 是走 projection outbox 持久化,还是接受 head 提交与 + Markdown 重写之间的崩溃窗口?*拟议答案:走 projection outbox,并与 Stage 2C + parity 半段共用同一个方向中立的 entry schema(artifact-first:outbox entry、 + TypeScript commit、Markdown 渲染、entry 退役)。读者用 front matter 水印比对 + head revision,落后则回放 outbox。已晋升的 goal 不再写 + `.lifecycle-operations` 与 `.lifecycle-fences`,事务取代二者。* +11. 什么声明一个 goal 已晋升,谁可以写这条声明?*拟议答案:goal 级 registry 记录 + `coordination.authority_source`(`legacy_local | file_aggregate`、provider、 + store identity 与目录、`promoted_at`、promotion operation id、源 digest、TEST + ONLY 标记、`rolled_back_from`),并复制到 state 文件 front matter 以随文件传播 + (附录 B 规则 2)。只有 `loopx authority promote|rollback --execute` 能写它; + `configure-goal` 拒绝修改;`bootstrap --force` 拒绝已晋升的 goal。v0 里另一 + 端点看到 front matter 声明后,对协调字段的写得到 + `goal_promoted_on_other_endpoint`;旧版本端点只能被检出 + (`projection_diverged`)而不能被阻止,直到 shared mode。* +12. 哪些 file profile 的保留、快速路径与容量规则是第一次真实 promotion 的前置, + `committed[].projection` 可否退化为 digest?*拟议答案:在任何真实 goal 晋升之 + 前把问题 5 落实到 file profile:head 文档之外的封段(create-only,按 path、 + digest、count 与 cursor 区间成链;缺段 fail closed)、每进程校验一次链或依靠 + 文档内 checkpoint 的 head-only 快速路径、以及 `store_capacity_exhausted` 的 + fail-closed 上限(拟 8 MiB)。已保留事务的 projection 只有在 Stage 3 scan 仍 + 保留 parity 比较的全部字段时才可退化为 digest。问题 6 在此耦合:Host 续约必须 + 经事务而不是直写 lease 文件,因为续约频率决定封段窗口。* +13. Python 参考执行器(`executor.py`、`file_provider.py`、`head.py`、 + `goal_state_shadow.py`)如何处置?*拟议答案:在 TypeScript 事务模块存在之前 + 保持 coverage-only,先把它们的场景电池移植为 TypeScript 测试,再在 promotion + PR 中删除;两种本地 aggregate 格式不能同时为准。file profile 的 + `qualification_holds` 翻为 `[]` 与 `stage` 字面量的改变只在该 PR 内发生。* --- @@ -1506,3 +1548,81 @@ shadow 读取决策,上述 migration、rollback、parity、read flip 与 legac characterization 阶段的负向用例在原清单上补充:软认领盖掉活租约、完成栅栏在 矛盾态失效、授权检查先于租约栅栏、尚未设防的认领变更入口、租约获取读取投影 与状态文件锁之间的窗口。 + + +## 附录 C:Stage 2C promotion 设计(提案,2026-09-03) + +本附录记录 Stage 2C 后半段所指向的设计,是第 12 节问题 8 到 13 的提案;其中没有 +任何内容已实现。parity 半段(事务绑定的 outbox、drain、typed parity verdict)合并 +且本设计获批之前,不得开始任何 promotion 代码。 + +### 目标态 + +- 字段拆分权威。本地 `FileAuthorityStore` aggregate 只对问题 8 命名的闭合协调字段集 + C 成为权威;prose 仍以 Markdown 为准。C 字段的每一次合法 transition 恰是一次 + `commitAuthority`(events、next head、receipt);`FileAuthorityStore.committed[]` + 按 operation_id 的 receipt 索引就是 receipt 存储,因此第 6.2 节的原子性无需把 + receipt 嵌进 head。 +- 决策留在 TypeScript。lease 决策复用 `evaluateTaskLeaseAcquireDecision` 与 + `decideTaskLeaseLifecycle`;todo、terminal 与 handoff 决策需先完成 Stage 1 Part 2 + 预留的 TypeScript cutover。新的 `local_authority_transaction.ts` handler 在 store + 锁内自读 head,执行第 5 节步骤,组合纯决策,提交,并经 `readReceipt` 调和 + conflict 与 ambiguity,返回受影响记录用于渲染。happy path 两次往返。 +- Markdown 与 lease 文件成为投影。front matter 携带 `authority_source`、 + `authority_store_identity`、`authority_revision` 与 + `authority_projection_digest`;lease 投影文件携带 `projected_from`。prose 持久化 + 经问题 10 的 projection outbox;`projection_freshness(goal)` 比对水印与 head + revision 并回放 outbox,digest 不符即 `projection_diverged`,阻塞直到 + `loopx authority reconcile`。 + +### fence legacy writer + +- 单一 gate `require_local_authority_write_grant` 在每个 Todo、follow-up、 + handoff-mode、refresh-state、bootstrap 与 feedback 写者取得 Markdown 锁之后、决策 + 之前运行;lease 侧经既有 native 请求拿到 `authority_source` 事实,对已晋升 goal + 拒绝写 lease 文件(`legacy_lease_writer_fenced`)。错误码: + `legacy_writer_fenced`、`authority_unavailable`(store 缺失、损坏或锁超时一律 + fail closed,绝不回退)、`store_lineage_mismatch`、`projection_diverged`、 + `goal_promoted_on_other_endpoint`、`promoted_field_write_without_transition`。 +- prose 写者在已晋升 goal 上仍允许,但必须过 gate:水印等于 head revision、outbox + 为空、写前后 C 字段解析逐字段一致。 +- 第 8 节"首次权威写之后禁止自动回退"在本地的落实:首次权威写即 + `authority_revision > 0`,gate 从不降级,只有 `loopx authority rollback + --execute` 能回到 `legacy_local`,并退役 store lineage,使再次晋升铸造新身份。 + +### 命令 + +- `loopx authority promote --goal-id G [--execute]` 依次取 Markdown(跨运行时)、 + lease 与 store 锁;前置:shadow 已启用且对当前源 digest 的 `verify` 为 `equal`、 + `handoff_mode == hard_lease`、附录 B 静止、无 prepared 态 `.lifecycle-operations` + 与 held 态 `.lifecycle-fences`、无仅事件 todo、单一 runtime root;从全部 todo + (含 done,带防 ABA 的 lease 水印)在 revision 0 引导 head;提交到新 store 目录 + (`operation_id = "promote:" + sha256(goal, source_digest)`,事件 + `authority_promoted`);渲染并校验投影 digest;再翻注册表记录并退役 + `authority_shadow`。崩溃后重跑若发现源 digest 不同的既有 store 则拒绝,除非给出 + `--discard-abandoned-store`;水印一旦落盘即幂等完成。 +- `loopx authority rollback --goal-id G --execute` 需同样的静止与空 outbox,把 head + 导出到 Markdown C 字段与 lease 终态记录,校验 `equal`,去水印,记录 + `legacy_local` 与 `rolled_back_from`,退役 lineage。revision 0 时即第 8 节回到未 + 触碰的本地源;之后即问题 3 要求的经评审的 fenced export,且永不在活跃 lease 期间 + 运行。 +- `loopx authority verify --goal-id G` 是晋升后的 parity 检查(`equal | diverged | + stale`,加 pending outbox),并入 `loopx doctor`。 + +### 增长是晋升前置 + +单文档 `retain_all_v0` 是二次方增长:TTL 600 秒、每 300 秒续约、三个活跃 todo 约 +等于每天 300 次、每月 9000 次 transition,在 15 KiB head 下约 140 MB 文档,且每条 +CLI 命令都要解析并哈希整个文件,超过 5 秒 effect 超时与 2 MiB 响应上限。因此问题 +12 先于任何真实 promotion;单文档全量保留只对 promotion bootstrap 与测试 goal 可接受。 + +### 顺序 + +A. todo、terminal 与 handoff 决策 cutover 到 TypeScript;B. +`local_authority_transaction.ts` 参考实现,未接线,移植执行器电池;C. file-store +保留、快速路径与容量(问题 5 与 12);D. 注册表记录、gate、水印与 projection +outbox,惰性发布,`authority_source` 恒为 `legacy_local`;E. promotion PR(promote、 +rollback、verify、路由到 B、渲染投影、删除四个参考模块、翻 holds 与 stage 字面量、 +治理行与本 RFC 状态段)。E 与 D 中一切返回 `file_aggregate` 的路径都等 parity 半 +段合并且本设计获批;D 的 outbox 复用 parity 半段的 entry schema,仓库永不同时携带 +两种记录格式。 From 5c34ff010f03c6a72dc2110d832727b58a4ef857 Mon Sep 17 00:00:00 2001 From: wchwawa Date: Fri, 4 Sep 2026 09:28:30 +1000 Subject: [PATCH 2/3] docs(rfc): align the promotion proposal with the merged runtime shadow and cutover kernel main now ships the default-off runtime shadow, the coordination-shadow inspect/qualify/read-candidate/bootstrap/rollback commands, the cutover kernel (promote, mutate, todo_read), and the legacy writer fence integration. The promotion questions and Appendix C therefore start from those pieces instead of proposing parallel ones: - question 8 adopts the shipped runtime-shadow projection as the promoted head, with three amendments (drop updated_at from the compared set, add the completion and archival fields, archive-completed as a transition); - question 11 keeps the durable fence as the local authority of the cutover and reduces the registry/front-matter record to discovery and cross-endpoint detection; - question 13 ties the reference executor's removal to routing the kernel's mutation path from the CLI; - new question 14 names the two shadow lineages now on main and proposes the runtime shadow as Stage 2C's lineage, with the parity half's transaction-bound outbox as the durable capture that closes the concurrent-sampling and commit-to-dispatch windows; - Appendix C lists what is shipped, what is still missing before promotion, and a sequence that builds on the kernel. Signed-off-by: wchwawa --- ...shared-goal-authority-state-provider-v0.md | 253 +++++++++--------- ...-goal-authority-state-provider-v0.zh-CN.md | 187 +++++++------ 2 files changed, 220 insertions(+), 220 deletions(-) diff --git a/docs/architecture/rfcs/shared-goal-authority-state-provider-v0.md b/docs/architecture/rfcs/shared-goal-authority-state-provider-v0.md index 0ba7b7603a..84de8a3bd3 100644 --- a/docs/architecture/rfcs/shared-goal-authority-state-provider-v0.md +++ b/docs/architecture/rfcs/shared-goal-authority-state-provider-v0.md @@ -1740,45 +1740,51 @@ it does not promote any provider or complete the Stage 2C promotion. store identity or lineage, and an explicit TEST ONLY canary marker; a Goal without that marker cannot be admitted by a shared-authority guard, and the runtime resolves the provider from the record rather than from argv.* -8. Which head schema does the local promotion commit, and which fields does - the aggregate own? *Proposed answer: a new `loopx_local_authority_head_v1` - that is a superset of the Stage 2C shadow projection (`goal_id`, - `handoff_mode`, the closed todo and lease field sets) plus - `authority_revision`, `store_binding`, and an `authority_source` record, - rather than the Python reference `loopx_coordination_head_v1`; Stage 3 - projects from it. The aggregate is canonical only for the closed - coordination set C (per todo: identity, role, status, claim and binding - fields, gates, task class and action kind, required scopes and - capabilities, continuation and successor links, the completion triple - and evidence pointers, the archived flag, `todo_revision`, and - `last_lease_epoch`; every lease record; the goal `handoff_mode`). Prose - (text, notes, next action, monitor metadata, feedback, `updated_at`) - stays Markdown-canonical. `archive-completed` is a head transition, not a - projection rewrite. Appendix C carries the design.* +8. Which head shape does the local promotion commit, and which fields does + the aggregate own? The merged runtime shadow already fixes the shape: + `loopx_coordination_runtime_shadow_projection_v0` (nineteen todo fields + including `updated_at`, `superseding_todo_id`, `task_domain`, and + `task_repository`; ten lease fields), and the cutover kernel commits + mutations against it as `canonical_authority: file_v0`. *Proposed answer: + adopt that projection as the promoted head rather than a superset schema, + with three amendments before promotion: drop `updated_at` from the compared + set (it is rewritten by every prose edit, so keeping it makes every prose + write a coordination transaction and every parity compare prose-sensitive; + it stays on the receipt as `source_version`); add the fields promotion must + own but the projection lacks (`required_capabilities`, `decision_scope` and + `required_decision_scopes`, the completion triple and evidence pointers, + the archived flag, `todo_revision`); and treat `archive-completed` as a + head transition, not a projection rewrite. Appendix C lists what the kernel + already provides.* 9. Does v0 promotion cover only `hard_lease` goals? *Proposed answer: yes. A `legacy` or `soft_claim` goal first switches mode under the Appendix B quiescence rule; promotion never changes the mode implicitly.* -10. Once the aggregate is canonical, is prose persisted through a projection - outbox, or does the RFC accept a crash window between the head commit and - the Markdown rewrite? *Proposed answer: a projection outbox that shares - the direction-neutral entry schema of the Stage 2C parity half - (artifact-first: outbox entry, TypeScript commit, Markdown render, entry - retirement). Readers compare the front-matter watermark with the head - revision and replay the outbox when behind. A promoted goal stops writing - `.lifecycle-operations` and `.lifecycle-fences`; the transaction replaces - both.* -11. What declares a promoted goal, and who may write that declaration? - *Proposed answer: a goal-level registry record - `coordination.authority_source` (`legacy_local | file_aggregate`, - provider, store identity and directory, `promoted_at`, the promotion - operation id, the source digest, a TEST ONLY marker, `rolled_back_from`), - duplicated into the state-file front matter so it travels with the file - (Appendix B, rule 2). Only `loopx authority promote|rollback --execute` - may write it; `configure-goal` refuses to edit it; `bootstrap --force` - refuses a promoted goal. In v0 a second endpoint that sees the - front-matter declaration gets `goal_promoted_on_other_endpoint` on - coordination-field writes; an older endpoint can only be detected - (`projection_diverged`), not blocked, until shared mode.* +10. After the provider-first read flip, Markdown and the lease files are + projections and the kernel forbids any fallback to them. How is prose + (text, notes, next action, monitor metadata, feedback) persisted then: + through a projection outbox, or by accepting a crash window between the + head commit and the Markdown rewrite? *Proposed answer: a projection + outbox that reuses the transaction-bound outbox entry schema of the + parity half (artifact-first: outbox entry, TypeScript commit, Markdown + render, entry retirement), so both directions share one record format; + readers compare the front-matter watermark with the head revision and + replay the outbox when behind.* +11. What declares a promoted goal, and who may write that declaration? The + merged fence is a durable TypeScript-owned file under + `authority-transition/file-v0/`, bound to a fence id, the source version, + and the qualified shadow revision, and every Python Todo mutation and + native task-lease mutation checks it under its own lock. *Proposed + answer: the fence file stays the local authority of the cutover; a + goal-level registry record (`coordination.authority_source`: provider, + store identity, `promoted_at`, promotion operation id, source digest, + `rolled_back_from`) plus its copy in the state-file front matter exists + for discovery and for cross-endpoint detection; only the promotion + orchestrator and rollback may write either; `configure-goal` refuses to + edit the record and `bootstrap --force` refuses a promoted goal. In v0 a + second endpoint that sees the front-matter copy gets + `goal_promoted_on_other_endpoint` on coordination-field writes; an older + endpoint can only be detected (`projection_diverged`), not blocked, until + shared mode.* 12. Which file-profile retention, fast-path, and capacity rules gate the first real promotion, and may `committed[].projection` degrade to a digest? *Proposed answer: question 5 applied to the file profile before @@ -1793,11 +1799,27 @@ it does not promote any provider or complete the Stage 2C promotion. because their rate sets the segment window.* 13. What happens to the Python reference executor (`executor.py`, `file_provider.py`, `head.py`, `goal_state_shadow.py`)? *Proposed answer: - keep it coverage-only until the TypeScript transaction module exists, - port its scenario batteries to TypeScript tests, then delete it in the - promotion PR; two local aggregate formats cannot both be canonical. + keep it coverage-only until the kernel's mutation path is routed from the + CLI, port its scenario batteries to TypeScript tests, then delete it in + the promotion PR; two local aggregate formats cannot both be canonical. Flipping the file profile's `qualification_holds` to `[]` and its `stage` literal happens only inside that PR.* +14. `main` now carries two default-off shadow lineages for the same writers: + the observation capture of #3818 (`coordination.authority_shadow`, + `authority-shadow/file/`, projection v0) and the runtime shadow + (`coordination.runtime_shadow`, `authority-shadow/file-v0`, projection v0 + with `inspect`, `qualify`, `bootstrap`, `rollback`, and `read-candidate`). + Both re-sample the source after the primary commit, so both share the + concurrent-writer and commit-to-dispatch loss windows that the review of + #3818 named. Which lineage is Stage 2C's, and what closes those windows? + *Proposed answer: the runtime shadow is the lineage, because the parity + report, bootstrap, quarantine rollback, read shape, and promotion kernel + already bind to it. The transaction-bound outbox of the parity half + (prepared entry inside the writer's own lock, committed marker after the + primary write, bounded drain with `operation_id = entry id`) becomes the + durable capture that feeds `coordination.runtime_shadow.commit`, and the + #3818 observation path retires once that capture is wired. The RFC must + not keep two shadow record formats.* --- @@ -1950,81 +1972,63 @@ entry points not yet gated; the window between the lease acquire's projection read and the state-file lock. -## Appendix C: Stage 2C Promotion Design (proposal, 2026-09-03) - -This appendix records the design the second half of Stage 2C promotes toward. -It is a proposal for questions 8 to 13 in section 12; nothing in it is -implemented, and the parity half (transaction-bound outbox, drain, typed -parity verdict) must merge and this design must be approved before any -promotion code starts. - -### Target state - -- Field-split authority. The local `FileAuthorityStore` aggregate becomes - canonical for the closed coordination set C named in question 8; prose - stays canonical in Markdown. Every legal transition of a C field is exactly - one `commitAuthority` (events, next head, receipt); the receipt index of - `FileAuthorityStore.committed[]` is the receipt store, so section 6.2 - atomicity holds without embedding receipts in the head. -- Decisions stay in TypeScript. Lease decisions reuse - `evaluateTaskLeaseAcquireDecision` and `decideTaskLeaseLifecycle`; todo, - terminal, and handoff decisions need the Stage 1 Part 2 TypeScript cutover - first. A new `local_authority_transaction.ts` handler reads the head under - the store lock, runs the section 5 steps, composes the pure decisions, - commits, reconciles conflict or ambiguity through `readReceipt`, and returns - the affected records for rendering. The happy path is two round trips. -- Markdown and lease files become projections. The front matter carries - `authority_source`, `authority_store_identity`, `authority_revision`, and - `authority_projection_digest`; lease projection files carry - `projected_from`. Prose persistence goes through the projection outbox of - question 10; `projection_freshness(goal)` compares the watermark with the - head revision and replays the outbox, and a digest mismatch is - `projection_diverged`, which blocks until `loopx authority reconcile`. - -### Fencing legacy writers - -- One gate, `require_local_authority_write_grant`, runs after every Markdown - lock is taken and before any decision, in every Todo, follow-up, - handoff-mode, refresh-state, bootstrap, and feedback writer; the lease side - receives the `authority_source` fact through the existing native requests - and refuses lease-file writes for a promoted goal - (`legacy_lease_writer_fenced`). Error codes: `legacy_writer_fenced`, - `authority_unavailable` (store missing, corrupt, or lock timeout; always - fail closed, never fall back), `store_lineage_mismatch`, - `projection_diverged`, `goal_promoted_on_other_endpoint`, - `promoted_field_write_without_transition`. -- Prose writers stay allowed on a promoted goal but must pass the gate: the - watermark equals the head revision, the outbox is empty, and the C fields - parse identically before and after the write. -- Section 8's no-automatic-fallback rule is local: the first authority write - is `authority_revision > 0`, the gate never degrades, and only - `loopx authority rollback --execute` returns a goal to `legacy_local`, - retiring the store lineage so a later promotion mints a new identity. - -### Commands - -- `loopx authority promote --goal-id G [--execute]` takes the Markdown - (cross-runtime), lease, and store locks in that order; requires an enabled - shadow whose `verify` is `equal` for the current source digest, - `handoff_mode == hard_lease`, Appendix B quiescence, no prepared - `.lifecycle-operations` or held `.lifecycle-fences`, no event-only todo, and - one runtime root; bootstraps the head from every todo (including done ones, - with lease watermarks against ABA) at revision 0; commits it to a fresh - store directory (`operation_id = "promote:" + sha256(goal, source_digest)`, - event `authority_promoted`); renders and verifies the projection digest; - then flips the registry record and retires `authority_shadow`. A rerun - after a crash refuses a store whose source digest differs unless - `--discard-abandoned-store` is given, and completes idempotently once the - watermark is in place. -- `loopx authority rollback --goal-id G --execute` needs the same quiescence - and an empty outbox, exports the head into the Markdown C fields and the - final lease records, verifies `equal`, removes the watermark, records - `legacy_local` plus `rolled_back_from`, and retires the lineage. At - revision 0 it is section 8's return to an untouched local source; after - that it is the reviewed fenced export of question 3 and never runs during - an active lease. -- `loopx authority verify --goal-id G` is the post-promotion parity check - (`equal | diverged | stale`, plus pending outbox) and joins `loopx doctor`. +## Appendix C: Stage 2C Promotion Design (proposal, 2026-09-04) + +This appendix records the design that questions 8 to 14 in section 12 +decide. It starts from what `main` already ships and names only what is still +missing; nothing below is implemented by this document, and the parity half +must merge and the questions must be answered before any promotion code +starts. + +### Shipped on `main` (2026-09-03) + +- The default-off runtime shadow: one `AuthorityStore` transaction per + committed Todo or task-lease mutation, keyed by the mutation's rollout event + id and `updated_at`, with receipt replay, content-drift rejection, + ambiguous-commit reconciliation, and read-back. +- `loopx coordination-shadow inspect | qualify | read-candidate | bootstrap | + rollback`: typed one-point parity (`missing | matched | drifted` with both + digests), a coverage-based sustained parity report, the provider-first read + shape (still `decision_read_from_shadow=false`), the bootstrap of an empty + shadow from the legacy projection, and a revision-fenced quarantine rollback + of a pre-promotion lineage. +- The cutover kernel: a pure reducer from mutation to projection, event, and + receipt; `coordination.local_authority.promote` requiring a qualified shadow + at one exact revision and digest plus an independently persisted legacy + writer fence bound to that revision; provider-first `mutate` and + `todo_read` that never fall back to Markdown. +- The fence integration: every Python Todo mutation and every native task-lease + acquire, renew, transfer, and release checks the durable fence while holding + its own lock; an absent fence costs no runtime call; a present, unreadable, + or invalid fence fails closed. + +### Still missing before promotion + +- Provider-first CLI routing and the lock-owning promotion orchestrator (the + kernel's own next slice): take the Todo and lease legacy locks, require + `qualify` to be `qualified` at the current revision and digest, engage the + fence, run `promote`, render the projections, and record the declaration of + question 11; refuse a rerun whose source digest changed unless the abandoned + store is explicitly discarded. +- A transaction-bound capture (question 14): the runtime shadow samples the + source after the commit, from outside the writer's lock, so a concurrent + writer can land inside the sampled projection and a crash between the commit + and the dispatch loses the mirror. The parity-half outbox closes both: the + prepared entry is written inside the lock the writer already holds, the + committed marker after the primary write returns, and a bounded drain turns + each entry into exactly one shadow transaction whose `operation_id` is the + entry id. `qualify` then counts entries, not samples. +- Prose persistence after the read flip (question 10) and the declaration + record (question 11). +- Retention, fast path, and capacity for the file profile (question 12); the + reference executor's removal and the status flips (question 13). +- Post-promotion rollback: the shipped rollback quarantines a pre-promotion + lineage. After the first authority write (`authority_revision > 0`) the + return path is question 3's reviewed fenced export: quiescence, an empty + projection outbox, export of the head into the Markdown coordination fields + and the final lease records, an `equal` verify, removal of the watermark and + fence, and retirement of the lineage; never automatic, never during an + active lease. ### Growth is a promotion prerequisite @@ -2038,14 +2042,13 @@ for test goals. ### Sequence -A. todo, terminal, and handoff decisions cut over to TypeScript; -B. `local_authority_transaction.ts` reference implementation, unwired, with -the executor batteries ported; C. file-store retention, fast path, and -capacity (questions 5 and 12); D. registry record, gate, watermark, and -projection outbox, dormant with `authority_source` fixed at `legacy_local`; -E. the promotion PR (promote, rollback, verify, routing to B, projection -rendering, deletion of the four reference modules, flipping the holds and -stage literal, governance rows, and this RFC's status section). E and every -`file_aggregate` return path in D wait for the parity half to merge and for -this design to be approved; D's outbox reuses the parity half's entry schema -so the repository never carries two record formats. +A. provider-first CLI routing and the lock-owning promotion orchestrator, on +the shipped kernel; B. the transaction-bound capture feeding +`coordination.runtime_shadow.commit`, retiring the #3818 observation path; +C. file-store retention, fast path, and capacity (questions 5 and 12); D. the +declaration record and the prose projection outbox, dormant with +`authority_source` fixed at `legacy_local`; E. the promotion PR (routing the +orchestrator to the kernel, projection rendering, deletion of the reference +modules, flipping the holds and stage literal, governance rows, and this RFC's +status section). E and every `file_aggregate` return path in D wait for the +parity half to merge and for questions 8 to 14 to be answered. diff --git a/docs/architecture/rfcs/shared-goal-authority-state-provider-v0.zh-CN.md b/docs/architecture/rfcs/shared-goal-authority-state-provider-v0.zh-CN.md index 5bbedc2187..67baf34b3e 100644 --- a/docs/architecture/rfcs/shared-goal-authority-state-provider-v0.zh-CN.md +++ b/docs/architecture/rfcs/shared-goal-authority-state-provider-v0.zh-CN.md @@ -1394,34 +1394,37 @@ Live 行按环境门控(`LOOPX_TEST_POSTGRES_URL`;`NOKV_COORDINATION_LIVE=1` 布,记录 provider 种类、store identity 或 lineage,以及显式的 TEST ONLY canary 标记;没有该标记的 Goal 不得被 shared-authority guard 准入,runtime 从该记录而 非 argv 解析 provider。* -8. 本地 promotion 提交哪种 head schema,aggregate 拥有哪些字段?*拟议答案:新的 - `loopx_local_authority_head_v1`,它是 Stage 2C shadow 投影(`goal_id`、 - `handoff_mode`、闭合的 todo 与 lease 字段集)的超集,再加 `authority_revision`、 - `store_binding` 与一条 `authority_source` 记录,而不是 Python 参考实现的 - `loopx_coordination_head_v1`;Stage 3 从它投影。aggregate 只对闭合的协调字段集 - C 具有权威(每个 todo:身份、role、status、claim 与 binding 字段、gate、task - class 与 action kind、required scopes 与 capabilities、continuation 与 - successor 链接、completion 三元组与 evidence 指针、archived 标记、 - `todo_revision`、`last_lease_epoch`;每条 lease 记录;goal 的 - `handoff_mode`)。prose(正文、note、next action、monitor 元数据、feedback、 - `updated_at`)仍以 Markdown 为准。`archive-completed` 是一次 head transition, - 不是投影重写。设计见附录 C。* +8. 本地 promotion 提交哪种 head 形状,aggregate 拥有哪些字段?已合并的 runtime + shadow 已经固定了形状:`loopx_coordination_runtime_shadow_projection_v0` + (19 个 todo 字段,含 `updated_at`、`superseding_todo_id`、`task_domain`、 + `task_repository`;10 个 lease 字段),cutover kernel 以 + `canonical_authority: file_v0` 对它提交 mutation。*拟议答案:采用该投影作为晋升 + head,而不是再造一个超集 schema,并在晋升前做三处修正:把 `updated_at` 移出比较 + 集(每次 prose 编辑都会改写它,留下它会让每次 prose 写都变成协调事务、每次 + parity 比较都对 prose 敏感;它留在 receipt 上作为 `source_version`);补上 + promotion 必须拥有但投影缺失的字段(`required_capabilities`、`decision_scope` + 与 `required_decision_scopes`、completion 三元组与 evidence 指针、archived 标 + 记、`todo_revision`);把 `archive-completed` 当作 head transition 而不是投影 + 重写。附录 C 列出 kernel 已提供的部分。* 9. v0 promotion 是否只覆盖 `hard_lease` goal?*拟议答案:是。`legacy` 或 `soft_claim` goal 先按附录 B 的静止规则切换模式;promotion 从不隐式改变模式。* -10. aggregate 成为权威后,prose 是走 projection outbox 持久化,还是接受 head 提交与 - Markdown 重写之间的崩溃窗口?*拟议答案:走 projection outbox,并与 Stage 2C - parity 半段共用同一个方向中立的 entry schema(artifact-first:outbox entry、 - TypeScript commit、Markdown 渲染、entry 退役)。读者用 front matter 水印比对 - head revision,落后则回放 outbox。已晋升的 goal 不再写 - `.lifecycle-operations` 与 `.lifecycle-fences`,事务取代二者。* -11. 什么声明一个 goal 已晋升,谁可以写这条声明?*拟议答案:goal 级 registry 记录 - `coordination.authority_source`(`legacy_local | file_aggregate`、provider、 - store identity 与目录、`promoted_at`、promotion operation id、源 digest、TEST - ONLY 标记、`rolled_back_from`),并复制到 state 文件 front matter 以随文件传播 - (附录 B 规则 2)。只有 `loopx authority promote|rollback --execute` 能写它; - `configure-goal` 拒绝修改;`bootstrap --force` 拒绝已晋升的 goal。v0 里另一 - 端点看到 front matter 声明后,对协调字段的写得到 - `goal_promoted_on_other_endpoint`;旧版本端点只能被检出 +10. provider-first read flip 之后 Markdown 与 lease 文件成为投影,kernel 禁止任何 + 回退到它们。那么 prose(正文、note、next action、monitor 元数据、feedback)如何 + 持久化:走 projection outbox,还是接受 head 提交与 Markdown 重写之间的崩溃窗 + 口?*拟议答案:走 projection outbox,并复用 parity 半段事务绑定 outbox 的 entry + schema(artifact-first:outbox entry、TypeScript commit、Markdown 渲染、entry + 退役),两个方向共用一种记录格式;读者用 front matter 水印比对 head revision, + 落后则回放 outbox。* +11. 什么声明一个 goal 已晋升,谁可以写这条声明?已合并的 fence 是 TypeScript 拥有的 + 持久文件,位于 `authority-transition/file-v0/`,绑定 fence id、源版本与已资格化的 + shadow revision;每个 Python Todo mutation 与 native task-lease mutation 都在自 + 己的锁内检查它。*拟议答案:fence 文件仍是 cutover 的本地权威;goal 级 registry + 记录(`coordination.authority_source`:provider、store identity、 + `promoted_at`、promotion operation id、源 digest、`rolled_back_from`)及其在 + state 文件 front matter 里的副本用于发现与跨端点检出;只有 promotion + orchestrator 与 rollback 能写这两者;`configure-goal` 拒绝修改该记录, + `bootstrap --force` 拒绝已晋升的 goal。v0 里看到 front matter 副本的另一端点对 + 协调字段的写得到 `goal_promoted_on_other_endpoint`;旧版本端点只能被检出 (`projection_diverged`)而不能被阻止,直到 shared mode。* 12. 哪些 file profile 的保留、快速路径与容量规则是第一次真实 promotion 的前置, `committed[].projection` 可否退化为 digest?*拟议答案:在任何真实 goal 晋升之 @@ -1432,10 +1435,22 @@ Live 行按环境门控(`LOOPX_TEST_POSTGRES_URL`;`NOKV_COORDINATION_LIVE=1` 保留 parity 比较的全部字段时才可退化为 digest。问题 6 在此耦合:Host 续约必须 经事务而不是直写 lease 文件,因为续约频率决定封段窗口。* 13. Python 参考执行器(`executor.py`、`file_provider.py`、`head.py`、 - `goal_state_shadow.py`)如何处置?*拟议答案:在 TypeScript 事务模块存在之前 - 保持 coverage-only,先把它们的场景电池移植为 TypeScript 测试,再在 promotion - PR 中删除;两种本地 aggregate 格式不能同时为准。file profile 的 + `goal_state_shadow.py`)如何处置?*拟议答案:在 kernel 的 mutation 路径接到 + CLI 之前保持 coverage-only,先把它们的场景电池移植为 TypeScript 测试,再在 + promotion PR 中删除;两种本地 aggregate 格式不能同时为准。file profile 的 `qualification_holds` 翻为 `[]` 与 `stage` 字面量的改变只在该 PR 内发生。* +14. `main` 上现在有两条针对同一批写者的默认关闭 shadow lineage:#3818 的观察捕获 + (`coordination.authority_shadow`,`authority-shadow/file/`,投影 v0)与 + runtime shadow(`coordination.runtime_shadow`,`authority-shadow/file-v0`, + 投影 v0,带 `inspect`、`qualify`、`bootstrap`、`rollback`、`read-candidate`)。 + 两者都在主写提交之后重新采样源,因此都带着 #3818 评审点名的并发写者混入与 + commit 到 dispatch 之间的丢失窗口。哪条是 Stage 2C 的 lineage,什么来关闭这两 + 个窗口?*拟议答案:runtime shadow 是 lineage,因为 parity 报告、bootstrap、隔离 + 式 rollback、读形状与 promotion kernel 已经绑定在它上面。parity 半段的事务绑 + 定 outbox(写者在自己已持有的锁内写 prepared entry,主写返回后写 committed + 标记,有界 drain 以 `operation_id = entry id` 提交)成为喂给 + `coordination.runtime_shadow.commit` 的持久捕获;该捕获接线后 #3818 的观察路径 + 退役。RFC 不得保留两种 shadow 记录格式。* --- @@ -1550,64 +1565,47 @@ characterization 阶段的负向用例在原清单上补充:软认领盖掉活 与状态文件锁之间的窗口。 -## 附录 C:Stage 2C promotion 设计(提案,2026-09-03) - -本附录记录 Stage 2C 后半段所指向的设计,是第 12 节问题 8 到 13 的提案;其中没有 -任何内容已实现。parity 半段(事务绑定的 outbox、drain、typed parity verdict)合并 -且本设计获批之前,不得开始任何 promotion 代码。 - -### 目标态 - -- 字段拆分权威。本地 `FileAuthorityStore` aggregate 只对问题 8 命名的闭合协调字段集 - C 成为权威;prose 仍以 Markdown 为准。C 字段的每一次合法 transition 恰是一次 - `commitAuthority`(events、next head、receipt);`FileAuthorityStore.committed[]` - 按 operation_id 的 receipt 索引就是 receipt 存储,因此第 6.2 节的原子性无需把 - receipt 嵌进 head。 -- 决策留在 TypeScript。lease 决策复用 `evaluateTaskLeaseAcquireDecision` 与 - `decideTaskLeaseLifecycle`;todo、terminal 与 handoff 决策需先完成 Stage 1 Part 2 - 预留的 TypeScript cutover。新的 `local_authority_transaction.ts` handler 在 store - 锁内自读 head,执行第 5 节步骤,组合纯决策,提交,并经 `readReceipt` 调和 - conflict 与 ambiguity,返回受影响记录用于渲染。happy path 两次往返。 -- Markdown 与 lease 文件成为投影。front matter 携带 `authority_source`、 - `authority_store_identity`、`authority_revision` 与 - `authority_projection_digest`;lease 投影文件携带 `projected_from`。prose 持久化 - 经问题 10 的 projection outbox;`projection_freshness(goal)` 比对水印与 head - revision 并回放 outbox,digest 不符即 `projection_diverged`,阻塞直到 - `loopx authority reconcile`。 - -### fence legacy writer - -- 单一 gate `require_local_authority_write_grant` 在每个 Todo、follow-up、 - handoff-mode、refresh-state、bootstrap 与 feedback 写者取得 Markdown 锁之后、决策 - 之前运行;lease 侧经既有 native 请求拿到 `authority_source` 事实,对已晋升 goal - 拒绝写 lease 文件(`legacy_lease_writer_fenced`)。错误码: - `legacy_writer_fenced`、`authority_unavailable`(store 缺失、损坏或锁超时一律 - fail closed,绝不回退)、`store_lineage_mismatch`、`projection_diverged`、 - `goal_promoted_on_other_endpoint`、`promoted_field_write_without_transition`。 -- prose 写者在已晋升 goal 上仍允许,但必须过 gate:水印等于 head revision、outbox - 为空、写前后 C 字段解析逐字段一致。 -- 第 8 节"首次权威写之后禁止自动回退"在本地的落实:首次权威写即 - `authority_revision > 0`,gate 从不降级,只有 `loopx authority rollback - --execute` 能回到 `legacy_local`,并退役 store lineage,使再次晋升铸造新身份。 - -### 命令 - -- `loopx authority promote --goal-id G [--execute]` 依次取 Markdown(跨运行时)、 - lease 与 store 锁;前置:shadow 已启用且对当前源 digest 的 `verify` 为 `equal`、 - `handoff_mode == hard_lease`、附录 B 静止、无 prepared 态 `.lifecycle-operations` - 与 held 态 `.lifecycle-fences`、无仅事件 todo、单一 runtime root;从全部 todo - (含 done,带防 ABA 的 lease 水印)在 revision 0 引导 head;提交到新 store 目录 - (`operation_id = "promote:" + sha256(goal, source_digest)`,事件 - `authority_promoted`);渲染并校验投影 digest;再翻注册表记录并退役 - `authority_shadow`。崩溃后重跑若发现源 digest 不同的既有 store 则拒绝,除非给出 - `--discard-abandoned-store`;水印一旦落盘即幂等完成。 -- `loopx authority rollback --goal-id G --execute` 需同样的静止与空 outbox,把 head - 导出到 Markdown C 字段与 lease 终态记录,校验 `equal`,去水印,记录 - `legacy_local` 与 `rolled_back_from`,退役 lineage。revision 0 时即第 8 节回到未 - 触碰的本地源;之后即问题 3 要求的经评审的 fenced export,且永不在活跃 lease 期间 - 运行。 -- `loopx authority verify --goal-id G` 是晋升后的 parity 检查(`equal | diverged | - stale`,加 pending outbox),并入 `loopx doctor`。 +## 附录 C:Stage 2C promotion 设计(提案,2026-09-04) + +本附录记录第 12 节问题 8 到 14 所决定的设计。它从 `main` 已交付的部分出发,只列出 +仍缺失的部分;本文档不实现其中任何内容,parity 半段合并且这些问题得到回答之前,不得 +开始任何 promotion 代码。 + +### `main` 已交付(2026-09-03) + +- 默认关闭的 runtime shadow:每次已提交的 Todo 或 task-lease mutation 对应一笔 + `AuthorityStore` 事务,以该 mutation 的 rollout event id 与 `updated_at` 为键,带 + receipt 重放、内容漂移拒绝、ambiguous commit 调和与回读。 +- `loopx coordination-shadow inspect | qualify | read-candidate | bootstrap | + rollback`:带双 digest 的单点 parity(`missing | matched | drifted`)、基于覆盖 + 的持续 parity 报告、provider-first 读形状(仍 `decision_read_from_shadow=false`)、 + 从 legacy 投影引导空 shadow、以及按 revision 围栏的晋升前 lineage 隔离式 rollback。 +- cutover kernel:从 mutation 到投影、事件与 receipt 的纯 reducer;要求 shadow 在 + 一个精确 revision 与 digest 上已资格化、并带独立持久化且绑定该 revision 的 legacy + writer fence 的 `coordination.local_authority.promote`;永不回退到 Markdown 的 + provider-first `mutate` 与 `todo_read`。 +- fence 集成:每个 Python Todo mutation 与每个 native task-lease + acquire/renew/transfer/release 都在自己的锁内检查持久 fence;fence 不存在时零运行时 + 调用;fence 存在但不可读或无效时 fail closed。 + +### 晋升前仍缺 + +- provider-first CLI 路由与持锁的 promotion orchestrator(kernel 自己声明的下一切 + 片):取 Todo 与 lease 两把 legacy 锁,要求 `qualify` 在当前 revision 与 digest 上 + 为 `qualified`,engage fence,执行 `promote`,渲染投影,写入问题 11 的声明;源 + digest 已变时拒绝重跑,除非显式丢弃被放弃的 store。 +- 事务绑定的捕获(问题 14):runtime shadow 在提交之后、写者锁之外采样源,并发写者 + 可能落进被采样的投影,commit 与 dispatch 之间崩溃则丢失镜像。parity 半段的 outbox + 同时关闭两者:prepared entry 在写者已持有的锁内写入,committed 标记在主写返回后 + 写入,有界 drain 把每条 entry 变成恰好一笔 `operation_id` 为 entry id 的 shadow + 事务。此后 `qualify` 数的是 entry,不是采样。 +- read flip 之后的 prose 持久化(问题 10)与声明记录(问题 11)。 +- file profile 的保留、快速路径与容量(问题 12);参考执行器的删除与状态翻转 + (问题 13)。 +- 晋升后的 rollback:已交付的 rollback 隔离的是晋升前 lineage。首次权威写 + (`authority_revision > 0`)之后的返回路径是问题 3 要求的经评审的 fenced export: + 静止、空 projection outbox、把 head 导出到 Markdown 协调字段与 lease 终态记录、 + `equal` 校验、去水印与 fence、退役 lineage;永不自动,永不在活跃 lease 期间。 ### 增长是晋升前置 @@ -1618,11 +1616,10 @@ CLI 命令都要解析并哈希整个文件,超过 5 秒 effect 超时与 2 Mi ### 顺序 -A. todo、terminal 与 handoff 决策 cutover 到 TypeScript;B. -`local_authority_transaction.ts` 参考实现,未接线,移植执行器电池;C. file-store -保留、快速路径与容量(问题 5 与 12);D. 注册表记录、gate、水印与 projection -outbox,惰性发布,`authority_source` 恒为 `legacy_local`;E. promotion PR(promote、 -rollback、verify、路由到 B、渲染投影、删除四个参考模块、翻 holds 与 stage 字面量、 -治理行与本 RFC 状态段)。E 与 D 中一切返回 `file_aggregate` 的路径都等 parity 半 -段合并且本设计获批;D 的 outbox 复用 parity 半段的 entry schema,仓库永不同时携带 -两种记录格式。 +A. 在已交付 kernel 上做 provider-first CLI 路由与持锁 promotion orchestrator;B. +喂给 `coordination.runtime_shadow.commit` 的事务绑定捕获,并退役 #3818 的观察路径; +C. file-store 保留、快速路径与容量(问题 5 与 12);D. 声明记录与 prose projection +outbox,惰性发布,`authority_source` 恒为 `legacy_local`;E. promotion PR(把 +orchestrator 路由到 kernel、渲染投影、删除参考模块、翻 holds 与 stage 字面量、治理行 +与本 RFC 状态段)。E 与 D 中一切返回 `file_aggregate` 的路径都等 parity 半段合并且 +问题 8 到 14 得到回答。 From 1e3b9edbd5c4ebb50786238b5188dab002583fe6 Mon Sep 17 00:00:00 2001 From: huangruiteng Date: Fri, 4 Sep 2026 21:04:01 +0800 Subject: [PATCH 3/3] docs(rfc): make authority promotion provider-neutral Signed-off-by: huangruiteng --- docs/architecture/rfcs/README.md | 33 +- docs/architecture/rfcs/TEMPLATE.md | 189 +++++++++++ ...shared-goal-authority-state-provider-v0.md | 299 ++++++++++++------ ...-goal-authority-state-provider-v0.zh-CN.md | 230 ++++++++++---- 4 files changed, 588 insertions(+), 163 deletions(-) create mode 100644 docs/architecture/rfcs/TEMPLATE.md diff --git a/docs/architecture/rfcs/README.md b/docs/architecture/rfcs/README.md index 32fbdee89c..b7a5e4aaf4 100644 --- a/docs/architecture/rfcs/README.md +++ b/docs/architecture/rfcs/README.md @@ -5,6 +5,10 @@ decision boundary, non-goals, smallest useful implementation slice, and validation criteria. An RFC may describe future work; current behavior is defined by the implementation and stable reference contracts. +Start new proposals from the [RFC template](TEMPLATE.md). Existing RFCs should +adopt its maintenance contract when substantial revision would otherwise mix +stable design, current progress, and historical evidence. + The [Current Technical Directions](../../project/technical-directions.md) page maps RFCs to strategic programs, contribution routes, and promotion gates. @@ -24,7 +28,21 @@ Within each entry, RFC maturity and delivery maturity remain separate facts: - **Current boundary** names what is real now and what is still excluded. It is a navigation aid, not a replacement for stable protocol documentation. -This index was last audited against `main` on **2026-09-02**. Update an entry +Within an RFC, keep the same separation: + +- normative sections define the current decision and acceptance contract; +- a dated execution ledger records shipped slices and experiments without + silently changing that contract; +- a decision log records explicit approval and the sections it changed; +- an evidence registry maps claims to reproducible, public-safe proof. + +Do not append progress reports to a normative delivery plan. Move long ledgers +to a linked `*-execution.md` companion. Schema reduction is never an incidental +cleanup: the RFC or PR must name each removed field, document producer/reader/ +writer and compatibility research, define migration and rollback, prove the +claimed semantic equivalence, and record explicit maintainer approval. + +This index was last audited against `main` on **2026-09-04**. Update an entry whenever its RFC status, promoted behavior, or meaningful delivery boundary changes. @@ -49,15 +67,16 @@ changes. ([中文版](shared-goal-authority-state-provider-v0.zh-CN.md), [validation boundary](shared-goal-authority-state-provider-v0-evidence.zh-CN.md)) - **RFC status:** Draft, under maintainer review. - - **Delivery on `main`:** Foundation and provider-contract slices - implemented. + - **Delivery on `main`:** Foundation, provider-contract, and local promotion + preparation slices implemented. - **Current boundary:** Recoverable shared-authority foundations, the - file-backed reference path, NoKV shadow/recovery evidence, and the - TypeScript store contract are on `main` + file-backed reference path, NoKV shadow/recovery evidence, the TypeScript + store contract, PostgreSQL candidate/conformance coverage, and the + default-off local shadow/cutover foundations are on `main` ([#3529](https://github.com/huangruiteng/loopx/pull/3529), [#3669](https://github.com/huangruiteng/loopx/pull/3669), - [#3798](https://github.com/huangruiteng/loopx/pull/3798)). No remote - provider is the promoted authority; PostgreSQL remains planned. + [#3798](https://github.com/huangruiteng/loopx/pull/3798)). No provider-first + runtime promotion or remote shared-authority service has shipped. - [Shared Goal Alignment and Governed Amendment Protocol v0](shared-goal-alignment-and-governed-amendment-v0.md) ([中文版](shared-goal-alignment-and-governed-amendment-v0.zh-CN.md)) - **RFC status:** Draft, under maintainer review. diff --git a/docs/architecture/rfcs/TEMPLATE.md b/docs/architecture/rfcs/TEMPLATE.md new file mode 100644 index 0000000000..27705306c2 --- /dev/null +++ b/docs/architecture/rfcs/TEMPLATE.md @@ -0,0 +1,189 @@ +# RFC: (v0) + +- **RFC status:** Draft | Under review | Accepted | Rejected | Superseded +- **Delivery maturity:** Proposal | Experiment | Partial | Implemented | Promoted +- **Authors / owners:** +- **Created:** YYYY-MM-DD +- **Last normative revision:** YYYY-MM-DD +- **Implementation baseline:** `` or not applicable +- **Related contracts:** +- **Language mirror:** + +## Document map and maintenance contract + +State which sections are normative, which are current implementation facts, +and which are historical evidence. Use this default: + +- Sections 1-10 are the durable design and acceptance contract. +- Section 11 is the normative delivery plan. +- Section 12 contains unresolved decisions; proposed answers are not approval. +- Appendices contain the non-normative execution ledger, decision log, evidence + registry, rejected alternatives, and incident lessons. + +RFC maturity and delivery maturity are independent. Dated progress entries do +not amend normative sections. If an appendix becomes hard to review, move it +without loss into a companion `-execution.md` and link it here. + +--- + +## 1. Decision summary + +Lead with the smallest set of decisions a maintainer must understand. State: + +1. what becomes authoritative or changes behavior; +2. what remains unchanged; +3. the default and opt-in boundary; +4. the principal safety or compatibility constraint; +5. what this RFC still does not approve. + +## 2. Problem and motivation + +Describe the user/operator failure, not only the implementation gap. Include a +concrete example and explain why the current owner cannot solve it locally. + +### Invariants + +List properties that every implementation must preserve. Prefer observable +semantics over mechanism names. + +## 3. Scope and non-goals + +### In scope + +- + +### Non-goals + +- + +## 4. Current-system contract + +Record the audited current behavior and its owners. Distinguish facts on the +named implementation baseline from proposed behavior. Link stable protocol or +code ownership surfaces; do not paste execution logs into this section. + +## 5. Proposed architecture + +### Ownership and authority + +Name the single decision owner, storage/provider boundary, identities, +transactions, and forbidden alternate authorities. + +### State model and schema + +Define canonical records, version manifests, required/optional fields, explicit +clear/delete semantics, ordering, and serialization. Default to preserving +legally stored fields. Any reduction must enumerate affected fields, producer / +reader / writer research, historical and external compatibility, migration, +rollback, and semantic-equivalence evidence, with explicit maintainer approval. + +### Command or event lifecycle + +Describe legal transitions, idempotency identity, preconditions, receipts, +replay, ambiguity reconciliation, and fail-closed behavior. + +### Provider or extension contract + +Keep logical semantics provider-neutral. Put provider-specific storage layouts, +limits, authentication, and operational details in named profiles. + +## 6. Alternatives and design choices + +Compare viable alternatives against the invariants. Keep the final choice and +its trade-off in the normative body; retain superseded detail in Appendix D. + +## 7. Safety, privacy, and compatibility + +Cover as applicable: + +- default-off and feature-off parity; +- authorization, tenancy, and credential boundaries; +- public/private data boundaries; +- legacy readers/writers and downgrade behavior; +- partial rollout, mixed versions, and split-brain prevention; +- capacity, availability, and fail-closed/fail-open choices. + +## 8. Migration and rollback + +Define admission, preflight, quiescence, cutover, readback, rollback, and the +point after which rollback requires export or migration. Every destructive or +irreversible step needs an explicit gate and recovery path. + +## 9. Validation and acceptance + +Express each claim as a reproducible acceptance row: + +| Claim | Test or evidence | Required result | Boundary / exclusions | +| --- | --- | --- | --- | +| | | | | + +Separate deterministic conformance, live qualification, performance evidence, +and production promotion. An unverified or skipped row is not green. + +## 10. Operational contract + +Describe observability, typed failures, capacity limits, backup/recovery, +upgrade/downgrade, on-call or operator actions, and user-visible status. Omit +this section only when the RFC cannot affect a running system, and say why. + +## 11. Normative delivery plan + +Use cohesive milestones with explicit entry and exit gates. A milestone may +ship while the RFC remains Draft. + +| Milestone | Shipped behavior | Entry gate | Exit evidence | Rollback | +| --- | --- | --- | --- | --- | +| M0 | | | | | + +Keep progress percentages and dated status reports out of this section. + +## 12. Open decisions + +Number each unresolved decision. For each, name the decision owner, options, +recommendation, evidence needed, and deadline or dependent milestone. A +recommendation remains non-authoritative until the decision log records +approval. + +--- + +## Appendix A: Execution ledger (non-normative) + +Append dated entries; do not rewrite history to resemble the current plan. +Each entry states the exact implementation baseline and claim boundary. + +### YYYY-MM-DD — + +- **Baseline:** `` / PR +- **Delivered:** +- **Evidence:** +- **Known gaps:** +- **Effect on normative design:** none | + +## Appendix B: Decision log + +| Date | Decision | Owner / approval | Alternatives | Normative sections changed | +| --- | --- | --- | --- | --- | +| YYYY-MM-DD | | | | | + +Do not infer approval from implementation progress, silence, or a proposed +answer. Schema/field removal entries name every removed field explicitly. + +## Appendix C: Evidence registry + +| Evidence id | Claim | Baseline / environment | Artifact or command | Result | Privacy / validity boundary | +| --- | --- | --- | --- | --- | --- | +| E1 | | | | pass/fail/unverified | | + +Never commit credentials, private links, raw transcripts, local paths, or +unredacted production evidence. + +## Appendix D: Rejected or superseded alternatives + +Preserve enough detail to prevent the same dead end from being rediscovered. +State why it failed an invariant and what evidence could reopen the decision. + +## Appendix E: Incident and review lessons + +Record generalized, public-safe lessons that changed an invariant, acceptance +row, or migration rule. Operational timelines and private incident material +belong outside the public RFC. diff --git a/docs/architecture/rfcs/shared-goal-authority-state-provider-v0.md b/docs/architecture/rfcs/shared-goal-authority-state-provider-v0.md index 84de8a3bd3..2fcb300f7f 100644 --- a/docs/architecture/rfcs/shared-goal-authority-state-provider-v0.md +++ b/docs/architecture/rfcs/shared-goal-authority-state-provider-v0.md @@ -3,7 +3,7 @@ - Status: Draft, under maintainer review - Initially proposed by: NoKV Lab - Widened by: LoopX maintainers -- Date: 2026-08-05; revised 2026-09-02 +- Date: 2026-08-05; revised 2026-09-04 - Scope: one provider-neutral LoopX authority contract with built-in file, optional NoKV, and optional PostgreSQL provider profiles, complementing [`host-integration-surface-v0`](../../reference/protocols/host-integration-surface-v0.md) @@ -21,6 +21,28 @@ [Chinese version](./shared-goal-authority-state-provider-v0.zh-CN.md) and this English version are semantic mirrors. A difference between them is a defect. +## Document map and maintenance contract + +This RFC separates durable decisions from delivery evidence: + +- Sections 0-10 define the problem, authority contract, provider boundary, + migration rules, and acceptance criteria. +- Section 11.1 is the normative delivery plan. Section 11.2 is a + non-normative execution ledger: it records what a dated `main` revision has + proved, but does not silently amend the contract. Section 11.3 lists the + remaining qualification and promotion work. +- Section 12 contains unresolved owner decisions. An implementation may not + infer approval from a proposed answer. +- Appendices A and B retain evidence and decision history; Appendix C contains + the detailed Stage 2C contract proposed by Section 12. When implementation + changes, update the ledger; when architecture changes, update the normative + section and record the decision explicitly. + +RFC maturity and delivery maturity are independent. A shipped experiment does +not accept this RFC, and a dated status entry never overrides a normative +invariant. If the execution ledger becomes difficult to review, it moves to a +companion `*-execution.md` document without dropping its evidence links. + --- ## 0. An Example to Help Everyone Understand @@ -1014,6 +1036,8 @@ particular deployment topology are deliberately non-normative. ## 11. Staged Delivery +### 11.1 Normative stage plan + The delivery program has one shared foundation and two provider-specific tracks. Workstream labels are responsibility boundaries, not authority grants: @@ -1057,15 +1081,17 @@ The sequence is: the NoKV adapter and storage envelope; the PostgreSQL owner implements the generic service/provider. Both reuse the same LoopX transition and receipt semantics. -4. **Stage 2C - promote the local canonical file aggregate.** First shadow the - current Markdown/task-lease writers into `FileAuthorityStore` without reading - it for decisions. Prove parity, crash recovery, migration, and one-command - rollback; then, in a separately reviewed promotion, make the file aggregate - the local coordination authority and fence the legacy writers. Markdown and - task-lease files become projections only after that promotion. Projection - shape or head-digest parity alone is insufficient: promotion must also prove - that the provider reproduces the complete consumer-visible Todo semantics at - the exact qualified revision. +4. **Stage 2C - qualify and promote the first canonical profile.** First shadow + the current Markdown/task-lease writers into `FileAuthorityStore` without + reading it for decisions. Prove parity, crash recovery, migration, and + one-command rollback; then, in a separately reviewed promotion, make that + profile the local coordination authority and fence the legacy writers. + Markdown and task-lease files become projections only after promotion. The + state machine, complete field manifest, receipts, and acceptance rows remain + provider-neutral so NoKV and PostgreSQL can qualify through the same route. + Projection shape or head-digest parity alone is insufficient: promotion + must prove complete consumer-visible Todo and lease semantics at the exact + qualified revision. 5. **Stage 3 - one-way remote shadow parity.** The promoted local `FileAuthorityStore` remains the only authority while committed observations are projected to a NoKV or PostgreSQL candidate. Provider parity compares @@ -1085,6 +1111,23 @@ The sequence is: cache, offline projection, and diagnostic material. Never keep a long-lived dual-write or dual-master mode. +### 11.2 Current implementation and evidence ledger (non-normative) + +The dated entries below preserve shipped boundaries, experiments, and review +findings. They are evidence for the stage plan, not additional specification. +Later entries supersede earlier status claims only when they identify the +relevant contract, exact implementation boundary, and validation evidence. + +| Ledger entry | What it records | +| --- | --- | +| Stage 2C observation foundation | Default-off post-commit capture and its crash window | +| Implementation prerequisite / Stage 1 Part 2 | Provider-neutral decision extraction and remaining Python/TypeScript ownership boundary | +| Stage 2B PostgreSQL candidate | PostgreSQL store/RLS conformance, without runtime promotion | +| Stage 2C runtime shadow | Parity, read-candidate, bootstrap, rollback, cutover kernel, and writer fence | +| Stage 2 slice | Reference aggregate/provider implementation and initial NoKV evidence | +| Stage 3 slice | Recoverable lifecycle, retention findings, and live provider limits | +| Stage-ladder evidence | Executable stage claims, environment gates, and pending rows | + #### Stage 2C observation foundation: local post-commit capture The first half of Stage 2C is an explicit, default-off product path. Preview @@ -1120,7 +1163,7 @@ write or migration seed refreshes the full current projection, but no durable shadow outbox or transaction-correlated receipt is claimed here. This plumbing is not parity evidence and cannot by itself support Stage 2C promotion. -### Implementation prerequisite: put local file mode behind the same coordination contract +#### Implementation prerequisite: put local file mode behind the same coordination contract Before wiring a live NoKV or another remote provider, the runtime should first extract the domain decisions in the current todo/lease write paths into a @@ -1672,7 +1715,9 @@ migration seed-and-drain, growth measurement) are declared as pending rows, not claimed. This subsection records executable evidence for the stages above; it does not promote any provider or complete the Stage 2C promotion. -### P0: contract and deterministic proof +### 11.3 Remaining qualification and promotion plan + +#### P0: contract and deterministic proof - this ownership matrix and explicit shared-mode boundary; - deterministic `loopx_command_v0` normalization and request digest; @@ -1684,7 +1729,7 @@ it does not promote any provider or complete the Stage 2C promotion. - A/B/A, identity-mismatch, crash-window, eligibility, privacy, and no-GC checks within the stated evidence boundary. -### Later runtime promotion and reviewed slices +#### Later runtime promotion and reviewed slices - the LoopX-owned TypeScript transaction/store boundary and file-provider conformance described in Section 6.2; @@ -1740,63 +1785,71 @@ it does not promote any provider or complete the Stage 2C promotion. store identity or lineage, and an explicit TEST ONLY canary marker; a Goal without that marker cannot be admitted by a shared-authority guard, and the runtime resolves the provider from the record rather than from argv.* -8. Which head shape does the local promotion commit, and which fields does - the aggregate own? The merged runtime shadow already fixes the shape: - `loopx_coordination_runtime_shadow_projection_v0` (nineteen todo fields - including `updated_at`, `superseding_todo_id`, `task_domain`, and - `task_repository`; ten lease fields), and the cutover kernel commits - mutations against it as `canonical_authority: file_v0`. *Proposed answer: - adopt that projection as the promoted head rather than a superset schema, - with three amendments before promotion: drop `updated_at` from the compared - set (it is rewritten by every prose edit, so keeping it makes every prose - write a coordination transaction and every parity compare prose-sensitive; - it stays on the receipt as `source_version`); add the fields promotion must - own but the projection lacks (`required_capabilities`, `decision_scope` and - `required_decision_scopes`, the completion triple and evidence pointers, - the archived flag, `todo_revision`); and treat `archive-completed` as a - head transition, not a projection rewrite. Appendix C lists what the kernel - already provides.* +8. Which head shape does promotion commit, and which fields does the aggregate + own? `main` now defines `loopx_todo_canonical_read_record_v0` as a versioned, + complete Todo read-record manifest and makes the TypeScript projection + reject an upsert that omits fields already present on the stored record. + *Proposed answer: the promoted head stores the complete normalized Todo and + lease records, flattened enough for a provider-independent readback. Every + field that has legally entered a canonical record remains present, including + `updated_at`, routing, capability, decision, dependency, resume, monitor, + completion, note/evidence, and archival fields. Omission is never a delete; + mutation must clear a field explicitly under its schema rule. A new field + must enter the versioned manifest before qualification, otherwise promotion + fails closed. "Complete" does not mean copying raw Markdown, host-local + paths, credentials, the whole registry, or data owned by another ledger.* + + *A later field reduction is a governed schema change even when the field is + stored but has no known runtime reader. Its PR must include a field inventory, + producer/reader/writer and static-reference research, historical and external + compatibility findings, migration and rollback, and proof that behavior is + preserved. The maintainer must approve the named removal explicitly in the + RFC decision log or PR review; absence of a discovered consumer is not + approval.* 9. Does v0 promotion cover only `hard_lease` goals? *Proposed answer: yes. A `legacy` or `soft_claim` goal first switches mode under the Appendix B quiescence rule; promotion never changes the mode implicitly.* -10. After the provider-first read flip, Markdown and the lease files are - projections and the kernel forbids any fallback to them. How is prose - (text, notes, next action, monitor metadata, feedback) persisted then: - through a projection outbox, or by accepting a crash window between the - head commit and the Markdown rewrite? *Proposed answer: a projection - outbox that reuses the transaction-bound outbox entry schema of the - parity half (artifact-first: outbox entry, TypeScript commit, Markdown - render, entry retirement), so both directions share one record format; - readers compare the front-matter watermark with the head revision and - replay the outbox when behind.* +10. After the provider-first read flip, Markdown and lease files are + projections and the kernel forbids fallback to them. Which data belongs in + the head, and how are compatibility views rendered? *Proposed answer: + every field in the canonical Todo/lease manifests, including monitor, + dependency, resume, decision, completion, text, note, evidence references, + and any feedback field admitted by the manifest, persists in the head. A + transaction-bound projection + outbox only renders Markdown and lease-file compatibility views; it is not + a second persistence path for omitted authority fields. Readers compare the + projection watermark with the head revision and replay the outbox when + behind. Rendering lag may make a view stale, but must not change a decision.* 11. What declares a promoted goal, and who may write that declaration? The - merged fence is a durable TypeScript-owned file under - `authority-transition/file-v0/`, bound to a fence id, the source version, - and the qualified shadow revision, and every Python Todo mutation and - native task-lease mutation checks it under its own lock. *Proposed - answer: the fence file stays the local authority of the cutover; a - goal-level registry record (`coordination.authority_source`: provider, - store identity, `promoted_at`, promotion operation id, source digest, - `rolled_back_from`) plus its copy in the state-file front matter exists - for discovery and for cross-endpoint detection; only the promotion - orchestrator and rollback may write either; `configure-goal` refuses to - edit the record and `bootstrap --force` refuses a promoted goal. In v0 a - second endpoint that sees the front-matter copy gets - `goal_promoted_on_other_endpoint` on coordination-field writes; an older - endpoint can only be detected (`projection_diverged`), not blocked, until - shared mode.* -12. Which file-profile retention, fast-path, and capacity rules gate the - first real promotion, and may `committed[].projection` degrade to a - digest? *Proposed answer: question 5 applied to the file profile before - any real goal is promoted: sealed segments outside the head document - (create-only, chained by path, digest, count, and cursor range; a missing - segment fails closed), a head-only fast path that validates the chain - once per process or through an in-document checkpoint, and a - `store_capacity_exhausted` fail-closed limit (proposed 8 MiB). The - projection of a retained transaction may degrade to a digest only if the - Stage 3 scan keeps every field parity compares. Question 6 couples here: - Host renewals must be transactions, never direct lease-file writes, - because their rate sets the segment window.* + merged TypeScript-owned file fence is the first local implementation, + bound to a fence id, source version, and qualified shadow revision; current + Todo and task-lease writers check it under their mutation locks. *Proposed + answer: define one provider-neutral authority binding containing provider + profile, store identity and lineage, schema manifest, `promoted_at`, + promotion operation id, source digest, and optional `rolled_back_from`. + Only the promotion orchestrator and rollback operation may change it. The + file profile realizes the binding with the durable local fence and registry + discovery copy; NoKV and PostgreSQL must realize the same logical fence, + CAS/transaction precondition, and readback receipt in their qualified store + contract. A provider-specific path or table name is not part of the + authority protocol. `configure-goal` refuses to edit the binding and + bootstrap refuses a promoted goal. An endpoint that cannot validate the + active binding fails closed rather than writing a legacy projection.* +12. Which retention, fast-path, and capacity rules gate promotion, and may a + retained transaction replace its projection with a digest? *Proposed + answer: the logical contract is common to file, NoKV, and PostgreSQL: the + latest complete head, ordered cursor, original operation receipt, segment + or row-chain integrity, deterministic scan, and recovery readback remain + available under a declared retention version. A digest may replace an old + transaction's duplicated projection only when the complete canonical head + and every field required by replay, audit, parity, and migration remain + reconstructable and the conformance matrix proves equivalence. Physical + policy is provider-specific: sealed create-only segments for file, a + qualified document/segment strategy for NoKV, and append rows with reviewed + indexing/partitioning for PostgreSQL. Each profile declares measured limits + and fails closed with `store_capacity_exhausted`; one file-size constant is + not a cross-provider contract. Host renewals remain authority transactions + because their rate drives every profile's retention envelope.* 13. What happens to the Python reference executor (`executor.py`, `file_provider.py`, `head.py`, `goal_state_shadow.py`)? *Proposed answer: keep it coverage-only until the kernel's mutation path is routed from the @@ -1974,11 +2027,23 @@ read and the state-file lock. ## Appendix C: Stage 2C Promotion Design (proposal, 2026-09-04) -This appendix records the design that questions 8 to 14 in section 12 -decide. It starts from what `main` already ships and names only what is still -missing; nothing below is implemented by this document, and the parity half -must merge and the questions must be answered before any promotion code -starts. +This appendix resolves questions 8 to 14 as a provider-neutral promotion +contract. It distinguishes logical authority semantics from each provider's +physical retention strategy. Nothing below is implemented by this document; +the unresolved gates remain prerequisites for a real promotion. + +### Decision summary + +1. Stage 2C promotes one canonical coordination head, not a file format. + File, NoKV, and PostgreSQL implement the same `AuthorityStore` semantics. +2. Canonical Todo and lease state is complete by default. Promotion may not + reduce it to the fields currently used by a known consumer. +3. Any later field removal requires field-level compatibility research, + migration and rollback evidence, and explicit maintainer approval. +4. Markdown and lease files become rendered projections after read flip. They + never supply missing decision state. +5. Retention is logically common and physically provider-specific. A provider + may segment or normalize history without changing head/readback semantics. ### Shipped on `main` (2026-09-03) @@ -2001,6 +2066,56 @@ starts. acquire, renew, transfer, and release checks the durable fence while holding its own lock; an absent fence costs no runtime call; a present, unreadable, or invalid fence fails closed. +- The complete Todo read model: `loopx_todo_canonical_read_record_v0` publishes + a versioned field manifest, and the TypeScript projection rejects a + replacement that drops fields already present on a stored record. + +### Normative promotion contract + +The promotion state machine is independent of provider kind: + +1. Resolve a reviewed goal-level authority binding to a qualified + `AuthorityStore` profile and exact store lineage. +2. Under the legacy Todo and lease locks, verify sustained parity at one source + revision, projection digest, field-manifest version, and provider cursor. +3. Engage the provider profile's durable writer fence, then commit the complete + canonical head and promotion receipt with a compare-and-set or transaction + precondition. A file marker, NoKV document identity, or PostgreSQL row/table + layout is an implementation detail. +4. Read back the binding, head, receipt, cursor, manifest, and digest before + allowing provider-first decisions. Any mismatch fails closed. +5. Render compatibility projections through the transaction-bound outbox. A + stale projection is repaired from the head; it is never consulted to fill a + missing canonical field. + +The canonical head preserves every field legally present in the normalized +Todo and lease records. The field manifest is part of qualification and parity. +An upsert carries a complete replacement or uses a typed patch whose clear +operations are explicit. Unknown additions fail closed until the manifest is +versioned. A removal proposal must enumerate the field and all producers, +readers, writers, persisted fixtures, static references, historical versions, +and known external consumers; state the migration, downgrade, rollback, and +semantic-equivalence argument; and receive explicit maintainer approval. A +field is not removable merely because code search found no current reader. + +This completeness boundary excludes raw Markdown formatting, credentials, +host-local paths, whole registry documents, raw evidence bodies, and state +owned by quota, run-history, settlement, inbox, scheduler, or another ledger. +Those remain references governed by their own contracts. + +The common retention contract keeps the latest complete head, ordered cursor, +original operation receipt, integrity chain, deterministic scan, and recovery +readback. Physical profiles may differ: + +- **file:** create-only sealed history segments plus a bounded head document; +- **NoKV:** a qualified document/segment layout with lineage-bound conditional + publication and recovery readback; +- **PostgreSQL:** transactionally appended history/receipt rows plus a current + head, with reviewed indexes, partitioning, RLS, and tenant context. + +All profiles expose the same logical result and field manifest. Each publishes +measured capacity limits and returns typed `store_capacity_exhausted` before a +write that cannot preserve the contract. ### Still missing before promotion @@ -2018,10 +2133,12 @@ starts. committed marker after the primary write returns, and a bounded drain turns each entry into exactly one shadow transaction whose `operation_id` is the entry id. `qualify` then counts entries, not samples. -- Prose persistence after the read flip (question 10) and the declaration - record (question 11). -- Retention, fast path, and capacity for the file profile (question 12); the - reference executor's removal and the status flips (question 13). +- The provider-neutral authority binding, compatibility projection outbox, + and conformance rows for file, NoKV, and PostgreSQL. This does not require all + providers to promote together; each profile must pass the same contract + before it is eligible. +- Retention, fast path, and measured capacity for the selected first-promotion + profile; the reference executor's removal and status flips (question 13). - Post-promotion rollback: the shipped rollback quarantines a pre-promotion lineage. After the first authority write (`authority_revision > 0`) the return path is question 3's reviewed fenced export: quiescence, an empty @@ -2033,22 +2150,22 @@ starts. ### Growth is a promotion prerequisite `retain_all_v0` in one document is quadratic: a 600 s TTL renewed every -300 s with three active todos is roughly 300 transitions a day and 9000 a -month, about 140 MB of document at a 15 KiB head, with every CLI command -parsing and hashing the whole file, past the 5 s effect timeout and the 2 MiB -response cap. Question 12 therefore precedes any real promotion; retaining -everything in one document is acceptable only for the promotion bootstrap and -for test goals. +300 s with three active todos produces 864 transitions a day and 25,920 in a +30-day month. At a 15 KiB complete head, cumulative rewritten payload alone is +about 380 MiB before receipt and envelope overhead; final document size depends +on the retained record layout and must be measured rather than inferred from +that rewrite total. This rate is already enough to require provider-specific +capacity, latency, response-size, and recovery tests. Retaining everything in +one document is acceptable only for promotion bootstrap and bounded test goals. ### Sequence -A. provider-first CLI routing and the lock-owning promotion orchestrator, on -the shipped kernel; B. the transaction-bound capture feeding -`coordination.runtime_shadow.commit`, retiring the #3818 observation path; -C. file-store retention, fast path, and capacity (questions 5 and 12); D. the -declaration record and the prose projection outbox, dormant with -`authority_source` fixed at `legacy_local`; E. the promotion PR (routing the -orchestrator to the kernel, projection rendering, deletion of the reference -modules, flipping the holds and stage literal, governance rows, and this RFC's -status section). E and every `file_aggregate` return path in D wait for the -parity half to merge and for questions 8 to 14 to be answered. +A. transaction-bound capture feeding `coordination.runtime_shadow.commit`, +retiring the duplicate observation lineage; B. freeze and test the complete +Todo/lease manifests and omission/explicit-clear rules; C. implement the common +authority binding and profile conformance, including retention/capacity for the +selected provider; D. add provider-first CLI routing, the lock-owning promotion +orchestrator, and compatibility projection outbox on the shipped kernel; E. a +separately reviewed promotion PR deletes the reference-only duplicate aggregate, +flips holds and stage literals, and updates the execution ledger. A profile is +eligible only after A-D pass for its exact implementation and lineage. diff --git a/docs/architecture/rfcs/shared-goal-authority-state-provider-v0.zh-CN.md b/docs/architecture/rfcs/shared-goal-authority-state-provider-v0.zh-CN.md index 67baf34b3e..8059bd7333 100644 --- a/docs/architecture/rfcs/shared-goal-authority-state-provider-v0.zh-CN.md +++ b/docs/architecture/rfcs/shared-goal-authority-state-provider-v0.zh-CN.md @@ -3,7 +3,7 @@ - 状态:Draft,正在接受 maintainer review - 最初提案方:NoKV Lab - 扩展修订方:LoopX maintainer -- 日期:2026-08-05;修订于 2026-09-02 +- 日期:2026-08-05;修订于 2026-09-04 - 范围:一个 provider-neutral 的 LoopX 权威合同,支持内置 file、可选 NoKV 与可选 PostgreSQL provider profile,用来补充 [`host-integration-surface-v0`](../../reference/protocols/host-integration-surface-v0.md) @@ -19,6 +19,21 @@ - 语言说明:[英文版](./shared-goal-authority-state-provider-v0.md)与本中文版互为 语义镜像;两者不一致属于缺陷 +## 文档地图与维护约定 + +本文将稳定决策与交付证据分开维护: + +- 第 0-10 节定义问题、authority contract、provider 边界、迁移规则与验收条件。 +- 第 11.1 节是规范性交付计划;第 11.2 节是非规范性的执行台账,只记录某个日期的 + `main` 已证明什么,不以进展记录静默修改合同;第 11.3 节列出剩余验证与晋升工作。 +- 第 12 节记录尚未解决的 owner 决策。实现不得把“拟议答案”当作已经批准。 +- 附录 A/B 保留证据与决策历史;附录 C 承载第 12 节提出的 Stage 2C 详细合同。实现 + 变化更新台账;架构变化修改规范正文,并显式记录决策。 + +RFC maturity 与 delivery maturity 相互独立:实验已合入不代表 RFC 已 accepted,带日期 +的状态段也不能覆盖规范性不变量。执行台账过长、影响 review 时,可整体迁移到配套的 +`*-execution.md`,但不得丢失证据链接。 + --- ## 0. 一个用来帮助大家理解的例子 @@ -812,6 +827,8 @@ gate/dependency ref、claim/lease field 与按 privacy class 标注的 opaque po ## 11. 分阶段交付 +### 11.1 规范性交付计划 + 交付计划由一条共享基础线和两条 provider-specific 线组成。下列 workstream 是职责 边界,不是额外的 authority grant: @@ -849,13 +866,15 @@ Stage 3/4 qualification 必须保持以下 ownership 与 proof 边界: 3. **Stage 2A/2B——并行实现 provider。** NoKV owner 验证 NoKV adapter 与存储 包络;PostgreSQL owner 实现通用 service/provider。两者复用同一套 LoopX transition 与 receipt 语义。 -4. **Stage 2C——晋升本地 canonical file aggregate。** 先在不读取其决策结果的前提下, - 把现有 Markdown/task-lease writer shadow 到 `FileAuthorityStore`;验证 parity、 - crash recovery、migration 与一键 rollback。随后通过一个单独评审的 promotion, - 让 file aggregate 成为本地 coordination authority,并 fence legacy writer; - 只有晋升后 Markdown 与 task-lease 文件才退为 projection。仅证明 projection - 结构或 head digest 相等仍然不够;promotion 还必须在精确的 qualified revision - 上证明 provider 完整复现 Todo 消费方可见语义。 +4. **Stage 2C——资格化并晋升第一个 canonical profile。** 先在不读取其决策结果的 + 前提下,把现有 Markdown/task-lease writer shadow 到 `FileAuthorityStore`;验证 + parity、crash recovery、migration 与一键 rollback。随后通过单独评审的 promotion, + 让该 profile 成为本地 coordination authority,并 fence legacy writer;只有晋升后 + Markdown 与 task-lease 文件才退为 projection。state machine、完整 field manifest、 + receipt 与 acceptance row 保持 provider-neutral,使 NoKV 与 PostgreSQL 可以复用 + 同一条 qualification 路线。仅证明 projection 结构或 head digest 相等仍然不够; + promotion 还必须在精确 qualified revision 上证明完整的 consumer-visible Todo 与 + lease 语义。 5. **Stage 3——远端单向 shadow parity。** 晋升后的本地 `FileAuthorityStore` 仍是 唯一 authority;将已提交观察投影到 NoKV 或 PostgreSQL 候选。Provider parity 只对比 Todo/claim、lease fence、 @@ -871,6 +890,22 @@ Stage 3/4 qualification 必须保持以下 ownership 与 proof 边界: LoopX service 成为唯一 writer。本地 `.loopx` 退为 cache、offline projection 与 诊断材料。绝不长期维持 dual-write 或 dual-master。 +### 11.2 当前实现与证据台账(非规范性) + +下面带日期的条目保留已交付边界、实验与评审结论。它们是分阶段计划的证据,不是 +额外规范。后来的条目只有在点明相关合同、精确实现边界与验证证据时,才可取代更早的 +状态判断。 + +| 台账条目 | 记录内容 | +| --- | --- | +| Stage 2C observation foundation | 默认关闭的提交后 capture 及其 crash window | +| 实施前置 / Stage 1 Part 2 | provider-neutral decision 抽取与剩余 Python/TypeScript ownership 边界 | +| Stage 2B PostgreSQL candidate | PostgreSQL store/RLS conformance,不代表 runtime promotion | +| Stage 2C runtime shadow | parity、read-candidate、bootstrap、rollback、cutover kernel 与 writer fence | +| Stage 2 slice | reference aggregate/provider 实现与初步 NoKV 证据 | +| Stage 3 slice | 可恢复 lifecycle、retention 结论与 live provider 限制 | +| Stage-ladder evidence | 可执行 stage claim、环境 gate 与 pending row | + #### Stage 2C 观察基础:本地提交后 capture Stage 2C 的前半段是一个显式开启、默认关闭的产品路径。先预览,再开启: @@ -901,7 +936,7 @@ provider,也没有完成 Stage 2C 后半段的本地 canonical promotion。若 seed 会刷新完整当前投影,但这里不宣称已有 durable shadow outbox 或与主写 transaction 关联的 receipt。这套 plumbing 不是 parity evidence,不能单独支持 Stage 2C promotion。 -### 实施前置条件:先让本地文件模式经过同一协调合同 +#### 实施前置条件:先让本地文件模式经过同一协调合同 在接入 live NoKV 或其他远端 provider 之前,runtime 应先把当前 todo/lease 写路径中的 领域判断抽成 provider-neutral coordination core,并让一个 file-backed provider 通过 @@ -1336,7 +1371,9 @@ Live 行按环境门控(`LOOPX_TEST_POSTGRES_URL`;`NOKV_COORDINATION_LIVE=1` 度量)以 pending 行声明,而非宣称已完成。本小节记录的是上述阶段的可执行证据; 它不晋升任何 provider,也不完成 Stage 2C promotion。 -### P0:合同与 deterministic proof +### 11.3 剩余验证与晋升计划 + +#### P0:合同与 deterministic proof - 本 ownership matrix 与显式 shared-mode boundary; - 确定性的 `loopx_command_v0` normalization 与 request digest; @@ -1347,7 +1384,7 @@ Live 行按环境门控(`LOOPX_TEST_POSTGRES_URL`;`NOKV_COORDINATION_LIVE=1` - 在所声明证据边界内的 A/B/A、identity mismatch、crash window、eligibility、 privacy 与 no-GC 检查。 -### 后续 runtime promotion 与需 review 的切片 +#### 后续 runtime promotion 与需 review 的切片 - §6.2 所述由 LoopX 持有的 TypeScript transaction/store boundary 与 file-provider conformance; @@ -1394,46 +1431,53 @@ Live 行按环境门控(`LOOPX_TEST_POSTGRES_URL`;`NOKV_COORDINATION_LIVE=1` 布,记录 provider 种类、store identity 或 lineage,以及显式的 TEST ONLY canary 标记;没有该标记的 Goal 不得被 shared-authority guard 准入,runtime 从该记录而 非 argv 解析 provider。* -8. 本地 promotion 提交哪种 head 形状,aggregate 拥有哪些字段?已合并的 runtime - shadow 已经固定了形状:`loopx_coordination_runtime_shadow_projection_v0` - (19 个 todo 字段,含 `updated_at`、`superseding_todo_id`、`task_domain`、 - `task_repository`;10 个 lease 字段),cutover kernel 以 - `canonical_authority: file_v0` 对它提交 mutation。*拟议答案:采用该投影作为晋升 - head,而不是再造一个超集 schema,并在晋升前做三处修正:把 `updated_at` 移出比较 - 集(每次 prose 编辑都会改写它,留下它会让每次 prose 写都变成协调事务、每次 - parity 比较都对 prose 敏感;它留在 receipt 上作为 `source_version`);补上 - promotion 必须拥有但投影缺失的字段(`required_capabilities`、`decision_scope` - 与 `required_decision_scopes`、completion 三元组与 evidence 指针、archived 标 - 记、`todo_revision`);把 `archive-completed` 当作 head transition 而不是投影 - 重写。附录 C 列出 kernel 已提供的部分。* +8. promotion 提交哪种 head 形状,aggregate 拥有哪些字段?`main` 已将 + `loopx_todo_canonical_read_record_v0` 定义为带版本的完整 Todo read-record manifest, + TypeScript projection 也会拒绝通过 upsert omission 丢掉既有字段。*拟议答案:晋升 + head 保存完整、规范化的 Todo 与 lease record,并打平到 provider 无关的 readback + 形状。任何已合法进入 canonical record 的字段都继续保留,包括 `updated_at`,以及 + routing、capability、decision、dependency、resume、monitor、completion、 + note/evidence 与 archival 字段。遗漏字段不等于删除;mutation 必须按 schema 规则 + 显式 clear。新字段必须先进入带版本 manifest,未进入则 qualification/promotion + fail closed。“完整”不等于复制 raw Markdown、host-local path、credential、整个 + registry,或其他 ledger 持有的数据。* + + *后续任何字段删减都是受治理的 schema change,即使该字段已经存储但暂未发现 runtime + reader。对应 PR 必须提供字段 inventory、producer/reader/writer 与静态引用调研、 + 历史和外部兼容性结论、migration/rollback,以及行为等价证明;maintainer 必须在 RFC + decision log 或 PR review 中对点名字段显式批准。没有发现 consumer 不等于批准删除。* 9. v0 promotion 是否只覆盖 `hard_lease` goal?*拟议答案:是。`legacy` 或 `soft_claim` goal 先按附录 B 的静止规则切换模式;promotion 从不隐式改变模式。* -10. provider-first read flip 之后 Markdown 与 lease 文件成为投影,kernel 禁止任何 - 回退到它们。那么 prose(正文、note、next action、monitor 元数据、feedback)如何 - 持久化:走 projection outbox,还是接受 head 提交与 Markdown 重写之间的崩溃窗 - 口?*拟议答案:走 projection outbox,并复用 parity 半段事务绑定 outbox 的 entry - schema(artifact-first:outbox entry、TypeScript commit、Markdown 渲染、entry - 退役),两个方向共用一种记录格式;读者用 front matter 水印比对 head revision, - 落后则回放 outbox。* -11. 什么声明一个 goal 已晋升,谁可以写这条声明?已合并的 fence 是 TypeScript 拥有的 - 持久文件,位于 `authority-transition/file-v0/`,绑定 fence id、源版本与已资格化的 - shadow revision;每个 Python Todo mutation 与 native task-lease mutation 都在自 - 己的锁内检查它。*拟议答案:fence 文件仍是 cutover 的本地权威;goal 级 registry - 记录(`coordination.authority_source`:provider、store identity、 - `promoted_at`、promotion operation id、源 digest、`rolled_back_from`)及其在 - state 文件 front matter 里的副本用于发现与跨端点检出;只有 promotion - orchestrator 与 rollback 能写这两者;`configure-goal` 拒绝修改该记录, - `bootstrap --force` 拒绝已晋升的 goal。v0 里看到 front matter 副本的另一端点对 - 协调字段的写得到 `goal_promoted_on_other_endpoint`;旧版本端点只能被检出 - (`projection_diverged`)而不能被阻止,直到 shared mode。* -12. 哪些 file profile 的保留、快速路径与容量规则是第一次真实 promotion 的前置, - `committed[].projection` 可否退化为 digest?*拟议答案:在任何真实 goal 晋升之 - 前把问题 5 落实到 file profile:head 文档之外的封段(create-only,按 path、 - digest、count 与 cursor 区间成链;缺段 fail closed)、每进程校验一次链或依靠 - 文档内 checkpoint 的 head-only 快速路径、以及 `store_capacity_exhausted` 的 - fail-closed 上限(拟 8 MiB)。已保留事务的 projection 只有在 Stage 3 scan 仍 - 保留 parity 比较的全部字段时才可退化为 digest。问题 6 在此耦合:Host 续约必须 - 经事务而不是直写 lease 文件,因为续约频率决定封段窗口。* +10. provider-first read flip 后,Markdown 与 lease 文件成为投影,kernel 禁止回退。 + 哪些数据进入 head,兼容视图如何渲染?*拟议答案:canonical Todo/lease manifest + 中的每个字段都持久化在 head,包括 monitor、dependency、resume、decision、 + completion、text、note、evidence reference,以及 manifest 已准入的 feedback 字段。 + 事务绑定的 projection outbox 只负责渲染 Markdown 与 lease-file 兼容视图,不是 + 遗漏 authority 字段的第二持久化路径。reader 用 projection watermark 对比 head + revision,落后则回放 + outbox。渲染延迟可以让视图暂时陈旧,但不得改变决策。* +11. 什么声明一个 goal 已晋升,谁可以写这条声明?已合并的 TypeScript-owned file + fence 是第一个本地实现,绑定 fence id、源版本与已资格化的 shadow revision;当前 + Todo 与 task-lease writer 在各自 mutation lock 内检查它。*拟议答案:定义一份 + provider-neutral authority binding,包含 provider profile、store identity 与 + lineage、schema manifest、`promoted_at`、promotion operation id、源 digest 和可选 + `rolled_back_from`。只有 promotion orchestrator 与 rollback operation 可以修改。 + file profile 用持久本地 fence 和 registry discovery copy 实现;NoKV 与 PostgreSQL + 必须在各自通过资格验证的 store contract 中实现相同的逻辑 fence、CAS/transaction + precondition 与 readback receipt。provider-specific path 或 table name 不属于 + authority protocol。`configure-goal` 拒绝编辑 binding,bootstrap 拒绝已晋升 goal。 + 无法验证 active binding 的端点 fail closed,不得写 legacy projection。* +12. 哪些 retention、fast-path 与 capacity 规则是 promotion 前置?已保留 transaction + 能否把 projection 替换为 digest?*拟议答案:file、NoKV、PostgreSQL 共用逻辑合同: + 在声明的 retention version 下,最新完整 head、ordered cursor、原始 operation + receipt、segment 或 row-chain integrity、确定性 scan 与 recovery readback 始终可用。 + 只有在完整 canonical head 仍可读取,且 replay、audit、parity、migration 所需字段 + 全部可重建,并由 conformance matrix 证明等价时,旧 transaction 的重复 projection + 才可替换为 digest。物理策略按 provider 实现:file 使用 create-only sealed segment; + NoKV 使用经过验证的 document/segment 策略;PostgreSQL 使用 append row 和经评审的 + index/partition。每个 profile 都声明实测上限,并以 `store_capacity_exhausted` fail + closed;单一 file size 常量不是跨 provider 合同。Host renewal 仍必须是 authority + transaction,因为它的频率决定所有 profile 的 retention envelope。* 13. Python 参考执行器(`executor.py`、`file_provider.py`、`head.py`、 `goal_state_shadow.py`)如何处置?*拟议答案:在 kernel 的 mutation 路径接到 CLI 之前保持 coverage-only,先把它们的场景电池移植为 TypeScript 测试,再在 @@ -1567,9 +1611,20 @@ characterization 阶段的负向用例在原清单上补充:软认领盖掉活 ## 附录 C:Stage 2C promotion 设计(提案,2026-09-04) -本附录记录第 12 节问题 8 到 14 所决定的设计。它从 `main` 已交付的部分出发,只列出 -仍缺失的部分;本文档不实现其中任何内容,parity 半段合并且这些问题得到回答之前,不得 -开始任何 promotion 代码。 +本附录将第 12 节问题 8 到 14 收敛为 provider-neutral promotion contract,并分开逻辑 +authority 语义与各 provider 的物理 retention 策略。本文档不实现以下内容;未关闭的门禁 +仍是任何真实 promotion 的前置。 + +### 决策摘要 + +1. Stage 2C 晋升的是一份 canonical coordination head,不是某种文件格式。file、NoKV、 + PostgreSQL 实现同一套 `AuthorityStore` 语义。 +2. Canonical Todo 与 lease state 默认完整。不得按“当前已知 consumer 使用的字段”缩减。 +3. 后续任何字段删除都需要字段级兼容性调研、migration/rollback 证据与 maintainer + 显式批准。 +4. read flip 后 Markdown 与 lease 文件是渲染投影,绝不补充缺失的 decision state。 +5. retention 的逻辑合同一致,物理实现按 provider 区分;分段或规范化历史不得改变 + head/readback 语义。 ### `main` 已交付(2026-09-03) @@ -1587,6 +1642,47 @@ characterization 阶段的负向用例在原清单上补充:软认领盖掉活 - fence 集成:每个 Python Todo mutation 与每个 native task-lease acquire/renew/transfer/release 都在自己的锁内检查持久 fence;fence 不存在时零运行时 调用;fence 存在但不可读或无效时 fail closed。 +- 完整 Todo read model:`loopx_todo_canonical_read_record_v0` 发布带版本字段 manifest; + TypeScript projection 拒绝 replacement 丢弃既有记录中已经存在的字段。 + +### 规范性 promotion contract + +promotion state machine 与 provider kind 无关: + +1. 从经过评审的 goal-level authority binding 解析已资格化的 `AuthorityStore` profile + 与精确 store lineage。 +2. 在 legacy Todo 与 lease lock 内,验证同一个 source revision、projection digest、 + field-manifest version 与 provider cursor 上的持续 parity。 +3. engage 该 provider profile 的持久 writer fence,再以 compare-and-set 或 transaction + precondition 提交完整 canonical head 与 promotion receipt。file marker、NoKV document + identity、PostgreSQL row/table layout 都只是实现细节。 +4. 允许 provider-first 决策前,回读 binding、head、receipt、cursor、manifest 与 digest; + 任一不一致都 fail closed。 +5. 通过 transaction-bound outbox 渲染兼容投影。陈旧投影从 head 修复,绝不用于填补 + canonical field。 + +canonical head 保留 normalized Todo 与 lease record 中每个已经合法存在的字段;field +manifest 属于 qualification 与 parity 合同。upsert 要么提交完整 replacement,要么使用 +clear operation 显式的 typed patch。未知新增字段在 manifest 升版前 fail closed。删除 +提案必须逐字段枚举 producer、reader、writer、persisted fixture、静态引用、历史版本与 +已知外部 consumer;写清 migration、downgrade、rollback 与 semantic-equivalence 论证; +并获得 maintainer 显式批准。code search 没有发现当前 reader,不构成删除理由。 + +完整性边界不包含 raw Markdown formatting、credential、host-local path、整个 registry、 +raw evidence body,也不包含 quota、run history、settlement、inbox、scheduler 或其他 +ledger 拥有的 state;这里只保存受各自合同治理的 reference。 + +共同 retention contract 保留最新完整 head、ordered cursor、原始 operation receipt、 +integrity chain、确定性 scan 与 recovery readback。物理 profile 可以不同: + +- **file:**create-only sealed history segment 加有界 head document; +- **NoKV:**经过资格验证、绑定 lineage 的 document/segment conditional publication + 与 recovery readback; +- **PostgreSQL:**事务追加的 history/receipt row 加 current head,并配套经过评审的 + index、partition、RLS 与 tenant context。 + +所有 profile 暴露相同逻辑结果与 field manifest。每个 profile 发布实测容量限制,并在 +无法保持合同的写入发生前返回 typed `store_capacity_exhausted`。 ### 晋升前仍缺 @@ -1599,9 +1695,11 @@ characterization 阶段的负向用例在原清单上补充:软认领盖掉活 同时关闭两者:prepared entry 在写者已持有的锁内写入,committed 标记在主写返回后 写入,有界 drain 把每条 entry 变成恰好一笔 `operation_id` 为 entry id 的 shadow 事务。此后 `qualify` 数的是 entry,不是采样。 -- read flip 之后的 prose 持久化(问题 10)与声明记录(问题 11)。 -- file profile 的保留、快速路径与容量(问题 12);参考执行器的删除与状态翻转 - (问题 13)。 +- provider-neutral authority binding、兼容投影 outbox,以及 file、NoKV、PostgreSQL + 的 conformance row。三个 provider 不必同时晋升,但每个 profile 都必须先通过同一 + 合同才具备资格。 +- 首个晋升 profile 的 retention、fast path 与实测 capacity;参考执行器的删除与 + status flip(问题 13)。 - 晋升后的 rollback:已交付的 rollback 隔离的是晋升前 lineage。首次权威写 (`authority_revision > 0`)之后的返回路径是问题 3 要求的经评审的 fenced export: 静止、空 projection outbox、把 head 导出到 Markdown 协调字段与 lease 终态记录、 @@ -1610,16 +1708,18 @@ characterization 阶段的负向用例在原清单上补充:软认领盖掉活 ### 增长是晋升前置 单文档 `retain_all_v0` 是二次方增长:TTL 600 秒、每 300 秒续约、三个活跃 todo 约 -等于每天 300 次、每月 9000 次 transition,在 15 KiB head 下约 140 MB 文档,且每条 -CLI 命令都要解析并哈希整个文件,超过 5 秒 effect 超时与 2 MiB 响应上限。因此问题 -12 先于任何真实 promotion;单文档全量保留只对 promotion bootstrap 与测试 goal 可接受。 +产生每天 864 次、30 天 25,920 次 transition。按 15 KiB 完整 head 估算,仅累计重写 +payload 已约 380 MiB,尚未计 receipt 与 envelope overhead;最终文档大小取决于 retained +record layout,不能由重写总量直接推断。这一频率已经要求逐 provider 验证 capacity、 +latency、response size 与 recovery。单文档全量保留只适用于 promotion bootstrap 与 +有界 test goal。 ### 顺序 -A. 在已交付 kernel 上做 provider-first CLI 路由与持锁 promotion orchestrator;B. -喂给 `coordination.runtime_shadow.commit` 的事务绑定捕获,并退役 #3818 的观察路径; -C. file-store 保留、快速路径与容量(问题 5 与 12);D. 声明记录与 prose projection -outbox,惰性发布,`authority_source` 恒为 `legacy_local`;E. promotion PR(把 -orchestrator 路由到 kernel、渲染投影、删除参考模块、翻 holds 与 stage 字面量、治理行 -与本 RFC 状态段)。E 与 D 中一切返回 `file_aggregate` 的路径都等 parity 半段合并且 -问题 8 到 14 得到回答。 +A. 用 transaction-bound capture 喂给 `coordination.runtime_shadow.commit`,退役重复 +observation lineage;B. 冻结并测试完整 Todo/lease manifest,以及 omission/explicit-clear +规则;C. 实现共同 authority binding 与 profile conformance,包括所选 provider 的 +retention/capacity;D. 在已交付 kernel 上加入 provider-first CLI routing、持锁 promotion +orchestrator 与兼容投影 outbox;E. 单独评审的 promotion PR 删除 reference-only 重复 +aggregate,翻转 hold 与 stage literal,并更新执行台账。任何 profile 只有在其精确实现与 +lineage 上通过 A-D 后才具备晋升资格。